import { Component, inject, input, OnInit, output, signal } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { firstValueFrom, Observable } from 'rxjs'; import { EditorTarget, GROUP_STYLES, GROUP_TYPES, GroupStyle, GroupType, DayPart, Show, } from '../core/api-models'; import { SchoolApi } from '../core/school-api'; import { apiError } from '../core/api-error'; import { DialogFrame } from './dialog-frame'; @Component({ selector: 'app-entity-editor', imports: [FormsModule, DialogFrame], template: ` @if (error()) { {{ error() }} } @if (target().kind === 'group') { Naam / categorie Type @for (option of types; track option.value) { {{ option.label }} } Leeftijd vanaf Leeftijd tot en met Dansstijlen (optioneel) @for (style of styles; track style.value) { {{ style.label }} } } @else { Naam @if (target().kind === 'dancer') { Geboortedatum } @if (target().kind === 'show') { Gedateerde dagdelen (maximaal zes) @for (part of dayParts; track $index; let i = $index) { Datum Dagdeel Ochtend Middag Avond Dagdeel verwijderen } = 6" (click)="addPart()" > Dagdeel toevoegen @if (dayParts.length) { Gewenst aantal dagdelen (x) Gewone groepen dansen in precies x dagdelen. Kleuters eenmaal; showcases ieder dagdeel. Is x groter dan het aantal dagdelen, corrigeer x vóór inplannen. } @else { Zonder dagdelen blijft deze show ongepland. } } } Annuleren {{ busy() ? 'Opslaan…' : 'Opslaan' }} `, }) export class EntityEditor implements OnInit { readonly target = input.required(); readonly revision = input(); private originalRevision?: number; readonly schoolId = input(); readonly shows = input([]); readonly closed = output(); readonly saved = output(); readonly busy = signal(false); readonly error = signal(''); readonly types = GROUP_TYPES; readonly styles = GROUP_STYLES; readonly today = new Date().toLocaleDateString('en-CA'); private readonly api = inject(SchoolApi); dayParts: DayPart[] = []; private preferredDayPartsValue = 2; private preferredDayPartsEdited = false; get preferredDayParts(): number { return this.preferredDayPartsValue; } set preferredDayParts(value: number) { this.preferredDayPartsValue = value; this.preferredDayPartsEdited = true; } name = ''; date = ''; type: GroupType = 'REGULAR'; startAge: number | null = 0; endAge: number | null = 18; selectedStyles: GroupStyle[] = []; get title(): string { const labels = { school: 'Dansschool', dancer: 'Danser', group: 'Dansgroep', show: 'Show' }; return `${labels[this.target().kind]} ${this.target().value ? 'bewerken' : 'toevoegen'}`; } ngOnInit(): void { this.originalRevision = this.revision(); const target = this.target(); if (!target.value) return; if (target.kind === 'group') { this.name = target.value.category; this.type = target.value.type; this.startAge = target.value.startAge; this.endAge = target.value.endAge; this.selectedStyles = [...target.value.styles]; } else { this.name = target.value.name; if (target.kind === 'dancer') this.date = target.value.birthDate; if (target.kind === 'show') { this.dayParts = target.value.dayParts.map((p) => ({ ...p })); this.preferredDayParts = target.value.preferredDayParts; } } } addPart(): void { if (this.dayParts.length >= 6) return; this.dayParts.push({ date: '', type: 'AFTERNOON', danceGroupIds: [] }); if (!this.target().value && !this.preferredDayPartsEdited) this.preferredDayPartsValue = Math.min(2, this.dayParts.length); } removePart(index: number): void { this.dayParts.splice(index, 1); } validCounts(): boolean { return ( Number.isInteger(this.preferredDayParts) && this.preferredDayParts >= 1 && this.preferredDayParts <= 6 ); } toggleStyle(style: GroupStyle, checked: boolean): void { this.selectedStyles = checked ? [...this.selectedStyles, style] : this.selectedStyles.filter((value) => value !== style); } validDetails(): boolean { if (!this.name.trim()) return false; if (this.target().kind === 'dancer') return !!this.date && this.date <= this.today; if (this.target().kind === 'show') return ( this.dayParts.length <= 6 && this.dayParts.every((p) => !!p.date && !!p.type) && new Set(this.dayParts.map((p) => p.date + ':' + p.type)).size === this.dayParts.length && (!this.dayParts.length || this.validCounts()) ); if (this.target().kind === 'group') return ( this.startAge !== null && this.endAge !== null && Number.isInteger(this.startAge) && Number.isInteger(this.endAge) && this.startAge >= 0 && this.endAge >= this.startAge ); return true; } async save(): Promise { if (this.busy() || !this.validDetails()) return; const current = this.target(); let affected: Show[] = []; if (current.kind === 'group' && (!current.value || current.value.type !== this.type)) affected = this.shows(); if ( current.kind === 'show' && current.value && (JSON.stringify(current.value.dayParts.map((p) => ({ date: p.date, type: p.type }))) !== JSON.stringify(this.dayParts.map((p) => ({ date: p.date, type: p.type }))) || current.value.preferredDayParts !== this.preferredDayParts) ) affected = [current.value]; const names = affected .filter((s) => s.dayParts.some((p) => p.danceGroupIds.length)) .map((s) => s.name); if ( names.length && !window.confirm( `Deze wijziging kan de planning ongeldig maken van: ${names.join(', ')}. Doorgaan?`, ) ) return; this.busy.set(true); this.error.set(''); const target = this.target(); const id = target.value?.id; const schoolId = this.schoolId(); let request: Observable; switch (target.kind) { case 'school': request = this.api.saveSchool({ name: this.name.trim() }, id, this.originalRevision); break; case 'dancer': request = this.api.saveDancer( schoolId!, { name: this.name.trim(), birthDate: this.date }, id, this.originalRevision, ); break; case 'group': request = this.api.saveGroup( schoolId!, { category: this.name.trim(), type: this.type, startAge: this.startAge!, endAge: this.endAge!, styles: this.selectedStyles, }, id, this.originalRevision, ); break; case 'show': request = this.api.saveShow( schoolId!, { name: this.name.trim(), dayParts: this.dayParts.map((p) => ({ date: p.date, type: p.type })), preferredDayParts: this.preferredDayParts, }, id, this.originalRevision, ); break; } try { await firstValueFrom(request); this.saved.emit(); } catch (error: unknown) { this.error.set(apiError(error)); } finally { this.busy.set(false); } } }
{{ error() }}
Gewone groepen dansen in precies x dagdelen. Kleuters eenmaal; showcases ieder dagdeel. Is x groter dan het aantal dagdelen, corrigeer x vóór inplannen.
Zonder dagdelen blijft deze show ongepland.