Frontend : consommation de l'API CRUD (matrice, affectations, validation)
Remplace le magasin d'état fabriqué par une vraie couche API typée, alignée sur le back-end (Étape 5) : - src/api/ : schema.ts (miroir de backend/app/api/schemas.py), client HTTP, implémentation mock en mémoire (même surface + portage du moteur de validation), sélection mock/http via VITE_API_MODE, hooks React Query (lectures + mutations avec invalidation). - Grille des besoins ← GET /matrix ; treillis de colonnes dérivé des créneaux réels (plus de plages figées). - Panneau d'affectation ← GET /assignments?shift_id, POST /assignments/validate par candidat (alertes bloquantes / avertissements du moteur), POST / PATCH / DELETE /assignments. - Vue par bénévole ← volunteers + shifts + assignments (surcharge / oubliés calculés côté client). - Confirmations (ex-« Publication ») ← affectations PROPOSED ; publication versionnée et emails signalés hors périmètre. - Mobiles ← assignments filtrées ; confirmation = PATCH ; pointage référent local (hors périmètre). - Configuration ← positions & shifts CRUD (min/idéal, ajout/retrait), volunteers CRUD dont suppression RGPD ; messages = maquette locale. - i18n mis à jour, aucun texte d'interface en dur. - Vérifs : build tsc + vite, eslint (0 problème), npm run smoke (API mock + rendu headless des 6 écrans avec données préchargées). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AwswqSChc3Rwtu61pRXiY2
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
ac469f0cd1
commit
c9dda65361
@@ -0,0 +1,210 @@
|
||||
// Types miroir des schémas de l'API back-end (`backend/app/api/schemas.py`).
|
||||
// Toute évolution du contrat doit être répercutée ici.
|
||||
|
||||
export type PositionCategory =
|
||||
| 'BAR'
|
||||
| 'TICKETING'
|
||||
| 'WELCOME'
|
||||
| 'PARKING'
|
||||
| 'CLEANING'
|
||||
| 'CATERING'
|
||||
| 'SETUP'
|
||||
| 'STAGE'
|
||||
| 'OTHER';
|
||||
|
||||
export type AssignmentStatus = 'PROPOSED' | 'CONFIRMED' | 'DECLINED' | 'CANCELLED';
|
||||
export type AssignmentSource = 'SOLVER' | 'MANUAL';
|
||||
export type TshirtSize = 'XS' | 'S' | 'M' | 'L' | 'XL' | 'XXL' | 'XXXL';
|
||||
export type AvailabilityStatus = 'AVAILABLE' | 'IF_NEEDED' | 'UNAVAILABLE';
|
||||
|
||||
/** Statuts « actifs » : seuls comptés pour chevauchement / cumul / repos. */
|
||||
export const ACTIVE_ASSIGNMENT_STATUSES: AssignmentStatus[] = ['PROPOSED', 'CONFIRMED'];
|
||||
|
||||
export type ViolationCode =
|
||||
| 'SHIFT_OVERLAP'
|
||||
| 'OUTSIDE_AVAILABILITY'
|
||||
| 'DAILY_HOURS_EXCEEDED'
|
||||
| 'INSUFFICIENT_REST'
|
||||
| 'MISSING_REQUIRED_SKILL';
|
||||
|
||||
export type ViolationSeverity = 'BLOCKING' | 'WARNING';
|
||||
|
||||
export interface Violation {
|
||||
code: ViolationCode;
|
||||
severity: ViolationSeverity;
|
||||
params: Record<string, string | number>;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------- //
|
||||
// Volunteer //
|
||||
// --------------------------------------------------------------------------- //
|
||||
export interface VolunteerRead {
|
||||
id: string;
|
||||
edition_id: string;
|
||||
first_name: string;
|
||||
last_name: string;
|
||||
email: string | null;
|
||||
phone: string | null;
|
||||
tshirt_size: TshirtSize | null;
|
||||
diet: string | null;
|
||||
comment: string | null;
|
||||
skill_codes: string[];
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
}
|
||||
|
||||
export interface VolunteerCreate {
|
||||
first_name: string;
|
||||
last_name: string;
|
||||
email?: string | null;
|
||||
phone?: string | null;
|
||||
tshirt_size?: TshirtSize | null;
|
||||
diet?: string | null;
|
||||
comment?: string | null;
|
||||
skill_codes?: string[];
|
||||
}
|
||||
|
||||
export interface VolunteerUpdate {
|
||||
first_name?: string;
|
||||
last_name?: string;
|
||||
email?: string | null;
|
||||
phone?: string | null;
|
||||
tshirt_size?: TshirtSize | null;
|
||||
diet?: string | null;
|
||||
comment?: string | null;
|
||||
/** null / absent = inchangé ; [] = vider les compétences. */
|
||||
skill_codes?: string[] | null;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------- //
|
||||
// Position //
|
||||
// --------------------------------------------------------------------------- //
|
||||
export interface PositionRead {
|
||||
id: string;
|
||||
edition_id: string;
|
||||
name: string;
|
||||
location: string | null;
|
||||
category: PositionCategory;
|
||||
lead_account_id: string | null;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
}
|
||||
|
||||
export interface PositionCreate {
|
||||
name: string;
|
||||
location?: string | null;
|
||||
category: PositionCategory;
|
||||
lead_account_id?: string | null;
|
||||
}
|
||||
|
||||
export interface PositionUpdate {
|
||||
name?: string;
|
||||
location?: string | null;
|
||||
category?: PositionCategory;
|
||||
lead_account_id?: string | null;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------- //
|
||||
// Shift //
|
||||
// --------------------------------------------------------------------------- //
|
||||
export interface ShiftRead {
|
||||
id: string;
|
||||
edition_id: string;
|
||||
position_id: string;
|
||||
/** Datetime naïf, heure locale Europe/Paris (ISO sans fuseau). */
|
||||
starts_at: string;
|
||||
ends_at: string;
|
||||
min_staff: number;
|
||||
ideal_staff: number;
|
||||
required_skill_codes: string[];
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
}
|
||||
|
||||
export interface ShiftCreate {
|
||||
position_id: string;
|
||||
starts_at: string;
|
||||
ends_at: string;
|
||||
min_staff?: number;
|
||||
ideal_staff?: number;
|
||||
required_skill_codes?: string[];
|
||||
}
|
||||
|
||||
export interface ShiftUpdate {
|
||||
position_id?: string;
|
||||
starts_at?: string;
|
||||
ends_at?: string;
|
||||
min_staff?: number;
|
||||
ideal_staff?: number;
|
||||
required_skill_codes?: string[] | null;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------- //
|
||||
// Assignment //
|
||||
// --------------------------------------------------------------------------- //
|
||||
export interface AssignmentRead {
|
||||
id: string;
|
||||
edition_id: string;
|
||||
volunteer_id: string;
|
||||
shift_id: string;
|
||||
status: AssignmentStatus;
|
||||
source: AssignmentSource;
|
||||
locked: boolean;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
}
|
||||
|
||||
export interface AssignmentCreate {
|
||||
volunteer_id: string;
|
||||
shift_id: string;
|
||||
status?: AssignmentStatus;
|
||||
source?: AssignmentSource;
|
||||
locked?: boolean;
|
||||
}
|
||||
|
||||
export interface AssignmentUpdate {
|
||||
status?: AssignmentStatus;
|
||||
source?: AssignmentSource;
|
||||
locked?: boolean;
|
||||
}
|
||||
|
||||
export interface AssignmentWriteResult {
|
||||
assignment: AssignmentRead;
|
||||
warnings: Violation[];
|
||||
}
|
||||
|
||||
export interface AssignmentValidateRequest {
|
||||
volunteer_id: string;
|
||||
shift_id: string;
|
||||
}
|
||||
|
||||
export interface AssignmentValidateResult {
|
||||
blocking: boolean;
|
||||
violations: Violation[];
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------- //
|
||||
// Matrice poste × créneau //
|
||||
// --------------------------------------------------------------------------- //
|
||||
export interface MatrixShift {
|
||||
shift_id: string;
|
||||
starts_at: string;
|
||||
ends_at: string;
|
||||
min_staff: number;
|
||||
ideal_staff: number;
|
||||
confirmed: number;
|
||||
proposed: number;
|
||||
}
|
||||
|
||||
export interface MatrixPosition {
|
||||
id: string;
|
||||
name: string;
|
||||
category: PositionCategory;
|
||||
location: string | null;
|
||||
shifts: MatrixShift[];
|
||||
}
|
||||
|
||||
export interface MatrixResponse {
|
||||
edition_id: string;
|
||||
positions: MatrixPosition[];
|
||||
}
|
||||
Reference in New Issue
Block a user