Frontend : suppression du statut d'affectation (suite Étape 9)

Le back-end (Étape 9) a retiré AssignmentStatus : une affectation est valide
dès sa création, la retirer = supprimer la ligne.

- schema.ts : AssignmentRead/Create/Update sans `status` ;
  MatrixShift.{confirmed, proposed} → `assigned`.
- mock : générateur sans statut (plus de rollStatus / DECLINED) ; matrice
  = compte unique ; create → validation toujours exécutée ; PATCH ne porte
  plus que source/locked ; runValidation compte toutes les affectations.
- lib/planning : suppression de isActive() et statusLabel().
- Grille + panneau : cellule « affectés / idéal », plus de +proposés ni de
  badge confirmé/proposé ni de bouton « confirmer ».
- Vue par bénévole : une seule trame d'affectation (bleu).
- Mobile bénévole : plus de bouton « JE CONFIRME » ni de bannière.
- Mobile référent : plus de libellé de statut par équipier.
- Onglet 03 « Confirmations » supprimé (route, page, nav, ROUTE_ROLES) —
  il n'a plus d'objet. Coordination : onglets 01, 02, 06.
- i18n : clés de statut / de l'écran Confirmations retirées.
- Vérifs : build tsc + vite, eslint (0 problème), npm run smoke.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AwswqSChc3Rwtu61pRXiY2
This commit is contained in:
AdamThomas
2026-09-10 07:51:11 +02:00
co-authored by Claude Sonnet 5
parent 6e16db4b7e
commit 8d333deb23
16 changed files with 75 additions and 353 deletions
+4 -23
View File
@@ -7,14 +7,13 @@ import {
useDeleteAssignment,
usePositions,
useShifts,
useUpdateAssignment,
useValidateAssignment,
useVolunteers,
} from '@/api/hooks';
import type { MatrixShift, PositionCategory, VolunteerRead } from '@/api/schema';
import { Badge, Btn } from '@/components/ui';
import { Btn } from '@/components/ui';
import { Loading } from '@/components/state';
import { categoryLabel, hoursByVolunteer, isActive, violationText } from '@/lib/planning';
import { categoryLabel, hoursByVolunteer, violationText } from '@/lib/planning';
import { hourRangeLabel, hoursBetween, shortDayLabel } from '@/lib/time';
import { t } from '@/i18n';
@@ -41,7 +40,6 @@ export function AssignmentPanel({
const shifts = useShifts();
const positions = usePositions();
const deleteAssignment = useDeleteAssignment();
const updateAssignment = useUpdateAssignment();
const shiftsById = useMemo(
() => new Map((shifts.data ?? []).map((s) => [s.id, s])),
@@ -58,7 +56,7 @@ export function AssignmentPanel({
const slotsByVolunteer = useMemo(() => {
const map = new Map<string, number>();
for (const a of allAssignments.data ?? []) {
if (isActive(a)) map.set(a.volunteer_id, (map.get(a.volunteer_id) ?? 0) + 1);
map.set(a.volunteer_id, (map.get(a.volunteer_id) ?? 0) + 1);
}
return map;
}, [allAssignments.data]);
@@ -114,11 +112,7 @@ export function AssignmentPanel({
</div>
<div className="mt-[9px] flex flex-wrap gap-[14px] font-mono text-[10px]">
<span>
{t('grille.panel.confirmed')}{' '}
<b className="text-[15px]">{shift.confirmed}</b>
</span>
<span className="opacity-70">
{t('grille.panel.proposedCount', { count: shift.proposed })}
{t('grille.panel.assigned')} <b className="text-[15px]">{shift.assigned}</b>
</span>
<span className="opacity-70">{t('grille.panel.min', { count: shift.min_staff })}</span>
<span className="opacity-70">{t('grille.panel.ideal', { count: shift.ideal_staff })}</span>
@@ -149,19 +143,6 @@ export function AssignmentPanel({
{t('grille.panel.hoursTotal', { hours: round(hours.get(volunteer.id) ?? 0) })}
</span>
</span>
{assignment.status === 'CONFIRMED' ? (
<Badge tone="confirmed">{t('assignment.status.confirmed')}</Badge>
) : (
<button
type="button"
onClick={() =>
updateAssignment.mutate({ id: assignment.id, body: { status: 'CONFIRMED' } })
}
className="border-2 border-dashed border-ink bg-paper px-[6px] py-[3px] font-display text-[8.5px] font-extrabold uppercase tracking-wide hover:bg-green hover:text-white"
>
{t('assignment.status.proposed')}
</button>
)}
<button
type="button"
onClick={() => deleteAssignment.mutate(assignment.id)}