Files
mimic/tasks/todo.md

10 KiB
Raw Blame History

Sprint 11 — Spectrum UX port : 4 primitives + compact density global

Base : origin/main (PR #12 mergée — sprint 8 + 9 + 10 en). Branch : sprint/11-spectrum-ux. Scope : frontend-only sprint, 5 livrables coordonnés. Pas de backend, pas de schéma, pas de nouvelle API. Pas de modif tokens couleurs/spacing/font dans tailwind.config.


Contexte

Analyse Spectrum (/home/user/Documents/01_Projects/spectrum) faite via workflow. Résultat : Spectrum n'a pas de design system (Tailwind raw + violet ramp + recipes inlinées 30× partout). Mimic est structurellement meilleur. On ne porte pas la philosophie Spectrum — on extrait 5 patterns isolés qui survivent au brutalisme Mimic.

Constraints absolues

  1. Primary #024ad8 (Electric Blue) reste. Pas de violet, pas de spectrum ramp.
  2. Brutalisme reste : rounded-none sur containers (sauf status pills + tab count pills + avatars), zero transition-*, zero shadow-*, hairline 1px borders.
  3. Pas de modif tailwind.config.* sauf si une nouvelle classe utility est strictement nécessaire (justifier dans le PR).
  4. DESIGN.md amendments additives uniquement — pas de modif des tokens existants.
  5. Mono uniquement pour data (IDs MITRE, dates ISO, commands, etc.) — rule sprint 7.

Décisions binding (lockées par user)

  • Q1 → B : Tabs primitive + consumer EngagementDetailPage (3 tabs : Schedule / Description / Simulations au lieu du stack vertical actuel).
  • Q2 → B : Compact density GLOBAL — toutes les tables passent à 32px row, pas opt-in. User accepte le tradeoff WCAG (touch target) sur Engagements list.
  • Q3 → A : AlertBanner + refactor SimulationFormPage (Done banner + SOC-blocked banner).

Livrables

Livrable 1 — Tabs primitive + useHashTab hook + consumer

Files :

  • frontend/src/hooks/useHashTab.ts (NEW) — ~30 LoC. Reads window.location.hash, falls back to default ID, listens to hashchange, updates URL without reload. TypeScript pur, zéro style.
  • frontend/src/components/Tabs.tsx (NEW) — composant <Tabs items={[{id, label, count?}]} activeId onChange />. Underline variant.
  • frontend/src/styles/index.css — nouvelles recipes :
    • .tab-underlinetext-graphite caption-bold cursor-pointer border-b-2 border-transparent hover:text-ink (instantané, pas de transition)
    • .tab-underline-activetext-primary border-primary (override)
    • .tab-count-pillrounded-pill bg-cloud text-graphite text-[11px] px-xs py-0 font-mono
    • .tab-count-pill-activebg-primary-soft text-primary (override)
  • frontend/src/pages/EngagementDetailPage.tsx — refactor en 3 tabs :
    • Tab 1 "Schedule" (id schedule, default) — dates + statut + bouton "Edit engagement"
    • Tab 2 "Description" (id description) — texte description
    • Tab 3 "Simulations" (id simulations) — la liste actuelle de simulations
    • Count pills sur tab 3 = nombre de simulations
    • Tabs branchées sur useHashTab('schedule')
  • DESIGN.md — nouvelle subsection ### Navigation Sub-page tabs (additive)

Brutalism check :

  • Tab : border-b-2 only, pas de bg, pas de rounded, pas de transition
  • Pill : rounded-pill autorisé (exception status pill DESIGN.md L125)
  • Hover : hover:text-ink instantané

Livrable 2 — AlertBanner component + 4 recipes

Files :

  • frontend/src/components/AlertBanner.tsx (NEW) — <AlertBanner variant="error|warn|success|info" title? children />. Brutalist : border-l-4 strip semantic, fill bg-paper, border border-hairline partout sauf le strip gauche, rounded-none. Icon Lucide à gauche (AlertCircle error, AlertTriangle warn, CheckCircle success, Info info) à size={16}.
  • frontend/src/styles/index.css — 4 nouvelles recipes :
    • .alert-errorbg-paper border border-hairline border-l-4 border-l-bloom-deep px-md py-sm flex items-start gap-sm
    • .alert-warn — same + border-l-warn
    • .alert-success — same + border-l-success
    • .alert-info — same + border-l-primary
  • frontend/src/pages/SimulationFormPage.tsx — refactor 2 banners hand-rollés (~L300 Done banner + ~L310 SOC-blocked banner) → <AlertBanner variant="success"> / <AlertBanner variant="warn">.
  • DESIGN.md — extend ### Toast Notifications avec subsection ### Inline Banners (additive, mêmes 4 variants).

Note : bloom-deep, warn, success, primary sont les tokens existants — pas de nouveau token couleur.

Files :

  • frontend/src/components/BackLink.tsx (NEW) — <BackLink to="/engagements">Back to engagements</BackLink>. Renders ← {children} (ArrowLeft Lucide size=14, gap-xxs). Style : text-graphite hover:text-primary caption-md instantané.
  • Refactor consumers (3 instances hand-rollées) :
    • frontend/src/pages/EngagementDetailPage.tsx (~L35)
    • frontend/src/pages/SimulationFormPage.tsx (back-link en edit mode)
    • frontend/src/pages/TemplateFormPage.tsx (back-link en edit mode)

Livrable 4 — .table-compact recipe + apply GLOBAL

Files :

  • frontend/src/styles/index.css — nouvelle recipe (mais appliquée comme default, pas opt-in cette sprint per user decision Q2-B) :
    • Soit : modifier la recipe .card-product table existante pour passer à 32px row, caption-md text, py-xxs px-xs cells, divider border-hairline.
    • Soit : ajouter .table-compact puis l'appliquer à TOUTES les list pages (Engagements, Templates, Users, etc.).
    • Choix builder : pick the cleaner path. Préférer modifier le default si toutes les tables doivent suivre, sinon .table-compact + apply partout.
  • Apply sur :
    • frontend/src/pages/EngagementsListPage.tsx
    • frontend/src/pages/TemplatesListPage.tsx
    • frontend/src/pages/UsersAdminPage.tsx
    • frontend/src/components/SimulationList.tsx (utilisé dans EngagementDetailPage)
    • Toute autre table dans le code (grep <table dans frontend/src/)
  • DESIGN.md### Data Tables section : modifier la ligne "Row min-height 44px" → "Row min-height 32px (compact density, all surfaces)". Documenter le tradeoff WCAG (touch target dérogé en faveur de la densité opérateur — c'est un BAS tool, pas un site grand public).

ATTENTION : WCAG SC 2.5.5 (Target Size: Minimum) recommande 44×44. Tu déroges. Garder les boutons d'action des rows ≥ 24px d'icône avec padding pour préserver le hit area minimal. Tester en clavier + lecteur d'écran nicht regressé.

Livrable 5 — DESIGN.md amendments + CHANGELOG

Files :

  • DESIGN.md :
    • ### Navigation : nouvelle subsection "Sub-page tabs" (livrable 1)
    • ### Toast Notifications : nouvelle subsection "Inline Banners" (livrable 2)
    • ### Data Tables : ligne row min-height passe à 32px + paragraphe rationale (livrable 4)
  • CHANGELOG.md : entrée Sprint 11 listant chaque primitive + ses tokens DESIGN.md.

Sequencing

  1. frontend-builder : livrables 1 + 2 + 3 + 4 + 5 en cascade. Une seule passe. Commits atomiques :
    feat(frontend): add Tabs primitive + useHashTab hook
    feat(frontend): refactor EngagementDetailPage to 3-tab layout
    feat(frontend): add AlertBanner component + 4 semantic variants
    refactor(frontend): migrate SimulationFormPage banners to AlertBanner
    feat(frontend): add BackLink helper + dedup 3 hand-rolled instances
    feat(frontend): compact table density (32px row) global
    docs(design): add tabs / inline banners / compact-table-density to DESIGN.md
    
    (collapse OK si plus efficace ; ~5-7 commits attendus)
  2. design-reviewer : visual + token compliance check. Focus :
    • Underline tabs : pas de transition, pas de bg fill, border-b instantané
    • Count pill : rounded-pill est dans l'exception DESIGN.md, OK
    • AlertBanner : border-l-4 semantic OK, pas de shadow
    • BackLink : caption + icon align baseline
    • 32px tables : densité lisible ? Hit area boutons d'action OK ?
  3. code-reviewer : sprint diff. Focus :
    • Aucune transition / shadow / rounded leak
    • useHashTab : hashchange listener cleanup dans useEffect return
    • Tabs : prop API stable, fallback default ID si hash invalide
    • AlertBanner : ARIA role="alert" ou role="status" selon variant
    • BackLink : to est string ou objet React Router ? Choisir un seul
  4. spec-reviewer : SPEC.md n'a pas vocation à mentionner ces primitives, skip ce sprint sauf si tu détectes une feature côté backend qui aurait dérivé.

Contraintes tests

  • vitest --run baseline 212/212 doit tenir.
  • Ajouter 4 specs (1 par new component) :
    • Tabs.test.tsx — active state, hash sync, count pill render
    • AlertBanner.test.tsx — 4 variants rendent les bonnes classes
    • BackLink.test.tsx — render + click navigation
    • useHashTab.test.tsx — hash read/write
  • tsc --noEmit + eslint --max-warnings=0 clean.
  • Brutalism unit assertions : pour chaque new component, un test qui vérifie expect(el).not.toHaveClass('rounded-md') (sauf pill counter) + .not.toHaveClass(/transition-/) + .not.toHaveClass(/shadow-/).

Out of scope (explicite)

  • Side nav / drawer / breadcrumb component — top nav + BackLink suffit
  • Global engagement selector top-bar (Spectrum pattern rejeté)
  • Editor dynamic-column grid — pas d'éditeur dans Mimic
  • Dark mode rework
  • Toast queue system (les toasts actuels via useToast restent)
  • PDF/document deliverable
  • Color palette changes
  • Tokens spacing/typo dans tailwind.config

Definition of Done

  • 5 primitives livrées + EngagementDetailPage refactorée en tabs + SimulationFormPage banners migrés + BackLink dédup × 3
  • vitest 212+/212+ (4 new specs ajoutées), tsc + lint clean
  • pytest 469/469 intact (zéro backend touché)
  • Toutes tables passent à 32px row density (validation visuelle)
  • Design-reviewer APPROVED (brutalism check sur les 4 new recipes)
  • Code-reviewer APPROVED
  • DESIGN.md amendé (3 subsections additives)
  • CHANGELOG.md entrée sprint 11
  • PR ouverte sur Gitea avec body