From 2931e4aaf975aab1f71a4c1d6333b4f726f11770 Mon Sep 17 00:00:00 2001 From: Knacky Date: Sun, 21 Jun 2026 23:56:47 +0200 Subject: [PATCH] feat(frontend): i18n shared state components + UsersAdminPage + CHANGELOG Co-Authored-By: Claude Sonnet 4.6 --- CHANGELOG.md | 37 +++++++++++ frontend/src/components/ErrorState.tsx | 10 ++- frontend/src/components/Toast.tsx | 18 +++--- frontend/src/i18n/fr.json | 14 +++- frontend/src/pages/UsersAdminPage.tsx | 89 +++++++++++++------------- frontend/tests/Toast.test.tsx | 2 +- frontend/tests/UsersAdminPage.test.tsx | 20 +++--- frontend/tests/states.test.tsx | 4 +- 8 files changed, 125 insertions(+), 69 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d27197d..88fa1ce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,43 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) ## [Unreleased] +### Changed — Sprint 12 (EngagementDetailPage 2-tab merge + full FR i18n) + +**Frontend only** (245 vitest passing — baseline 233 + 12 new i18n smoke tests) + +- `frontend/src/i18n/fr.json` — NEW. ~160-key nested translation file (French). Covers: common, nav, auth, engagement, simulation, template, user.admin, c2, mitre, state, toast, status. +- `frontend/src/i18n/index.ts` — NEW. i18next init (lng: fr, no fallback to en, escapeValue: false). +- `frontend/src/i18n/status.ts` — NEW. `engagementStatusLabel()` / `simulationStatusLabel()` helpers mapping API status to translated strings. +- `frontend/src/lib/format.ts` — NEW. `formatDate()` / `formatDateTime()` using `fr-FR` locale. +- `frontend/src/main.tsx` — Added `import './i18n'` bootstrap. +- `frontend/vitest.setup.ts` — Added `import './src/i18n'` so all tests use real French translations. +- `frontend/src/pages/EngagementDetailPage.tsx` — Merged 3-tab layout → 2 tabs (Description + Simulations); full i18n pass. +- `frontend/src/pages/EngagementsListPage.tsx` — Full i18n pass; local `formatDate` removed, `@/lib/format` used. +- `frontend/src/pages/EngagementFormPage.tsx` — Full i18n pass; `validate()` inside component; `data-testid="btn-submit"` added. +- `frontend/src/pages/SimulationFormPage.tsx` — Full i18n pass; `data-testid` on 4 action buttons. +- `frontend/src/pages/TemplatesListPage.tsx` — Full i18n pass. +- `frontend/src/pages/TemplateFormPage.tsx` — Full i18n pass. +- `frontend/src/pages/UsersAdminPage.tsx` — Full i18n pass; `data-testid="new-role-select"` added. +- `frontend/src/components/Layout.tsx` — Nav labels translated. +- `frontend/src/components/LoginPage.tsx` — Form labels and error message translated. +- `frontend/src/components/ProtectedRoute.tsx` — Forbidden/loading messages translated. +- `frontend/src/components/SimulationList.tsx` — Full i18n pass. +- `frontend/src/components/TemplatePickerModal.tsx` — Full i18n pass. +- `frontend/src/components/MitreTechniqueTag.tsx` — `aria-label` translated (Retirer TX…). +- `frontend/src/components/MitreTechniquesField.tsx` — Full i18n pass. +- `frontend/src/components/MitreMatrixModal.tsx` — Full i18n pass; plural apply button. +- `frontend/src/components/MitreTechniquePicker.tsx` — Placeholder and messages translated. +- `frontend/src/components/C2ConfigCard.tsx` — Full i18n pass. +- `frontend/src/components/C2TasksPanel.tsx` — Full i18n pass. +- `frontend/src/components/ExecuteViaC2Modal.tsx` — Full i18n pass. +- `frontend/src/components/ImportC2HistoryModal.tsx` — Full i18n pass. +- `frontend/src/components/C2CallbackPicker.tsx` — Full i18n pass. +- `frontend/src/components/Toast.tsx` — Dismiss aria-label translated. +- `frontend/src/components/ErrorState.tsx` — Title default and Retry button translated. +- All test files updated to use French strings or `data-testid` where button labels changed. + +**No backend changes. No DB schema change. No migration.** + ### Added — Sprint 11 (Spectrum UX port: 4 primitives + compact density global) **Frontend only** (233 vitest passing — baseline 212 + 21 new tests across 4 new specs) diff --git a/frontend/src/components/ErrorState.tsx b/frontend/src/components/ErrorState.tsx index 9bf984b..4b5a612 100644 --- a/frontend/src/components/ErrorState.tsx +++ b/frontend/src/components/ErrorState.tsx @@ -1,21 +1,25 @@ +import { useTranslation } from 'react-i18next'; + interface ErrorStateProps { title?: string; message: string; onRetry?: () => void; } -export function ErrorState({ title = 'Something went wrong', message, onRetry }: ErrorStateProps): JSX.Element { +export function ErrorState({ title, message, onRetry }: ErrorStateProps): JSX.Element { + const { t } = useTranslation(); + const resolvedTitle = title ?? t('state.error.title'); return (
-

{title}

+

{resolvedTitle}

{message}

{onRetry ? ( ) : null}
diff --git a/frontend/src/components/Toast.tsx b/frontend/src/components/Toast.tsx index 3e0ed7e..a7ba5d4 100644 --- a/frontend/src/components/Toast.tsx +++ b/frontend/src/components/Toast.tsx @@ -1,6 +1,8 @@ +import { useTranslation } from 'react-i18next'; import { useToast } from '@/hooks/useToast'; export function ToastViewport(): JSX.Element { + const { t } = useTranslation(); const { toasts, dismiss } = useToast(); return (
- {toasts.map((t) => { - const isError = t.kind === 'error'; - const isSuccess = t.kind === 'success'; + {toasts.map((toast) => { + const isError = toast.kind === 'error'; + const isSuccess = toast.kind === 'success'; const surface = isError ? 'bg-paper text-ink border border-hairline border-l-4 border-l-bloom-deep' : isSuccess @@ -18,18 +20,18 @@ export function ToastViewport(): JSX.Element { : 'bg-paper text-ink border border-hairline border-l-4 border-l-primary'; return (
- {t.message} + {toast.message}