visual + updates
This commit is contained in:
+1
-1
@@ -12,7 +12,7 @@
|
|||||||
"packageManager": "pnpm@10.24.0+sha512.01ff8ae71b4419903b65c60fb2dc9d34cf8bb6e06d03bde112ef38f7a34d6904c424ba66bea5cdcf12890230bf39f9580473140ed9c946fef328b6e5238a345a",
|
"packageManager": "pnpm@10.24.0+sha512.01ff8ae71b4419903b65c60fb2dc9d34cf8bb6e06d03bde112ef38f7a34d6904c424ba66bea5cdcf12890230bf39f9580473140ed9c946fef328b6e5238a345a",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"nodemon": "^3.1.11",
|
"nodemon": "^3.1.14",
|
||||||
"tsoa": "^6.6.0"
|
"tsoa": "^6.6.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,3 +5,6 @@ generated/
|
|||||||
.env
|
.env
|
||||||
|
|
||||||
keycloak/realm-export
|
keycloak/realm-export
|
||||||
|
|
||||||
|
src/cert.pem
|
||||||
|
src/key.pem
|
||||||
|
|||||||
+13
-13
@@ -12,27 +12,27 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/connect-pg-simple": "^7.0.3",
|
"@types/connect-pg-simple": "^7.0.3",
|
||||||
"@types/cors": "^2.8.19",
|
"@types/cors": "^2.8.19",
|
||||||
"@types/express": "^5.0.5",
|
"@types/express": "^5.0.6",
|
||||||
"@types/express-session": "^1.18.2",
|
"@types/express-session": "^1.18.2",
|
||||||
"@types/node": "^24.9.2",
|
"@types/node": "^25.3.5",
|
||||||
"@types/pg": "^8.15.6",
|
"@types/pg": "^8.18.0",
|
||||||
"prisma": "^7.0.1",
|
"prisma": "^7.4.2",
|
||||||
"tsx": "^4.20.6",
|
"tsx": "^4.21.0",
|
||||||
"typescript": "~5.9.3"
|
"typescript": "~5.9.3"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@keycloak/keycloak-admin-client": "^26.4.4",
|
"@keycloak/keycloak-admin-client": "^26.5.5",
|
||||||
"@prisma/adapter-pg": "^7.0.1",
|
"@prisma/adapter-pg": "^7.4.2",
|
||||||
"@prisma/client": "^7.0.1",
|
"@prisma/client": "^7.4.2",
|
||||||
"@tsoa/runtime": "^6.6.0",
|
"@tsoa/runtime": "^6.6.0",
|
||||||
"connect-pg-simple": "^10.0.0",
|
"connect-pg-simple": "^10.0.0",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.6",
|
||||||
"dotenv": "^17.2.3",
|
"dotenv": "^17.3.1",
|
||||||
"express": "^5.1.0",
|
"express": "^5.2.1",
|
||||||
"express-session": "^1.18.2",
|
"express-session": "^1.19.0",
|
||||||
"helmet": "^8.1.0",
|
"helmet": "^8.1.0",
|
||||||
"keycloak-connect": "^26.1.1",
|
"keycloak-connect": "^26.1.1",
|
||||||
"pg": "^8.16.3",
|
"pg": "^8.20.0",
|
||||||
"rxjs": "~7.8.2",
|
"rxjs": "~7.8.2",
|
||||||
"tsoa": "^6.6.0"
|
"tsoa": "^6.6.0"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "Member" ADD COLUMN "creationDate" DATE;
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "Member" ALTER COLUMN "creationDate" SET DEFAULT CURRENT_TIMESTAMP;
|
||||||
@@ -21,6 +21,7 @@ model Member {
|
|||||||
kontakt String? @db.VarChar
|
kontakt String? @db.VarChar
|
||||||
kuendigungzum DateTime? @db.Date
|
kuendigungzum DateTime? @db.Date
|
||||||
marker Boolean?
|
marker Boolean?
|
||||||
|
creationDate DateTime? @db.Date @default(now())
|
||||||
}
|
}
|
||||||
|
|
||||||
model session {
|
model session {
|
||||||
|
|||||||
@@ -14,11 +14,12 @@ export interface MemberDto {
|
|||||||
kontakt: string;
|
kontakt: string;
|
||||||
kuendigungzum: string;
|
kuendigungzum: string;
|
||||||
marker: boolean;
|
marker: boolean;
|
||||||
|
creationDate: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class MemberCreateArgs implements Omit<
|
export class MemberCreateArgs implements Omit<
|
||||||
MemberDto,
|
MemberDto,
|
||||||
"id" | "kuendigungzum" | "letztePruefung"
|
"id" | "kuendigungzum" | "letztePruefung" | "creationDate"
|
||||||
> {
|
> {
|
||||||
vorname!: string;
|
vorname!: string;
|
||||||
nachname!: string;
|
nachname!: string;
|
||||||
@@ -47,6 +48,7 @@ export function mapDbMemberToMember(dbMember: DbMember): MemberDto {
|
|||||||
passnummer: dbMember.passnummer ?? "",
|
passnummer: dbMember.passnummer ?? "",
|
||||||
verein: dbMember.verein ?? false,
|
verein: dbMember.verein ?? false,
|
||||||
marker: dbMember.marker ?? false,
|
marker: dbMember.marker ?? false,
|
||||||
|
creationDate: dbMember.creationDate?.toDateString() ?? "",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,5 +73,8 @@ export function mapMemberDtoToDbMember(member: MemberDto): DbMember {
|
|||||||
? new Date(member.kuendigungzum)
|
? new Date(member.kuendigungzum)
|
||||||
: null,
|
: null,
|
||||||
marker: member.marker,
|
marker: member.marker,
|
||||||
|
creationDate: member.creationDate
|
||||||
|
? new Date(member.creationDate)
|
||||||
|
: null,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
+29
-14
@@ -66,25 +66,40 @@ async function run() {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
if (environment.isDev()) {
|
const isDev = environment.isDev();
|
||||||
//TODO create self-signed for localhost
|
|
||||||
const port = 3000;
|
let cert: https.ServerOptions["cert"];
|
||||||
app.listen(port, () => {
|
let key: https.ServerOptions["key"];
|
||||||
|
let SSL_KEY_PASSWORD: string | undefined;
|
||||||
|
let port: number;
|
||||||
|
|
||||||
|
if (isDev) {
|
||||||
|
// cert = fs.readFileSync("src/cert.pem");
|
||||||
|
// key = fs.readFileSync("src/key.pem");
|
||||||
|
|
||||||
|
port = 3000;
|
||||||
|
app.listen(port, error => {
|
||||||
|
if (error) {
|
||||||
|
console.error("Startup crashed:", error);
|
||||||
|
}
|
||||||
console.log(`Server is running at http://localhost:${port}`);
|
console.log(`Server is running at http://localhost:${port}`);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
//TODO start with HTTPS in production
|
//TODO start with HTTPS in production
|
||||||
const port = 5443;
|
port = 5443;
|
||||||
const SSL_CERT_PATH = process.env["SSL_CERT_PATH"];
|
const SSL_CERT_PATH = process.env["SSL_CERT_PATH"];
|
||||||
const SSL_KEY_PATH = process.env["SSL_KEY_PATH"];
|
const SSL_KEY_PATH = process.env["SSL_KEY_PATH"];
|
||||||
const SSL_KEY_PASSWORD = process.env["SSL_KEY_PASSWORD"];
|
SSL_KEY_PASSWORD = process.env["SSL_KEY_PASSWORD"];
|
||||||
if (!SSL_CERT_PATH || !SSL_KEY_PATH || !SSL_KEY_PASSWORD) {
|
if (!SSL_CERT_PATH || !SSL_KEY_PATH || !SSL_KEY_PASSWORD) {
|
||||||
throw new Error("Missing SSL configuration");
|
throw new Error("Missing SSL configuration");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cert = fs.readFileSync(SSL_CERT_PATH);
|
||||||
|
key = fs.readFileSync(SSL_KEY_PATH);
|
||||||
|
|
||||||
const sslOptions: https.ServerOptions = {
|
const sslOptions: https.ServerOptions = {
|
||||||
cert: fs.readFileSync(SSL_CERT_PATH),
|
cert: cert,
|
||||||
key: fs.readFileSync(SSL_KEY_PATH),
|
key: key,
|
||||||
passphrase: SSL_KEY_PASSWORD,
|
passphrase: SSL_KEY_PASSWORD,
|
||||||
minVersion: "TLSv1.3",
|
minVersion: "TLSv1.3",
|
||||||
maxVersion: "TLSv1.3",
|
maxVersion: "TLSv1.3",
|
||||||
@@ -100,14 +115,14 @@ async function run() {
|
|||||||
https.createServer(sslOptions, app).listen(port, () => {
|
https.createServer(sslOptions, app).listen(port, () => {
|
||||||
console.log(`Server is running on port ${port}`);
|
console.log(`Server is running on port ${port}`);
|
||||||
});
|
});
|
||||||
|
|
||||||
// const h2SslOptions={};
|
|
||||||
// http2.createServer(h2SslOptions, app).listen(port, ()=>{
|
|
||||||
// console.log(`HTTP/2 server is running on port ${port}`);
|
|
||||||
// })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("done");
|
// const h2SslOptions={};
|
||||||
|
// http2.createServer(h2SslOptions, app).listen(port, ()=>{
|
||||||
|
// console.log(`HTTP/2 server is running on port ${port}`);
|
||||||
|
// })
|
||||||
|
|
||||||
|
console.log("setup done");
|
||||||
}
|
}
|
||||||
|
|
||||||
run();
|
run();
|
||||||
|
|||||||
@@ -82,6 +82,9 @@ export class MembersService {
|
|||||||
kuendigungzum: member.kuendigungzum
|
kuendigungzum: member.kuendigungzum
|
||||||
? member.kuendigungzum
|
? member.kuendigungzum
|
||||||
: null,
|
: null,
|
||||||
|
creationDate: member.creationDate
|
||||||
|
? member.creationDate
|
||||||
|
: null,
|
||||||
},
|
},
|
||||||
where: { id: id },
|
where: { id: id },
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -25,37 +25,37 @@
|
|||||||
},
|
},
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@angular/cdk": "21.0.5",
|
"@angular/cdk": "^21.2.1",
|
||||||
"@angular/common": "^21.0.6",
|
"@angular/common": "^21.2.1",
|
||||||
"@angular/compiler": "^21.0.6",
|
"@angular/compiler": "^21.2.1",
|
||||||
"@angular/core": "^21.0.6",
|
"@angular/core": "^21.2.1",
|
||||||
"@angular/forms": "^21.0.6",
|
"@angular/forms": "^21.2.1",
|
||||||
"@angular/material": "21.0.5",
|
"@angular/material": "^21.2.1",
|
||||||
"@angular/material-date-fns-adapter": "^21.0.5",
|
"@angular/material-date-fns-adapter": "^21.2.1",
|
||||||
"@angular/platform-browser": "^21.0.6",
|
"@angular/platform-browser": "^21.2.1",
|
||||||
"@angular/router": "^21.0.6",
|
"@angular/router": "^21.2.1",
|
||||||
"@angular/service-worker": "^21.0.6",
|
"@angular/service-worker": "^21.2.1",
|
||||||
"@date-fns/tz": "^1.4.1",
|
"@date-fns/tz": "^1.4.1",
|
||||||
"@date-fns/utc": "^2.1.1",
|
"@date-fns/utc": "^2.1.1",
|
||||||
"@oazapfts/runtime": "^1.0.4",
|
"@oazapfts/runtime": "^1.2.0",
|
||||||
"date-fns": "^4.1.0",
|
"date-fns": "^4.1.0",
|
||||||
"keycloak-angular": "^20.0.0",
|
"keycloak-angular": "^21.0.0",
|
||||||
"keycloak-js": "^26.2.1",
|
"keycloak-js": "^26.2.3",
|
||||||
"rxjs": "~7.8.2",
|
"rxjs": "^7.8.2",
|
||||||
"tslib": "^2.8.1"
|
"tslib": "^2.8.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@angular/build": "^21.0.4",
|
"@angular/build": "^21.2.1",
|
||||||
"@angular/cli": "^21.0.4",
|
"@angular/cli": "^21.2.1",
|
||||||
"@angular/compiler-cli": "^21.0.6",
|
"@angular/compiler-cli": "^21.2.1",
|
||||||
"@types/jasmine": "~5.1.13",
|
"@types/jasmine": "~6.0.0",
|
||||||
"jasmine-core": "~5.9.0",
|
"jasmine-core": "~6.1.0",
|
||||||
"karma": "~6.4.4",
|
"karma": "~6.4.4",
|
||||||
"karma-chrome-launcher": "~3.2.0",
|
"karma-chrome-launcher": "~3.2.0",
|
||||||
"karma-coverage": "~2.2.1",
|
"karma-coverage": "~2.2.1",
|
||||||
"karma-jasmine": "~5.1.0",
|
"karma-jasmine": "~5.1.0",
|
||||||
"karma-jasmine-html-reporter": "~2.1.0",
|
"karma-jasmine-html-reporter": "~2.2.0",
|
||||||
"oazapfts": "^6.3.1",
|
"oazapfts": "^7.4.2",
|
||||||
"typescript": "~5.9.3"
|
"typescript": "~5.9.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-2
@@ -1,10 +1,13 @@
|
|||||||
<form (submit)="submit(); $event.preventDefault()">
|
<form [formRoot]="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 | datePipe }}</p>
|
<p>Geburtsdatum: {{ member.geburtsdatum | datePipe }}</p>
|
||||||
<mat-form-field>
|
<mat-form-field>
|
||||||
<mat-label>Kündigun zum</mat-label>
|
<mat-label>Kündigun zum</mat-label>
|
||||||
<input matInput [matDatepicker]="picker" [field]="form.kuendigungzum" />
|
<input
|
||||||
|
matInput
|
||||||
|
[matDatepicker]="picker"
|
||||||
|
[formField]="form.kuendigungzum" />
|
||||||
<mat-hint>DD.MM.YYYY</mat-hint>
|
<mat-hint>DD.MM.YYYY</mat-hint>
|
||||||
<mat-datepicker-toggle
|
<mat-datepicker-toggle
|
||||||
matIconSuffix
|
matIconSuffix
|
||||||
|
|||||||
+24
-10
@@ -4,7 +4,12 @@ import {
|
|||||||
inject,
|
inject,
|
||||||
signal,
|
signal,
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
import { Field, form } from '@angular/forms/signals';
|
import {
|
||||||
|
form,
|
||||||
|
FormField,
|
||||||
|
FormRoot,
|
||||||
|
TreeValidationResult,
|
||||||
|
} 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';
|
||||||
@@ -33,7 +38,8 @@ import { EditMemberDialogComponent } from '../edit-member-dialog/edit-member-dia
|
|||||||
MatOptionModule,
|
MatOptionModule,
|
||||||
MatDatepickerModule,
|
MatDatepickerModule,
|
||||||
DatePipePipe,
|
DatePipePipe,
|
||||||
Field,
|
FormField,
|
||||||
|
FormRoot,
|
||||||
],
|
],
|
||||||
templateUrl: './delete-member-dialog.component.html',
|
templateUrl: './delete-member-dialog.component.html',
|
||||||
styleUrl: './delete-member-dialog.component.scss',
|
styleUrl: './delete-member-dialog.component.scss',
|
||||||
@@ -49,16 +55,24 @@ export class DeleteMemberDialogComponent {
|
|||||||
? dateToUTC(this.data.member.kuendigungzum)
|
? dateToUTC(this.data.member.kuendigungzum)
|
||||||
: null,
|
: null,
|
||||||
});
|
});
|
||||||
protected readonly form = form(this.deletionModel);
|
protected readonly form = form(this.deletionModel, {
|
||||||
|
submission: {
|
||||||
|
action: async () => {
|
||||||
|
return await this.submit();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
protected readonly member = this.data.member;
|
protected readonly member = this.data.member;
|
||||||
|
|
||||||
protected submit() {
|
protected async submit(): Promise<TreeValidationResult> {
|
||||||
const date = this.deletionModel().kuendigungzum;
|
const date = this.deletionModel().kuendigungzum;
|
||||||
api.scheduleDeletion(
|
await api
|
||||||
this.member.id,
|
.scheduleDeletion(
|
||||||
date ? dateToLocal(date).toISOString() : '',
|
this.member.id,
|
||||||
).then(() => {
|
date ? dateToLocal(date).toISOString() : '',
|
||||||
this.dialogRef.close();
|
)
|
||||||
});
|
.then(() => {
|
||||||
|
this.dialogRef.close();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+20
-8
@@ -1,23 +1,32 @@
|
|||||||
<form (submit)="submit(); $event.preventDefault()">
|
<form [formRoot]="form">
|
||||||
<mat-form-field>
|
<mat-form-field>
|
||||||
<mat-label>Vorname</mat-label>
|
<mat-label>Vorname</mat-label>
|
||||||
<input matInput placeholder="Vorname" [field]="form.vorname" />
|
<input matInput placeholder="Vorname" [formField]="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" [field]="form.nachname" />
|
<input matInput placeholder="Nachname" [formField]="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" [field]="form.geschlecht" />
|
<input
|
||||||
|
matInput
|
||||||
|
placeholder="Geschlecht"
|
||||||
|
[formField]="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" [field]="form.passnummer" />
|
<input
|
||||||
|
matInput
|
||||||
|
placeholder="Passnummer"
|
||||||
|
[formField]="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" [field]="form.geburtsdatum" />
|
<input
|
||||||
|
matInput
|
||||||
|
[matDatepicker]="picker"
|
||||||
|
[formField]="form.geburtsdatum" />
|
||||||
<mat-hint>DD.MM.YYYY</mat-hint>
|
<mat-hint>DD.MM.YYYY</mat-hint>
|
||||||
<mat-datepicker-toggle
|
<mat-datepicker-toggle
|
||||||
matIconSuffix
|
matIconSuffix
|
||||||
@@ -26,11 +35,14 @@
|
|||||||
</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" [field]="form.geburtsort" />
|
<input
|
||||||
|
matInput
|
||||||
|
placeholder="Geburtsort"
|
||||||
|
[formField]="form.geburtsort" />
|
||||||
</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 [field]="form.verein">
|
<mat-select matInput [formField]="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>
|
||||||
|
|||||||
+17
-5
@@ -4,7 +4,12 @@ import {
|
|||||||
inject,
|
inject,
|
||||||
signal,
|
signal,
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
import { Field, form } from '@angular/forms/signals';
|
import {
|
||||||
|
form,
|
||||||
|
FormField,
|
||||||
|
FormRoot,
|
||||||
|
TreeValidationResult,
|
||||||
|
} 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';
|
||||||
@@ -27,7 +32,8 @@ import { MemberDtoHelper, MemberEditForm } from '../../util/member.js';
|
|||||||
MatSelectModule,
|
MatSelectModule,
|
||||||
MatOptionModule,
|
MatOptionModule,
|
||||||
MatDatepickerModule,
|
MatDatepickerModule,
|
||||||
Field,
|
FormField,
|
||||||
|
FormRoot,
|
||||||
],
|
],
|
||||||
templateUrl: './edit-member-dialog.component.html',
|
templateUrl: './edit-member-dialog.component.html',
|
||||||
styleUrl: './edit-member-dialog.component.scss',
|
styleUrl: './edit-member-dialog.component.scss',
|
||||||
@@ -43,13 +49,19 @@ export class EditMemberDialogComponent {
|
|||||||
private readonly memberModel = signal<MemberEditForm>(
|
private readonly memberModel = signal<MemberEditForm>(
|
||||||
this.memberDtoHelper.editMemberForm(this.data.member),
|
this.memberDtoHelper.editMemberForm(this.data.member),
|
||||||
);
|
);
|
||||||
protected readonly form = form(this.memberModel);
|
protected readonly form = form(this.memberModel, {
|
||||||
|
submission: {
|
||||||
|
action: async () => {
|
||||||
|
return await this.submit();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
protected submit() {
|
protected async submit(): Promise<TreeValidationResult> {
|
||||||
const member: api.MemberDto = this.memberDtoHelper.memberFromSignal(
|
const member: api.MemberDto = this.memberDtoHelper.memberFromSignal(
|
||||||
this.memberModel(),
|
this.memberModel(),
|
||||||
);
|
);
|
||||||
api.updateMember(member).then(() => {
|
await api.updateMember(member).then(() => {
|
||||||
this.dialogRef.close();
|
this.dialogRef.close();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,23 +1,23 @@
|
|||||||
<h1>Neues Mitglied anlegen</h1>
|
<h1>Neues Mitglied anlegen</h1>
|
||||||
<form (submit)="onSubmit(); $event.preventDefault()">
|
<form [formRoot]="memberForm">
|
||||||
<mat-form-field>
|
<mat-form-field>
|
||||||
<mat-label>Vorname</mat-label>
|
<mat-label>Vorname</mat-label>
|
||||||
<input matInput [field]="memberForm.vorname" />
|
<input matInput [formField]="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 [field]="memberForm.nachname" />
|
<input matInput [formField]="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 [field]="memberForm.geschlecht" />
|
<input matInput [formField]="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"
|
||||||
[field]="memberForm.geburtsdatum" />
|
[formField]="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,16 +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 [field]="memberForm.geburtsort" />
|
<input matInput [formField]="memberForm.geburtsort" />
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
<mat-checkbox [field]="memberForm.verein">Verein Beigetreten?</mat-checkbox>
|
<mat-checkbox [formField]="memberForm.verein"
|
||||||
|
>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 [field]="memberForm.passnummer" />
|
<input matInput [formField]="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 [field]="memberForm.kontakt" />
|
<input matInput [formField]="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" type="submit">Hinzufügen</button>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -4,7 +4,12 @@ import {
|
|||||||
inject,
|
inject,
|
||||||
signal,
|
signal,
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
import { Field, form } from '@angular/forms/signals';
|
import {
|
||||||
|
form,
|
||||||
|
FormField,
|
||||||
|
FormRoot,
|
||||||
|
TreeValidationResult,
|
||||||
|
} 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';
|
||||||
@@ -24,7 +29,8 @@ import { MemberCreateForm, MemberDtoHelper } from '../../util/member.js';
|
|||||||
MatInputModule,
|
MatInputModule,
|
||||||
MatCheckboxModule,
|
MatCheckboxModule,
|
||||||
MatButtonModule,
|
MatButtonModule,
|
||||||
Field,
|
FormField,
|
||||||
|
FormRoot,
|
||||||
],
|
],
|
||||||
templateUrl: './new-member.component.html',
|
templateUrl: './new-member.component.html',
|
||||||
styleUrl: './new-member.component.scss',
|
styleUrl: './new-member.component.scss',
|
||||||
@@ -39,18 +45,21 @@ export class NewMemberComponent {
|
|||||||
private readonly memberModel = signal<MemberCreateForm>(
|
private readonly memberModel = signal<MemberCreateForm>(
|
||||||
this.memberDtoHelper.newMemberForm(),
|
this.memberDtoHelper.newMemberForm(),
|
||||||
);
|
);
|
||||||
protected readonly memberForm = form(this.memberModel);
|
protected readonly memberForm = form(this.memberModel, {
|
||||||
|
submission: {
|
||||||
protected onSubmit() {
|
action: async () => {
|
||||||
if (!this.memberForm().valid()) {
|
return await this.submit();
|
||||||
return;
|
},
|
||||||
}
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
protected async submit(): Promise<TreeValidationResult> {
|
||||||
const newMember = this.memberDtoHelper.newMemberFromSignal(
|
const newMember = this.memberDtoHelper.newMemberFromSignal(
|
||||||
this.memberModel(),
|
this.memberModel(),
|
||||||
);
|
);
|
||||||
|
|
||||||
api.createMember(newMember)
|
await api
|
||||||
|
.createMember(newMember)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
console.log('Successfully added Member');
|
console.log('Successfully added Member');
|
||||||
this.dialogRef.close();
|
this.dialogRef.close();
|
||||||
|
|||||||
@@ -20,16 +20,16 @@
|
|||||||
<label>Filter:</label>
|
<label>Filter:</label>
|
||||||
<input
|
<input
|
||||||
matInput
|
matInput
|
||||||
[field]="filterForm.filterString"
|
[formField]="filterForm.filterString"
|
||||||
placeholder="Filter" />
|
placeholder="Filter" />
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
<div class="status-filters">
|
<div class="status-filters">
|
||||||
<mat-radio-group [field]="filterForm.status">
|
<mat-radio-group [formField]="filterForm.status">
|
||||||
<mat-radio-button [value]="MemberStatusFilter.ACTIVE"
|
<mat-radio-button [value]="MemberStatusFilter.ACTIVE"
|
||||||
>Aktiv</mat-radio-button
|
>Aktiv zum</mat-radio-button
|
||||||
>
|
>
|
||||||
<mat-radio-button [value]="MemberStatusFilter.INACTIVE"
|
<mat-radio-button [value]="MemberStatusFilter.INACTIVE"
|
||||||
>Inaktiv</mat-radio-button
|
>Gekündigt seit</mat-radio-button
|
||||||
>
|
>
|
||||||
<mat-radio-button [value]="MemberStatusFilter.ALL"
|
<mat-radio-button [value]="MemberStatusFilter.ALL"
|
||||||
>Alle</mat-radio-button
|
>Alle</mat-radio-button
|
||||||
@@ -42,7 +42,7 @@
|
|||||||
<input
|
<input
|
||||||
matInput
|
matInput
|
||||||
[matDatepicker]="picker"
|
[matDatepicker]="picker"
|
||||||
[field]="filterForm.statusDate"
|
[formField]="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
|
||||||
@@ -52,7 +52,7 @@
|
|||||||
<mat-datepicker #picker></mat-datepicker>
|
<mat-datepicker #picker></mat-datepicker>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
</div>
|
</div>
|
||||||
<mat-checkbox [field]="filterForm.onlyWithID"
|
<mat-checkbox [formField]="filterForm.onlyWithID"
|
||||||
>Nur mit Passnummer</mat-checkbox
|
>Nur mit Passnummer</mat-checkbox
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
@@ -110,7 +110,7 @@
|
|||||||
</ng-container>
|
</ng-container>
|
||||||
<ng-container matColumnDef="geburtsdatum">
|
<ng-container matColumnDef="geburtsdatum">
|
||||||
<th mat-header-cell mat-sort-header *matHeaderCellDef>
|
<th mat-header-cell mat-sort-header *matHeaderCellDef>
|
||||||
Geburtsdatum
|
Geburtsdatum Geboren am
|
||||||
</th>
|
</th>
|
||||||
<td mat-cell *matCellDef="let element">
|
<td mat-cell *matCellDef="let element">
|
||||||
{{ element.geburtsdatum | datePipe }}
|
{{ element.geburtsdatum | datePipe }}
|
||||||
@@ -123,9 +123,7 @@
|
|||||||
<td mat-cell *matCellDef="let element">{{ element.geburtsort }}</td>
|
<td mat-cell *matCellDef="let element">{{ element.geburtsort }}</td>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<ng-container matColumnDef="verein">
|
<ng-container matColumnDef="verein">
|
||||||
<th mat-header-cell mat-sort-header *matHeaderCellDef>
|
<th mat-header-cell mat-sort-header *matHeaderCellDef>Verein?</th>
|
||||||
Vereinsmitgliedschaft
|
|
||||||
</th>
|
|
||||||
<td mat-cell *matCellDef="let element">
|
<td mat-cell *matCellDef="let element">
|
||||||
@if(element.verein){
|
@if(element.verein){
|
||||||
<mat-icon
|
<mat-icon
|
||||||
@@ -149,12 +147,18 @@
|
|||||||
|
|
||||||
<ng-container matColumnDef="kuendigungzum">
|
<ng-container matColumnDef="kuendigungzum">
|
||||||
<th mat-header-cell mat-sort-header *matHeaderCellDef>
|
<th mat-header-cell mat-sort-header *matHeaderCellDef>
|
||||||
Kündigung zum
|
Kündigung
|
||||||
</th>
|
</th>
|
||||||
<td mat-cell *matCellDef="let element">
|
<td mat-cell *matCellDef="let element">
|
||||||
{{ (element.kuendigungzum | datePipe) ?? "-" }}
|
{{ (element.kuendigungzum | datePipe) ?? "-" }}
|
||||||
</td>
|
</td>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
<ng-container matColumnDef="creationDate">
|
||||||
|
<th mat-header-cell mat-sort-header *matHeaderCellDef>Beitritt</th>
|
||||||
|
<td mat-cell *matCellDef="let element">
|
||||||
|
{{ element.creationDate | datePipe }}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
<ng-container matColumnDef="delete">
|
<ng-container matColumnDef="delete">
|
||||||
<th mat-header-cell *matHeaderCellDef>Entf.</th>
|
<th mat-header-cell *matHeaderCellDef>Entf.</th>
|
||||||
@@ -187,7 +191,8 @@
|
|||||||
<tr
|
<tr
|
||||||
mat-row
|
mat-row
|
||||||
*matRowDef="let row; columns: displayedColumns"
|
*matRowDef="let row; columns: displayedColumns"
|
||||||
class="table-row"></tr>
|
class="table-row"
|
||||||
|
[class.expired]="isInPast(row.kuendigungzum)"></tr>
|
||||||
<tr mat-footer-row *matFooterRowDef="['totalCount']; sticky: true"></tr>
|
<tr mat-footer-row *matFooterRowDef="['totalCount']; sticky: true"></tr>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -49,3 +49,16 @@ td {
|
|||||||
.table-row:nth-child(odd) {
|
.table-row:nth-child(odd) {
|
||||||
background-color: var(--mat-sys-surface);
|
background-color: var(--mat-sys-surface);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.table-row.expired {
|
||||||
|
//background-color: lightsalmon;
|
||||||
|
filter: sepia(50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.positive {
|
||||||
|
color: green;
|
||||||
|
}
|
||||||
|
|
||||||
|
.negative {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import {
|
|||||||
signal,
|
signal,
|
||||||
viewChild,
|
viewChild,
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
import { Field, form } from '@angular/forms/signals';
|
import { form, FormField } 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';
|
||||||
@@ -30,6 +30,44 @@ import { DeleteMemberDialogComponent } from '../delete-member-dialog/delete-memb
|
|||||||
import { EditMemberDialogComponent } from '../edit-member-dialog/edit-member-dialog.component.js';
|
import { EditMemberDialogComponent } from '../edit-member-dialog/edit-member-dialog.component.js';
|
||||||
import { NewMemberComponent } from '../new-member/new-member.component';
|
import { NewMemberComponent } from '../new-member/new-member.component';
|
||||||
|
|
||||||
|
class MemberFilters {
|
||||||
|
public filterString: string;
|
||||||
|
public status: MemberStatusFilter;
|
||||||
|
public statusDate: Date | null;
|
||||||
|
public onlyWithID: boolean;
|
||||||
|
|
||||||
|
public constructor(data: {
|
||||||
|
filterString: string;
|
||||||
|
status: MemberStatusFilter;
|
||||||
|
statusDate: Date | null;
|
||||||
|
onlyWithID: boolean;
|
||||||
|
}) {
|
||||||
|
this.filterString = data.filterString;
|
||||||
|
this.status = data.status;
|
||||||
|
this.statusDate = data.statusDate;
|
||||||
|
this.onlyWithID = data.onlyWithID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public encode(): string {
|
||||||
|
return `${this.filterString?.replaceAll(';', '') ?? ''};${this.status};${this.statusDate?.toISOString()};${this.onlyWithID}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static decode(encoded: string): MemberFilters {
|
||||||
|
const parts = encoded.split(';');
|
||||||
|
const statusDateStr = parts[2];
|
||||||
|
|
||||||
|
return new MemberFilters({
|
||||||
|
filterString: parts[0],
|
||||||
|
status: parts[1] as MemberStatusFilter,
|
||||||
|
statusDate:
|
||||||
|
statusDateStr && statusDateStr != ''
|
||||||
|
? new Date(statusDateStr)
|
||||||
|
: null,
|
||||||
|
onlyWithID: parts[3] === 'true',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
enum MemberStatusFilter {
|
enum MemberStatusFilter {
|
||||||
ACTIVE = 'ACTIVE',
|
ACTIVE = 'ACTIVE',
|
||||||
INACTIVE = 'INACTIVE',
|
INACTIVE = 'INACTIVE',
|
||||||
@@ -53,7 +91,7 @@ enum MemberStatusFilter {
|
|||||||
MatCheckbox,
|
MatCheckbox,
|
||||||
MatRadioModule,
|
MatRadioModule,
|
||||||
MatSortModule,
|
MatSortModule,
|
||||||
Field,
|
FormField,
|
||||||
],
|
],
|
||||||
templateUrl: './view-members.html',
|
templateUrl: './view-members.html',
|
||||||
styleUrl: './view-members.scss',
|
styleUrl: './view-members.scss',
|
||||||
@@ -66,7 +104,7 @@ export class ViewMembers implements AfterViewInit {
|
|||||||
private readonly filterModel = signal({
|
private readonly filterModel = signal({
|
||||||
filterString: '',
|
filterString: '',
|
||||||
status: MemberStatusFilter.ACTIVE,
|
status: MemberStatusFilter.ACTIVE,
|
||||||
statusDate: null as Date | null,
|
statusDate: new Date() as Date | null,
|
||||||
onlyWithID: false,
|
onlyWithID: false,
|
||||||
});
|
});
|
||||||
protected readonly filterForm = form(this.filterModel);
|
protected readonly filterForm = form(this.filterModel);
|
||||||
@@ -89,6 +127,7 @@ export class ViewMembers implements AfterViewInit {
|
|||||||
'geburtsort',
|
'geburtsort',
|
||||||
'verein',
|
'verein',
|
||||||
'kontakt',
|
'kontakt',
|
||||||
|
'creationDate',
|
||||||
'kuendigungzum',
|
'kuendigungzum',
|
||||||
'delete',
|
'delete',
|
||||||
];
|
];
|
||||||
@@ -139,35 +178,41 @@ export class ViewMembers implements AfterViewInit {
|
|||||||
data: api.MemberDto,
|
data: api.MemberDto,
|
||||||
encodedFilter: string,
|
encodedFilter: string,
|
||||||
) => {
|
) => {
|
||||||
const parts = encodedFilter.split(';');
|
const filters = MemberFilters.decode(encodedFilter);
|
||||||
const filter = parts[0];
|
|
||||||
const status = parts[1] as MemberStatusFilter;
|
|
||||||
const statusDateStr = parts[2];
|
|
||||||
const onlyWithID = parts[3] === 'true';
|
|
||||||
|
|
||||||
const currentDate =
|
if (filters.statusDate) {
|
||||||
statusDateStr && statusDateStr != ''
|
const cancellationDate = data.kuendigungzum
|
||||||
? new Date(statusDateStr)
|
? new Date(data.kuendigungzum)
|
||||||
: null;
|
: null;
|
||||||
const cancellationDate = data.kuendigungzum
|
|
||||||
? new Date(data.kuendigungzum)
|
switch (filters.status) {
|
||||||
: null;
|
case MemberStatusFilter.ACTIVE:
|
||||||
if (
|
const creationDate = data.creationDate
|
||||||
currentDate &&
|
? new Date(data.creationDate)
|
||||||
cancellationDate &&
|
: null;
|
||||||
((status === MemberStatusFilter.ACTIVE &&
|
if (
|
||||||
cancellationDate <= currentDate) ||
|
(cancellationDate &&
|
||||||
(status === MemberStatusFilter.INACTIVE &&
|
cancellationDate <= filters.statusDate) ||
|
||||||
cancellationDate > currentDate))
|
(creationDate && creationDate > filters.statusDate)
|
||||||
) {
|
) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (status === MemberStatusFilter.INACTIVE && !cancellationDate) {
|
break;
|
||||||
return false;
|
case MemberStatusFilter.INACTIVE:
|
||||||
|
if (
|
||||||
|
!cancellationDate ||
|
||||||
|
cancellationDate < filters.statusDate
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case MemberStatusFilter.ALL:
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
onlyWithID &&
|
filters.onlyWithID &&
|
||||||
(!data.passnummer || data.passnummer.trim() === '')
|
(!data.passnummer || data.passnummer.trim() === '')
|
||||||
) {
|
) {
|
||||||
return false;
|
return false;
|
||||||
@@ -186,14 +231,23 @@ export class ViewMembers implements AfterViewInit {
|
|||||||
letztePruefung: this.datePipe.transform(
|
letztePruefung: this.datePipe.transform(
|
||||||
data.letztePruefung ?? undefined,
|
data.letztePruefung ?? undefined,
|
||||||
),
|
),
|
||||||
|
creationDate: this.datePipe.transform(
|
||||||
|
data.creationDate ?? undefined,
|
||||||
|
),
|
||||||
},
|
},
|
||||||
filter,
|
filters.filterString,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
effect(() => {
|
effect(() => {
|
||||||
const filters = this.filterModel();
|
const filtersSignal = this.filterModel();
|
||||||
this.dataSource.filter = `${filters.filterString?.replaceAll(';', '') ?? ''};${filters.status};${filters.statusDate?.toISOString()};${filters.onlyWithID}`;
|
const filters = new MemberFilters({
|
||||||
|
filterString: filtersSignal.filterString,
|
||||||
|
status: filtersSignal.status,
|
||||||
|
statusDate: filtersSignal.statusDate,
|
||||||
|
onlyWithID: filtersSignal.onlyWithID,
|
||||||
|
});
|
||||||
|
this.dataSource.filter = filters.encode();
|
||||||
});
|
});
|
||||||
|
|
||||||
this.updateData();
|
this.updateData();
|
||||||
@@ -234,7 +288,7 @@ export class ViewMembers implements AfterViewInit {
|
|||||||
headerRow.appendChild(clubnrHeader);
|
headerRow.appendChild(clubnrHeader);
|
||||||
|
|
||||||
const birthdayHeader = document.createElement('th');
|
const birthdayHeader = document.createElement('th');
|
||||||
birthdayHeader.innerText = 'Geburtsdatum';
|
birthdayHeader.innerText = 'Geboren am';
|
||||||
headerRow.appendChild(birthdayHeader);
|
headerRow.appendChild(birthdayHeader);
|
||||||
|
|
||||||
const geburtsortHeader = document.createElement('th');
|
const geburtsortHeader = document.createElement('th');
|
||||||
@@ -242,7 +296,7 @@ export class ViewMembers implements AfterViewInit {
|
|||||||
headerRow.appendChild(geburtsortHeader);
|
headerRow.appendChild(geburtsortHeader);
|
||||||
|
|
||||||
const cancelDateHeader = document.createElement('th');
|
const cancelDateHeader = document.createElement('th');
|
||||||
cancelDateHeader.innerText = 'Kündigungsdatum';
|
cancelDateHeader.innerText = 'Kündigung';
|
||||||
headerRow.appendChild(cancelDateHeader);
|
headerRow.appendChild(cancelDateHeader);
|
||||||
|
|
||||||
table.appendChild(headerRow);
|
table.appendChild(headerRow);
|
||||||
@@ -323,4 +377,11 @@ export class ViewMembers implements AfterViewInit {
|
|||||||
document.body.appendChild(printWindow);
|
document.body.appendChild(printWindow);
|
||||||
printWindow.contentWindow?.print();
|
printWindow.contentWindow?.print();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected isInPast(date: string): boolean {
|
||||||
|
if (!date) return false;
|
||||||
|
const today = new Date();
|
||||||
|
const cancellationDate = new Date(date);
|
||||||
|
return cancellationDate <= today;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -116,7 +116,7 @@ export class Authentication {
|
|||||||
return await this.keycloak.login({
|
return await this.keycloak.login({
|
||||||
redirectUri: redirectUri,
|
redirectUri: redirectUri,
|
||||||
locale: 'de-DE',
|
locale: 'de-DE',
|
||||||
//TODO loginHint:''
|
//TODO loginHint:''
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ interface MemberForm {
|
|||||||
|
|
||||||
export interface MemberCreateForm extends Omit<
|
export interface MemberCreateForm extends Omit<
|
||||||
MemberCreateArgs,
|
MemberCreateArgs,
|
||||||
'geburtsdatum'
|
'geburtsdatum' | 'creationDate'
|
||||||
> {
|
> {
|
||||||
geburtsdatum: Date | null;
|
geburtsdatum: Date | null;
|
||||||
}
|
}
|
||||||
|
|||||||
Generated
+1707
-2682
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user