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 -39
View File
@@ -1,18 +1,11 @@
import { useMemo } from 'react';
import { ApiError } from '@/api';
import {
useAssignments,
usePositions,
useShifts,
useUpdateAssignment,
useVolunteer,
} from '@/api/hooks';
import { useAssignments, usePositions, useShifts, useVolunteer } from '@/api/hooks';
import { useAuth } from '@/auth/auth-context';
import { ErrorNote, Loading } from '@/components/state';
import { DesignNote, PhoneFrame } from '@/pages/mobile/PhoneFrame';
import { hourRangeLabel, hoursBetween, longDayLabel, naiveMinutes } from '@/lib/time';
import { isActive } from '@/lib/planning';
import { t } from '@/i18n';
export function MobileVolunteerPage() {
@@ -25,7 +18,6 @@ export function MobileVolunteerPage() {
});
const shifts = useShifts();
const positions = usePositions();
const updateAssignment = useUpdateAssignment();
const shiftsById = useMemo(
() => new Map((shifts.data ?? []).map((s) => [s.id, s])),
@@ -54,7 +46,6 @@ export function MobileVolunteerPage() {
(positions.error instanceof ApiError && positions.error.status === 403);
const slots = (assignments.data ?? [])
.filter(isActive)
.map((a) => ({ assignment: a, shift: shiftsById.get(a.shift_id) ?? null }))
.sort((a, b) => {
const av = a.shift ? naiveMinutes(a.shift.starts_at) : 0;
@@ -69,7 +60,6 @@ export function MobileVolunteerPage() {
0,
) * 10,
) / 10;
const toConfirm = slots.filter((s) => s.assignment.status !== 'CONFIRMED').length;
return (
<div className="flex flex-wrap items-start gap-[26px] p-6">
@@ -91,12 +81,6 @@ export function MobileVolunteerPage() {
</div>
</div>
{toConfirm > 0 ? (
<div className="border-b-[3px] border-ink bg-orange px-4 py-[11px] font-display text-[14px] font-black leading-snug">
{t('mobileVol.toConfirm', { count: toConfirm })}
</div>
) : null}
<div className="flex flex-col gap-[14px] px-[14px] pb-[26px] pt-[14px]">
{detailsUnavailable ? (
<div className="border-2 border-dashed border-ink bg-yellow-soft p-3 font-mono text-[10px] leading-relaxed">
@@ -128,28 +112,9 @@ export function MobileVolunteerPage() {
{t('mobileVol.location', { location: position.location })}
</div>
) : null}
{assignment.status === 'CONFIRMED' ? (
<div className="mt-[11px] flex min-h-[62px] items-center justify-center gap-[9px] border-[3px] border-ink bg-green font-display text-[17px] font-black text-white">
{t('mobileVol.confirmed')}
</div>
) : (
<button
type="button"
disabled={updateAssignment.isPending}
onClick={() =>
updateAssignment.mutate({ id: assignment.id, body: { status: 'CONFIRMED' } })
}
className="mt-[11px] min-h-[62px] w-full cursor-pointer border-[3px] border-ink bg-yellow font-display text-[19px] font-black shadow-brutal-md hover:bg-green hover:text-white disabled:opacity-60"
>
{t('mobileVol.confirm')}
</button>
)}
{updateAssignment.error instanceof ApiError &&
updateAssignment.variables?.id === assignment.id ? (
<div className="mt-2 border-2 border-ink bg-orange px-2 py-1 font-display text-[10px] font-bold">
{t(updateAssignment.error.code)}
</div>
) : null}
<div className="mt-[11px] flex min-h-[52px] items-center justify-center border-[3px] border-ink bg-green font-display text-[15px] font-black text-white">
{t('mobileVol.slotConfirmed')}
</div>
</div>
</div>
);