feat(frontend): full FR i18n via react-i18next + 2-tab engagement detail (sprint 12) #14

Merged
knacky merged 14 commits from sprint/12-i18n-fr into main 2026-06-22 08:25:51 +00:00
Owner

Summary

  • Sprint 12 — Full FR i18n via react-i18next + EngagementDetailPage tab merge : Mimic est désormais entièrement en français pour les opérateurs CACIB, depuis l'infrastructure (react-i18next + JSON FR) jusqu'aux aria-labels et au footer. Toutes les dates basculées en fr-FR (jj/mm/aaaa). 13 commits livrés + le 2c7fcec du sprint 11 (build fix text-caption-*).
  • EngagementDetailPage : passé de 3 tabs à 2 tabs (Description + Simulations). Le contenu Schedule (dates de début/fin, statut, créé le, bouton Modifier) devient un header block dans le panel Description.
  • Acronymes en EN (locked) : RT, SOC, MITRE, ATT&CK, C2, BAS, TLS, MITM, JWT, IP, URL, API, ID. Brand "Mimic" gardé. "Red Team" reste en idiom.
  • Status enum : valeurs DB en EN inchangées (pending/active/done/etc.), traduction au rendering layer via i18n/status.ts + hook useTranslation() inside StatusBadge / SimulationStatusBadge (subscription réactive correcte).
  • Date format : formatDate() / formatDateTime() dans lib/format.ts utilisent toLocaleDateString('fr-FR') / toLocaleString('fr-FR'). Appliqués à 5 sites (engagements list/detail, users admin, templates list, simulations list).
  • Reviews 2 passes : design-review (4 MAJOR + 2 MINOR patched) → code-review (1 MAJOR + 4 MINOR patched). Tous APPROVED post-polish.

Dépendance — sprint 11

