catchup #13

Merged
toniwalter.green merged 16 commits from devel into main 2026-09-15 01:47:26 +02:00
12 changed files with 298 additions and 296 deletions
Showing only changes of commit 5a19b3a7a6 - Show all commits
@@ -103,7 +103,7 @@ export class MembersContoller extends Controller {
) { ) {
await this.membersService.scheduleDeletion( await this.membersService.scheduleDeletion(
memberId, memberId,
cancelDate, cancelDate ? cancelDate : null,
request, request,
); );
} }
+39 -38
View File
@@ -1,51 +1,52 @@
import { type MemberModel as DbMember } from "../generated/prisma/models.js"; import { type MemberModel as DbMember } from "../generated/prisma/models.js";
export interface MemberDto { export interface MemberDto {
id: string | null; id: string;
vorname: string | null; vorname: string;
nachname: string | null; nachname: string;
geburtsdatum: string | null; geburtsdatum: string;
geburtsort: string | null; geburtsort: string;
geschlecht: string | null; geschlecht: string;
passnummer: string | null; passnummer: string;
verein: boolean | null; verein: boolean;
graduierung: string | null; graduierung: string;
letztePruefung: string | null; letztePruefung: string;
kontakt: string | null; kontakt: string;
kuendigungzum: string | null; kuendigungzum: string;
marker: boolean | null; marker: boolean;
} }
export class MemberCreateArgs implements Omit<MemberDto, "id"> { export class MemberCreateArgs implements Omit<
vorname!: string | null; MemberDto,
nachname!: string | null; "id" | "kuendigungzum" | "letztePruefung"
geburtsdatum!: string | null; > {
geburtsort!: string | null; vorname!: string;
geschlecht!: string | null; nachname!: string;
passnummer!: string | null; geburtsdatum!: string;
verein!: boolean | null; geburtsort!: string;
graduierung!: string | null; geschlecht!: string;
letztePruefung!: string | null; passnummer!: string;
kontakt!: string | null; verein!: boolean;
kuendigungzum!: string | null; graduierung!: string;
marker!: boolean | null; kontakt!: string;
marker!: boolean;
} }
export function mapDbMemberToMember(dbMember: DbMember): MemberDto { export function mapDbMemberToMember(dbMember: DbMember): MemberDto {
return { return {
geburtsdatum: dbMember.geburtsdatum?.toDateString() ?? null, geburtsdatum: dbMember.geburtsdatum?.toDateString() ?? "",
id: dbMember.id, id: dbMember.id,
nachname: dbMember.nachname, nachname: dbMember.nachname ?? "",
geburtsort: dbMember.geburtsort, geburtsort: dbMember.geburtsort ?? "",
geschlecht: dbMember.geschlecht, geschlecht: dbMember.geschlecht ?? "",
graduierung: dbMember.graduierung, graduierung: dbMember.graduierung ?? "",
vorname: dbMember.vorname, vorname: dbMember.vorname ?? "",
kontakt: dbMember.kontakt, kontakt: dbMember.kontakt ?? "",
kuendigungzum: dbMember.kuendigungzum?.toDateString() ?? null, kuendigungzum: dbMember.kuendigungzum?.toDateString() ?? "",
letztePruefung: dbMember.letztePruefung?.toDateString() ?? null, letztePruefung: dbMember.letztePruefung?.toDateString() ?? "",
passnummer: dbMember.passnummer, passnummer: dbMember.passnummer ?? "",
verein: dbMember.verein, verein: dbMember.verein ?? false,
marker: dbMember.marker, marker: dbMember.marker ?? false,
}; };
} }
+14 -2
View File
@@ -68,7 +68,19 @@ export class MembersService {
const modifiedMember = await this.database.doRequest( const modifiedMember = await this.database.doRequest(
async prisma => async prisma =>
await prisma.member.update({ await prisma.member.update({
data: { ...member, id: id }, data: {
...member,
id: id,
letztePruefung: member.letztePruefung
? member.letztePruefung
: null,
geburtsdatum: member.geburtsdatum
? member.geburtsdatum
: null,
kuendigungzum: member.kuendigungzum
? member.kuendigungzum
: null,
},
where: { id: id }, where: { id: id },
}), }),
request, request,
@@ -79,7 +91,7 @@ export class MembersService {
public async scheduleDeletion( public async scheduleDeletion(
memberId: string, memberId: string,
cancelDate: Date, cancelDate: Date | null,
request: express.Request, request: express.Request,
) { ) {
await this.database.doRequest( await this.database.doRequest(
@@ -1,13 +1,17 @@
<form [formGroup]="form" (ngSubmit)="submit()"> <form>
<p>Name: {{ member.vorname }} {{ member.nachname }}</p> <p>Name: {{ member.vorname }} {{ member.nachname }}</p>
<p>Passnummer: {{ member.passnummer }}</p> <p>Passnummer: {{ member.passnummer }}</p>
<p>Geburtsdatum: {{ member.geburtsdatum??undefined | datePipe }}</p> <p>Geburtsdatum: {{ member.geburtsdatum | datePipe }}</p>
<mat-form-field> <mat-form-field>
<mat-label>K&uuml;ndigun zum</mat-label> <mat-label>K&uuml;ndigun zum</mat-label>
<input matInput [matDatepicker]="picker" formControlName="kuendigungzum" /> <input matInput [matDatepicker]="picker" [field]="form.kuendigungzum" />
<mat-hint>DD.MM.YYYY</mat-hint> <mat-hint>DD.MM.YYYY</mat-hint>
<mat-datepicker-toggle matIconSuffix [for]="picker"></mat-datepicker-toggle> <mat-datepicker-toggle
matIconSuffix
[for]="picker"></mat-datepicker-toggle>
<mat-datepicker #picker></mat-datepicker> <mat-datepicker #picker></mat-datepicker>
</mat-form-field> </mat-form-field>
<button mat-raised-button color="primary" type="submit">Speichern</button> <button mat-raised-button color="primary" (click)="submit()">
Speichern
</button>
</form> </form>
@@ -1,5 +1,5 @@
import { Component, Inject } from '@angular/core'; import { Component, inject, signal } from '@angular/core';
import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms'; import { Field, form } from '@angular/forms/signals';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
import { MatCheckboxModule } from '@angular/material/checkbox'; import { MatCheckboxModule } from '@angular/material/checkbox';
import { MatOptionModule } from '@angular/material/core'; import { MatOptionModule } from '@angular/material/core';
@@ -9,18 +9,17 @@ import { MatFormFieldModule } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input'; import { MatInputModule } from '@angular/material/input';
import { MatSelectModule } from '@angular/material/select'; import { MatSelectModule } from '@angular/material/select';
import * as api from '../../generated-api/api'; import * as api from '../../generated-api/api';
import { DatePipePipe, dateToLocal } from '../../pipes/date-pipe.pipe.js'; import {
DatePipePipe,
dateToLocal,
dateToUTC,
} from '../../pipes/date-pipe.pipe.js';
import { EditMemberDialogComponent } from '../edit-member-dialog/edit-member-dialog.component.js'; import { EditMemberDialogComponent } from '../edit-member-dialog/edit-member-dialog.component.js';
type DeletionForm = {
kuendigungzum: FormControl<Date | null>;
};
@Component({ @Component({
selector: 'app-delete-member-dialog', selector: 'app-delete-member-dialog',
standalone: true, standalone: true,
imports: [ imports: [
ReactiveFormsModule,
MatFormFieldModule, MatFormFieldModule,
MatInputModule, MatInputModule,
MatCheckboxModule, MatCheckboxModule,
@@ -29,32 +28,31 @@ type DeletionForm = {
MatOptionModule, MatOptionModule,
MatDatepickerModule, MatDatepickerModule,
DatePipePipe, DatePipePipe,
Field,
], ],
templateUrl: './delete-member-dialog.component.html', templateUrl: './delete-member-dialog.component.html',
styleUrl: './delete-member-dialog.component.scss', styleUrl: './delete-member-dialog.component.scss',
}) })
export class DeleteMemberDialogComponent { export class DeleteMemberDialogComponent {
form: FormGroup<DeletionForm>; private readonly data = inject<{ member: api.MemberDto }>(MAT_DIALOG_DATA);
member: api.MemberDto; private readonly dialogRef = inject(
constructor( MatDialogRef<EditMemberDialogComponent>,
@Inject(MAT_DIALOG_DATA) public data: { member: api.MemberDto }, );
private dialogRef: MatDialogRef<EditMemberDialogComponent>, private readonly deletionModel = signal({
) { kuendigungzum: this.data.member.kuendigungzum
this.member = data.member; ? dateToUTC(this.data.member.kuendigungzum)
this.form = new FormGroup<DeletionForm>({ : null,
kuendigungzum: new FormControl(null),
}); });
} protected readonly form = form(this.deletionModel);
protected readonly member = this.data.member;
async submit() { protected async submit() {
const date = this.form.controls.kuendigungzum.value; const date = this.deletionModel().kuendigungzum;
if (date) {
await api.scheduleDeletion( await api.scheduleDeletion(
this.member.id!, this.member.id,
dateToLocal(date).toISOString(), date ? dateToLocal(date).toISOString() : '',
); );
this.dialogRef.close(); this.dialogRef.close();
} }
} }
}
@@ -1,44 +1,41 @@
<form [formGroup]="form" (ngSubmit)="submit()"> <form>
<mat-form-field> <mat-form-field>
<mat-label>Vorname</mat-label> <mat-label>Vorname</mat-label>
<input matInput placeholder="Vorname" formControlName="vorname" /> <input matInput placeholder="Vorname" [field]="form.vorname" />
</mat-form-field> </mat-form-field>
<mat-form-field> <mat-form-field>
<mat-label>Nachname</mat-label> <mat-label>Nachname</mat-label>
<input matInput placeholder="Nachname" formControlName="nachname" /> <input matInput placeholder="Nachname" [field]="form.nachname" />
</mat-form-field> </mat-form-field>
<mat-form-field> <mat-form-field>
<mat-label>Geschlecht</mat-label> <mat-label>Geschlecht</mat-label>
<input matInput placeholder="Geschlecht" formControlName="geschlecht" /> <input matInput placeholder="Geschlecht" [field]="form.geschlecht" />
</mat-form-field> </mat-form-field>
<mat-form-field> <mat-form-field>
<mat-label>Passnummer</mat-label> <mat-label>Passnummer</mat-label>
<input matInput placeholder="Passnummer" formControlName="passnummer" /> <input matInput placeholder="Passnummer" [field]="form.passnummer" />
</mat-form-field> </mat-form-field>
<mat-form-field> <mat-form-field>
<mat-label>Geburtsdatum</mat-label> <mat-label>Geburtsdatum</mat-label>
<input matInput [matDatepicker]="picker" formControlName="geburtsdatum" /> <input matInput [matDatepicker]="picker" [field]="form.geburtsdatum" />
<mat-hint>DD.MM.YYYY</mat-hint> <mat-hint>DD.MM.YYYY</mat-hint>
<mat-datepicker-toggle matIconSuffix [for]="picker"></mat-datepicker-toggle> <mat-datepicker-toggle
matIconSuffix
[for]="picker"></mat-datepicker-toggle>
<mat-datepicker #picker></mat-datepicker> <mat-datepicker #picker></mat-datepicker>
</mat-form-field> </mat-form-field>
<mat-form-field> <mat-form-field>
<mat-label>Geburtsort</mat-label> <mat-label>Geburtsort</mat-label>
<input matInput placeholder="Geburtsort" formControlName="geburtsort" /> <input matInput placeholder="Geburtsort" [field]="form.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-form-field> <mat-form-field>
<mat-label>Vereinsmitglied?</mat-label> <mat-label>Vereinsmitglied?</mat-label>
<mat-select matInput formControlName="verein"> <mat-select matInput [field]="form.verein">
<mat-option [value]="true">Ja</mat-option> <mat-option [value]="true">Ja</mat-option>
<mat-option [value]="false">Nein</mat-option> <mat-option [value]="false">Nein</mat-option>
</mat-select> </mat-select>
</mat-form-field> </mat-form-field>
<button mat-raised-button color="primary" type="submit">Speichern</button> <button mat-raised-button color="primary" (click)="submit()">
Speichern
</button>
</form> </form>
@@ -1,5 +1,5 @@
import { Component, Inject } from '@angular/core'; import { Component, inject, signal } from '@angular/core';
import { FormGroup, ReactiveFormsModule } from '@angular/forms'; import { Field, form } from '@angular/forms/signals';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
import { MatCheckboxModule } from '@angular/material/checkbox'; import { MatCheckboxModule } from '@angular/material/checkbox';
import { MatOptionModule } from '@angular/material/core'; import { MatOptionModule } from '@angular/material/core';
@@ -9,13 +9,12 @@ import { MatFormFieldModule } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input'; import { MatInputModule } from '@angular/material/input';
import { MatSelectModule } from '@angular/material/select'; import { MatSelectModule } from '@angular/material/select';
import * as api from '../../generated-api/api'; import * as api from '../../generated-api/api';
import { MemberDtoHelper } from '../../util/member.js'; import { MemberDtoHelper, MemberEditForm } from '../../util/member.js';
@Component({ @Component({
selector: 'app-edit-member-dialog', selector: 'app-edit-member-dialog',
standalone: true, standalone: true,
imports: [ imports: [
ReactiveFormsModule,
MatFormFieldModule, MatFormFieldModule,
MatInputModule, MatInputModule,
MatCheckboxModule, MatCheckboxModule,
@@ -23,25 +22,28 @@ import { MemberDtoHelper } from '../../util/member.js';
MatSelectModule, MatSelectModule,
MatOptionModule, MatOptionModule,
MatDatepickerModule, MatDatepickerModule,
Field,
], ],
templateUrl: './edit-member-dialog.component.html', templateUrl: './edit-member-dialog.component.html',
styleUrl: './edit-member-dialog.component.scss', styleUrl: './edit-member-dialog.component.scss',
}) })
export class EditMemberDialogComponent { export class EditMemberDialogComponent {
form: FormGroup; private readonly data = inject<{ member: api.MemberDto }>(MAT_DIALOG_DATA);
constructor( private readonly memberDtoHelper = inject(MemberDtoHelper);
@Inject(MAT_DIALOG_DATA) public data: { member: api.MemberDto }, private readonly dialogRef = inject(
private memberDtoHelper: MemberDtoHelper, MatDialogRef<EditMemberDialogComponent>,
private dialogRef: MatDialogRef<EditMemberDialogComponent>,
) {
this.form = this.memberDtoHelper.newMemberForm(data.member);
}
submit() {
const member: api.MemberDto = this.memberDtoHelper.memberFromForm(
this.form,
); );
api.updateMember(member);
private readonly memberModel = signal<MemberEditForm>(
this.memberDtoHelper.editMemberForm(this.data.member),
);
protected readonly form = form(this.memberModel);
protected async submit() {
const member: api.MemberDto = this.memberDtoHelper.memberFromSignal(
this.memberModel(),
);
await api.updateMember(member);
this.dialogRef.close(); this.dialogRef.close();
} }
} }
@@ -1,23 +1,23 @@
<h1>Neues Mitglied anlegen</h1> <h1>Neues Mitglied anlegen</h1>
<form [formGroup]="memberForm" (ngSubmit)="onSubmit()"> <form>
<mat-form-field> <mat-form-field>
<mat-label>Vorname</mat-label> <mat-label>Vorname</mat-label>
<input matInput formControlName="vorname" /> <input matInput [field]="memberForm.vorname" />
</mat-form-field> </mat-form-field>
<mat-form-field> <mat-form-field>
<mat-label>Nachname</mat-label> <mat-label>Nachname</mat-label>
<input matInput formControlName="nachname" /> <input matInput [field]="memberForm.nachname" />
</mat-form-field> </mat-form-field>
<mat-form-field> <mat-form-field>
<mat-label>Geschlecht</mat-label> <mat-label>Geschlecht</mat-label>
<input matInput formControlName="geschlecht" /> <input matInput [field]="memberForm.geschlecht" />
</mat-form-field> </mat-form-field>
<mat-form-field> <mat-form-field>
<mat-label>Geburtsdatum</mat-label> <mat-label>Geburtsdatum</mat-label>
<input <input
matInput matInput
[matDatepicker]="picker" [matDatepicker]="picker"
formControlName="geburtsdatum" /> [field]="memberForm.geburtsdatum" />
<mat-hint>DD.MM.YYYY</mat-hint> <mat-hint>DD.MM.YYYY</mat-hint>
<mat-datepicker-toggle <mat-datepicker-toggle
matIconSuffix matIconSuffix
@@ -26,18 +26,18 @@
</mat-form-field> </mat-form-field>
<mat-form-field> <mat-form-field>
<mat-label>Geburtsort</mat-label> <mat-label>Geburtsort</mat-label>
<input matInput formControlName="geburtsort" /> <input matInput [field]="memberForm.geburtsort" />
</mat-form-field> </mat-form-field>
<mat-checkbox [formControl]="memberForm.controls.verein" <mat-checkbox [field]="memberForm.verein">Verein Beigetreten?</mat-checkbox>
>Verein Beigetreten?</mat-checkbox
>
<mat-form-field> <mat-form-field>
<mat-label>Passnummer (falls vorhanden)</mat-label> <mat-label>Passnummer (falls vorhanden)</mat-label>
<input matInput formControlName="passnummer" /> <input matInput [field]="memberForm.passnummer" />
</mat-form-field> </mat-form-field>
<mat-form-field> <mat-form-field>
<mat-label>Kontakt</mat-label> <mat-label>Kontakt</mat-label>
<input matInput formControlName="kontakt" /> <input matInput [field]="memberForm.kontakt" />
</mat-form-field> </mat-form-field>
<button mat-flat-button color="primary" type="submit">Hinzufügen</button> <button mat-flat-button color="primary" (click)="onSubmit()">
Hinzufügen
</button>
</form> </form>
@@ -1,5 +1,5 @@
import { Component, inject } from '@angular/core'; import { Component, inject, signal } from '@angular/core';
import { ReactiveFormsModule } from '@angular/forms'; import { Field, form } from '@angular/forms/signals';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
import { MatCheckboxModule } from '@angular/material/checkbox'; import { MatCheckboxModule } from '@angular/material/checkbox';
import { MatDatepickerModule } from '@angular/material/datepicker'; import { MatDatepickerModule } from '@angular/material/datepicker';
@@ -7,43 +7,49 @@ import { MatDialogRef } from '@angular/material/dialog';
import { MatFormFieldModule } from '@angular/material/form-field'; import { MatFormFieldModule } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input'; import { MatInputModule } from '@angular/material/input';
import * as api from '../../generated-api/api.js'; import * as api from '../../generated-api/api.js';
import { MemberDtoHelper } from '../../util/member.js'; import { MemberCreateForm, MemberDtoHelper } from '../../util/member.js';
@Component({ @Component({
selector: 'app-new-member', selector: 'app-new-member',
standalone: true, standalone: true,
providers: [], providers: [],
imports: [ imports: [
ReactiveFormsModule,
MatFormFieldModule, MatFormFieldModule,
MatDatepickerModule, MatDatepickerModule,
MatInputModule, MatInputModule,
MatCheckboxModule, MatCheckboxModule,
MatButtonModule, MatButtonModule,
Field,
], ],
templateUrl: './new-member.component.html', templateUrl: './new-member.component.html',
styleUrl: './new-member.component.scss', styleUrl: './new-member.component.scss',
}) })
export class NewMemberComponent { export class NewMemberComponent {
private readonly memberDtoHelper = inject(MemberDtoHelper); private readonly memberDtoHelper = inject(MemberDtoHelper);
private readonly dialogRef: MatDialogRef<NewMemberComponent> = inject(
constructor() {}
memberForm = this.memberDtoHelper.newMemberForm();
private dialogRef: MatDialogRef<NewMemberComponent> = inject(
MatDialogRef<NewMemberComponent>, MatDialogRef<NewMemberComponent>,
); );
onSubmit() { private readonly memberModel = signal<MemberCreateForm>(
const newMember: api.MemberCreateArgs = this.memberDtoHelper.newMemberForm(),
this.memberDtoHelper.newMemberFromForm(this.memberForm); );
protected readonly memberForm = form(this.memberModel);
api.createMember(newMember) protected async onSubmit(): Promise<void> {
.then(() => { if (!this.memberForm().valid()) {
return;
}
const newMember = this.memberDtoHelper.newMemberFromSignal(
this.memberModel(),
);
try {
await api.createMember(newMember);
console.log('Successfully added Member'); console.log('Successfully added Member');
this.dialogRef.close(); this.dialogRef.close();
}) } catch (e) {
.catch(e => {
console.error('Error adding member', e); console.error('Error adding member', e);
}); }
} }
} }
@@ -9,10 +9,13 @@
<div class="filter-container"> <div class="filter-container">
<mat-form-field> <mat-form-field>
<label>Filter:</label> <label>Filter:</label>
<input matInput [formControl]="filterControl" placeholder="Filter" /> <input
matInput
[field]="filterForm.filterString"
placeholder="Filter" />
</mat-form-field> </mat-form-field>
<div class="status-filters"> <div class="status-filters">
<mat-radio-group [formControl]="statusControl"> <mat-radio-group [field]="filterForm.status">
<mat-radio-button [value]="MemberStatusFilter.ACTIVE" <mat-radio-button [value]="MemberStatusFilter.ACTIVE"
>Aktiv</mat-radio-button >Aktiv</mat-radio-button
> >
@@ -30,20 +33,21 @@
<input <input
matInput matInput
[matDatepicker]="picker" [matDatepicker]="picker"
[formControl]="statusDateControl" [field]="filterForm.statusDate"
placeholder="Aktiv bis" /> placeholder="Aktiv bis" />
<mat-hint>DD.MM.YYYY</mat-hint> <mat-hint>DD.MM.YYYY</mat-hint>
<mat-datepicker-toggle <mat-datepicker-toggle
matIconSuffix matIconSuffix
[disabled]="statusControl.value===MemberStatusFilter.ALL" [disabled]="statusDateDisabled()"
[for]="picker"></mat-datepicker-toggle> [for]="picker"></mat-datepicker-toggle>
<mat-datepicker #picker></mat-datepicker> <mat-datepicker #picker></mat-datepicker>
</mat-form-field> </mat-form-field>
</div> </div>
<mat-checkbox [formControl]="onlyWithID">Nur mit Passnummer</mat-checkbox> <mat-checkbox [field]="filterForm.onlyWithID"
>Nur mit Passnummer</mat-checkbox
>
</div> </div>
<div class="table-container"> <div class="table-container">
<!--TODO implement row count, selecting and alternating colors-->
<table <table
mat-table mat-table
[dataSource]="dataSource" [dataSource]="dataSource"
@@ -1,6 +1,14 @@
import { AfterViewInit, Component, viewChild } from '@angular/core'; import {
import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; AfterViewInit,
import { FormControl, ReactiveFormsModule } from '@angular/forms'; ChangeDetectorRef,
Component,
computed,
effect,
inject,
signal,
viewChild,
} from '@angular/core';
import { Field, form } from '@angular/forms/signals';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
import { MatCheckbox } from '@angular/material/checkbox'; import { MatCheckbox } from '@angular/material/checkbox';
import { MatDatepickerModule } from '@angular/material/datepicker'; import { MatDatepickerModule } from '@angular/material/datepicker';
@@ -13,7 +21,6 @@ import { MatSort, MatSortModule } from '@angular/material/sort';
import { MatTableDataSource, MatTableModule } from '@angular/material/table'; import { MatTableDataSource, MatTableModule } from '@angular/material/table';
import { MatTooltipModule } from '@angular/material/tooltip'; import { MatTooltipModule } from '@angular/material/tooltip';
import { RouterModule } from '@angular/router'; import { RouterModule } from '@angular/router';
import { combineLatest, startWith } from 'rxjs';
import * as api from '../../generated-api/api'; import * as api from '../../generated-api/api';
import { DatePipePipe } from '../../pipes/date-pipe.pipe.js'; import { DatePipePipe } from '../../pipes/date-pipe.pipe.js';
import { NobreakHyphenPipe } from '../../pipes/nobreak-hyphen.pipe.js'; import { NobreakHyphenPipe } from '../../pipes/nobreak-hyphen.pipe.js';
@@ -40,25 +47,34 @@ enum MemberStatusFilter {
RouterModule, RouterModule,
MatButtonModule, MatButtonModule,
DatePipePipe, DatePipePipe,
ReactiveFormsModule,
MatDatepickerModule, MatDatepickerModule,
MatTooltipModule, MatTooltipModule,
MatCheckbox, MatCheckbox,
MatRadioModule, MatRadioModule,
MatSortModule, MatSortModule,
Field,
], ],
templateUrl: './view-members.html', templateUrl: './view-members.html',
styleUrl: './view-members.scss', styleUrl: './view-members.scss',
}) })
export class ViewMembers implements AfterViewInit { export class ViewMembers implements AfterViewInit {
protected MemberStatusFilter = MemberStatusFilter; private readonly changeDetectorRef = inject(ChangeDetectorRef);
protected readonly filterControl = new FormControl<string>('');
protected readonly statusControl = new FormControl<MemberStatusFilter>( protected readonly MemberStatusFilter = MemberStatusFilter;
MemberStatusFilter.ACTIVE, private readonly filterModel = signal({
filterString: '',
status: MemberStatusFilter.ACTIVE,
statusDate: null as Date | null,
onlyWithID: false,
});
protected readonly filterForm = form(this.filterModel);
protected readonly statusDateDisabled = computed(
() => this.filterForm.status().value() === MemberStatusFilter.ALL,
); );
protected readonly statusDateControl = new FormControl<Date | null>(null);
protected readonly onlyWithID = new FormControl<boolean>(false);
protected dataSource = new MatTableDataSource<api.MemberDto>(); protected readonly dataSource = new MatTableDataSource<api.MemberDto>();
private readonly datePipe = new DatePipePipe(); private readonly datePipe = new DatePipePipe();
protected readonly displayedColumns: string[] = [ protected readonly displayedColumns: string[] = [
@@ -159,7 +175,7 @@ export class ViewMembers implements AfterViewInit {
return originalFilterPredicate( return originalFilterPredicate(
{ {
...data, ...data,
id: null, id: '',
geburtsdatum: this.datePipe.transform( geburtsdatum: this.datePipe.transform(
data.geburtsdatum ?? undefined, data.geburtsdatum ?? undefined,
), ),
@@ -173,41 +189,10 @@ export class ViewMembers implements AfterViewInit {
filter, filter,
); );
}; };
combineLatest([
this.filterControl.valueChanges.pipe(
startWith(this.filterControl.value),
),
this.statusControl.valueChanges.pipe(
startWith(this.statusControl.value),
),
this.statusDateControl.valueChanges.pipe(
startWith(this.statusDateControl.value),
),
this.onlyWithID.valueChanges.pipe(startWith(this.onlyWithID.value)),
])
// .pipe(takeUntilDestroyed())
.subscribe(combined => {
const filter = combined[0];
const status = combined[1];
const date = combined[2];
const onlyWithID = combined[3];
this.dataSource.filter = `${filter?.replaceAll(';', '') ?? ''};${status};${date?.toISOString()};${onlyWithID}`;
});
this.statusControl.valueChanges effect(() => {
.pipe(takeUntilDestroyed()) const filters = this.filterModel();
.subscribe(value => { this.dataSource.filter = `${filters.filterString?.replaceAll(';', '') ?? ''};${filters.status};${filters.statusDate?.toISOString()};${filters.onlyWithID}`;
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(); this.updateData();
@@ -221,5 +206,6 @@ export class ViewMembers implements AfterViewInit {
private async updateData(): Promise<void> { private async updateData(): Promise<void> {
const members = await api.getMembers(); const members = await api.getMembers();
this.dataSource.data = members; this.dataSource.data = members;
this.changeDetectorRef.detectChanges();
} }
} }
+63 -71
View File
@@ -1,7 +1,7 @@
import { Inject, Injectable } from '@angular/core'; import { Inject, Injectable } from '@angular/core';
import { FormControl, FormGroup } from '@angular/forms'; import { FormControl } from '@angular/forms';
import { MAT_DATE_LOCALE } from '@angular/material/core'; import { MAT_DATE_LOCALE } from '@angular/material/core';
import { Locale, parse } from 'date-fns'; import { Locale } from 'date-fns';
import { MemberCreateArgs, MemberDto } from '../generated-api/api'; import { MemberCreateArgs, MemberDto } from '../generated-api/api';
import { dateToLocal, dateToUTC } from '../pipes/date-pipe.pipe'; import { dateToLocal, dateToUTC } from '../pipes/date-pipe.pipe';
@@ -21,86 +21,78 @@ interface MemberForm {
marker: FormControl<boolean | null | undefined>; marker: FormControl<boolean | null | undefined>;
} }
export interface MemberCreateForm extends Omit<
MemberCreateArgs,
'geburtsdatum'
> {
geburtsdatum: Date | null;
}
export interface MemberEditForm extends Omit<
MemberDto,
'geburtsdatum' | 'kuendigungzum' | 'letztePruefung'
> {
geburtsdatum: Date | null;
kuendigungzum: Date | null;
letztePruefung: Date | null;
}
@Injectable({ @Injectable({
providedIn: 'root', providedIn: 'root',
}) })
export class MemberDtoHelper { export class MemberDtoHelper {
constructor(@Inject(MAT_DATE_LOCALE) private locale: Locale) {} constructor(@Inject(MAT_DATE_LOCALE) private locale: Locale) {}
public newMemberForm(member?: MemberDto): FormGroup<MemberForm> { public newMemberForm(): MemberCreateForm {
const geburtsdatum = member?.geburtsdatum;
let enDate: Date | null = null;
if (geburtsdatum) {
enDate = parse(geburtsdatum, 'dd.MM.yyyy', new Date(), {
//TODO get dd.MM.yyyy from MAT_DATE_LOCALE or LOCALE_ID
locale: this.locale,
});
}
return new FormGroup<MemberForm>({
id: new FormControl(member?.id),
vorname: new FormControl(member?.vorname),
nachname: new FormControl(member?.nachname),
geschlecht: new FormControl(member?.geschlecht),
geburtsdatum: new FormControl(
member?.geburtsdatum ? dateToUTC(member?.geburtsdatum) : null,
),
geburtsort: new FormControl(member?.geburtsort),
passnummer: new FormControl(member?.passnummer),
verein: new FormControl(member?.verein),
kontakt: new FormControl(member?.kontakt),
graduierung: new FormControl(member?.graduierung),
kuendigungzum: new FormControl(
member?.kuendigungzum ? dateToUTC(member?.kuendigungzum) : null,
),
letztePruefung: new FormControl(
member?.letztePruefung
? dateToUTC(member?.letztePruefung)
: null,
),
marker: new FormControl(false),
});
}
memberFromForm(form: FormGroup<MemberForm>): MemberDto {
return { return {
id: form.controls.id.value ?? null, vorname: '',
vorname: form.controls.vorname.value ?? null, nachname: '',
nachname: form.controls.nachname.value ?? null, geschlecht: '',
geschlecht: form.controls.geschlecht.value ?? null, geburtsdatum: null,
geburtsdatum: form.controls.geburtsdatum.value geburtsort: '',
? dateToLocal(form.controls.geburtsdatum.value).toISOString() passnummer: '',
: null, verein: false,
geburtsort: form.controls.geburtsort.value ?? null, kontakt: '',
passnummer: form.controls.passnummer.value ?? null, graduierung: '',
verein: form.controls.verein.value ?? null, marker: false,
kontakt: form.controls.kontakt.value ?? null,
graduierung: form.controls.graduierung?.value ?? null,
kuendigungzum: form.controls.kuendigungzum.value
? dateToLocal(form.controls.kuendigungzum.value).toISOString()
: null,
letztePruefung: form.controls.letztePruefung.value
? dateToLocal(form.controls.letztePruefung.value).toISOString()
: null,
marker: form.controls.marker.value ?? false,
}; };
} }
newMemberFromForm(form: FormGroup<MemberForm>): MemberCreateArgs { public newMemberFromSignal(sig: MemberCreateForm): MemberCreateArgs {
return { return {
vorname: form.controls.vorname.value ?? null, ...sig,
nachname: form.controls.nachname.value ?? null, geburtsdatum: sig.geburtsdatum
geschlecht: form.controls.geschlecht.value ?? null, ? dateToLocal(sig.geburtsdatum).toISOString()
geburtsdatum: form.controls.geburtsdatum.value : '',
? dateToLocal(form.controls.geburtsdatum.value).toISOString() };
}
public editMemberForm(member: MemberDto): MemberEditForm {
return {
...member,
geburtsdatum: member.geburtsdatum
? dateToUTC(member.geburtsdatum)
: null, : null,
geburtsort: form.controls.geburtsort.value ?? null, kuendigungzum: member.kuendigungzum
passnummer: form.controls.passnummer.value ?? null, ? dateToUTC(member.kuendigungzum)
verein: form.controls.verein.value ?? null, : null,
kontakt: form.controls.kontakt.value ?? null, letztePruefung: member.letztePruefung
graduierung: form.controls.graduierung?.value ?? null, ? dateToUTC(member.letztePruefung)
kuendigungzum: null, //TODO not for new member : null,
letztePruefung: null, //TODO not for new member };
marker: form.controls.marker.value ?? false, }
public memberFromSignal(sig: MemberEditForm): MemberDto {
return {
...sig,
geburtsdatum: sig.geburtsdatum
? dateToLocal(sig.geburtsdatum).toISOString()
: '',
kuendigungzum: sig.kuendigungzum
? dateToLocal(sig.kuendigungzum).toISOString()
: '',
letztePruefung: sig.letztePruefung
? dateToLocal(sig.letztePruefung).toISOString()
: '',
}; };
} }
} }