Front : programmation des artistes et vœux des bénévoles

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

- Types : retrait de `tshirt_size` / `diet` (Volunteer et mapping
  d'import) ; ajout de `ArtistRead/Create/Update` et des champs
  `artist_ids` / `wished_position_ids` sur la fiche bénévole.
- Surface API `artists` (HTTP + mock) et hooks
  `useArtists` / `useCreateArtist` / `useUpdateArtist` / `useDeleteArtist`.
- Nouvel onglet Configuration « Programmation » : liste d'artistes
  éditable, colonne par jour de l'édition (création, renommage,
  suppression ; 409 doublon, 422 nom vide).
- Fiche bénévole & formulaire d'ajout manuel : champs t-shirt / régime
  retirés, sélecteurs « Artistes à voir » et « Postes souhaités »
  (puces retirables, 422 si id étranger).
- Mock : jeu de démo enrichi (2 artistes par jour, vœux aléatoires),
  cascade sur suppression d'artiste / de poste, import CSV sans
  t-shirt / régime.

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 22:22:45 +02:00
co-authored by Claude Sonnet 5
parent 24caece7df
commit 81609eecc5
10 changed files with 533 additions and 99 deletions
+38
View File
@@ -7,6 +7,8 @@ import {
import { EDITION_ID, api } from './index';
import type {
ArtistCreate,
ArtistUpdate,
AssignmentCreate,
AssignmentUpdate,
AssignmentValidateResult,
@@ -48,6 +50,10 @@ export function useDayParts() {
return useQuery({ queryKey: ['dayParts', edition], queryFn: () => api.dayParts.list(edition) });
}
export function useArtists() {
return useQuery({ queryKey: ['artists', edition], queryFn: () => api.artists.list(edition) });
}
export function useVolunteers(options?: { enabled?: boolean }) {
return useQuery({
queryKey: queryKeys.volunteers,
@@ -111,6 +117,7 @@ function useInvalidate() {
const client = useQueryClient();
return {
volunteers: () => client.invalidateQueries({ queryKey: ['volunteers', edition] }),
artists: () => client.invalidateQueries({ queryKey: ['artists', edition] }),
positions: () => client.invalidateQueries({ queryKey: ['positions', edition] }),
shifts: () => client.invalidateQueries({ queryKey: ['shifts', edition] }),
planning: () => {
@@ -170,6 +177,35 @@ export function useGenerateShifts() {
});
}
export function useCreateArtist() {
const invalidate = useInvalidate();
return useMutation({
mutationFn: (body: ArtistCreate) => api.artists.create(edition, body),
onSuccess: invalidate.artists,
});
}
export function useUpdateArtist() {
const invalidate = useInvalidate();
return useMutation({
mutationFn: ({ id, body }: { id: string; body: ArtistUpdate }) =>
api.artists.update(edition, id, body),
onSuccess: invalidate.artists,
});
}
export function useDeleteArtist() {
const invalidate = useInvalidate();
return useMutation({
mutationFn: (id: string) => api.artists.remove(edition, id),
onSuccess: () => {
invalidate.artists();
// La suppression retire l'artiste des vœux des bénévoles.
invalidate.volunteers();
},
});
}
export function useCreateVolunteer() {
const invalidate = useInvalidate();
return useMutation({
@@ -252,6 +288,8 @@ export function useDeletePosition() {
invalidate.positions();
invalidate.shifts();
invalidate.planning();
// Le poste disparaît des vœux des bénévoles.
invalidate.volunteers();
},
});
}