Front : suggestions du solveur en tête de liste dans la grille

- Surface API `solver.run(editionId)` (HTTP + mock) et hooks
  `useRunSolver` (mutation, alimente le cache) / `useSolverSuggestions`
  (lecture seule du cache, ne déclenche jamais de calcul).
- Grille des besoins : bouton « Suggestions (solveur) », affiche le
  nombre de suggestions calculées.
- Panneau d'affectation d'un créneau : les bénévoles suggérés par le
  solveur passent en tête de la liste des candidats (avant le tri par
  disponibilité / charge), avec un badge « Suggéré ». Ne pré-affecte
  jamais personne — la coordination choisit et confirme comme avant.
- Mock : portage glouton (par créneau, du plus urgent au moins urgent)
  du scoring du moteur back-end, pour une démonstration cohérente sans
  CP-SAT côté navigateur.

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-11 08:16:29 +02:00
co-authored by Claude Sonnet 5
parent 803db5e9a9
commit e47c29d35b
8 changed files with 251 additions and 8 deletions
+26
View File
@@ -36,6 +36,7 @@ export const queryKeys = {
assignments: (params?: { volunteerId?: string; shiftId?: string }) =>
['assignments', edition, params?.volunteerId ?? null, params?.shiftId ?? null] as const,
matrix: ['matrix', edition] as const,
solver: ['solver', edition] as const,
validate: (volunteerId: string, shiftId: string) =>
['validate', edition, volunteerId, shiftId] as const,
};
@@ -115,6 +116,19 @@ export function useMatrix() {
return useQuery({ queryKey: queryKeys.matrix, queryFn: () => api.matrix.get(edition) });
}
/**
* Dernières suggestions du solveur (lecture seule du cache posé par
* `useRunSolver`). Ne déclenche jamais de calcul elle-même.
*/
export function useSolverSuggestions() {
return useQuery({
queryKey: queryKeys.solver,
queryFn: () => api.solver.run(edition),
enabled: false,
staleTime: Infinity,
});
}
export function useValidateAssignment(
volunteerId: string,
shiftId: string,
@@ -386,6 +400,18 @@ export function useDeleteAssignment() {
});
}
/**
* Lance le solveur (CP-SAT) : calcule des suggestions, n'affecte jamais
* personne. Le résultat alimente le cache lu par `useSolverSuggestions`.
*/
export function useRunSolver() {
const client = useQueryClient();
return useMutation({
mutationFn: () => api.solver.run(edition),
onSuccess: (data) => client.setQueryData(queryKeys.solver, data),
});
}
export function useSendPlanningNotification() {
return useMutation({
mutationFn: (body: PlanningNotificationRequest) =>