add edit delete and input forms
This commit is contained in:
parent
8d964947d3
commit
57f9acfcdb
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"$schema": "@angular/cli/lib/config/schema.json",
|
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
|
||||||
"version": 1,
|
"version": 1,
|
||||||
"newProjectRoot": "projects",
|
"newProjectRoot": "projects",
|
||||||
"projects": {
|
"projects": {
|
||||||
|
|||||||
@ -1,10 +1,14 @@
|
|||||||
import { Routes } from '@angular/router';
|
import { Routes } from '@angular/router';
|
||||||
import { LoginComponent } from './components/login/login.component';
|
import { LoginComponent } from './components/login/login.component';
|
||||||
import { RegisterComponent } from './components/register/register.component';
|
import { RegisterComponent } from './components/register/register.component';
|
||||||
|
import { ListComponent } from './components/list/list.component';
|
||||||
|
import { CreateComponent } from './components/create/create.component';
|
||||||
|
|
||||||
export const routes: Routes = [
|
export const routes: Routes = [
|
||||||
{ path: '', redirectTo: '/login', pathMatch: 'full' },
|
{ path: '', redirectTo: '/login', pathMatch: 'full' },
|
||||||
{ path: 'login', component: LoginComponent },
|
{ path: 'login', component: LoginComponent },
|
||||||
{ path: 'register', component: RegisterComponent },
|
{ path: 'register', component: RegisterComponent },
|
||||||
|
{ path: 'list', component: ListComponent },
|
||||||
|
{ path: 'create', component: CreateComponent },
|
||||||
{ path: '**', redirectTo: '/login' },
|
{ path: '**', redirectTo: '/login' },
|
||||||
];
|
];
|
||||||
|
|||||||
98
src/app/components/create/create.component.css
Normal file
98
src/app/components/create/create.component.css
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
.create-card {
|
||||||
|
width: min(100% - 2rem, 700px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.glass-card {
|
||||||
|
margin: 2rem auto;
|
||||||
|
padding: 1.25rem;
|
||||||
|
border-radius: 1rem;
|
||||||
|
background: rgba(255, 255, 255, 0.18);
|
||||||
|
box-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.37);
|
||||||
|
backdrop-filter: blur(8px);
|
||||||
|
-webkit-backdrop-filter: blur(8px);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.18);
|
||||||
|
color: #111;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status {
|
||||||
|
padding: 0.75rem 1rem;
|
||||||
|
border-radius: 0.75rem;
|
||||||
|
background: rgba(255, 255, 255, 0.55);
|
||||||
|
margin-bottom: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status.error {
|
||||||
|
color: #8b0000;
|
||||||
|
background: rgba(255, 230, 230, 0.8);
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
margin: 0 0 1rem 0;
|
||||||
|
font-size: clamp(1.25rem, 1.5vw + 1rem, 2rem);
|
||||||
|
color: #000000b0;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form {
|
||||||
|
display: grid;
|
||||||
|
gap: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-row {
|
||||||
|
display: grid;
|
||||||
|
gap: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-row input {
|
||||||
|
padding: 0.6rem 0.75rem;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
font-size: 1rem;
|
||||||
|
transition:
|
||||||
|
background-color 120ms ease,
|
||||||
|
color 120ms ease,
|
||||||
|
border-color 120ms ease,
|
||||||
|
box-shadow 120ms ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Bessere Lesbarkeit beim Fokussieren */
|
||||||
|
.form-row input:focus,
|
||||||
|
.form-row input:focus-visible {
|
||||||
|
background: #ffffff;
|
||||||
|
color: #000000;
|
||||||
|
outline: none;
|
||||||
|
border-color: #000000;
|
||||||
|
box-shadow: 0 0 0 3px rgba(0, 0, 0, 0.08);
|
||||||
|
caret-color: #000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-row input::placeholder {
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-row input:focus::placeholder {
|
||||||
|
color: #888;
|
||||||
|
}
|
||||||
|
|
||||||
|
.actions {
|
||||||
|
margin-top: 0.75rem;
|
||||||
|
display: flex;
|
||||||
|
gap: 0.5rem;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
padding: 0.6rem 1rem;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn.primary {
|
||||||
|
background: linear-gradient(135deg, #ff0048, #e03164);
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn.secondary {
|
||||||
|
background: rgba(255, 255, 255, 0.6);
|
||||||
|
}
|
||||||
42
src/app/components/create/create.component.html
Normal file
42
src/app/components/create/create.component.html
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
<main class="login-container">
|
||||||
|
<div class="glass-card create-card">
|
||||||
|
<h2 class="title">Neuer Kantinen-Eintrag</h2>
|
||||||
|
|
||||||
|
<div *ngIf="errorMessage" class="status error">{{ errorMessage }}</div>
|
||||||
|
|
||||||
|
<form (ngSubmit)="save()" class="form">
|
||||||
|
<label class="form-row">
|
||||||
|
<span>Datum</span>
|
||||||
|
<input type="date" [(ngModel)]="byeDate" name="byeDate" required />
|
||||||
|
</label>
|
||||||
|
<label class="form-row">
|
||||||
|
<span>Betrag (€)</span>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
step="0.01"
|
||||||
|
[(ngModel)]="betrag"
|
||||||
|
name="betrag"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<label class="form-row">
|
||||||
|
<span>Belegname</span>
|
||||||
|
<input type="text" [(ngModel)]="belegName" name="belegName" />
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<div class="actions">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="btn secondary"
|
||||||
|
(click)="goToList()"
|
||||||
|
[disabled]="loading"
|
||||||
|
>
|
||||||
|
Abbrechen
|
||||||
|
</button>
|
||||||
|
<button type="submit" class="btn primary" [disabled]="loading">
|
||||||
|
{{ loading ? "Speichere…" : "Speichern" }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
51
src/app/components/create/create.component.ts
Normal file
51
src/app/components/create/create.component.ts
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
import { Component } from '@angular/core';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { FormsModule } from '@angular/forms';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
import { AppwriteService } from '../../services/appwrite.service';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-create',
|
||||||
|
standalone: true,
|
||||||
|
imports: [CommonModule, FormsModule],
|
||||||
|
templateUrl: './create.component.html',
|
||||||
|
styleUrl: './create.component.css',
|
||||||
|
})
|
||||||
|
export class CreateComponent {
|
||||||
|
constructor(
|
||||||
|
private appwrite: AppwriteService,
|
||||||
|
private router: Router,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
byeDate = '';
|
||||||
|
betrag: number | null = null;
|
||||||
|
belegName = '';
|
||||||
|
loading = false;
|
||||||
|
errorMessage: string | null = null;
|
||||||
|
|
||||||
|
async save(): Promise<void> {
|
||||||
|
if (!this.byeDate || this.betrag === null || Number.isNaN(this.betrag)) {
|
||||||
|
this.errorMessage = 'Bitte Datum und Betrag korrekt ausfüllen.';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.loading = true;
|
||||||
|
this.errorMessage = null;
|
||||||
|
try {
|
||||||
|
await this.appwrite.createDocument({
|
||||||
|
ByeDate: this.byeDate,
|
||||||
|
Betrag: this.betrag,
|
||||||
|
BelegName: this.belegName,
|
||||||
|
});
|
||||||
|
this.router.navigate(['/list']);
|
||||||
|
} catch (e: any) {
|
||||||
|
console.error(e);
|
||||||
|
this.errorMessage = e?.message ?? 'Fehler beim Speichern.';
|
||||||
|
} finally {
|
||||||
|
this.loading = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
goToList(): void {
|
||||||
|
this.router.navigate(['/list']);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,13 +0,0 @@
|
|||||||
|
|
||||||
.headerText {
|
|
||||||
text-align: center;
|
|
||||||
color: rgb(42, 42, 88);
|
|
||||||
font-weight: bold;
|
|
||||||
font-size: 1.75rem;
|
|
||||||
font-family: "Libre Baskerville", serif;
|
|
||||||
}
|
|
||||||
|
|
||||||
i{
|
|
||||||
color: rgb(42, 42, 88);
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
@ -1,8 +0,0 @@
|
|||||||
<div class="flex flex-row justify-between items-center bg-red-500/80 py-4 px-2">
|
|
||||||
<h2 class="headerText pl-4 ">Kegel Spielplan</h2>
|
|
||||||
<div>
|
|
||||||
<i class="fa-solid fa-file-lines fa-2xl pr-10" (click)="goToSummery()"></i>
|
|
||||||
<i class="fa-solid fa-file-circle-plus fa-2xl pr-10" (click)="goToInput()" ></i>
|
|
||||||
<i class="fa-solid fa-right-from-bracket fa-2xl pr-10" (click)="logout()"></i>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
@ -1,40 +0,0 @@
|
|||||||
import { Component } from '@angular/core';
|
|
||||||
import { AppwriteService } from '../../services/appwrite.service';
|
|
||||||
import { Router } from '@angular/router';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-header',
|
|
||||||
standalone: true,
|
|
||||||
imports: [],
|
|
||||||
templateUrl: './header.component.html',
|
|
||||||
styleUrl: './header.component.css',
|
|
||||||
})
|
|
||||||
export class HeaderComponentComponent {
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
private appwriteService: AppwriteService,
|
|
||||||
private routes: Router,
|
|
||||||
) {}
|
|
||||||
|
|
||||||
goToSummery() {
|
|
||||||
// this.routes.navigate(['/list']);
|
|
||||||
}
|
|
||||||
|
|
||||||
goToInput() {
|
|
||||||
// this.routes.navigate(['/input']);
|
|
||||||
}
|
|
||||||
|
|
||||||
async logout() {
|
|
||||||
try {
|
|
||||||
const accountService = await this.appwriteService.accountService;
|
|
||||||
await this.appwriteService.accountService.deleteSession(
|
|
||||||
(await accountService.getSession('current')).$id,
|
|
||||||
);
|
|
||||||
// Bei erfolgreicher Abmeldung
|
|
||||||
//this.toast.success('Logout erfolgreich!', 'Auf Wiedersehen!');
|
|
||||||
this.routes.navigate(['/login']);
|
|
||||||
} catch (err: any) {
|
|
||||||
console.error('Fehler beim Abmelden:', err.message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
164
src/app/components/list/list.component.css
Normal file
164
src/app/components/list/list.component.css
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
/* Glas/Glassmorphism Card auf demselben Hintergrund wie Login (login-container liefert BG) */
|
||||||
|
.glass-card {
|
||||||
|
width: min(100% - 2rem, 1000px);
|
||||||
|
margin: 2rem auto;
|
||||||
|
padding: 1.25rem;
|
||||||
|
border-radius: 1rem;
|
||||||
|
background: rgba(255, 255, 255, 0.18);
|
||||||
|
box-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.37);
|
||||||
|
backdrop-filter: blur(8px);
|
||||||
|
-webkit-backdrop-filter: blur(8px);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.18);
|
||||||
|
color: #111;
|
||||||
|
}
|
||||||
|
|
||||||
|
.glass-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 1rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.glass-header h2 {
|
||||||
|
margin: 0;
|
||||||
|
font-size: clamp(1.25rem, 1.5vw + 1rem, 2rem);
|
||||||
|
color: #000000b0;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Scrollbarer Bereich, der sich dynamisch anpasst */
|
||||||
|
.glass-scroll {
|
||||||
|
max-height: min(70vh, 800px);
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tabelle */
|
||||||
|
.list-table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-table th,
|
||||||
|
.list-table td {
|
||||||
|
text-align: left;
|
||||||
|
padding: 0.75rem 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-table thead th {
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
background: rgba(255, 255, 255, 0.4);
|
||||||
|
backdrop-filter: blur(8px);
|
||||||
|
-webkit-backdrop-filter: blur(8px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-table tbody tr:nth-child(even) {
|
||||||
|
background: rgba(255, 255, 255, 0.25);
|
||||||
|
}
|
||||||
|
|
||||||
|
.actions-col {
|
||||||
|
width: 1%;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row-actions {
|
||||||
|
display: inline-flex;
|
||||||
|
gap: 0.35rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-btn {
|
||||||
|
appearance: none;
|
||||||
|
border: none;
|
||||||
|
width: 2rem;
|
||||||
|
height: 2rem;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
cursor: pointer;
|
||||||
|
background: rgba(255, 255, 255, 0.8);
|
||||||
|
color: #000;
|
||||||
|
transition:
|
||||||
|
transform 120ms ease,
|
||||||
|
background-color 120ms ease,
|
||||||
|
filter 120ms ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-btn:hover {
|
||||||
|
transform: translateY(-1px);
|
||||||
|
filter: brightness(1.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-btn.edit {
|
||||||
|
background: rgba(255, 255, 255, 0.85);
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-btn.delete {
|
||||||
|
background: #ff0048;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-btn.delete:hover {
|
||||||
|
background: #e03164;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status {
|
||||||
|
padding: 0.75rem 1rem;
|
||||||
|
border-radius: 0.75rem;
|
||||||
|
background: rgba(255, 255, 255, 0.55);
|
||||||
|
}
|
||||||
|
|
||||||
|
.status.error {
|
||||||
|
color: #8b0000;
|
||||||
|
background: rgba(255, 230, 230, 0.8);
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty {
|
||||||
|
padding: 1rem;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
.glass-card {
|
||||||
|
margin: 1rem auto;
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
.list-table th,
|
||||||
|
.list-table td {
|
||||||
|
padding: 0.5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Floating Action Button */
|
||||||
|
.fab {
|
||||||
|
position: fixed;
|
||||||
|
right: 1.25rem;
|
||||||
|
bottom: 1.25rem;
|
||||||
|
width: clamp(3rem, 4vw + 2rem, 4rem);
|
||||||
|
height: clamp(3rem, 4vw + 2rem, 4rem);
|
||||||
|
border-radius: 9999px;
|
||||||
|
border: none;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background: linear-gradient(135deg, #ff0048, #e03164);
|
||||||
|
color: #fff;
|
||||||
|
font-size: clamp(1.5rem, 2vw + 1rem, 2rem);
|
||||||
|
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.25);
|
||||||
|
cursor: pointer;
|
||||||
|
transition:
|
||||||
|
transform 0.15s ease,
|
||||||
|
box-shadow 0.15s ease,
|
||||||
|
filter 0.15s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fab:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 14px 28px rgba(0, 0, 0, 0.3);
|
||||||
|
filter: brightness(1.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.fab:active {
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
66
src/app/components/list/list.component.html
Normal file
66
src/app/components/list/list.component.html
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
<main class="login-container">
|
||||||
|
<div class="glass-card">
|
||||||
|
<div class="glass-header">
|
||||||
|
<h2>Kantinenliste</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div *ngIf="loading" class="status">Lade Daten…</div>
|
||||||
|
<div *ngIf="!loading && errorMessage" class="status error">
|
||||||
|
{{ errorMessage }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div *ngIf="!loading && !errorMessage" class="glass-scroll">
|
||||||
|
<table class="list-table" *ngIf="kantinList?.length; else noEntries">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Datum</th>
|
||||||
|
<th>Betrag</th>
|
||||||
|
<th>Beleg</th>
|
||||||
|
<th class="actions-col">Aktionen</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr *ngFor="let k of kantinList; trackBy: trackById">
|
||||||
|
<td>{{ k.szByeDate }}</td>
|
||||||
|
<td>{{ k.mnBetrag | number: "1.2-2" }} €</td>
|
||||||
|
<td>{{ k.szBelegName }}</td>
|
||||||
|
<td>
|
||||||
|
<div class="row-actions">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="icon-btn edit"
|
||||||
|
(click)="onEdit(k)"
|
||||||
|
title="Bearbeiten"
|
||||||
|
aria-label="Bearbeiten"
|
||||||
|
>
|
||||||
|
<i class="fa-solid fa-pen"></i>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="icon-btn delete"
|
||||||
|
(click)="onDelete(k)"
|
||||||
|
title="Löschen"
|
||||||
|
aria-label="Löschen"
|
||||||
|
>
|
||||||
|
<i class="fa-solid fa-trash"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<ng-template #noEntries>
|
||||||
|
<div class="empty">Keine Einträge vorhanden.</div>
|
||||||
|
</ng-template>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- Floating Action Button für neuen Eintrag -->
|
||||||
|
<button
|
||||||
|
class="fab"
|
||||||
|
(click)="goToCreate()"
|
||||||
|
aria-label="Neuen Eintrag hinzufügen"
|
||||||
|
title="Neuen Eintrag hinzufügen"
|
||||||
|
>
|
||||||
|
<span>+</span>
|
||||||
|
</button>
|
||||||
|
</main>
|
||||||
91
src/app/components/list/list.component.ts
Normal file
91
src/app/components/list/list.component.ts
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { AppwriteService } from '../../services/appwrite.service';
|
||||||
|
import { FormsModule } from '@angular/forms';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
import { Kantin } from '../../models/kantin.model';
|
||||||
|
import { firstValueFrom } from 'rxjs';
|
||||||
|
import { Query } from 'appwrite';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-list',
|
||||||
|
imports: [CommonModule, FormsModule],
|
||||||
|
templateUrl: './list.component.html',
|
||||||
|
styleUrl: './list.component.css',
|
||||||
|
})
|
||||||
|
export class ListComponent implements OnInit {
|
||||||
|
constructor(
|
||||||
|
private router: Router,
|
||||||
|
private appwriteService: AppwriteService,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public kantinList: Kantin[] = [];
|
||||||
|
public loading = false;
|
||||||
|
public errorMessage: string | null = null;
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
// Async Logik in eigene Methode ausgelagert
|
||||||
|
void this.loadRecords();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async loadRecords(): Promise<void> {
|
||||||
|
this.loading = true;
|
||||||
|
this.errorMessage = null;
|
||||||
|
try {
|
||||||
|
// getRecords() liefert ein Observable -> mit firstValueFrom einmalig auflösen
|
||||||
|
// Serverseitiges Sortieren: neueste Einträge zuerst nach ByeDate
|
||||||
|
const documents = await firstValueFrom(
|
||||||
|
this.appwriteService.getRecords([Query.orderDesc('ByeDate')]),
|
||||||
|
);
|
||||||
|
this.kantinList = (documents ?? []).map(
|
||||||
|
(doc: any) =>
|
||||||
|
new Kantin(
|
||||||
|
doc?.$id ?? '',
|
||||||
|
doc?.ByeDate ?? '',
|
||||||
|
Number(doc?.Betrag ?? 0),
|
||||||
|
doc?.BelegName ?? '',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} catch (error: any) {
|
||||||
|
console.error('Fehler beim Laden der Kantin-Einträge:', error);
|
||||||
|
this.errorMessage = error?.message ?? 'Unbekannter Fehler beim Laden.';
|
||||||
|
} finally {
|
||||||
|
this.loading = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// trackBy für *ngFor Performance
|
||||||
|
trackById(index: number, item: Kantin): string {
|
||||||
|
return item.szDocumentId ?? index.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Navigation zu einer (noch zu erstellenden) Create-Ansicht
|
||||||
|
goToCreate(): void {
|
||||||
|
this.router.navigate(['/create']);
|
||||||
|
}
|
||||||
|
|
||||||
|
onEdit(item: Kantin): void {
|
||||||
|
// TODO: Edit-Ansicht bauen. Bis dahin könnte Create mit Prefill genutzt werden.
|
||||||
|
this.router.navigate(['/create'], {
|
||||||
|
queryParams: {
|
||||||
|
id: item.szDocumentId,
|
||||||
|
date: item.szByeDate,
|
||||||
|
amount: item.mnBetrag,
|
||||||
|
name: item.szBelegName,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async onDelete(item: Kantin): Promise<void> {
|
||||||
|
const confirmed = window.confirm('Diesen Eintrag wirklich löschen?');
|
||||||
|
if (!confirmed) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this.appwriteService.deleteDocument(item.szDocumentId);
|
||||||
|
await this.loadRecords();
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Löschen fehlgeschlagen', e);
|
||||||
|
this.errorMessage = 'Löschen fehlgeschlagen.';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -52,7 +52,7 @@ detailHeight: number = 0;
|
|||||||
this.sessionId = response.$id;
|
this.sessionId = response.$id;
|
||||||
this.email = '';
|
this.email = '';
|
||||||
this.password = '';
|
this.password = '';
|
||||||
//this.router.navigate(['/input']);
|
this.router.navigate(['/list']);
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
console.error('Fehler beim Anmelden: ' + error.message);
|
console.error('Fehler beim Anmelden: ' + error.message);
|
||||||
alert('Fehler beim Anmelden: ' + error.message);
|
alert('Fehler beim Anmelden: ' + error.message);
|
||||||
|
|||||||
19
src/app/models/kantin.model.ts
Normal file
19
src/app/models/kantin.model.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
export class Kantin {
|
||||||
|
public szDocumentId: string;
|
||||||
|
public szByeDate: string;
|
||||||
|
public mnBetrag: number;
|
||||||
|
public szBelegName: string;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
documentId: string,
|
||||||
|
byeDate: string,
|
||||||
|
betrag: number,
|
||||||
|
belegName: string
|
||||||
|
) {
|
||||||
|
this.szDocumentId = documentId;
|
||||||
|
this.szByeDate = byeDate;
|
||||||
|
this.mnBetrag = betrag;
|
||||||
|
this.szBelegName = belegName;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user