Front : retrait des compétences, disponibilités en fenêtres horaires

Aligne le frontend sur le back-end (étape 14).

- Types : suppression de `skill_codes` / `required_skill_codes` /
  `AvailabilityStatus` / `MISSING_REQUIRED_SKILL` ; ajout de
  `AvailabilityRead` / `AvailabilitySet` et de la surface API
  `availability` (list / replace / listEdition) + hooks associés.
- Fiche bénévole : bloc « compétences » remplacé par un éditeur de
  disponibilités (plages date + heures libres, `PUT` en bloc).
- Assistant d'import : plus de colonnes compétences ni disponibilités
  (mapping réduit à l'identité) ; rapport simplifié.
- Grille des besoins : chaque candidat porte un badge de couverture
  (disponible / partiel / non déclaré) calculé sur ses fenêtres, et la
  liste est triée « disponibles d'abord ». Nouvel util
  `availabilityCoverage` / `uncoveredMinutes` dans `lib/planning`,
  portage du moteur back-end.
- Mock : `state.availability` passe à des fenêtres `AvailabilityRead[]`,
  validation `OUTSIDE_AVAILABILITY` recalculée sur l'union des fenêtres,
  jeu de démo et import CSV adaptés.

typecheck / lint / build / smoke au vert.

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 23:01:59 +02:00
co-authored by Claude Sonnet 5
parent be51770b51
commit 36d108b47d
11 changed files with 487 additions and 439 deletions
+33
View File
@@ -12,6 +12,7 @@ import type {
AssignmentCreate,
AssignmentUpdate,
AssignmentValidateResult,
AvailabilitySet,
DayPartCreate,
DayPartUpdate,
EditionUpdate,
@@ -82,6 +83,23 @@ export function useShifts(positionId?: string) {
});
}
/** Fenêtres de disponibilité d'un bénévole. */
export function useVolunteerAvailability(volunteerId: string | null | undefined) {
return useQuery({
queryKey: ['availability', edition, volunteerId ?? null],
queryFn: () => api.availability.list(edition, volunteerId as string),
enabled: Boolean(volunteerId),
});
}
/** Toutes les disponibilités de l'édition (grille des besoins). */
export function useEditionAvailability() {
return useQuery({
queryKey: ['availability', edition, 'all'],
queryFn: () => api.availability.listEdition(edition),
});
}
export function useAssignments(
params?: { volunteerId?: string; shiftId?: string },
options?: { enabled?: boolean },
@@ -117,6 +135,7 @@ function useInvalidate() {
const client = useQueryClient();
return {
volunteers: () => client.invalidateQueries({ queryKey: ['volunteers', edition] }),
availability: () => client.invalidateQueries({ queryKey: ['availability', edition] }),
artists: () => client.invalidateQueries({ queryKey: ['artists', edition] }),
positions: () => client.invalidateQueries({ queryKey: ['positions', edition] }),
shifts: () => client.invalidateQueries({ queryKey: ['shifts', edition] }),
@@ -234,6 +253,20 @@ export function useDeleteVolunteer() {
});
}
/** Remplace toutes les fenêtres de disponibilité d'un bénévole. */
export function useReplaceAvailability() {
const invalidate = useInvalidate();
return useMutation({
mutationFn: ({ volunteerId, body }: { volunteerId: string; body: AvailabilitySet }) =>
api.availability.replace(edition, volunteerId, body),
onSuccess: () => {
invalidate.availability();
// Les avertissements « hors disponibilité » de la grille en dépendent.
invalidate.planning();
},
});
}
/** Import de bénévoles — phase 1 : analyse du fichier. */
export function useAnalyzeImport() {
return useMutation({