import legacy frontend & fix dependency issues

This commit is contained in:
tw-blue
2025-12-07 12:51:57 +01:00
parent 91ac875a32
commit cd6cfec7f3
52 changed files with 3963 additions and 267 deletions
@@ -0,0 +1,13 @@
<form [formGroup]="form" (ngSubmit)="submit()">
<p>Name: {{ member.vorname }} {{ member.nachname }}</p>
<p>Passnummer: {{ member.passnummer }}</p>
<p>Geburtsdatum: {{ member.geburtsdatum??undefined | datePipe }}</p>
<mat-form-field>
<mat-label>K&uuml;ndigun zum</mat-label>
<input matInput [matDatepicker]="picker" formControlName="kuendigungzum" />
<mat-hint>DD.MM.YYYY</mat-hint>
<mat-datepicker-toggle matIconSuffix [for]="picker"></mat-datepicker-toggle>
<mat-datepicker #picker></mat-datepicker>
</mat-form-field>
<button mat-raised-button color="primary" type="submit">Speichern</button>
</form>
@@ -0,0 +1,11 @@
form{
display: flex;
flex-direction: column;
justify-content: space-evenly;
align-items: center;
height: 100%;
}
form > *{
margin-top: 20px;
}
@@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { DeleteMemberDialogComponent } from './delete-member-dialog.component.js';
describe('DeleteMemberDialogComponent', () => {
let component: DeleteMemberDialogComponent;
let fixture: ComponentFixture<DeleteMemberDialogComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [DeleteMemberDialogComponent]
})
.compileComponents();
fixture = TestBed.createComponent(DeleteMemberDialogComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
@@ -0,0 +1,57 @@
import { Component, Inject } from '@angular/core';
import {
FormControl,
FormGroup,
ReactiveFormsModule,
Validators,
} from '@angular/forms';
import { MatButtonModule } from '@angular/material/button';
import { MatCheckboxModule } from '@angular/material/checkbox';
import { MatOptionModule } from '@angular/material/core';
import { MatDatepickerModule } from '@angular/material/datepicker';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input';
import { MatSelectModule } from '@angular/material/select';
import * as api from '../../generated-api/api';
import { DatePipePipe } from '../../pipes/date-pipe.pipe.js';
import { EditMemberDialogComponent } from '../edit-member-dialog/edit-member-dialog.component.js';
@Component({
selector: 'app-delete-member-dialog',
standalone: true,
imports: [
ReactiveFormsModule,
MatFormFieldModule,
MatInputModule,
MatCheckboxModule,
MatButtonModule,
MatSelectModule,
MatOptionModule,
MatDatepickerModule,
DatePipePipe,
],
templateUrl: './delete-member-dialog.component.html',
styleUrl: './delete-member-dialog.component.scss',
})
export class DeleteMemberDialogComponent {
form: FormGroup;
member: api.Member;
constructor(
@Inject(MAT_DIALOG_DATA) public data: { member: api.Member },
private dialogRef: MatDialogRef<EditMemberDialogComponent>,
) {
this.member = data.member;
this.form = new FormGroup({
kuendigungzum: new FormControl('', [Validators.required]),
});
}
async submit() {
await api.scheduleDeletion(
this.member.id!,
this.form.controls['kuendigungzum'].value,
);
this.dialogRef.close();
}
}
@@ -0,0 +1,44 @@
<form [formGroup]="form" (ngSubmit)="submit()">
<mat-form-field>
<mat-label>Vorname</mat-label>
<input matInput placeholder="Vorname" formControlName="vorname" />
</mat-form-field>
<mat-form-field>
<mat-label>Nachname</mat-label>
<input matInput placeholder="Nachname" formControlName="nachname" />
</mat-form-field>
<mat-form-field>
<mat-label>Geschlecht</mat-label>
<input matInput placeholder="Geschlecht" formControlName="geschlecht" />
</mat-form-field>
<mat-form-field>
<mat-label>Passnummer</mat-label>
<input matInput placeholder="Passnummer" formControlName="passnummer" />
</mat-form-field>
<mat-form-field>
<mat-label>Geburtsdatum</mat-label>
<input matInput [matDatepicker]="picker" formControlName="geburtsdatum" />
<mat-hint>DD.MM.YYYY</mat-hint>
<mat-datepicker-toggle matIconSuffix [for]="picker"></mat-datepicker-toggle>
<mat-datepicker #picker></mat-datepicker>
</mat-form-field>
<mat-form-field>
<mat-label>Geburtsort</mat-label>
<input matInput placeholder="Geburtsort" formControlName="geburtsort" />
</mat-form-field>
<mat-form-field>
<mat-label>Unterrichtsvertrag?</mat-label>
<mat-select matInput formControlName="uvertrag">
<mat-option [value]="true">Ja</mat-option>
<mat-option [value]="false">Nein</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field>
<mat-label>Vereinsmitglied?</mat-label>
<mat-select matInput formControlName="verein">
<mat-option [value]="true">Ja</mat-option>
<mat-option [value]="false">Nein</mat-option>
</mat-select>
</mat-form-field>
<button mat-raised-button color="primary" type="submit">Speichern</button>
</form>
@@ -0,0 +1,8 @@
form{
display: flex;
flex-direction: column;
}
form > *{
margin-top: 20px;
}
@@ -0,0 +1,22 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { EditMemberDialogComponent } from './edit-member-dialog.component.js';
describe('EditMemberDialogComponent', () => {
let component: EditMemberDialogComponent;
let fixture: ComponentFixture<EditMemberDialogComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [EditMemberDialogComponent],
}).compileComponents();
fixture = TestBed.createComponent(EditMemberDialogComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
@@ -0,0 +1,47 @@
import { Component, Inject } from '@angular/core';
import { FormGroup, ReactiveFormsModule } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button';
import { MatCheckboxModule } from '@angular/material/checkbox';
import { MatOptionModule } from '@angular/material/core';
import { MatDatepickerModule } from '@angular/material/datepicker';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input';
import { MatSelectModule } from '@angular/material/select';
import * as api from '../../generated-api/api';
import { MemberDtoHelper } from '../../util/member.js';
@Component({
selector: 'app-edit-member-dialog',
standalone: true,
imports: [
ReactiveFormsModule,
MatFormFieldModule,
MatInputModule,
MatCheckboxModule,
MatButtonModule,
MatSelectModule,
MatOptionModule,
MatDatepickerModule,
],
templateUrl: './edit-member-dialog.component.html',
styleUrl: './edit-member-dialog.component.scss',
})
export class EditMemberDialogComponent {
form: FormGroup;
constructor(
@Inject(MAT_DIALOG_DATA) public data: { member: api.Member },
private memberDtoHelper: MemberDtoHelper,
private dialogRef: MatDialogRef<EditMemberDialogComponent>,
) {
this.form = this.memberDtoHelper.newMemberForm(data.member);
}
submit() {
const member: api.Member = this.memberDtoHelper.memberFromForm(
this.form,
);
api.updateMember(member);
this.dialogRef.close();
}
}
@@ -1 +1,6 @@
<p>missing-page works!</p>
<p>Diese Seite existiert nicht!</p>
@if(this.auth.loggedIn()){
<a mat-stroked-button [routerLink]="['/home']">Zur Starteite</a>
}@else {
<a mat-stroked-button [routerLink]="['/']">Zur Login Seite</a>
}
@@ -1,11 +1,14 @@
import { Component } from '@angular/core';
import { Component, inject } from '@angular/core';
import { MatButtonModule } from '@angular/material/button';
import { RouterModule } from '@angular/router';
import { Authentication } from '../../services/authentication';
@Component({
selector: 'app-missing-page',
imports: [],
templateUrl: './missing-page.html',
styleUrl: './missing-page.scss',
selector: 'app-missing-page',
imports: [MatButtonModule, RouterModule],
templateUrl: './missing-page.html',
styleUrl: './missing-page.scss',
})
export class MissingPage {
protected readonly auth = inject(Authentication);
}
@@ -0,0 +1,39 @@
<h1>Neues Mitglied anlegen</h1>
<form [formGroup]="memberForm" (ngSubmit)="onSubmit()">
<mat-form-field>
<mat-label>Vorname</mat-label>
<input matInput formControlName="vorname" />
</mat-form-field>
<mat-form-field>
<mat-label>Nachname</mat-label>
<input matInput formControlName="nachname" />
</mat-form-field>
<mat-form-field>
<mat-label>Geschlecht</mat-label>
<input matInput formControlName="geschlecht" />
</mat-form-field>
<mat-form-field>
<mat-label>Geburtsdatum</mat-label>
<input matInput [matDatepicker]="picker" formControlName="geburtsdatum" />
<mat-hint>DD.MM.YYYY</mat-hint>
<mat-datepicker-toggle matIconSuffix [for]="picker"></mat-datepicker-toggle>
<mat-datepicker #picker></mat-datepicker>
</mat-form-field>
<mat-form-field>
<mat-label>Geburtsort</mat-label>
<input matInput formControlName="geburtsort" />
</mat-form-field>
<mat-checkbox formControlName="uvertrag"
>Unterrichtsvertrag Abgegeben?</mat-checkbox
>
<mat-checkbox formControlName="uvertrag">Verein Beigetreten?</mat-checkbox>
<mat-form-field>
<mat-label>Passnummer (falls vorhanden)</mat-label>
<input matInput formControlName="passnummer" />
</mat-form-field>
<mat-form-field>
<mat-label>Kontakt</mat-label>
<input matInput formControlName="kontakt" />
</mat-form-field>
<button mat-flat-button color="primary" type="submit">Hinzufügen</button>
</form>
@@ -0,0 +1,9 @@
form {
display: flex;
flex-direction: column;
align-items: center;
}
h1 {
text-align: center;
}
@@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { NewMemberComponent } from './new-member.component.js';
describe('NewMemberComponent', () => {
let component: NewMemberComponent;
let fixture: ComponentFixture<NewMemberComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [NewMemberComponent]
})
.compileComponents();
fixture = TestBed.createComponent(NewMemberComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
@@ -0,0 +1,49 @@
import { Component, inject } from '@angular/core';
import { ReactiveFormsModule } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button';
import { MatCheckboxModule } from '@angular/material/checkbox';
import { MatDatepickerModule } from '@angular/material/datepicker';
import { MatDialogRef } from '@angular/material/dialog';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input';
import * as api from '../../generated-api/api.js';
import { MemberDtoHelper } from '../../util/member.js';
@Component({
selector: 'app-new-member',
standalone: true,
providers: [],
imports: [
ReactiveFormsModule,
MatFormFieldModule,
MatDatepickerModule,
MatInputModule,
MatCheckboxModule,
MatButtonModule,
],
templateUrl: './new-member.component.html',
styleUrl: './new-member.component.scss',
})
export class NewMemberComponent {
private readonly memberDtoHelper = inject(MemberDtoHelper);
constructor() {}
memberForm = this.memberDtoHelper.newMemberForm();
private dialogRef: MatDialogRef<NewMemberComponent> = inject(
MatDialogRef<NewMemberComponent>,
);
onSubmit() {
const newMember: api.PickMemberExcludeKeyofMemberIdOrLoeschenam =
this.memberDtoHelper.newMemberFromForm(this.memberForm);
api.createMember(newMember)
.then(() => {
console.log('Successfully added Member');
this.dialogRef.close();
})
.catch(e => {
console.error('Error adding member', e);
});
}
}
@@ -1 +1,17 @@
<p>test works!</p>
<h1>Testeite für interne Berechtigung</h1>
<p>API: {{apiStatus()}}</p>
<p>Auth: {{authTest()}}</p>
Claim:
<ul>
@let claims = this.auth.claims();
@if(claims) {
<pre>
{{claims | json}}
</pre>
}
</ul>
<details name="UserInfo:">
<pre>
{{this.auth.userInfo() | json}}
</pre>
</details>
@@ -1,11 +1,42 @@
import { Component } from '@angular/core';
import { JsonPipe } from '@angular/common';
import { Component, effect, inject, signal } from '@angular/core';
import * as api from '../../generated-api/api';
import { Authentication } from '../../services/authentication';
@Component({
selector: 'app-test',
imports: [],
imports: [JsonPipe],
templateUrl: './test.html',
styleUrl: './test.scss',
})
export class Test {
protected auth = inject(Authentication);
protected apiStatus = signal<string>("Waiting");
protected authTest = signal<string>("Waiting");
public constructor(){
effect(()=>{
const loggedIn=this.auth.loggedIn();
if(!loggedIn){
this.apiStatus.set("N/A - not logged in");
this.authTest.set("N/A - not logged in");
return;
}
this.apiStatus.set("Waiting for API");
this.authTest.set("Waiting for API");
api.getOk().then((status) => {
this.apiStatus.set("Ok");
}).catch((e) => {
this.apiStatus.set("Error");
});
api.getAuth().then((response) => {
this.authTest.set("Ok");
}).catch((e) => {
this.authTest.set(e);
});
})
}
}
@@ -0,0 +1,125 @@
<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>
<input
matInput
(keyup)="applyFilter($event)"
[value]="filter$ | async"
placeholder="Filter"
/>
</mat-form-field>
</div>
</div>
<ng-container *ngIf="dataSource$ | async as dataSource">
<table mat-table [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 *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 *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 *matHeaderCellDef>Geschlecht</th>
<td mat-cell *matCellDef="let element">{{ element.geschlecht }}</td>
</ng-container>
<ng-container matColumnDef="passnummer">
<th mat-header-cell *matHeaderCellDef>Passnummer</th>
<td mat-cell *matCellDef="let element">
{{ element.passnummer | nobreakHyphen }}
</td>
</ng-container>
<ng-container matColumnDef="geburtsdatum">
<th mat-header-cell *matHeaderCellDef>Geburtsdatum</th>
<td mat-cell *matCellDef="let element">
{{ element.geburtsdatum | datePipe }}
</td>
</ng-container>
<ng-container matColumnDef="geburtsort">
<th mat-header-cell *matHeaderCellDef>Geburtsort</th>
<td mat-cell *matCellDef="let element">{{ element.geburtsort }}</td>
</ng-container>
<ng-container matColumnDef="uvertrag">
<th mat-header-cell *matHeaderCellDef>Unterrichtsvertrag</th>
<td mat-cell *matCellDef="let element">
<mat-icon
*ngIf="element.uvertrag"
class="positive"
aria-label="Unterrichtsvertrag Check"
fontIcon="check"
></mat-icon>
<mat-icon
*ngIf="!element.uvertrag"
class="negative"
aria-label="Unterrichtsvertrag Cross"
fontIcon="close"
></mat-icon>
</td>
</ng-container>
<ng-container matColumnDef="verein">
<th mat-header-cell *matHeaderCellDef>Vereinsmitgliedschaft</th>
<td mat-cell *matCellDef="let element">
<mat-icon
*ngIf="element.verein"
class="positive"
aria-label="Verein Check"
fontIcon="check"
></mat-icon>
<mat-icon
*ngIf="!element.verein"
class="negative"
aria-label="Verein Cross"
fontIcon="close"
></mat-icon>
</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>
<ng-container matColumnDef="kuendigungzum">
<th mat-header-cell *matHeaderCellDef>K&uuml;ndigung zum</th>
<td mat-cell *matCellDef="let element">
{{ (element.kuendigungzum | datePipe) ?? "-" }}
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns"></tr>
</table>
</ng-container>
@@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ViewMembers } from './view-members';
describe('ViewMembers', () => {
let component: ViewMembers;
let fixture: ComponentFixture<ViewMembers>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [ViewMembers]
})
.compileComponents();
fixture = TestBed.createComponent(ViewMembers);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
@@ -0,0 +1,115 @@
import { AsyncPipe, NgIf } from '@angular/common';
import { Component } from '@angular/core';
import { MatButtonModule } from '@angular/material/button';
import { MatDialog } from '@angular/material/dialog';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input';
import { MatTableDataSource, MatTableModule } from '@angular/material/table';
import { RouterModule } from '@angular/router';
import { BehaviorSubject, firstValueFrom, from, Observable } from 'rxjs';
import * as api from '../../generated-api/api';
import { DatePipePipe } from '../../pipes/date-pipe.pipe.js';
import { NobreakHyphenPipe } from '../../pipes/nobreak-hyphen.pipe.js';
import { StylizeNamesPipe } from '../../pipes/stylize-names.pipe.js';
import { DeleteMemberDialogComponent } from '../delete-member-dialog/delete-member-dialog.component.js';
import { EditMemberDialogComponent } from '../edit-member-dialog/edit-member-dialog.component.js';
import { NewMemberComponent } from '../new-member/new-member.component';
@Component({
selector: 'app-view-members',
imports: [
MatTableModule,
MatFormFieldModule,
MatInputModule,
MatIconModule,
NgIf,
StylizeNamesPipe,
NobreakHyphenPipe,
RouterModule,
MatButtonModule,
AsyncPipe,
DatePipePipe,
],
templateUrl: './view-members.html',
styleUrl: './view-members.scss',
})
export class ViewMembers {
async deleteMember(memberId: any) {
const dataSource = await firstValueFrom(this.dataSource$);
const dialogRef = this.dialog.open(DeleteMemberDialogComponent, {
data: {
member: dataSource.data.find(member => member.id === memberId),
},
hasBackdrop: true,
width: '90%',
height: '90%',
});
dialogRef.afterClosed().subscribe(async result => {
this.dataSource$ = this.updateData();
});
}
newMember() {
const dialogRef = this.dialog.open(NewMemberComponent, {});
dialogRef.afterClosed().subscribe(() => {
this.dataSource$ = this.updateData();
});
}
async editMember(memberId: any) {
const dataSource = await firstValueFrom(this.dataSource$);
const dialogRef = this.dialog.open(EditMemberDialogComponent, {
data: {
member: dataSource.data.find(member => member.id === memberId),
},
hasBackdrop: true,
width: '90%',
height: '90%',
});
dialogRef.afterClosed().subscribe(async result => {
this.dataSource$ = this.updateData();
});
}
dataSource$: Observable<MatTableDataSource<api.Member>> =
new BehaviorSubject(new MatTableDataSource([] as api.Member[]));
filter$: BehaviorSubject<string> = new BehaviorSubject('');
displayedColumns: string[] = [
'edit',
'delete',
'vorname',
'nachname',
'geschlecht',
'passnummer',
'geburtsdatum',
'geburtsort',
'uvertrag',
'verein',
'kontakt',
'kuendigungzum',
];
applyFilter(event: KeyboardEvent) {
let filterValue = (event.target as HTMLInputElement).value ?? '';
filterValue = filterValue.trim();
filterValue = filterValue.toLowerCase();
this.filter$.next(filterValue);
}
constructor(private dialog: MatDialog) {
this.dataSource$ = this.updateData();
}
updateData() {
return from(
api.getMembers().then(members => {
let dataSource = new MatTableDataSource<api.Member>(members);
this.filter$.subscribe(filter => {
dataSource.filter = filter;
});
return dataSource;
}),
);
}
}