155 lines
5.8 KiB
TypeScript
155 lines
5.8 KiB
TypeScript
import { CommonModule } from '@angular/common';
|
|
import { ChangeDetectionStrategy, Component, signal, ViewChild } from '@angular/core';
|
|
import { DataTable, TableColumn } from '@shared/components/data-table/data-table';
|
|
import { ZardButtonComponent } from '@shared/components/button/button.component';
|
|
import { ZardPaginationModule } from '@shared/components/pagination/pagination.module';
|
|
import { ListParams, PagedResult } from '@shared/paging/paging';
|
|
import { ResultatApiResponse, ResultatStatut } from 'src/app/core/interfaces/resultat';
|
|
import { ResultatService } from 'src/app/core/services/resultat';
|
|
import { Depouillement, ResultatCourse } from 'src/app/core/services/depouillement';
|
|
import { Course } from 'src/app/core/interfaces/course';
|
|
import { toast } from 'ngx-sonner';
|
|
|
|
@Component({
|
|
standalone: true,
|
|
selector: 'app-rapport',
|
|
templateUrl: './rapport.html',
|
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
imports: [CommonModule, DataTable, ZardButtonComponent, ZardPaginationModule],
|
|
})
|
|
export class Rapport {
|
|
rows = signal<ResultatApiResponse[]>([]);
|
|
loading = signal(false);
|
|
sending = signal<Map<string, boolean>>(new Map());
|
|
// Pagination state
|
|
page = signal<number>(1);
|
|
perPage = signal<number>(10);
|
|
totalPages = signal<number>(1);
|
|
totalElements = signal<number>(0);
|
|
@ViewChild(DataTable) table?: DataTable<ResultatApiResponse>;
|
|
|
|
cols: TableColumn<ResultatApiResponse>[] = [
|
|
{ key: 'courseNumero', label: 'N°' },
|
|
{ key: 'hippodromeNom', label: 'Hippodrome' },
|
|
{ key: 'courseNom', label: 'Course' },
|
|
{ key: 'ordreArrivee', label: "Ordre d'arrivée", cell: (r) => String(r.ordreArrivee ?? '').replace(/,/g, ' - ') },
|
|
{ key: 'statut', label: 'Statut', cell: (r)=>(r.statut.toString().toLowerCase().replace("_", " ")) },
|
|
{ key: 'datePublication', label: 'Date pub.', cell: (r) => r.datePublication ?? r.createdAt ?? '—' },
|
|
{ key: 'dateValidation', label: 'Date validation' },
|
|
];
|
|
|
|
constructor(private api: ResultatService, private depouillement: Depouillement) {
|
|
// initial load
|
|
this.fetch();
|
|
}
|
|
private fetchPage(params?: Partial<ListParams>) {
|
|
this.loading.set(true);
|
|
const p: ListParams = { page: this.page(), size: this.perPage(), ...(params || {}) };
|
|
this.api.listRawPaged(p).subscribe({
|
|
next: (res: PagedResult<ResultatApiResponse>) => {
|
|
const filtered = (res?.content || []).filter((r) => !!(r.ordreArrivee && String(r.ordreArrivee).trim()));
|
|
this.rows.set(filtered);
|
|
// normalize paging meta
|
|
this.totalPages.set(res.totalPages ?? 1);
|
|
this.totalElements.set(res.totalElements ?? (filtered.length || 0));
|
|
// ensure local page is in sync with backend response
|
|
if (res.pageable?.pageNumber) this.page.set(res.pageable.pageNumber);
|
|
this.loading.set(false);
|
|
},
|
|
error: (err) => {
|
|
console.error('Error fetching paged reports:', err);
|
|
this.rows.set([]);
|
|
this.loading.set(false);
|
|
},
|
|
});
|
|
}
|
|
|
|
fetch() {
|
|
this.fetchPage();
|
|
}
|
|
|
|
onPageChange(nextPage: number) {
|
|
this.page.set(nextPage);
|
|
this.fetchPage();
|
|
}
|
|
|
|
onPerPageChange(size: number) {
|
|
this.perPage.set(size);
|
|
this.page.set(1);
|
|
this.fetchPage();
|
|
}
|
|
|
|
// wrapper for template change event to avoid $event typing issues
|
|
onPerPageChangeEvent(e: Event) {
|
|
const v = (e.target as HTMLSelectElement)?.value;
|
|
const size = Number(v) || 10;
|
|
this.onPerPageChange(size);
|
|
}
|
|
|
|
openReport(row: ResultatApiResponse) {
|
|
try {
|
|
// Open a per-result report URL in a new tab. Adjust path if your server uses another route.
|
|
const url = `/resultat/${row.id}`;
|
|
window.open(url, '_blank');
|
|
} catch (err) {
|
|
console.error('Failed to open report for', row, err);
|
|
}
|
|
}
|
|
|
|
isSending(id: string | number) {
|
|
return !!this.sending().get(String(id));
|
|
}
|
|
|
|
private setSending(id: string | number, v: boolean) {
|
|
const map = new Map(this.sending());
|
|
map.set(String(id), v);
|
|
this.sending.set(map);
|
|
}
|
|
|
|
sendToDepouillement(row: ResultatApiResponse) {
|
|
if (!row || !row.id) return;
|
|
const id = String(row.id);
|
|
if (this.isSending(id)) return; // already sending
|
|
|
|
this.setSending(id, true);
|
|
|
|
// Build a minimal ResultatCourse payload using available fields.
|
|
const course = {
|
|
id: String((row as any).courseId ?? '')
|
|
};
|
|
|
|
const payload: Omit<ResultatCourse, "id"> = {
|
|
course,
|
|
statut: (row.statut as any) ?? (0 as any),
|
|
ordreArrivee: String(row.ordreArrivee ?? ''),
|
|
datePublication: row.datePublication ?? row.createdAt,
|
|
};
|
|
|
|
this.depouillement.sendResultat(payload).subscribe({
|
|
next: (res) => {
|
|
// After successful depouillement, update the resultat statut to PROVISOIRE
|
|
const updateId = String((res && (res as any).id) ?? row.id);
|
|
this.api.update(updateId, { statut: ResultatStatut.PROVISOIRE }).subscribe({
|
|
next: (updated) => {
|
|
// Update the local rows to reflect the new statut
|
|
this.rows.set(
|
|
this.rows().map((r) => (String(r.id) === String(updateId) ? { ...r, statut: ResultatStatut.PROVISOIRE } : r))
|
|
);
|
|
toast.success('Résultat envoyé au dépouillement et statut mis à jour.');
|
|
this.setSending(id, false);
|
|
},
|
|
error: (err) => {
|
|
console.error('Error updating resultat statut after depouillement:', err);
|
|
toast.error('Échec de la mise à jour du statut du résultat.');
|
|
this.setSending(id, false);
|
|
},
|
|
});
|
|
},
|
|
error: (err) => {
|
|
console.error('Error sending to depouillement:', err);
|
|
this.setSending(id, false);
|
|
},
|
|
});
|
|
}
|
|
}
|