feat(onboarding): getting-started banner on top of every page #403

Merged
maximus merged 3 commits from issue-397-onboarding-banner into main 2026-10-09 01:10:32 +00:00
Owner

Resolves #397

Generated autonomously by /autopilot run of 2026-10-08

PR empilée sur #402 (base issue-396-onboarding-shared-state) : elle ne montre que son propre diff.

Ce qui change pour l'utilisateur

  • Un bandeau de démarrage en haut de chaque page, dans le flux de la page : « Démarrage 3/6 · Étape suivante : <étape> → », « Voir la liste » (la liste détaillée se déplie en place) et « Masquer ».
  • Il n'apparaît pas sur le tableau de bord d'un profil sans transaction (la carte d'accueil contient déjà la liste), quand le parcours est masqué, ni avant le premier chargement. Comme CARD_PAGES est encore vide, il reste aussi sur /import, /transactions et /budget.
  • Fin du parcours : « Démarrage terminé. Le guide d'utilisation vous accompagne pour la suite. », « Ouvrir le guide » (section « Premiers pas » de Paramètres → Utilisateurs) et « Fermer », qui masque le parcours.
  • La barre compacte du tableau de bord disparaît ; la carte d'accueil du profil vide garde sa liste détaillée.
  • Dans la liste détaillée, chaque état est annoncé aux lecteurs d'écran : « (faite) », « (passée) », « (verrouillée) », « (en attente) ». Une étape passée montre l'icône « passer », le badge « Passée » et « Reprendre ». Aucune interface ne passe encore une étape : les cartes arrivent avec #398.
  • Paramètres → Utilisateurs : « Afficher la liste de démarrage » apparaît dès que le parcours est masqué, y compris depuis le bandeau de cette même page, sans la quitter. Nouveaux textes : « Parcours de démarrage » et « Le parcours de démarrage réapparaît en haut des pages ».

Technique

  • src/components/onboarding/OnboardingBanner.tsx (nouveau), piloté par props : mode, checklist, nextStep, budgetReady, listOpen, onToggleList, onHide, onResume. Il ne rend rien hors des modes banner et complete. Le bouton de la liste porte aria-expanded et un aria-controls vers un conteneur toujours rendu.
  • AppShell : OnboardingBannerSlot lit useOnboarding() et tient l'état déplié. AppShell le monte avant <Outlet/>, dans le fournisseur, avec key={pathname} : chaque page commence repliée.
  • onboardingPath.ts : stepNavState(key) rend { onboarding: { focus: true } }, avec guided: true en plus pour manualCategory. Il sert à tout lien vers une étape : le bandeau, les lignes de la liste et « Reprendre ».
  • OnboardingChecklist :
    • le mode compact et la prop mode sont retirés ;
    • nouvel état skipped, avec cette priorité : en attente, verrouillée, faite, passée, à faire ;
    • texte d'état en sr-only après le libellé ;
    • « Reprendre » est un <Link onClick={() => onResume(key)}> (react-router appelle onClick avant de naviguer) ;
    • la prop onResume est requise.
  • DashboardEmptyState transmet onResume à sa liste (la prop mode disparaît). Son rendu ne change pas, et un test l'épingle.
  • DashboardPage : la barre compacte est retirée.
  • UsersSettingsPage :
    • lit hidden et show dans useOnboarding(), sans getPreference ni showChecklist directs ;
    • un drapeau local restored sert à la confirmation.
  • i18n FR/EN : onboarding.banner.*, onboarding.state.*, onboarding.checklist.skippedBadge et .resume, ainsi que settings.users.onboarding.label et .confirmation.
  • docs/architecture.md : les lignes que ce changement rend fausses (compte de composants, dossier onboarding/, mode compact, réaffichage), comme #396.
  • CHANGELOG FR/EN : Ajouté (le bandeau) ; Modifié (barre compacte remplacée, états annoncés, réaffichage).

Preuves

  • npm run build vert.
  • npx vitest run : 1680 → 1727 tests (89 → 94 fichiers), tous verts.
  • Tests ajoutés ou adaptés :
    • OnboardingBanner.render.test.tsx (13) ;
    • AppShell.render.test.tsx (12) : vrai rendu d'AppShell, avec le mode dérivé par deriveOnboardingView et le vrai CARD_PAGES ;
    • AppShell.slot.test.tsx (5) ;
    • UsersSettingsPage.test.tsx (6) : useState simulé, donc le même état d'un rendu à l'autre, sans remontage ;
    • DashboardEmptyState.render.test.tsx (3) ;
    • OnboardingChecklist.test.tsx : 3 tests du mode compact retirés, 8 ajoutés ;
    • DashboardPage.render.test.tsx adapté (+1) ;
    • onboardingPath.test.ts (+2).
  • Passe de mutations sur l'arbre commité, une mutation à la fois, restaurée par git checkout : 8/8 rouges.
# Mutation Tests rouges
M1 Le bandeau s'affiche aussi en mode card 2
M2 AppShell passe mode="banner" en dur (parcours masqué, tableau de bord vide, carte) 5
M3 Le compteur n'attend plus budgetReady 2
M4 aria-expanded figé à false 1
M5 « Reprendre » n'appelle plus resume 1
M6 Paramètres → Utilisateurs lit son drapeau local au lieu de hidden 2
M7 La barre compacte revient sur le tableau de bord 1
M8 Plus de key={pathname} sur l'emplacement du bandeau 1

Avant : 1727 verts. Après restauration : 1727 verts, arbre propre.

Décisions

Seize décisions, toutes consignées dans le journal du run. Les principales :

  • [MEDIUM] Un seul état de navigation pour tout lien vers une étape (stepNavState). L'entrée guidée de l'étape 5 s'applique donc aussi depuis la liste. Écarté : le focus seul dans la liste.
  • [HIGH] La ligne verrouillée n'a pas d'état de navigation : la carte Licence ne porte pas de carte d'étape.
  • [MEDIUM] « Reprendre » est un lien avec onClick, sans attendre l'écriture : le reducer règle les deux ordres de RESUME et de la relecture. Écarté : useNavigate après await, qui ajouterait un hook de routeur à un composant pur.
  • [HIGH] Une ligne faite et passée s'affiche faite, sans « Reprendre ». Le badge « Passée » est aria-hidden : le texte masqué l'annonce déjà.
  • [MEDIUM] « DashboardEmptyState inchangé » est lu comme « la carte garde sa liste détaillée » : la carte ne fait que passer des props, et son rendu est épinglé par un test.
  • [MEDIUM] La liste est repliée à chaque page (key={pathname}). Écartés : un état qui suit l'utilisateur de page en page, et une liste qui se rouvre en revenant sur sa page.
  • [HIGH] La condition de montage vit dans le bandeau seul ; un second test dans AppShell serait du code mort.
  • [HIGH] settings.users.onboarding.show reste tel quel : il est absent de la table de textes de l'issue.
  • [MEDIUM] Le guide (docs/guide-utilisateur.md l. 86 et 581, et les clés docs.*) décrit encore la barre compacte : laissé à #401, comme le prévoit le plan.
  • [HIGH] Le CHANGELOG ne parle ni de « Passer » ni de « Reprendre » : aucune interface ne passe une étape avant #398.

Pour relire

  • Ordre de lecture conseillé : OnboardingBanner.tsx, puis AppShell.tsx (emplacement et clé de route), puis OnboardingChecklist.tsx.
  • Points à trancher :
    • la lecture de « DashboardEmptyState inchangé » (prop onResume transmise) ;
    • le choix de replier la liste à chaque changement de page.
  • Le guide utilisateur reste à réécrire dans #401.
  • Vérification manuelle, pour la liste QA de #401 :
    • le bandeau sur chaque page, mais pas sur le tableau de bord vide ;
    • « Voir la liste » et « Fermer la liste » ;
    • « Masquer » depuis Paramètres → Utilisateurs, où le bouton de réaffichage apparaît sans quitter la page ;
    • le message de fin et « Ouvrir le guide » ;
    • les annonces NVDA et Orca.

🤖 Generated with Claude Code

Resolves #397 Generated autonomously by /autopilot run of 2026-10-08 > PR empilée sur #402 (base issue-396-onboarding-shared-state) : elle ne montre que son propre diff. ## Ce qui change pour l'utilisateur - Un bandeau de démarrage en haut de chaque page, dans le flux de la page : « Démarrage 3/6 · Étape suivante : <étape> → », « Voir la liste » (la liste détaillée se déplie en place) et « Masquer ». - Il n'apparaît pas sur le tableau de bord d'un profil sans transaction (la carte d'accueil contient déjà la liste), quand le parcours est masqué, ni avant le premier chargement. Comme `CARD_PAGES` est encore vide, il reste aussi sur /import, /transactions et /budget. - Fin du parcours : « Démarrage terminé. Le guide d'utilisation vous accompagne pour la suite. », « Ouvrir le guide » (section « Premiers pas » de Paramètres → Utilisateurs) et « Fermer », qui masque le parcours. - La barre compacte du tableau de bord disparaît ; la carte d'accueil du profil vide garde sa liste détaillée. - Dans la liste détaillée, chaque état est annoncé aux lecteurs d'écran : « (faite) », « (passée) », « (verrouillée) », « (en attente) ». Une étape passée montre l'icône « passer », le badge « Passée » et « Reprendre ». Aucune interface ne passe encore une étape : les cartes arrivent avec #398. - Paramètres → Utilisateurs : « Afficher la liste de démarrage » apparaît dès que le parcours est masqué, y compris depuis le bandeau de cette même page, sans la quitter. Nouveaux textes : « Parcours de démarrage » et « Le parcours de démarrage réapparaît en haut des pages ». ## Technique - **`src/components/onboarding/OnboardingBanner.tsx`** (nouveau), piloté par props : `mode`, `checklist`, `nextStep`, `budgetReady`, `listOpen`, `onToggleList`, `onHide`, `onResume`. Il ne rend rien hors des modes `banner` et `complete`. Le bouton de la liste porte `aria-expanded` et un `aria-controls` vers un conteneur toujours rendu. - **`AppShell`** : `OnboardingBannerSlot` lit `useOnboarding()` et tient l'état déplié. AppShell le monte avant `<Outlet/>`, dans le fournisseur, avec `key={pathname}` : chaque page commence repliée. - **`onboardingPath.ts`** : `stepNavState(key)` rend `{ onboarding: { focus: true } }`, avec `guided: true` en plus pour `manualCategory`. Il sert à tout lien vers une étape : le bandeau, les lignes de la liste et « Reprendre ». - **`OnboardingChecklist`** : - le mode `compact` et la prop `mode` sont retirés ; - nouvel état `skipped`, avec cette priorité : en attente, verrouillée, faite, passée, à faire ; - texte d'état en `sr-only` après le libellé ; - « Reprendre » est un `<Link onClick={() => onResume(key)}>` (react-router appelle `onClick` avant de naviguer) ; - la prop `onResume` est requise. - **`DashboardEmptyState`** transmet `onResume` à sa liste (la prop `mode` disparaît). Son rendu ne change pas, et un test l'épingle. - **`DashboardPage`** : la barre compacte est retirée. - **`UsersSettingsPage`** : - lit `hidden` et `show` dans `useOnboarding()`, sans `getPreference` ni `showChecklist` directs ; - un drapeau local `restored` sert à la confirmation. - **i18n FR/EN** : `onboarding.banner.*`, `onboarding.state.*`, `onboarding.checklist.skippedBadge` et `.resume`, ainsi que `settings.users.onboarding.label` et `.confirmation`. - **`docs/architecture.md`** : les lignes que ce changement rend fausses (compte de composants, dossier `onboarding/`, mode compact, réaffichage), comme #396. - **CHANGELOG FR/EN** : Ajouté (le bandeau) ; Modifié (barre compacte remplacée, états annoncés, réaffichage). ## Preuves - `npm run build` vert. - `npx vitest run` : **1680 → 1727** tests (89 → 94 fichiers), tous verts. - Tests ajoutés ou adaptés : - `OnboardingBanner.render.test.tsx` (13) ; - `AppShell.render.test.tsx` (12) : vrai rendu d'AppShell, avec le mode dérivé par `deriveOnboardingView` et le vrai `CARD_PAGES` ; - `AppShell.slot.test.tsx` (5) ; - `UsersSettingsPage.test.tsx` (6) : `useState` simulé, donc le même état d'un rendu à l'autre, sans remontage ; - `DashboardEmptyState.render.test.tsx` (3) ; - `OnboardingChecklist.test.tsx` : 3 tests du mode compact retirés, 8 ajoutés ; - `DashboardPage.render.test.tsx` adapté (+1) ; - `onboardingPath.test.ts` (+2). - Passe de mutations sur l'arbre commité, une mutation à la fois, restaurée par `git checkout` : **8/8 rouges**. | # | Mutation | Tests rouges | |---|---|---| | M1 | Le bandeau s'affiche aussi en mode `card` | 2 | | M2 | AppShell passe `mode="banner"` en dur (parcours masqué, tableau de bord vide, carte) | 5 | | M3 | Le compteur n'attend plus `budgetReady` | 2 | | M4 | `aria-expanded` figé à `false` | 1 | | M5 | « Reprendre » n'appelle plus `resume` | 1 | | M6 | Paramètres → Utilisateurs lit son drapeau local au lieu de `hidden` | 2 | | M7 | La barre compacte revient sur le tableau de bord | 1 | | M8 | Plus de `key={pathname}` sur l'emplacement du bandeau | 1 | Avant : 1727 verts. Après restauration : 1727 verts, arbre propre. ## Décisions Seize décisions, toutes consignées dans le journal du run. Les principales : - **[MEDIUM]** Un seul état de navigation pour tout lien vers une étape (`stepNavState`). L'entrée guidée de l'étape 5 s'applique donc aussi depuis la liste. Écarté : le focus seul dans la liste. - **[HIGH]** La ligne verrouillée n'a pas d'état de navigation : la carte Licence ne porte pas de carte d'étape. - **[MEDIUM]** « Reprendre » est un lien avec `onClick`, sans attendre l'écriture : le reducer règle les deux ordres de RESUME et de la relecture. Écarté : `useNavigate` après `await`, qui ajouterait un hook de routeur à un composant pur. - **[HIGH]** Une ligne faite et passée s'affiche faite, sans « Reprendre ». Le badge « Passée » est `aria-hidden` : le texte masqué l'annonce déjà. - **[MEDIUM]** « `DashboardEmptyState` inchangé » est lu comme « la carte garde sa liste détaillée » : la carte ne fait que passer des props, et son rendu est épinglé par un test. - **[MEDIUM]** La liste est repliée à chaque page (`key={pathname}`). Écartés : un état qui suit l'utilisateur de page en page, et une liste qui se rouvre en revenant sur sa page. - **[HIGH]** La condition de montage vit dans le bandeau seul ; un second test dans AppShell serait du code mort. - **[HIGH]** `settings.users.onboarding.show` reste tel quel : il est absent de la table de textes de l'issue. - **[MEDIUM]** Le guide (`docs/guide-utilisateur.md` l. 86 et 581, et les clés `docs.*`) décrit encore la barre compacte : laissé à #401, comme le prévoit le plan. - **[HIGH]** Le CHANGELOG ne parle ni de « Passer » ni de « Reprendre » : aucune interface ne passe une étape avant #398. ## Pour relire - Ordre de lecture conseillé : `OnboardingBanner.tsx`, puis `AppShell.tsx` (emplacement et clé de route), puis `OnboardingChecklist.tsx`. - Points à trancher : - la lecture de « `DashboardEmptyState` inchangé » (prop `onResume` transmise) ; - le choix de replier la liste à chaque changement de page. - Le guide utilisateur reste à réécrire dans #401. - Vérification manuelle, pour la liste QA de #401 : - le bandeau sur chaque page, mais pas sur le tableau de bord vide ; - « Voir la liste » et « Fermer la liste » ; - « Masquer » depuis Paramètres → Utilisateurs, où le bouton de réaffichage apparaît sans quitter la page ; - le message de fin et « Ouvrir le guide » ; - les annonces NVDA et Orca. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
maximus added 3 commits 2026-10-08 12:05:39 +00:00
The getting-started path now follows the user: AppShell mounts
OnboardingBanner at the top of <main>, in the page flow, and the
provider's mode keeps it off a hidden path, the dashboard of a profile
without transactions, a page that carries a step card and the first
load. The banner shows "Getting started n/N" once the Budget
entitlement is known, the next step linked with its navigation state,
"Show the list", which unfolds the detailed list in place, and "Hide";
at the end, the closing message, "Open the guide" and "Close". Each
route mounts its own banner slot, so a page always starts folded.

The dashboard's compact bar and the compact mode of
OnboardingChecklist are gone (D7). The detailed list reads each state
out to screen readers, shows a skipped step with its badge and
"Resume", and gives every link to a step the same navigation state
(stepNavState), with the guided entry for the manual category.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Settings → Users no longer reads the hidden preference at mount: it
takes hidden and show from useOnboarding(), the provider being the only
writer of the path's preferences. Hiding the path from the banner of
this very page now offers "Show the getting-started checklist" at once,
and a local flag keeps the confirmation in place of the button until
the path is hidden again.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
docs: changelog and architecture for the getting-started banner
All checks were successful
PR Check — Frontend / frontend (pull_request) Successful in 1m49s
46e714234e
CHANGELOG (EN and FR): the banner under Added; the compact bar replaced
by the banner, the states read out by the list, and the re-show button
of Settings → Users under Changed. docs/architecture.md: the lines this
change made false (component count, the onboarding/ folder, the compact
mode and the re-show sentences). The user guide is rewritten by the
last issue of the milestone.

Resolves #397

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
maximus added the
autopilot:pending-human
label 2026-10-08 12:05:42 +00:00
Author
Owner

Auto-revue /autopilot — verdict : APPROVE

Revue du diff de ce seul maillon (20 fichiers, base issue-396-onboarding-shared-state), critère par critère de #397.

Critères d'acceptation

  • OnboardingBanner, tests de rendu (OnboardingBanner.render.test.tsx, 13 tests) :
    • le compteur ;
    • le lien vers nextStep.to avec son état, guided compris pour l'étape 5 ;
    • aria-expanded selon la liste ;
    • le mode fin avec « Ouvrir le guide » (/settings/users#gettingStarted) et « Fermer » ;
    • rien en mode none, ni en mode card.
  • Où le bandeau s'affiche (AppShell.render.test.tsx). Le test fait un vrai rendu d'AppShell, et le mode y est dérivé par deriveOnboardingView avec le vrai CARD_PAGES :
    • absent sur le tableau de bord vide, quand le parcours est masqué et avant le premier chargement ;
    • présent ailleurs, y compris sur /import, /transactions et /budget, chacune étant la page de l'étape suivante.
  • Plus de barre compacte ni de mode compact :
    • la prop mode est retirée, ainsi que les 3 tests du mode compact ;
    • le test du tableau de bord rougit si la barre revient (M7) ;
    • DashboardEmptyState garde le même rendu, épinglé par un test ; seule la prop onResume y passe désormais (décision MEDIUM).
  • OnboardingChecklist :
    • les textes d'état sont annoncés (sr-only) ;
    • la ligne passée porte l'icône, le badge (aria-hidden) et « Reprendre », qui appelle resume(key) puis navigue avec l'état de focus.
  • UsersSettingsPage : hidden et show viennent du contexte. Le bouton de réaffichage apparaît sans remontage : l'état est conservé d'un rendu à l'autre, et seule la valeur de useOnboarding() change.
  • Textes FR et EN (parité des clés vérifiée), CHANGELOG FR et EN, npm run build et npx vitest run verts (1680 → 1727).

Mises en garde de l'issue et décisions de planification

  • Pile mergée en partie : CARD_PAGES n'est pas touché, donc le bandeau reste sur toute page sans carte.
  • Le fournisseur reste le seul écrivain : la page n'appelle plus getPreference ni showChecklist.
  • D1 (dans le flux, sans position fixe), D4, D7, D8 (« Masquer » le bandeau masque aussi la liste de la carte d'accueil) et D11 sont respectés.

Conventions

  • Aucune chaîne en dur : le séparateur est un point CSS, la flèche une icône.
  • Commentaires en anglais ; la règle pure stepNavState vit dans utils/.
  • Aucun secret.
  • Aucun code mort : OnboardingChecklistMode et le marqueur data-mode des tests sont retirés.
  • Les tests sont significatifs : la passe de mutations rend 8 rouges sur 8 (voir le corps de la PR).

Constats non bloquants

  1. La liste dépliée dans le bandeau répète le compteur et « Masquer » du bandeau. C'est la liste détaillée telle quelle, sans nouveau mode d'affichage ; à juger à l'œil.
  2. Le lien d'une ligne verrouillée (carte Licence) ne porte pas l'état de focus, puisque cette page n'a pas de carte d'étape. Le texte de l'issue (« les liens des lignes non faites ») peut se lire plus largement ; décision HIGH consignée.
  3. La liste se replie à chaque changement de page (key={pathname}). En contrepartie, le focus d'un lien du bandeau retombe sur la page à la navigation.
  4. Le harnais useState simulé est dupliqué entre AppShell.slot.test.tsx et UsersSettingsPage.test.tsx.
  5. Le guide (docs/guide-utilisateur.md, lignes 86 et 581, et les clés docs.*) décrit encore la barre compacte : à reprendre dans #401.

CI

check-frontend, run 501 sur 46e7142 : vert. Le journal du job montre le build (tsc puis vite build) et vitest run à 94 fichiers et 1727 tests, tous verts. check-rust ne se déclenche pas : aucun changement Rust.

## Auto-revue /autopilot — verdict : APPROVE Revue du diff de ce seul maillon (20 fichiers, base `issue-396-onboarding-shared-state`), critère par critère de #397. ### Critères d'acceptation - [x] **`OnboardingBanner`, tests de rendu** (`OnboardingBanner.render.test.tsx`, 13 tests) : - le compteur ; - le lien vers `nextStep.to` avec son état, `guided` compris pour l'étape 5 ; - `aria-expanded` selon la liste ; - le mode fin avec « Ouvrir le guide » (`/settings/users#gettingStarted`) et « Fermer » ; - rien en mode `none`, ni en mode `card`. - [x] **Où le bandeau s'affiche** (`AppShell.render.test.tsx`). Le test fait un vrai rendu d'AppShell, et le mode y est dérivé par `deriveOnboardingView` avec le vrai `CARD_PAGES` : - absent sur le tableau de bord vide, quand le parcours est masqué et avant le premier chargement ; - présent ailleurs, y compris sur /import, /transactions et /budget, chacune étant la page de l'étape suivante. - [x] **Plus de barre compacte ni de mode `compact`** : - la prop `mode` est retirée, ainsi que les 3 tests du mode compact ; - le test du tableau de bord rougit si la barre revient (M7) ; - `DashboardEmptyState` garde le même rendu, épinglé par un test ; seule la prop `onResume` y passe désormais (décision MEDIUM). - [x] **`OnboardingChecklist`** : - les textes d'état sont annoncés (`sr-only`) ; - la ligne passée porte l'icône, le badge (`aria-hidden`) et « Reprendre », qui appelle `resume(key)` puis navigue avec l'état de focus. - [x] **`UsersSettingsPage`** : `hidden` et `show` viennent du contexte. Le bouton de réaffichage apparaît sans remontage : l'état est conservé d'un rendu à l'autre, et seule la valeur de `useOnboarding()` change. - [x] Textes FR et EN (parité des clés vérifiée), CHANGELOG FR et EN, `npm run build` et `npx vitest run` verts (1680 → 1727). ### Mises en garde de l'issue et décisions de planification - **Pile mergée en partie** : `CARD_PAGES` n'est pas touché, donc le bandeau reste sur toute page sans carte. - **Le fournisseur reste le seul écrivain** : la page n'appelle plus `getPreference` ni `showChecklist`. - D1 (dans le flux, sans position fixe), D4, D7, D8 (« Masquer » le bandeau masque aussi la liste de la carte d'accueil) et D11 sont respectés. ### Conventions - Aucune chaîne en dur : le séparateur est un point CSS, la flèche une icône. - Commentaires en anglais ; la règle pure `stepNavState` vit dans `utils/`. - Aucun secret. - Aucun code mort : `OnboardingChecklistMode` et le marqueur `data-mode` des tests sont retirés. - Les tests sont significatifs : la passe de mutations rend 8 rouges sur 8 (voir le corps de la PR). ### Constats non bloquants 1. La liste dépliée dans le bandeau répète le compteur et « Masquer » du bandeau. C'est la liste détaillée telle quelle, sans nouveau mode d'affichage ; à juger à l'œil. 2. Le lien d'une ligne verrouillée (carte Licence) ne porte pas l'état de focus, puisque cette page n'a pas de carte d'étape. Le texte de l'issue (« les liens des lignes non faites ») peut se lire plus largement ; décision HIGH consignée. 3. La liste se replie à chaque changement de page (`key={pathname}`). En contrepartie, le focus d'un lien du bandeau retombe sur la page à la navigation. 4. Le harnais `useState` simulé est dupliqué entre `AppShell.slot.test.tsx` et `UsersSettingsPage.test.tsx`. 5. Le guide (`docs/guide-utilisateur.md`, lignes 86 et 581, et les clés `docs.*`) décrit encore la barre compacte : à reprendre dans #401. ### CI `check-frontend`, run 501 sur `46e7142` : vert. Le journal du job montre le build (`tsc` puis `vite build`) et `vitest run` à **94 fichiers et 1727 tests**, tous verts. `check-rust` ne se déclenche pas : aucun changement Rust.
Author
Owner

Revue adversariale — verdict : APPROVE

Revue indépendante du seul maillon #403 (base issue-396-onboarding-shared-state, tête 46e7142), après l'auto-revue du worker.

Résumé : le bandeau, son montage dans AppShell, le retrait de la barre compacte, les états annoncés de la liste et la lecture de l'état partagé par Paramètres → Utilisateurs font ce que demande #397, critère par critère. Aucun défaut bloquant ; les douze mutations du relecteur rougissent toutes.

Exécuté

  • Copie hors dépôt (clone dans /tmp, npm ci), tête 46e7142 : npm run build vert (tsc + vite) ; npx vitest run : 94 fichiers, 1727 tests verts.
  • CI : run 501 (check-frontend) vert sur 46e7142, log lu : build, 94 fichiers, 1727 tests.
  • Mutations du relecteur, choisies hors de la liste de l'auteur (RM4 est voisine de sa M6), une à la fois, restaurées par git checkout, arbre propre après chacune : 12/12 rouges.
# Mutation Tests rouges
RM1 rowState : « passée » l'emporte sur « faite » 1
RM2 Le lien d'une ligne à faire perd son état de navigation 1
RM3 stepNavState pose guided sur toutes les étapes 5
RM4 Paramètres : bouton ou confirmation décidés par restored au lieu de hidden 2
RM5 Le libellé d'une ligne passée devient un second lien 1
RM6 L'emplacement du bandeau passe après <Outlet/> 2
RM7 La ligne « en attente » n'est plus annoncée 1
RM8 La cible d'aria-controls n'existe que dépliée 1
RM9 Le bouton de la liste ne fait que déplier 1
RM10 « Fermer » (fin du parcours) replie la liste au lieu de masquer 1
RM11 La carte d'accueil garde la liste sur un parcours masqué 1
RM12 Garde checklist === null du bandeau retirée 1

Après restauration : 1727 verts, arbre propre.

Vérifié

  • Les six critères de #397 et ses deux mises en garde. CARD_PAGES est vide, donc le bandeau reste sur /import, /transactions et /budget, épinglé avec le vrai CARD_PAGES. La page Paramètres ne lit ni n'écrit plus la préférence elle-même.
  • Textes : les 15 clés de la table de l'issue ont les valeurs exactes en FR et en EN, et la parité FR/EN est complète. Les deux clés réutilisées (onboarding.checklist.hide, common.close) existent. sr-only (première utilisation dans le projet) est bien généré par Tailwind dans le CSS du build.
  • « Reprendre » : react-router 7.18.1 appelle onClick avant de naviguer (handleClick, lu dans node_modules). Entre l'écriture de resume et la relecture que lance la navigation, les deux ordres finissent sur la ligne reprise, grâce à skippedSetAt (#396).
  • Aucun profil existant ne verra surgir le message de fin : un parcours complet suppose une transaction classée à la main, et la règle D9 masque déjà ces profils à la première ouverture.
  • Tip de la pile : les textes du guide qui décrivent encore la barre compacte (docs.dashboard.steps.1, docs.settings.features.8 et .steps.7, guide-utilisateur.md l. 86) n'existent plus sur issue-401-onboarding-guide-qa (#407). Le report à #401 tient donc sur la pile complète ; seule une pile mergée jusqu'à #403 sans la suite livrerait un guide faux.
  • Fermeture : la timeline de #397 porte un pull_ref closes depuis #403, et 46e7142 contient Resolves #397. L'issue se fermera aussi par un merge en avance rapide. Les trois commits suivent le format conventionnel.

Suggestions (non bloquantes)

  1. Focus après « Masquer » et « Fermer » : le bouton disparaît avec le bandeau, et le focus retombe sur <body>. Il en va de même après un lien du bandeau vers une page sans carte (« Ouvrir le guide », ligne Budget verrouillée), puisque key={pathname} remonte le bandeau. L'ancienne barre compacte se comportait pareil, ce n'est donc pas une régression. À ajouter à la liste NVDA/Orca de #401, ou à une issue de suivi (focus sur <main> ou sur le titre de la page).
  2. Doublons dans la liste dépliée : sous le bandeau, la liste garde son propre en-tête. On voit donc deux compteurs (« Démarrage 3/6 » et « 3 / 6 ») et deux boutons « Masquer » dans la même région. C'est conforme à la lettre de l'issue (« déplie en place OnboardingChecklist »). À juger en QA, avec au besoin une prop qui retire l'en-tête de la liste dans le bandeau.
  3. Test « never reads the preference itself » (UsersSettingsPage.test.tsx) : son assertion ne peut pas rougir pour la régression réaliste, une lecture dans un useEffect, puisqu'un effet ne tourne pas quand le composant est appelé comme une fonction. Mutation faite (un useEffect qui appelle getPreference, remis dans la page) : 6/6 rouges, mais par « Invalid hook call », pas par cette assertion. La mise en garde de l'issue est donc gardée par accident. Une vérification sur la source (aucun import de userPreferenceService ni de ONBOARDING_CHECKLIST_HIDDEN_KEY dans la page), ou un nom de test qui dit ce qu'il prouve, serait plus juste.
  4. guided: true depuis la liste et « Reprendre » : pour les lignes, l'issue ne demande que « l'état de focus ». La PR étend l'entrée guidée de l'étape 5 à tout lien vers elle (décision MEDIUM consignée). C'est cohérent ; à confirmer par Max.

Points à trancher du corps de la PR

  • Lecture de « DashboardEmptyState inchangé » : d'accord. Sans onResume, une ligne passée de la carte d'accueil naviguerait sans être reprise. Ce cas deviendra possible quand #398 ajoutera « Passer ».
  • Liste repliée à chaque page : d'accord, avec la conséquence sur le focus décrite en suggestion 1.

Actions faites

  • Commentaire posté une seule fois ; #397 passe de status:review à status:approved.
  • Copie hors dépôt et fichiers temporaires /tmp/pr403-* supprimés en fin de revue ; dépôt local intact.
## Revue adversariale — verdict : APPROVE Revue indépendante du seul maillon #403 (base `issue-396-onboarding-shared-state`, tête `46e7142`), après l'auto-revue du worker. **Résumé** : le bandeau, son montage dans `AppShell`, le retrait de la barre compacte, les états annoncés de la liste et la lecture de l'état partagé par Paramètres → Utilisateurs font ce que demande #397, critère par critère. Aucun défaut bloquant ; les douze mutations du relecteur rougissent toutes. ### Exécuté - Copie hors dépôt (clone dans `/tmp`, `npm ci`), tête `46e7142` : `npm run build` vert (tsc + vite) ; `npx vitest run` : 94 fichiers, 1727 tests verts. - CI : run 501 (`check-frontend`) vert sur `46e7142`, log lu : build, 94 fichiers, 1727 tests. - Mutations du relecteur, choisies hors de la liste de l'auteur (RM4 est voisine de sa M6), une à la fois, restaurées par `git checkout`, arbre propre après chacune : **12/12 rouges**. | # | Mutation | Tests rouges | |---|---|---| | RM1 | `rowState` : « passée » l'emporte sur « faite » | 1 | | RM2 | Le lien d'une ligne à faire perd son état de navigation | 1 | | RM3 | `stepNavState` pose `guided` sur toutes les étapes | 5 | | RM4 | Paramètres : bouton ou confirmation décidés par `restored` au lieu de `hidden` | 2 | | RM5 | Le libellé d'une ligne passée devient un second lien | 1 | | RM6 | L'emplacement du bandeau passe après `<Outlet/>` | 2 | | RM7 | La ligne « en attente » n'est plus annoncée | 1 | | RM8 | La cible d'`aria-controls` n'existe que dépliée | 1 | | RM9 | Le bouton de la liste ne fait que déplier | 1 | | RM10 | « Fermer » (fin du parcours) replie la liste au lieu de masquer | 1 | | RM11 | La carte d'accueil garde la liste sur un parcours masqué | 1 | | RM12 | Garde `checklist === null` du bandeau retirée | 1 | Après restauration : 1727 verts, arbre propre. ### Vérifié - Les six critères de #397 et ses deux mises en garde. `CARD_PAGES` est vide, donc le bandeau reste sur /import, /transactions et /budget, épinglé avec le vrai `CARD_PAGES`. La page Paramètres ne lit ni n'écrit plus la préférence elle-même. - Textes : les 15 clés de la table de l'issue ont les valeurs exactes en FR et en EN, et la parité FR/EN est complète. Les deux clés réutilisées (`onboarding.checklist.hide`, `common.close`) existent. `sr-only` (première utilisation dans le projet) est bien généré par Tailwind dans le CSS du build. - « Reprendre » : react-router 7.18.1 appelle `onClick` avant de naviguer (`handleClick`, lu dans `node_modules`). Entre l'écriture de `resume` et la relecture que lance la navigation, les deux ordres finissent sur la ligne reprise, grâce à `skippedSetAt` (#396). - Aucun profil existant ne verra surgir le message de fin : un parcours complet suppose une transaction classée à la main, et la règle D9 masque déjà ces profils à la première ouverture. - Tip de la pile : les textes du guide qui décrivent encore la barre compacte (`docs.dashboard.steps.1`, `docs.settings.features.8` et `.steps.7`, `guide-utilisateur.md` l. 86) n'existent plus sur `issue-401-onboarding-guide-qa` (#407). Le report à #401 tient donc sur la pile complète ; seule une pile mergée jusqu'à #403 sans la suite livrerait un guide faux. - Fermeture : la timeline de #397 porte un `pull_ref` `closes` depuis #403, et `46e7142` contient `Resolves #397`. L'issue se fermera aussi par un merge en avance rapide. Les trois commits suivent le format conventionnel. ### Suggestions (non bloquantes) 1. **Focus après « Masquer » et « Fermer »** : le bouton disparaît avec le bandeau, et le focus retombe sur `<body>`. Il en va de même après un lien du bandeau vers une page sans carte (« Ouvrir le guide », ligne Budget verrouillée), puisque `key={pathname}` remonte le bandeau. L'ancienne barre compacte se comportait pareil, ce n'est donc pas une régression. À ajouter à la liste NVDA/Orca de #401, ou à une issue de suivi (focus sur `<main>` ou sur le titre de la page). 2. **Doublons dans la liste dépliée** : sous le bandeau, la liste garde son propre en-tête. On voit donc deux compteurs (« Démarrage 3/6 » et « 3 / 6 ») et deux boutons « Masquer » dans la même région. C'est conforme à la lettre de l'issue (« déplie en place `OnboardingChecklist` »). À juger en QA, avec au besoin une prop qui retire l'en-tête de la liste dans le bandeau. 3. **Test « never reads the preference itself »** (`UsersSettingsPage.test.tsx`) : son assertion ne peut pas rougir pour la régression réaliste, une lecture dans un `useEffect`, puisqu'un effet ne tourne pas quand le composant est appelé comme une fonction. Mutation faite (un `useEffect` qui appelle `getPreference`, remis dans la page) : 6/6 rouges, mais par « Invalid hook call », pas par cette assertion. La mise en garde de l'issue est donc gardée par accident. Une vérification sur la source (aucun import de `userPreferenceService` ni de `ONBOARDING_CHECKLIST_HIDDEN_KEY` dans la page), ou un nom de test qui dit ce qu'il prouve, serait plus juste. 4. **`guided: true` depuis la liste et « Reprendre »** : pour les lignes, l'issue ne demande que « l'état de focus ». La PR étend l'entrée guidée de l'étape 5 à tout lien vers elle (décision MEDIUM consignée). C'est cohérent ; à confirmer par Max. ### Points à trancher du corps de la PR - Lecture de « `DashboardEmptyState` inchangé » : d'accord. Sans `onResume`, une ligne passée de la carte d'accueil naviguerait sans être reprise. Ce cas deviendra possible quand #398 ajoutera « Passer ». - Liste repliée à chaque page : d'accord, avec la conséquence sur le focus décrite en suggestion 1. ### Actions faites - Commentaire posté une seule fois ; #397 passe de `status:review` à `status:approved`. - Copie hors dépôt et fichiers temporaires `/tmp/pr403-*` supprimés en fin de revue ; dépôt local intact.
maximus changed target branch from issue-396-onboarding-shared-state to main 2026-10-09 01:10:25 +00:00
maximus merged commit 46e714234e into main 2026-10-09 01:10:32 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: maximus/Simpl-Resultat#403
No description provided.