fix theming + ui improvements

This commit is contained in:
toni
2025-12-25 20:56:05 +01:00
parent e15be9c7e3
commit 17c0ff208b
14 changed files with 559 additions and 422 deletions
@@ -1,14 +1,26 @@
<h1>Startseite</h1>
<ul>
<!--TODO fix theming/colors-->
<button matButton="outlined" class="warn-button" [routerLink]="['/test']">
Entwichlungsinformationen
</button>
<button matButton="filled" color="primary" [routerLink]="['/list']">Mitglieder</button>
<button matButton="filled" color="warn" [routerLink]="['/manageUsers']">
Benutzer Verwalten
</button>
<button matButton="filled" color="primary" [routerLink]="['/exam']">
Prüfungsanmeldung
</button>
</ul>
<mat-list>
<mat-list-item [claimGuard]="Claim.Memberadmin">
<button matButton="filled" color="primary" [routerLink]="['/list']">
Mitgliederliste
</button>
</mat-list-item>
<mat-list-item [claimGuard]="Claim.Useradmin">
<button matButton="filled" color="warn" [routerLink]="['/manageUsers']">
Benutzer Verwalten
</button>
</mat-list-item>
<mat-list-item [claimGuard]="Claim.Memberadmin">
<button matButton="filled" color="primary" [routerLink]="['/exam']">
Prüfungsanmeldung
</button>
</mat-list-item>
<mat-list-item>
<button
matButton="outlined"
class="warn-button"
[routerLink]="['/test']">
Entwichlungsinformationen
</button>
</mat-list-item>
</mat-list>
@@ -1,13 +1,21 @@
import { Component } from '@angular/core';
import { MatButtonModule } from '@angular/material/button';
import { MatListModule } from '@angular/material/list';
import { RouterModule } from '@angular/router';
import { ClaimGuardDirective } from '../../directives/claim-guard.directive';
import { Claim } from '../../generated-api/api';
@Component({
selector: 'app-home',
imports: [RouterModule, MatButtonModule],
templateUrl: './home.html',
styleUrl: './home.scss',
selector: 'app-home',
imports: [
RouterModule,
MatButtonModule,
MatListModule,
ClaimGuardDirective,
],
templateUrl: './home.html',
styleUrl: './home.scss',
})
export class Home {
protected Claim = Claim;
}
@@ -1,142 +1,180 @@
<h1>Mitglieder & Sportlerübersicht</h1>
<button mat-raised-button color="accent" (click)="newMember()">
Neues Mitglied
</button>
<div class="filter-container mat-elevation-z8">
<div class="filter-header">
<mat-form-field>
<label>Filter:</label>
<div>
<h1>Mitglieder & Sportlerübersicht</h1>
</div>
<div>
<button matButton="filled" class="primary-btn" (click)="newMember()">
Neues Mitglied
</button>
</div>
<div class="filter-container">
<mat-form-field>
<label>Filter:</label>
<input matInput [formControl]="filterControl" placeholder="Filter" />
</mat-form-field>
<div class="status-filters">
<mat-radio-group [formControl]="statusControl">
<mat-radio-button [value]="MemberStatusFilter.ACTIVE"
>Aktiv</mat-radio-button
>
<mat-radio-button [value]="MemberStatusFilter.INACTIVE"
>Inaktiv</mat-radio-button
>
<mat-radio-button [value]="MemberStatusFilter.ALL"
>Alle</mat-radio-button
>
</mat-radio-group>
<mat-form-field
matTooltip="Zeigt nur Mitglieder, die zum angegebenen Datum den ausgewählten status hatten"
matTooltipPosition="above">
<label>Zum Datum:</label>
<input
matInput
[formControl]="filterControl"
placeholder="Filter" />
[matDatepicker]="picker"
[formControl]="statusDateControl"
placeholder="Aktiv bis" />
<mat-hint>DD.MM.YYYY</mat-hint>
<mat-datepicker-toggle
matIconSuffix
[disabled]="statusControl.value===MemberStatusFilter.ALL"
[for]="picker"></mat-datepicker-toggle>
<mat-datepicker #picker></mat-datepicker>
</mat-form-field>
<div class="status-filters">
<mat-radio-group [formControl]="statusControl">
<mat-radio-button [value]="MemberStatusFilter.ACTIVE"
>Aktiv</mat-radio-button
>
<mat-radio-button [value]="MemberStatusFilter.INACTIVE"
>Inaktiv</mat-radio-button
>
<mat-radio-button [value]="MemberStatusFilter.ALL"
>Alle</mat-radio-button
>
</mat-radio-group>
<mat-form-field
matTooltip="Zeigt nur Mitglieder, die zum angegebenen Datum den ausgewählten status hatten"
matTooltipPosition="above">
<label>Zum Datum:</label>
<input
matInput
[matDatepicker]="picker"
[formControl]="statusDateControl"
[disabled]="statusControl.value===MemberStatusFilter.ALL"
placeholder="Aktiv bis" />
<mat-hint>DD.MM.YYYY</mat-hint>
<mat-datepicker-toggle
matIconSuffix
[disabled]="statusControl.value===MemberStatusFilter.ALL"
[for]="picker"></mat-datepicker-toggle>
<mat-datepicker #picker></mat-datepicker>
</mat-form-field>
</div>
<mat-checkbox [formControl]="onlyWithID"
>Nur mit Passnummer</mat-checkbox
>
</div>
<mat-checkbox [formControl]="onlyWithID">Nur mit Passnummer</mat-checkbox>
</div>
<div class="table-container">
<!--TODO implement row count, selecting and alternating colors-->
<table
mat-table
[dataSource]="dataSource"
class="member-table"
matSort
matSortActive="vorname"
matSortDirection="asc"
matSortDisableClear>
<ng-container matColumnDef="edit">
<th mat-header-cell *matHeaderCellDef>Bearb.</th>
<td mat-cell *matCellDef="let element">
<button
mat-mini-fab-button
(click)="editMember(element.id)"
aria-label="Edit Member">
<mat-icon fontIcon="edit"></mat-icon>
</button>
</td>
</ng-container>
<!--TODO implement row count, selecting and alternating colors-->
<table mat-table matSort [dataSource]="dataSource" class="mat-elevation-z8">
<ng-container matColumnDef="edit">
<th mat-header-cell *matHeaderCellDef>Bearb.</th>
<td mat-cell *matCellDef="let element">
<button
mat-mini-fab-button
(click)="editMember(element.id)"
aria-label="Edit Member">
<mat-icon fontIcon="edit"></mat-icon>
</button>
</td>
</ng-container>
<ng-container matColumnDef="delete">
<th mat-header-cell *matHeaderCellDef>Entf.</th>
<td mat-cell *matCellDef="let element">
<button
mat-mini-fab-button
(click)="deleteMember(element.id)"
aria-label="Delete Member">
<mat-icon fontIcon="delete"></mat-icon>
</button>
</td>
</ng-container>
<ng-container matColumnDef="vorname">
<th mat-header-cell mat-sort-header *matHeaderCellDef>Vorname</th>
<td mat-cell *matCellDef="let element">
<span [innerHTML]="element.vorname | stylizeNames"></span>
</td>
</ng-container>
<ng-container matColumnDef="nachname">
<th mat-header-cell mat-sort-header *matHeaderCellDef>Nachname</th>
<td mat-cell *matCellDef="let element">
<span [innerHTML]="element.nachname | stylizeNames"></span>
</td>
</ng-container>
<ng-container matColumnDef="geschlecht">
<th mat-header-cell mat-sort-header *matHeaderCellDef>Geschlecht</th>
<td mat-cell *matCellDef="let element">{{ element.geschlecht }}</td>
</ng-container>
<ng-container matColumnDef="passnummer">
<th mat-header-cell mat-sort-header *matHeaderCellDef>Passnummer</th>
<td mat-cell *matCellDef="let element">
{{ element.passnummer | nobreakHyphen }}
</td>
</ng-container>
<ng-container matColumnDef="geburtsdatum">
<th mat-header-cell mat-sort-header *matHeaderCellDef>Geburtsdatum</th>
<td mat-cell *matCellDef="let element">
{{ element.geburtsdatum | datePipe }}
</td>
</ng-container>
<ng-container matColumnDef="geburtsort">
<th mat-header-cell mat-sort-header *matHeaderCellDef>Geburtsort</th>
<td mat-cell *matCellDef="let element">{{ element.geburtsort }}</td>
</ng-container>
<ng-container matColumnDef="verein">
<th mat-header-cell mat-sort-header *matHeaderCellDef>
Vereinsmitgliedschaft
</th>
<td mat-cell *matCellDef="let element">
@if(element.verein){
<mat-icon
class="positive"
aria-label="Verein Check"
fontIcon="check"></mat-icon>
}@else {
<ng-container matColumnDef="index" sticky>
<th mat-header-cell *matHeaderCellDef>Nr.</th>
<td mat-cell *matCellDef="let element; let i=index">{{i+1}}</td>
</ng-container>
<mat-icon
class="negative"
aria-label="Verein Cross"
fontIcon="close"></mat-icon>
}
</td>
</ng-container>
<ng-container matColumnDef="vorname">
<th mat-header-cell mat-sort-header *matHeaderCellDef>Vorname</th>
<td mat-cell *matCellDef="let element">
<span [innerHTML]="element.vorname | stylizeNames"></span>
</td>
</ng-container>
<ng-container matColumnDef="nachname">
<th mat-header-cell mat-sort-header *matHeaderCellDef>Nachname</th>
<td mat-cell *matCellDef="let element">
<span [innerHTML]="element.nachname | stylizeNames"></span>
</td>
</ng-container>
<ng-container matColumnDef="geschlecht">
<th mat-header-cell mat-sort-header *matHeaderCellDef>
Geschlecht
</th>
<td mat-cell *matCellDef="let element">{{ element.geschlecht }}</td>
</ng-container>
<ng-container matColumnDef="passnummer">
<th mat-header-cell mat-sort-header *matHeaderCellDef>
Passnummer
</th>
<td mat-cell *matCellDef="let element">
{{ element.passnummer | nobreakHyphen }}
</td>
</ng-container>
<ng-container matColumnDef="geburtsdatum">
<th mat-header-cell mat-sort-header *matHeaderCellDef>
Geburtsdatum
</th>
<td mat-cell *matCellDef="let element">
{{ element.geburtsdatum | datePipe }}
</td>
</ng-container>
<ng-container matColumnDef="geburtsort">
<th mat-header-cell mat-sort-header *matHeaderCellDef>
Geburtsort
</th>
<td mat-cell *matCellDef="let element">{{ element.geburtsort }}</td>
</ng-container>
<ng-container matColumnDef="verein">
<th mat-header-cell mat-sort-header *matHeaderCellDef>
Vereinsmitgliedschaft
</th>
<td mat-cell *matCellDef="let element">
@if(element.verein){
<mat-icon
class="positive"
aria-label="Verein Check"
fontIcon="check"></mat-icon>
}@else {
<ng-container matColumnDef="kontakt">
<th mat-header-cell *matHeaderCellDef>Kontakt</th>
<td mat-cell *matCellDef="let element">{{ element.kontakt }}</td>
</ng-container>
<mat-icon
class="negative"
aria-label="Verein Cross"
fontIcon="close"></mat-icon>
}
</td>
</ng-container>
<ng-container matColumnDef="kuendigungzum">
<th mat-header-cell mat-sort-header *matHeaderCellDef>
K&uuml;ndigung zum
</th>
<td mat-cell *matCellDef="let element">
{{ (element.kuendigungzum | datePipe) ?? "-" }}
</td>
</ng-container>
<ng-container matColumnDef="kontakt">
<th mat-header-cell *matHeaderCellDef>Kontakt</th>
<td mat-cell *matCellDef="let element">{{ element.kontakt }}</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns"></tr>
</table>
<ng-container matColumnDef="kuendigungzum">
<th mat-header-cell mat-sort-header *matHeaderCellDef>
K&uuml;ndigung zum
</th>
<td mat-cell *matCellDef="let element">
{{ (element.kuendigungzum | datePipe) ?? "-" }}
</td>
</ng-container>
<ng-container matColumnDef="delete">
<th mat-header-cell *matHeaderCellDef>Entf.</th>
<td mat-cell *matCellDef="let element">
<button
mat-mini-fab-button
(click)="deleteMember(element.id)"
aria-label="Delete Member">
<mat-icon fontIcon="delete"></mat-icon>
</button>
</td>
</ng-container>
<ng-container matColumnDef="totalCount">
<td
colspan="100%"
mat-footer-cell
*matFooterCellDef
class="footer-entry">
Zeige {{dataSource.filteredData.length}} von
{{dataSource.data.length}} Mitgliedern
@if(dataSource.filteredData.length !== dataSource.data.length)
{(gefiltert) }
</td>
</ng-container>
<tr
mat-header-row
*matHeaderRowDef="displayedColumns; sticky: true"></tr>
<tr
mat-row
*matRowDef="let row; columns: displayedColumns"
class="table-row"></tr>
<tr mat-footer-row *matFooterRowDef="['totalCount']; sticky: true"></tr>
</table>
</div>
@@ -1,16 +1,33 @@
mat-radio-group {
@use "@angular/material" as mat;
:host {
display: flex;
flex-direction: column;
flex-flow: column;
height: 100%;
// overflow: auto;
}
.filter-header {
.filter-container {
display: flex;
flex-direction: row;
justify-content: space-evenly;
justify-content: start;
gap: 5vw;
flex-wrap: wrap;
}
.table-container {
overflow: auto;
border: 1px solid var(--mat-sys-outline);
min-height: 400px;
}
mat-form-field {
height: 100%;
display: inline-block;
}
mat-radio-group {
display: flex;
flex-direction: column;
}
.status-filters {
@@ -19,3 +36,16 @@ mat-form-field {
justify-content: center;
gap: 1rem;
}
td {
border: 1px solid var(--mat-sys-outline-variant);
border-collapse: collapse;
}
.table-row:nth-child(even) {
background-color: var(--mat-sys-surface-variant);
}
.table-row:nth-child(odd) {
background-color: var(--mat-sys-surface);
}
@@ -1,4 +1,5 @@
import { AfterViewInit, Component, viewChild } from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { FormControl, ReactiveFormsModule } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button';
import { MatCheckbox } from '@angular/material/checkbox';
@@ -62,7 +63,7 @@ export class ViewMembers implements AfterViewInit {
protected readonly displayedColumns: string[] = [
'edit',
'delete',
'index',
'vorname',
'nachname',
'geschlecht',
@@ -72,6 +73,7 @@ export class ViewMembers implements AfterViewInit {
'verein',
'kontakt',
'kuendigungzum',
'delete',
];
protected deleteMember(memberId: any) {
@@ -157,6 +159,7 @@ export class ViewMembers implements AfterViewInit {
return originalFilterPredicate(
{
...data,
id: null,
geburtsdatum: this.datePipe.transform(
data.geburtsdatum ?? undefined,
),
@@ -189,7 +192,22 @@ export class ViewMembers implements AfterViewInit {
const date = combined[2];
const onlyWithID = combined[3];
this.dataSource.filter = `${filter?.replaceAll(';', '') ?? ''};${status};${date?.toISOString()};${onlyWithID}`;
console.log('filter', this.dataSource.filter);
});
this.statusControl.valueChanges
.pipe(takeUntilDestroyed())
.subscribe(value => {
if (
value === MemberStatusFilter.ALL &&
this.statusDateControl.enabled
) {
return this.statusDateControl.disable();
} else if (
value !== MemberStatusFilter.ALL &&
this.statusDateControl.disabled
) {
return this.statusDateControl.enable();
}
});
this.updateData();
@@ -202,8 +220,6 @@ export class ViewMembers implements AfterViewInit {
private async updateData(): Promise<void> {
const members = await api.getMembers();
console.log('Members loaded');
console.dir(members);
this.dataSource.data = members;
}
}