frontend-toegevoegd
This commit is contained in:
162
agiledancers-fe/src/app/schools/school-page.ts
Normal file
162
agiledancers-fe/src/app/schools/school-page.ts
Normal file
@@ -0,0 +1,162 @@
|
||||
import { Component, computed, DestroyRef, inject, OnInit, signal } from '@angular/core';
|
||||
import { DatePipe } from '@angular/common';
|
||||
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
||||
import { ActivatedRoute, Router, RouterLink, RouterLinkActive } from '@angular/router';
|
||||
import { catchError, EMPTY, firstValueFrom, switchMap } from 'rxjs';
|
||||
import {
|
||||
DanceGroup,
|
||||
EditorTarget,
|
||||
GROUP_STYLES,
|
||||
GROUP_TYPES,
|
||||
ResourceKind,
|
||||
SchoolData,
|
||||
} from '../core/api-models';
|
||||
import { apiError } from '../core/api-error';
|
||||
import { SchoolApi } from '../core/school-api';
|
||||
import { EntityEditor } from '../shared/entity-editor';
|
||||
import { ConfirmDialog } from '../shared/confirm-dialog';
|
||||
import { GroupMembers } from '../shared/group-members';
|
||||
|
||||
interface DeleteTarget {
|
||||
kind: ResourceKind;
|
||||
id: number;
|
||||
name: string;
|
||||
explanation: string;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'app-school-page',
|
||||
imports: [DatePipe, RouterLink, RouterLinkActive, EntityEditor, ConfirmDialog, GroupMembers],
|
||||
templateUrl: './school-page.html',
|
||||
})
|
||||
export class SchoolPage implements OnInit {
|
||||
private readonly api = inject(SchoolApi);
|
||||
private readonly route = inject(ActivatedRoute);
|
||||
private readonly router = inject(Router);
|
||||
private readonly destroyRef = inject(DestroyRef);
|
||||
readonly data = signal<SchoolData | null>(null);
|
||||
readonly section = signal('overview');
|
||||
readonly loading = signal(true);
|
||||
readonly error = signal('');
|
||||
readonly notice = signal('');
|
||||
readonly editor = signal<EditorTarget | null>(null);
|
||||
readonly members = signal<DanceGroup | null>(null);
|
||||
readonly deleting = signal<DeleteTarget | null>(null);
|
||||
readonly deleteBusy = signal(false);
|
||||
readonly deleteError = signal('');
|
||||
readonly search = signal('');
|
||||
readonly dancers = computed(() =>
|
||||
[...(this.data()?.dancers ?? [])]
|
||||
.filter((d) => d.name.toLocaleLowerCase('nl').includes(this.search().toLocaleLowerCase('nl')))
|
||||
.sort((a, b) => a.name.localeCompare(b.name, 'nl')),
|
||||
);
|
||||
readonly shows = computed(() =>
|
||||
[...(this.data()?.shows ?? [])].sort((a, b) => a.date.localeCompare(b.date)),
|
||||
);
|
||||
readonly today = new Date().toLocaleDateString('en-CA');
|
||||
readonly upcoming = computed(() => this.shows().find((show) => show.date >= this.today));
|
||||
readonly tabs = [
|
||||
{ value: 'overview', label: 'Overzicht' },
|
||||
{ value: 'dancers', label: 'Dansers' },
|
||||
{ value: 'groups', label: 'Dansgroepen' },
|
||||
{ value: 'shows', label: 'Shows' },
|
||||
];
|
||||
|
||||
ngOnInit(): void {
|
||||
this.route.paramMap
|
||||
.pipe(
|
||||
switchMap((params) => {
|
||||
this.loading.set(true);
|
||||
this.error.set('');
|
||||
this.search.set('');
|
||||
this.editor.set(null);
|
||||
this.members.set(null);
|
||||
this.deleting.set(null);
|
||||
this.section.set(params.get('section') ?? 'overview');
|
||||
return this.api.schoolData(Number(params.get('schoolId'))).pipe(
|
||||
catchError((error) => {
|
||||
this.data.set(null);
|
||||
this.error.set(apiError(error));
|
||||
this.loading.set(false);
|
||||
return EMPTY;
|
||||
}),
|
||||
);
|
||||
}),
|
||||
takeUntilDestroyed(this.destroyRef),
|
||||
)
|
||||
.subscribe((data) => {
|
||||
this.data.set(data);
|
||||
this.loading.set(false);
|
||||
});
|
||||
}
|
||||
async reload(): Promise<void> {
|
||||
const id = this.data()?.school.id;
|
||||
if (id === undefined) return;
|
||||
this.error.set('');
|
||||
try {
|
||||
const data = await firstValueFrom(this.api.schoolData(id));
|
||||
if (this.data()?.school.id === id) this.data.set(data);
|
||||
} catch (error: unknown) {
|
||||
this.error.set(apiError(error));
|
||||
}
|
||||
}
|
||||
retry(): void {
|
||||
window.location.reload();
|
||||
}
|
||||
async saved(): Promise<void> {
|
||||
this.editor.set(null);
|
||||
this.notice.set('Wijzigingen opgeslagen.');
|
||||
await this.reload();
|
||||
}
|
||||
groupType(value: string): string {
|
||||
return GROUP_TYPES.find((option) => option.value === value)?.label ?? value;
|
||||
}
|
||||
groupStyle(value: string): string {
|
||||
return GROUP_STYLES.find((option) => option.value === value)?.label ?? value;
|
||||
}
|
||||
groupNames(dancerId: number): string {
|
||||
return (
|
||||
this.data()
|
||||
?.groups.filter((group) => group.dancerIds.includes(dancerId))
|
||||
.map((group) => group.category)
|
||||
.join(', ') || 'Nog geen groep'
|
||||
);
|
||||
}
|
||||
askDelete(target: DeleteTarget): void {
|
||||
this.deleteError.set('');
|
||||
this.deleting.set(target);
|
||||
}
|
||||
membershipChanged(ids: number[]): void {
|
||||
const groupId = this.members()?.id;
|
||||
this.data.update((data) =>
|
||||
data
|
||||
? {
|
||||
...data,
|
||||
groups: data.groups.map((group) =>
|
||||
group.id === groupId ? { ...group, dancerIds: [...ids] } : group,
|
||||
),
|
||||
}
|
||||
: data,
|
||||
);
|
||||
}
|
||||
async confirmDelete(): Promise<void> {
|
||||
const target = this.deleting();
|
||||
const schoolId = this.data()?.school.id;
|
||||
if (!target || schoolId === undefined || this.deleteBusy()) return;
|
||||
this.deleteBusy.set(true);
|
||||
this.deleteError.set('');
|
||||
try {
|
||||
await firstValueFrom(this.api.delete(target.kind, schoolId, target.id));
|
||||
this.deleting.set(null);
|
||||
if (target.kind === 'school') await this.router.navigate(['/schools']);
|
||||
else {
|
||||
this.notice.set(`${target.name} is verwijderd.`);
|
||||
await this.reload();
|
||||
}
|
||||
} catch (error: unknown) {
|
||||
this.deleteError.set(apiError(error));
|
||||
} finally {
|
||||
this.deleteBusy.set(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user