This commit is contained in:
Bastian Wagner
2026-06-09 09:45:33 +02:00
commit 537c7cbbee
124 changed files with 27283 additions and 0 deletions

View File

@@ -0,0 +1,149 @@
<section class="list-detail-page">
<header class="detail-header">
<button mat-icon-button type="button" aria-label="Zurueck" (click)="backToLists()">
<mat-icon aria-hidden="true">arrow_back</mat-icon>
</button>
<div>
<h1>{{ list()?.name || (isCreateMode() ? 'Neue Liste' : 'Liste') }}</h1>
@if (isCreateMode()) {
<p>Liste anlegen</p>
} @else if (list()) {
<p>{{ checkedCount(list()!) }} / {{ list()!.items.length }} erledigt</p>
}
</div>
</header>
@if (loading()) {
<mat-card class="state-card" appearance="outlined">
<mat-card-content>
<mat-progress-spinner mode="indeterminate" diameter="40" />
<h2>Liste wird geladen</h2>
</mat-card-content>
</mat-card>
} @else if (errorMessage()) {
<mat-card class="state-card error-state" appearance="outlined">
<mat-card-content>
<mat-icon aria-hidden="true">error</mat-icon>
<h2>Liste konnte nicht geladen werden</h2>
<p>{{ errorMessage() }}</p>
<button mat-stroked-button type="button" (click)="loadList()">
<mat-icon aria-hidden="true">refresh</mat-icon>
Erneut laden
</button>
</mat-card-content>
</mat-card>
} @else {
<mat-card class="editor-card" appearance="outlined">
<mat-card-header>
<mat-card-title>Details</mat-card-title>
</mat-card-header>
<mat-card-content>
<form [formGroup]="listForm" class="list-form" (ngSubmit)="saveList()">
<mat-form-field appearance="outline">
<mat-label>Titel</mat-label>
<input matInput formControlName="name" autocomplete="off" />
@if (listForm.controls.name.hasError('required')) {
<mat-error>Titel ist erforderlich.</mat-error>
}
</mat-form-field>
<mat-form-field appearance="outline">
<mat-label>Beschreibung</mat-label>
<textarea matInput formControlName="description" rows="4"></textarea>
</mat-form-field>
<button mat-flat-button type="submit" [disabled]="saving()">
@if (saving()) {
<mat-progress-spinner mode="indeterminate" diameter="18" />
} @else {
<mat-icon aria-hidden="true">save</mat-icon>
}
{{ isCreateMode() ? 'Liste anlegen' : 'Speichern' }}
</button>
</form>
</mat-card-content>
</mat-card>
<mat-card class="items-card" appearance="outlined">
<mat-card-header>
<mat-card-title>Items</mat-card-title>
<mat-card-subtitle>
@if (canEditItems()) {
{{ list()?.items?.length || 0 }} Eintraege
} @else {
Nach dem Speichern verfuegbar
}
</mat-card-subtitle>
</mat-card-header>
<mat-card-content>
<form [formGroup]="itemForm" class="item-form" (ngSubmit)="addItem()">
<mat-form-field appearance="outline">
<mat-label>Neues Item</mat-label>
<input matInput formControlName="title" autocomplete="off" [disabled]="!canEditItems()" />
@if (itemForm.controls.title.hasError('required')) {
<mat-error>Item-Titel ist erforderlich.</mat-error>
}
</mat-form-field>
<mat-checkbox formControlName="required">Pflicht</mat-checkbox>
<button mat-flat-button type="submit" [disabled]="addingItem() || !canEditItems()">
@if (addingItem()) {
<mat-progress-spinner mode="indeterminate" diameter="18" />
} @else {
<mat-icon aria-hidden="true">add</mat-icon>
}
Hinzufuegen
</button>
</form>
@if (!canEditItems()) {
<div class="inline-empty">
<mat-icon aria-hidden="true">save</mat-icon>
<span>Speichere die Liste, bevor du Items hinzufuegst.</span>
</div>
} @else if (list()?.items?.length) {
<ul class="check-items">
@for (item of list()!.items; track item.id) {
<li [class.checked]="item.checked">
<mat-checkbox
[checked]="item.checked"
[disabled]="updatingItemId() === item.id"
(change)="toggleItem(item, $event.checked)"
>
<span class="item-title">{{ item.title }}</span>
</mat-checkbox>
@if (updatingItemId() === item.id) {
<mat-progress-spinner mode="indeterminate" diameter="18" />
}
@if (item.checked && item.checkedAt && item.checkedByName) {
<div class="check-meta">
<mat-icon aria-hidden="true">verified</mat-icon>
<span>
Abgehakt von {{ item.checkedByName }} am
{{ item.checkedAt | date: 'dd.MM.yyyy, HH:mm' }}
</span>
</div>
}
</li>
}
</ul>
} @else {
<div class="inline-empty">
<mat-icon aria-hidden="true">playlist_add</mat-icon>
<span>Noch keine Items.</span>
</div>
}
</mat-card-content>
</mat-card>
<a mat-button routerLink="/lists" class="secondary-back">
<mat-icon aria-hidden="true">arrow_back</mat-icon>
Zur Listenuebersicht
</a>
}
</section>