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:
co-authored by
Claude Sonnet 5
parent
803db5e9a9
commit
e47c29d35b
@@ -1,6 +1,6 @@
|
||||
import { useMemo, useState } from 'react';
|
||||
|
||||
import { useMatrix } from '@/api/hooks';
|
||||
import { useMatrix, useRunSolver } from '@/api/hooks';
|
||||
import type { MatrixPosition, MatrixShift, PositionCategory } from '@/api/schema';
|
||||
import { Chip } from '@/components/ui';
|
||||
import { EmptyNote, ErrorNote, Loading } from '@/components/state';
|
||||
@@ -12,6 +12,7 @@ import { cx } from '@/lib/cx';
|
||||
|
||||
export function GrillePage() {
|
||||
const matrix = useMatrix();
|
||||
const runSolver = useRunSolver();
|
||||
const [dayFilter, setDayFilter] = useState<'all' | string>('all');
|
||||
const [categoryFilter, setCategoryFilter] = useState<'all' | PositionCategory>('all');
|
||||
const [selectedShiftId, setSelectedShiftId] = useState<string | null>(null);
|
||||
@@ -81,6 +82,27 @@ export function GrillePage() {
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-2">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => runSolver.mutate()}
|
||||
disabled={runSolver.isPending}
|
||||
className="cursor-pointer border-2 border-ink bg-yellow px-[10px] py-[6px] font-display text-[10.5px] font-extrabold uppercase tracking-wide shadow-brutal-sm hover:bg-green hover:text-white disabled:opacity-60"
|
||||
>
|
||||
{runSolver.isPending ? t('grille.solver.running') : t('grille.solver.run')}
|
||||
</button>
|
||||
{runSolver.data ? (
|
||||
<span className="font-mono text-[9px] opacity-70">
|
||||
{t('grille.solver.result', { count: runSolver.data.suggestions.length })}
|
||||
</span>
|
||||
) : null}
|
||||
{runSolver.isError ? (
|
||||
<span className="font-mono text-[9px] font-bold text-orange-dark">
|
||||
{t('error.generic')}
|
||||
</span>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
<div className="ml-auto flex items-center gap-3 border-2 border-ink bg-cream px-[9px] py-[5px]">
|
||||
<span className="font-mono text-[8.5px] opacity-60">{t('legend.title')}</span>
|
||||
<LegendItem swatch="bg-hatch-orange" glyph="▲">
|
||||
|
||||
Reference in New Issue
Block a user