S12 - Async feedback for scheduling
All checks were successful
Build, test and publish / verify (push) Successful in 7m25s
All checks were successful
Build, test and publish / verify (push) Successful in 7m25s
This commit is contained in:
@@ -344,7 +344,7 @@
|
||||
</div>
|
||||
<div class="show-list">
|
||||
@for (show of shows(); track show.id) {
|
||||
<article class="show-card">
|
||||
<article class="show-card" [id]="'show-' + show.id">
|
||||
<div class="date-tile">
|
||||
<strong>{{ firstDate(show) | date: 'dd' }}</strong
|
||||
><span>{{ firstDate(show) | date: 'MMM yyyy' }}</span>
|
||||
@@ -372,6 +372,48 @@
|
||||
: 'Geen geldige planning. Plan de groepen opnieuw in.'
|
||||
}}
|
||||
</p>
|
||||
@if (schedulingStatus.run(context.school.id, show.id); as run) {
|
||||
<div class="scheduling-progress" role="status">
|
||||
@if (!schedulingStatus.connected()) {
|
||||
<p>
|
||||
Verbinding verbroken. Status mogelijk verouderd; de berekening loopt door.
|
||||
</p>
|
||||
}
|
||||
@if (run.state === 'RUNNING') {
|
||||
<p>
|
||||
Bezig met inplannen · {{ schedulingStatus.phase(run) }} ·
|
||||
{{ schedulingStatus.elapsed(run) }}
|
||||
</p>
|
||||
<p>
|
||||
{{
|
||||
run.progress.feasible
|
||||
? 'Geldige oplossing gevonden; optimalisatie loopt door.'
|
||||
: 'Nog geen geldige oplossing gevonden.'
|
||||
}}
|
||||
</p>
|
||||
} @else {
|
||||
<p>{{ run.message }}</p>
|
||||
<p>
|
||||
Afgerond: {{ run.finishedAt | date: 'dd-MM-yyyy HH:mm:ss' }} · Duur:
|
||||
{{ schedulingStatus.elapsed(run) }}
|
||||
</p>
|
||||
@if (run.summary; as summary) {
|
||||
<p>
|
||||
Aanwezigheid: {{ summary.attendanceExcess }} extra dagdelen boven voorkeur
|
||||
(showcasedansers, exclusief coaches).
|
||||
</p>
|
||||
<p>
|
||||
Spreiding: {{ summary.performanceSpread }} optredens verschil tussen drukste
|
||||
en rustigste dagdeel.
|
||||
</p>
|
||||
<p>
|
||||
Variatie: {{ summary.varietyPenalty }} strafpunten voor opeenvolgende
|
||||
groepen (lager is beter).
|
||||
</p>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
}
|
||||
<button
|
||||
type="button"
|
||||
class="button primary"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { SchoolTransfer } from '../shared/school-transfer';
|
||||
import { Component, computed, DestroyRef, inject, OnInit, signal } from '@angular/core';
|
||||
import { Component, computed, DestroyRef, effect, 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';
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
Show,
|
||||
} from '../core/api-models';
|
||||
import { apiError } from '../core/api-error';
|
||||
import { SchedulingStatus } from '../core/scheduling-status';
|
||||
import { SchoolApi } from '../core/school-api';
|
||||
import { EntityEditor } from '../shared/entity-editor';
|
||||
import { ConfirmDialog } from '../shared/confirm-dialog';
|
||||
@@ -57,7 +58,30 @@ export class SchoolPage implements OnInit {
|
||||
readonly scheduleEditing = signal<Show | null>(null);
|
||||
readonly members = signal<DanceGroup | null>(null);
|
||||
readonly deleting = signal<DeleteTarget | null>(null);
|
||||
readonly scheduling = signal<number[]>([]);
|
||||
readonly schedulingStatus = inject(SchedulingStatus);
|
||||
private readonly starting = signal<number[]>([]);
|
||||
readonly scheduling = computed(() => [
|
||||
...this.starting(),
|
||||
...this.schedulingStatus
|
||||
.runs()
|
||||
.filter((run) => run.schoolId === this.data()?.school.id && run.state === 'RUNNING')
|
||||
.map((run) => run.showId),
|
||||
]);
|
||||
private readonly completed = new Set<string>();
|
||||
|
||||
constructor() {
|
||||
effect(() => {
|
||||
const schoolId = this.data()?.school.id;
|
||||
let changed = false;
|
||||
for (const run of this.schedulingStatus.runs()) {
|
||||
if (run.schoolId !== schoolId || run.state === 'RUNNING' || this.completed.has(run.runId))
|
||||
continue;
|
||||
this.completed.add(run.runId);
|
||||
changed = true;
|
||||
}
|
||||
if (changed) void this.reload();
|
||||
});
|
||||
}
|
||||
readonly deleteBusy = signal(false);
|
||||
readonly deleteError = signal('');
|
||||
readonly search = signal('');
|
||||
@@ -151,20 +175,15 @@ export class SchoolPage implements OnInit {
|
||||
)
|
||||
)
|
||||
return;
|
||||
this.scheduling.update((ids) => [...ids, show.id]);
|
||||
this.starting.update((ids) => [...ids, show.id]);
|
||||
this.error.set('');
|
||||
try {
|
||||
const result = await firstValueFrom(this.api.schedule(schoolId, show.id));
|
||||
this.notice.set(
|
||||
result.optimal
|
||||
? 'Optimaal conceptrooster.'
|
||||
: 'Geldig conceptrooster; optimaliteit niet bewezen.',
|
||||
);
|
||||
await this.reload();
|
||||
this.schedulingStatus.started(result);
|
||||
} catch (error: unknown) {
|
||||
this.error.set(apiError(error));
|
||||
} finally {
|
||||
this.scheduling.update((ids) => ids.filter((id) => id !== show.id));
|
||||
this.starting.update((ids) => ids.filter((id) => id !== show.id));
|
||||
}
|
||||
}
|
||||
async reload(): Promise<void> {
|
||||
|
||||
Reference in New Issue
Block a user