// 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'; // Depuis l'Étape 9 back-end : une affectation est valide dès sa création, // il n'y a plus de statut (proposée / confirmée…). La retirer = supprimer la ligne. export type AssignmentSource = 'SOLVER' | 'MANUAL'; export type TshirtSize = 'XS' | 'S' | 'M' | 'L' | 'XL' | 'XXL' | 'XXXL'; export type AvailabilityStatus = 'AVAILABLE' | 'IF_NEEDED' | 'UNAVAILABLE'; export type Role = 'COORDINATOR' | 'LEAD' | 'VOLUNTEER'; export type AuthMethod = 'password' | 'magic_link'; 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; } // --------------------------------------------------------------------------- // // Edition // // --------------------------------------------------------------------------- // export interface EditionRead { id: string; year: number; name: string | null; /** Dates ISO (YYYY-MM-DD). */ start_date: string; end_date: string; retention_days: number | null; created_at: string; updated_at: string; } export interface EditionUpdate { name?: string | null; year?: number; start_date?: string; end_date?: string; retention_days?: number | null; } // --------------------------------------------------------------------------- // // 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; source: AssignmentSource; locked: boolean; created_at: string; updated_at: string; } export interface AssignmentCreate { volunteer_id: string; shift_id: string; source?: AssignmentSource; locked?: boolean; } export interface AssignmentUpdate { 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; /** Nombre d'affectations sur ce créneau. */ assigned: number; } export interface MatrixPosition { id: string; name: string; category: PositionCategory; location: string | null; shifts: MatrixShift[]; } export interface MatrixResponse { edition_id: string; positions: MatrixPosition[]; } // --------------------------------------------------------------------------- // // Authentification // // --------------------------------------------------------------------------- // export interface AccountRead { id: string; edition_id: string; email: string; role: Role; volunteer_id: string | null; /** "password" (coordination) ou "magic_link" (référents, bénévoles). */ auth_method: AuthMethod; created_at: string; updated_at: string; } export interface TokenResponse { access_token: string; token_type: string; account: AccountRead; } export interface LoginRequest { email: string; password: string; } export interface MagicLinkRequest { email: string; } export interface VerifyRequest { token: string; } // --------------------------------------------------------------------------- // // Notifications par email // // --------------------------------------------------------------------------- // /** * `subject` / `body` sont des modèles. Variables : {prenom} {nom} * {nb_creneaux} {nb_heures} {creneaux} {lien_connexion} (ce dernier déclenche * l'émission d'un lien magique personnel). */ export interface PlanningNotificationRequest { subject?: string | null; body?: string | null; /** null / absent = tous les bénévoles de l'édition qui ont un email. */ volunteer_ids?: string[] | null; } export interface PlanningNotificationResult { queued: number; skipped_no_email: number; recipients: string[]; }