Cette branche est basée sur sprint/11-spectrum-ux (PR #13 toujours ouverte). Le diff inclura temporairement les commits sprint 11 — il se réduira automatiquement à sprint 12 seul une fois PR #13 mergée. Merger PR #13 avant celui-ci pour un diff propre.

Test plan

  • Frontend : 245 / 245 vitest (baseline 236 + 5 i18n smoke + 4 nouveaux tests sur badges/buttons/aria). tsc --noEmit clean, eslint --max-warnings=0 clean.
  • Build : npm run build (= vite build) clean — PostCSS résout tous les @apply, aucune classe Tailwind non-résolue (leçon sprint 11 feedback_css_apply_tokens).
  • Backend : 469 / 469 pytest intact (zéro fichier backend touché).
  • HMR cache : node_modules/.vite bustéavant la prise des screenshots de validation finale — élimine la suspicion de stale bundle (code-reviewer flag REG 1).
  • E2E : non re-joué (mur Vite proxy connu). Smoke manuel ci-dessous.

Comment tester en local

git checkout sprint/12-i18n-fr
make build && make start
# http://127.0.0.1:5000
make create-admin USER=alice PASS=changeme8   # si premier setup

Scénarios :

  1. Login en FRMimic (brand), Connectez-vous pour accéder à vos engagements., fields Nom d'utilisateur / Mot de passe, bouton Se connecter.
  2. NavEngagements / Templates / Utilisateurs / Se déconnecter. Footer FR : Mimic — Outillage interne Purple Team. Engagements autorisés uniquement.
  3. Engagements list — title Engagements, subtitle Missions red team et leur statut., columns NOM / STATUT / DÉBUT / FIN / CRÉÉ PAR / ACTIONS, badges status FR (Planifié / Actif / Clôturé), dates jj/mm/aaaa, actions Voir / Modifier / Supprimer.
  4. Engagement detail (2 tabs) — back link Retour aux engagements, badge status FR juste sous le titre (Actif), tabs Description (default) + Simulations avec count pill. Dans le panel Description : header block avec Date de début / Date de fin / Statut (badge) / Créé le + bouton MODIFIER, séparateur, puis Description text. Boutons header droite : EXPORTER + MODIFIER.
  5. Engagement form (edit) — labels FR, validation messages FR (Le nom est requis, etc.), boutons Enregistrer les modifications / Annuler, toast Engagement mis à jour.
  6. Simulation form — toutes les sections (Red Team / SOC), tous les boutons, banners success/warn en FR, status badges traduits réactivement.
  7. Templates — list + form + picker modal en FR.
  8. Users admin — rôles FR (admin / redteam / soc peuvent rester en values), labels en FR, dates jj/mm/aaaa.
  9. MITRE matrix — modal title Matrice MITRE ATT&CK, filter Filtrer, empty state Aucun résultat, aria-labels Replier {{id}} / Déplier {{id}}.
  10. C2 config card — title Configuration C2, fields en FR, checkbox Vérifier le certificat TLS + helper text MITM en FR (depuis sprint 10).

Fichiers nouveaux

Path Notes
frontend/src/i18n/index.ts i18next init, single FR locale
frontend/src/i18n/fr.json 324 leaf keys (brief estimait 140-160, finer granularity acceptable)
frontend/src/i18n/status.ts engagementStatusLabel / simulationStatusLabel non-réactifs (call sites hors-component)
frontend/tests/i18n.test.ts 8 assertions smoke (init, missing key, status, interpolation)

Fichiers modifiés (sample, ~30 src files + 23 test files)

Path Type
frontend/src/main.tsx mount import './i18n' avant <App />
frontend/src/lib/format.ts formatDate / formatDateTime wrappers fr-FR
frontend/src/components/StatusBadge.tsx useTranslation() réactif
frontend/src/components/SimulationStatusBadge.tsx useTranslation() réactif
frontend/src/components/Layout.tsx nav + footer + theme aria via t()
frontend/src/components/ExportEngagementButton.tsx i18n complet (Exporter + aria + loading)
frontend/src/components/AlertBanner.tsx (variants restent en API, contenus traduits en JSX appelant)
frontend/src/components/MitreMatrixModal.tsx Aucun résultat + aria Replier/Déplier {{id}}
frontend/src/components/C2*.tsx tous traduits (config, tasks panel, execute/import modals, picker)
frontend/src/pages/EngagementDetailPage.tsx 3-tab → 2-tab + Schedule header dans Description
frontend/src/pages/LoginPage.tsx i18n
frontend/src/pages/EngagementsListPage.tsx i18n + dates fr-FR
frontend/src/pages/EngagementFormPage.tsx i18n
frontend/src/pages/SimulationFormPage.tsx i18n + AlertBanner FR
frontend/src/pages/TemplatesListPage.tsx i18n + dates fr-FR
frontend/src/pages/TemplateFormPage.tsx i18n
frontend/src/pages/UsersAdminPage.tsx i18n + dates fr-FR + data-testid="new-role-select"
CHANGELOG.md entrée sprint 12

Décisions verrouillées (de la phase planning)

  • Q1 : react-i18next complet (deps + hook + JSON FR + interpolation + pluralization)
  • Q2 : Acronymes restent en EN (RT/SOC/MITRE/etc., brand Mimic, Red Team idiom)
  • Q3 : Status enum DB en EN, traduction au rendering layer via i18n/status.ts
  • Q4 : Format date fr-FR (jj/mm/aaaa)
  • Q5 : Ton toasts/alerts bref (Engagement créé, pas Engagement enregistré avec succès)
  • Q6 : EngagementDetailPage 3→2 tabs (Schedule fusionné dans Description)

Patterns notables

  • i18n init avant App mount : main.tsx importe ./i18n avant <App /> pour éviter les premières renders avec keys raw.
  • Réactivité badges : useTranslation() inline dans les composants visibles → re-render automatique si la langue change (et timing-safe vis-à-vis HMR).
  • Helpers non-réactifs : engagementStatusLabel() / simulationStatusLabel() gardés pour les call sites hors-component (form options, banner content construction, etc.).
  • Date helpers monolocale : formatDate / formatDateTime hardcodent 'fr-FR' — pas de prop locale, pas de switcher. Si multi-langue un jour, c'est ici qu'on passera à i18n.language.
  • Variable shadowing évité : les loop vars t ont été renommés en tech / tac / tpl / toast partout pour ne pas masquer le t de useTranslation().
  • Pluralization : 4 sites utilisent les keys _one / _other (c2.modal.execute.commandCount, c2.modal.import.toast.imported/partial, mitre.matrix.applyItem).

Out of scope (reconnu, sprint 13+)

  • "Simulations" double label+h1 (design MINOR 2) : le tab "Simulations" et l'h1 du panel font doublon visuel. Pre-existing depuis sprint 11. Design call séparé pour le futur (probablement supprimer l'h1).
  • Multi-langue : infra prête pour ajouter en.json si jamais besoin, mais aucun switcher UI ni détection navigator. FR-only.
  • Backend i18n : zéro touché. Les error messages backend (rares pour l'utilisateur) restent en EN — à voir si nécessaire un jour.
  • ForbiddenState / NotFoundState : composants jamais créés (pas dans le code), pas de keys i18n.

NITs rejetés

  • text-caption-mdcaption-md (design NIT 1) : rejeté. Le sprint 11 build-fix a établi que les fontSize tokens (registered dans theme.extend) génèrent des utilities text-<key>, jamais des bare-name utilities. Le prefix text- est correct, omettre casserait PostCSS. Documenté dans feedback_css_apply_tokens.md.

Commits (14 sur la branche, dont 12 nouveaux sprint 12)

3cdfba6 feat(frontend): merge Schedule tab into Description on EngagementDetailPage
51fb2e4 chore(frontend): install react-i18next + i18next deps
3723bd0 feat(frontend): scaffold i18n init, fr.json, status map, format helpers
fe597e9 feat(frontend): i18n common + nav + auth (Layout, LoginPage, ProtectedRoute)
ea870af feat(frontend): i18n engagement pages + fr-FR date formatting
5b93f88 feat(frontend): i18n simulation pages
284494c feat(frontend): i18n template pages
bdda02b feat(frontend): i18n MITRE components
ad0a3f5 feat(frontend): i18n C2 components
2931e4a feat(frontend): i18n shared state components + UsersAdminPage + CHANGELOG
6648d1c fix(frontend): translate status badge labels via i18n helpers
ac0bc8d fix(frontend): i18n reactive badges + fr-FR dates on detail/admin + FR footer (design-review)
2ff097b fix(frontend): i18n ExportEngagementButton + aria-labels + MitreMatrix noMatch (code-review)

(Plus 2c7fcec du sprint 11 trail tail, sera absorbé par le merge.)

🤖 Generated with Claude Code

## Summary - **Sprint 12 — Full FR i18n via react-i18next + EngagementDetailPage tab merge** : Mimic est désormais entièrement en français pour les opérateurs CACIB, depuis l'infrastructure (react-i18next + JSON FR) jusqu'aux aria-labels et au footer. Toutes les dates basculées en `fr-FR` (jj/mm/aaaa). 13 commits livrés + le `2c7fcec` du sprint 11 (build fix `text-caption-*`). - **EngagementDetailPage** : passé de 3 tabs à **2 tabs** (Description + Simulations). Le contenu Schedule (dates de début/fin, statut, créé le, bouton Modifier) devient un header block dans le panel Description. - **Acronymes en EN** (locked) : RT, SOC, MITRE, ATT&CK, C2, BAS, TLS, MITM, JWT, IP, URL, API, ID. Brand "Mimic" gardé. "Red Team" reste en idiom. - **Status enum** : valeurs DB en EN inchangées (`pending`/`active`/`done`/etc.), traduction au rendering layer via `i18n/status.ts` + hook `useTranslation()` inside `StatusBadge` / `SimulationStatusBadge` (subscription réactive correcte). - **Date format** : `formatDate()` / `formatDateTime()` dans `lib/format.ts` utilisent `toLocaleDateString('fr-FR')` / `toLocaleString('fr-FR')`. Appliqués à 5 sites (engagements list/detail, users admin, templates list, simulations list). - **Reviews 2 passes** : design-review (4 MAJOR + 2 MINOR patched) → code-review (1 MAJOR + 4 MINOR patched). Tous APPROVED post-polish. ## Dépendance — sprint 11 Cette branche est basée sur `sprint/11-spectrum-ux` (PR #13 toujours ouverte). Le diff inclura temporairement les commits sprint 11 — il se réduira automatiquement à sprint 12 seul une fois PR #13 mergée. **Merger PR #13 avant celui-ci** pour un diff propre. ## Test plan - **Frontend** : **245 / 245** vitest (baseline 236 + 5 i18n smoke + 4 nouveaux tests sur badges/buttons/aria). `tsc --noEmit` clean, `eslint --max-warnings=0` clean. - **Build** : `npm run build` (= `vite build`) clean — PostCSS résout tous les `@apply`, aucune classe Tailwind non-résolue (leçon sprint 11 `feedback_css_apply_tokens`). - **Backend** : **469 / 469** pytest intact (zéro fichier backend touché). - **HMR cache** : `node_modules/.vite` bustéavant la prise des screenshots de validation finale — élimine la suspicion de stale bundle (code-reviewer flag REG 1). - **E2E** : non re-joué (mur Vite proxy connu). Smoke manuel ci-dessous. ## Comment tester en local ```bash git checkout sprint/12-i18n-fr make build && make start # http://127.0.0.1:5000 make create-admin USER=alice PASS=changeme8 # si premier setup ``` Scénarios : 1. **Login en FR** — `Mimic` (brand), `Connectez-vous pour accéder à vos engagements.`, fields `Nom d'utilisateur` / `Mot de passe`, bouton `Se connecter`. 2. **Nav** — `Engagements` / `Templates` / `Utilisateurs` / `Se déconnecter`. Footer FR : `Mimic — Outillage interne Purple Team. Engagements autorisés uniquement.` 3. **Engagements list** — title `Engagements`, subtitle `Missions red team et leur statut.`, columns `NOM / STATUT / DÉBUT / FIN / CRÉÉ PAR / ACTIONS`, badges status FR (`Planifié` / `Actif` / `Clôturé`), dates `jj/mm/aaaa`, actions `Voir` / `Modifier` / `Supprimer`. 4. **Engagement detail (2 tabs)** — back link `Retour aux engagements`, badge status FR juste sous le titre (`Actif`), tabs `Description` (default) + `Simulations` avec count pill. Dans le panel Description : header block avec `Date de début` / `Date de fin` / `Statut` (badge) / `Créé le` + bouton `MODIFIER`, séparateur, puis `Description` text. Boutons header droite : `EXPORTER` + `MODIFIER`. 5. **Engagement form (edit)** — labels FR, validation messages FR (`Le nom est requis`, etc.), boutons `Enregistrer les modifications` / `Annuler`, toast `Engagement mis à jour`. 6. **Simulation form** — toutes les sections (Red Team / SOC), tous les boutons, banners `success`/`warn` en FR, status badges traduits réactivement. 7. **Templates** — list + form + picker modal en FR. 8. **Users admin** — rôles FR (`admin` / `redteam` / `soc` peuvent rester en values), labels en FR, dates `jj/mm/aaaa`. 9. **MITRE matrix** — modal title `Matrice MITRE ATT&CK`, filter `Filtrer`, empty state `Aucun résultat`, aria-labels `Replier {{id}}` / `Déplier {{id}}`. 10. **C2 config card** — title `Configuration C2`, fields en FR, checkbox `Vérifier le certificat TLS` + helper text MITM en FR (depuis sprint 10). ## Fichiers nouveaux | Path | Notes | |---|---| | `frontend/src/i18n/index.ts` | i18next init, single FR locale | | `frontend/src/i18n/fr.json` | 324 leaf keys (brief estimait 140-160, finer granularity acceptable) | | `frontend/src/i18n/status.ts` | `engagementStatusLabel` / `simulationStatusLabel` non-réactifs (call sites hors-component) | | `frontend/tests/i18n.test.ts` | 8 assertions smoke (init, missing key, status, interpolation) | ## Fichiers modifiés (sample, ~30 src files + 23 test files) | Path | Type | |---|---| | `frontend/src/main.tsx` | mount `import './i18n'` avant `<App />` | | `frontend/src/lib/format.ts` | `formatDate` / `formatDateTime` wrappers `fr-FR` | | `frontend/src/components/StatusBadge.tsx` | `useTranslation()` réactif | | `frontend/src/components/SimulationStatusBadge.tsx` | `useTranslation()` réactif | | `frontend/src/components/Layout.tsx` | nav + footer + theme aria via `t()` | | `frontend/src/components/ExportEngagementButton.tsx` | i18n complet (`Exporter` + aria + loading) | | `frontend/src/components/AlertBanner.tsx` | (variants restent en API, contenus traduits en JSX appelant) | | `frontend/src/components/MitreMatrixModal.tsx` | `Aucun résultat` + aria `Replier/Déplier {{id}}` | | `frontend/src/components/C2*.tsx` | tous traduits (config, tasks panel, execute/import modals, picker) | | `frontend/src/pages/EngagementDetailPage.tsx` | 3-tab → 2-tab + Schedule header dans Description | | `frontend/src/pages/LoginPage.tsx` | i18n | | `frontend/src/pages/EngagementsListPage.tsx` | i18n + dates fr-FR | | `frontend/src/pages/EngagementFormPage.tsx` | i18n | | `frontend/src/pages/SimulationFormPage.tsx` | i18n + AlertBanner FR | | `frontend/src/pages/TemplatesListPage.tsx` | i18n + dates fr-FR | | `frontend/src/pages/TemplateFormPage.tsx` | i18n | | `frontend/src/pages/UsersAdminPage.tsx` | i18n + dates fr-FR + `data-testid="new-role-select"` | | `CHANGELOG.md` | entrée sprint 12 | ## Décisions verrouillées (de la phase planning) - **Q1** : `react-i18next` complet (deps + hook + JSON FR + interpolation + pluralization) - **Q2** : Acronymes restent en EN (RT/SOC/MITRE/etc., brand Mimic, Red Team idiom) - **Q3** : Status enum DB en EN, traduction au rendering layer via `i18n/status.ts` - **Q4** : Format date `fr-FR` (jj/mm/aaaa) - **Q5** : Ton toasts/alerts bref (`Engagement créé`, pas `Engagement enregistré avec succès`) - **Q6** : EngagementDetailPage 3→2 tabs (Schedule fusionné dans Description) ## Patterns notables - **i18n init avant App mount** : `main.tsx` importe `./i18n` avant `<App />` pour éviter les premières renders avec keys raw. - **Réactivité badges** : `useTranslation()` inline dans les composants visibles → re-render automatique si la langue change (et timing-safe vis-à-vis HMR). - **Helpers non-réactifs** : `engagementStatusLabel()` / `simulationStatusLabel()` gardés pour les call sites hors-component (form options, banner content construction, etc.). - **Date helpers monolocale** : `formatDate` / `formatDateTime` hardcodent `'fr-FR'` — pas de prop locale, pas de switcher. Si multi-langue un jour, c'est ici qu'on passera à `i18n.language`. - **Variable shadowing évité** : les loop vars `t` ont été renommés en `tech` / `tac` / `tpl` / `toast` partout pour ne pas masquer le `t` de `useTranslation()`. - **Pluralization** : 4 sites utilisent les keys `_one` / `_other` (c2.modal.execute.commandCount, c2.modal.import.toast.imported/partial, mitre.matrix.applyItem). ## Out of scope (reconnu, sprint 13+) - **"Simulations" double label+h1** (design MINOR 2) : le tab "Simulations" et l'h1 du panel font doublon visuel. Pre-existing depuis sprint 11. Design call séparé pour le futur (probablement supprimer l'h1). - **Multi-langue** : infra prête pour ajouter `en.json` si jamais besoin, mais aucun switcher UI ni détection navigator. FR-only. - **Backend i18n** : zéro touché. Les error messages backend (rares pour l'utilisateur) restent en EN — à voir si nécessaire un jour. - **`ForbiddenState` / `NotFoundState`** : composants jamais créés (pas dans le code), pas de keys i18n. ## NITs rejetés - **`text-caption-md` → `caption-md`** (design NIT 1) : rejeté. Le sprint 11 build-fix a établi que les fontSize tokens (registered dans `theme.extend`) génèrent des utilities `text-<key>`, jamais des bare-name utilities. Le prefix `text-` est correct, omettre casserait PostCSS. Documenté dans `feedback_css_apply_tokens.md`. ## Commits (14 sur la branche, dont 12 nouveaux sprint 12) ``` 3cdfba6 feat(frontend): merge Schedule tab into Description on EngagementDetailPage 51fb2e4 chore(frontend): install react-i18next + i18next deps 3723bd0 feat(frontend): scaffold i18n init, fr.json, status map, format helpers fe597e9 feat(frontend): i18n common + nav + auth (Layout, LoginPage, ProtectedRoute) ea870af feat(frontend): i18n engagement pages + fr-FR date formatting 5b93f88 feat(frontend): i18n simulation pages 284494c feat(frontend): i18n template pages bdda02b feat(frontend): i18n MITRE components ad0a3f5 feat(frontend): i18n C2 components 2931e4a feat(frontend): i18n shared state components + UsersAdminPage + CHANGELOG 6648d1c fix(frontend): translate status badge labels via i18n helpers ac0bc8d fix(frontend): i18n reactive badges + fr-FR dates on detail/admin + FR footer (design-review) 2ff097b fix(frontend): i18n ExportEngagementButton + aria-labels + MitreMatrix noMatch (code-review) ``` (Plus `2c7fcec` du sprint 11 trail tail, sera absorbé par le merge.) 🤖 Generated with [Claude Code](https://claude.com/claude-code)
knacky added 25 commits 2026-06-21 22:25:28 +00:00
- useHashTab: pure-TS hook reading window.location.hash, falls back to
  defaultId, hashchange listener cleaned up on unmount
- Tabs: <Tabs items activeId onChange> with tab-underline / tab-underline-active
  recipes and count-pill (rounded-pill exception per DESIGN.md)
- index.css: tab-underline*, tab-count-pill*, alert-*, table-compact recipes

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Tabs: Schedule (default) / Description / Simulations, wired via
useHashTab('schedule'). Count pill on Simulations tab. BackLink
replaces hand-rolled Link.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
border-l-4 semantic strip, bg-paper, Lucide icons at size=16.
ARIA role="alert" for error/warn, role="status" for success/info.
No shadow, no radius, no transition.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
BackLink: ArrowLeft (14px) + caption-md text-graphite hover:text-primary,
instant. Replaces inline Link patterns in EngagementDetailPage (already
committed), SimulationFormPage, and TemplateFormPage. Also migrates
SimulationFormPage Done/SOC banners to AlertBanner.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add table-compact class to all 4 list surfaces: EngagementsListPage,
TemplatesListPage, UsersAdminPage, SimulationList. Remove inline
px-xl py-md from th/td — recipe handles padding. Row border-b moved
to recipe. WCAG SC 2.5.5 tradeoff accepted (BAS operator tool).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
4 new spec files adding 21 tests (7+6+4+4). Each component spec
includes brutalism invariant assertions (no rounded-md, no transition-*,
no shadow-*).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds:
- Navigation > Sub-page tabs subsection (tab-underline recipes, count pill, ARIA)
- Data Tables: row min-height 44px -> 32px + WCAG SC 2.5.5 rationale
- Toast Notifications > Inline Banners subsection (4 variants table)
Also adds Sprint 11 entry to CHANGELOG.md.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Fix 1 (A): move row height constraint from min-height on tbody tr
(CSS no-op on table-row) to height: 32px on td (works on table-cell).
Fix 2 (A): table header text-[11px] -> text-[12px] to align with
documented caption scale.
Fix 3: add aria-controls + id to Tabs buttons; wrap active tab content
in role="tabpanel" + aria-labelledby in EngagementDetailPage.
Test: add aria-controls/id assertion to Tabs.test.tsx (234 total).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Fix 4: useHashTab navigate() uses history.replaceState instead of
window.location.hash assignment — no spurious history entries, no
anchor-jump scroll side-effect.
Fix 5: Tabs ArrowLeft/ArrowRight keyboard nav (WAI-ARIA tabs pattern).
Fix 6: TabId union type in EngagementDetailPage, cast from string for
type-safe switch on activeTab without breaking Tabs.onChange signature.
Fix 7: intentional comment on UsersAdminPage reset-password aerated row.
Tests: 236/236 (+2 arrow-key assertions in Tabs.test.tsx).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
caption-bold and caption-md are fontSize tokens — Tailwind generates
text-caption-bold / text-caption-md utilities, not bare-name utilities.
Missing text- prefix caused @apply resolution failure in vite build
(PostCSS step), while vitest passed because it does not resolve @apply.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Reduces from 3 tabs (Schedule / Description / Simulations) to 2
(Description / Simulations). Schedule content (start/end/status/created_at
+ Edit button) moved into a header block at the top of the Description
panel, separated from description text by a hairline <hr>. Default tab
changed from 'schedule' to 'description'.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
StatusBadge and SimulationStatusBadge now call engagementStatusLabel() /
simulationStatusLabel() from @/i18n/status.ts instead of hardcoded English
strings. Tests updated to expect French labels (Planifié/Actif/Clôturé,
En attente/En cours/Révision requise/Terminé).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- StatusBadge/SimulationStatusBadge: swap bare i18n.t() helper for
  useTranslation() hook so badges re-render reactively on i18n init
- EngagementDetailPage: formatDate() on start_date/end_date/created_at
  (was: raw ISO string, formatDateTime with noisy 00:00:00)
- UsersAdminPage: formatDate() on created_at column (was: raw ISO string)
- Layout footer: t('nav.footer') with FR string in fr.json

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- ExportEngagementButton: full i18n (export.btn, export.btnLoading, export.aria)
- Layout: aria-label nav.brandHome on brand link, nav.themeAria on theme button
- SimulationList: aria-label simulation.list.rowActions on chevron button
- C2TasksPanel: aria-label c2.tasks.col.expand on expand column header
- ImportC2HistoryModal: aria-label common.select on select column header
- MitreMatrixModal: common.collapse/expand interpolated aria-label + mitre.matrix.noMatch
- fr.json: new keys common.expand/collapse/select, nav.brandHome/themeAria, export.*, simulation.list.rowActions, mitre.matrix.noMatch, c2.tasks.col.expand

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
knacky merged commit addfb5ad38 into main 2026-06-22 08:25:51 +00:00
knacky deleted branch sprint/12-i18n-fr 2026-06-22 08:25:51 +00:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: knacky/mimic#14