frontend-toegevoegd
This commit is contained in:
98
agiledancers-fe/src/app/shared/group-members.ts
Normal file
98
agiledancers-fe/src/app/shared/group-members.ts
Normal file
@@ -0,0 +1,98 @@
|
||||
import { Component, computed, inject, input, OnInit, output, signal } from '@angular/core';
|
||||
import { firstValueFrom } from 'rxjs';
|
||||
import { DanceGroup, Dancer } from '../core/api-models';
|
||||
import { SchoolApi } from '../core/school-api';
|
||||
import { apiError } from '../core/api-error';
|
||||
import { DialogFrame } from './dialog-frame';
|
||||
|
||||
@Component({
|
||||
selector: 'app-group-members',
|
||||
imports: [DialogFrame],
|
||||
template: `
|
||||
<app-dialog-frame
|
||||
[title]="'Dansers in ' + group().category"
|
||||
[busy]="busy()"
|
||||
(dismissed)="closed.emit()"
|
||||
>
|
||||
<p class="muted">
|
||||
Koppel dansers aan deze groep. Een danser kan in meerdere groepen zitten. Wijzigingen worden
|
||||
direct opgeslagen.
|
||||
</p>
|
||||
@if (error()) {
|
||||
<p class="alert error" role="alert">{{ error() }}</p>
|
||||
}
|
||||
<p class="member-count" role="status">
|
||||
{{
|
||||
busy()
|
||||
? 'Koppeling opslaan…'
|
||||
: memberIds().length + ' van ' + dancers().length + ' dansers gekoppeld'
|
||||
}}
|
||||
</p>
|
||||
<div class="member-list" [attr.aria-busy]="busy()">
|
||||
@for (dancer of sortedDancers(); track dancer.id) {
|
||||
<label class="member-row"
|
||||
><span aria-hidden="true" class="avatar small">{{
|
||||
dancer.name.charAt(0).toUpperCase()
|
||||
}}</span>
|
||||
<span>{{ dancer.name }}</span>
|
||||
<input
|
||||
type="checkbox"
|
||||
[checked]="memberIds().includes(dancer.id)"
|
||||
[disabled]="busy()"
|
||||
(change)="toggle(dancer.id, $event)"
|
||||
/>
|
||||
</label>
|
||||
} @empty {
|
||||
<p class="empty-inline">
|
||||
Er zijn nog geen dansers. Voeg eerst een danser toe via het tabblad Dansers.
|
||||
</p>
|
||||
}
|
||||
</div>
|
||||
<div class="dialog-actions">
|
||||
<button class="button primary" type="button" [disabled]="busy()" (click)="closed.emit()">
|
||||
Gereed
|
||||
</button>
|
||||
</div>
|
||||
</app-dialog-frame>
|
||||
`,
|
||||
})
|
||||
export class GroupMembers implements OnInit {
|
||||
readonly schoolId = input.required<number>();
|
||||
readonly group = input.required<DanceGroup>();
|
||||
readonly dancers = input.required<Dancer[]>();
|
||||
readonly closed = output<void>();
|
||||
readonly changed = output<number[]>();
|
||||
readonly memberIds = signal<number[]>([]);
|
||||
readonly busy = signal(false);
|
||||
readonly error = signal('');
|
||||
readonly sortedDancers = computed(() =>
|
||||
[...this.dancers()].sort((a, b) => a.name.localeCompare(b.name, 'nl')),
|
||||
);
|
||||
private readonly api = inject(SchoolApi);
|
||||
ngOnInit(): void {
|
||||
this.memberIds.set([...this.group().dancerIds]);
|
||||
}
|
||||
async toggle(dancerId: number, event: Event): Promise<void> {
|
||||
const checkbox = event.target as HTMLInputElement;
|
||||
const add = checkbox.checked;
|
||||
if (this.busy()) {
|
||||
checkbox.checked = this.memberIds().includes(dancerId);
|
||||
return;
|
||||
}
|
||||
this.busy.set(true);
|
||||
this.error.set('');
|
||||
try {
|
||||
await firstValueFrom(this.api.membership(this.schoolId(), this.group().id, dancerId, add));
|
||||
this.memberIds.update((ids) =>
|
||||
add ? [...ids, dancerId] : ids.filter((id) => id !== dancerId),
|
||||
);
|
||||
checkbox.checked = add;
|
||||
this.changed.emit(this.memberIds());
|
||||
} catch (error: unknown) {
|
||||
checkbox.checked = this.memberIds().includes(dancerId);
|
||||
this.error.set(apiError(error));
|
||||
} finally {
|
||||
this.busy.set(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user