Import bénévoles : couche API (analyse + exécution) et mock CSV

- `PlanningApi.imports.analyze/execute` : envoi `multipart/form-data`
  (le client HTTP laisse le navigateur poser le Content-Type).
- Types miroir des schémas back-end (`ImportAnalyzeResponse`,
  `ImportMapping`, `ImportReport`, `ImportRowReport`, `ImportIssue`…).
- Hooks `useAnalyzeImport` / `useExecuteImport` ; l'exécution réelle
  invalide les bénévoles et le planning.
- Implémentation mock (mode démo) : lecture CSV, suggestion de mapping,
  simulation/exécution avec rapport ligne par ligne. Excel renvoie
  `import.error.xlsx_demo_only`.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017uUsRg7XzopkCPWimwg9ZM
This commit is contained in:
AdamThomas
2026-09-10 21:55:50 +02:00
co-authored by Claude Sonnet 5
parent 06f8736c33
commit df8d6733db
5 changed files with 505 additions and 1 deletions
+90
View File
@@ -135,6 +135,96 @@ export interface VolunteerUpdate {
skill_codes?: string[] | null;
}
// --------------------------------------------------------------------------- //
// Import de bénévoles (fichier CSV / Excel) //
// --------------------------------------------------------------------------- //
export type ImportFieldKey =
| 'first_name'
| 'last_name'
| 'email'
| 'phone'
| 'tshirt_size'
| 'diet'
| 'comment'
| 'skills';
export interface ImportColumnMapping {
first_name: string;
last_name: string;
email?: string | null;
phone?: string | null;
tshirt_size?: string | null;
diet?: string | null;
comment?: string | null;
skills?: string | null;
}
export interface ImportAvailabilityColumn {
header: string;
/** Date ISO (YYYY-MM-DD). */
date: string;
day_part_code: string;
}
export interface ImportMapping {
dry_run: boolean;
columns: ImportColumnMapping;
availability_columns: ImportAvailabilityColumn[];
}
export interface ImportPreviewRow {
line: number;
values: Record<string, string>;
}
export interface ImportDayPartInfo {
code: string;
label: string;
start_time: string;
end_time: string;
order: number;
}
export interface ImportAnalyzeResponse {
filename: string;
format: 'csv' | 'xlsx';
headers: string[];
row_count: number;
preview: ImportPreviewRow[];
suggested_mapping: Partial<Record<ImportFieldKey, string>>;
/** Dates ISO couvertes par l'édition. */
edition_dates: string[];
day_parts: ImportDayPartInfo[];
}
export type ImportSeverity = 'ERROR' | 'WARNING';
export type ImportRowAction = 'CREATED' | 'UPDATED' | 'SKIPPED';
export interface ImportIssue {
code: string;
severity: ImportSeverity;
field?: string | null;
params: Record<string, string | number | null>;
}
export interface ImportRowReport {
line: number;
action: ImportRowAction;
volunteer_id: string | null;
issues: ImportIssue[];
}
export interface ImportReport {
dry_run: boolean;
total_rows: number;
created: number;
updated: number;
skipped: number;
availabilities_written: number;
skills_created: string[];
rows: ImportRowReport[];
}
// --------------------------------------------------------------------------- //
// Position //
// --------------------------------------------------------------------------- //