import { Component, inject, input, OnInit, output, signal } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { firstValueFrom } from 'rxjs'; import { DanceGroup, 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-schedule-editor', imports: [FormsModule, DialogFrame], template: `

{{ !dirty() && current()?.finalized ? 'Definitief' : 'Concept' }} · preferredDayParts = {{ show().preferredDayParts }}

Tussen twee dansen van dezelfde danser zijn twee andere optredens nodig. Je kunt een onvolledige indeling als concept opslaan.

@if (error()) { } @if (dirty()) {

Niet-opgeslagen wijzigingen. Sla het concept op om alle regels te controleren.

} @for (violation of current()?.violations ?? []; track $index) {

{{ violation }}

}
@for (part of show().dayParts; track $index; let p = $index) {
{{ part.date }} · {{ part.type }} ({{ assignments[p].length }} optredens)
    @for (id of assignments[p]; track $index; let i = $index) {
  1. {{ name(id) }}
  2. }
}
`, }) export class ScheduleEditor implements OnInit { readonly show = input.required(); readonly groups = input.required(); readonly revision = input(); private originalRevision?: number; readonly schoolId = input.required(); readonly closed = output(); readonly saved = output(); readonly busy = signal(false); readonly dirty = signal(false); readonly error = signal(''); readonly current = signal(null); private readonly api = inject(SchoolApi); assignments: number[][] = []; ngOnInit(): void { this.originalRevision = this.revision(); this.current.set(this.show()); this.assignments = this.show().dayParts.map((p) => [...p.danceGroupIds]); } name(id: number): string { return this.groups().find((g) => g.id === id)?.category ?? `Groep ${id}`; } reorder(p: number, i: number, delta: number): void { const list = this.assignments[p]; [list[i], list[i + delta]] = [list[i + delta], list[i]]; this.dirty.set(true); } move(p: number, i: number, target: number): void { if (target === p || this.assignments[target].includes(this.assignments[p][i])) return; this.assignments[target].push(this.assignments[p].splice(i, 1)[0]); this.dirty.set(true); } add(p: number, id: number): void { if (!id || this.assignments[p].includes(id)) return; this.assignments[p].push(id); this.dirty.set(true); } remove(p: number, i: number): void { this.assignments[p].splice(i, 1); this.dirty.set(true); } async save(): Promise { await this.perform(false); } async finalize(): Promise { await this.perform(true); } private async perform(finalize: boolean): Promise { if (this.busy()) return; this.busy.set(true); this.error.set(''); try { const result = await firstValueFrom( finalize ? this.api.finalize(this.schoolId(), this.show().id, this.originalRevision) : this.api.saveAssignments( this.schoolId(), this.show().id, this.assignments, this.originalRevision, ), ); if (this.originalRevision !== undefined) this.originalRevision++; this.current.set(result); this.assignments = result.dayParts.map((p) => [...p.danceGroupIds]); this.dirty.set(false); this.saved.emit(); } catch (error: unknown) { this.error.set(apiError(error)); } finally { this.busy.set(false); } } }