Import: parent folder, one subfolder per account, signs at the preview, quoted controls (#422) #439

Merged
maximus merged 1 commit from issue-422-import-per-account into main 2026-10-16 00:36:59 +00:00
Owner

Generated autonomously by /autopilot run of 2026-10-13 (milestone planned-2026-10-12-retours-onboarding-v0171, link 2 of 8). Stacked on #438 (issue-424-budget-row-order).

What

  • Expected structure box in the Import empty state, both variants (no folder, folder without sources): new ImportFolderStructure component, a nested <ul> (parent folder > two account folders > two CSV files under the first), lucide Folder / FileText icons with aria-hidden, and the note that files at the root of the parent folder are ignored. Rendered above the bank CSV guide link.
  • Cards, per account instead of per bank: onboarding.card.import.noFolder, noSources, sourceList rewritten with the issue's texts (FR + EN, word for word).
  • Signs at the preview: onboarding.card.import.filePreview says an expense must count among the outflows, as a negative amount, and names "Inverser les signes" before "Vérifier les doublons". WIZARD_ROWS rows now carry a labels record; the file-preview row has { button: "import.wizard.checkDuplicates", flip: "import.preview.flipSigns" }, and importCardModel passes the whole record.
  • Quotation marks around every control label in the Import card sentences (« » FR, “ ” EN), including duplicateCheck, confirm and sourceConfig (see decisions).
  • Help tips (import.help.tips[0] rewritten, new tip at index 3 on the signs), import.empty.noSources.description, docs.import.steps[1], [5], [6] and the matching lines of docs/guide-utilisateur.md. The "Formats reconnus par banque" bullet is unchanged, as the issue says.

No migration, no Rust, no network call, no Journal line.

Files

  • src/components/import/ImportFolderStructure.tsx (new), ImportFolderStructure.test.tsx (new)
  • src/components/import/ImportEmptyState.tsx, ImportEmptyState.test.tsx
  • src/utils/onboardingPath.ts, onboardingPath.test.ts, onboardingPath.locales.test.ts (new)
  • src/pages/ImportPage.render.test.tsx
  • src/i18n/locales/fr.json, en.json
  • docs/guide-utilisateur.md, docs/qa-onboarding.html (new section retours-v0171, 4 cases), docs/architecture.md
  • CHANGELOG.md, CHANGELOG.fr.md (Changed / Modifié)

Decisions

  • [MEDIUM] sourceConfig gets quotation marks around {{button}} too (only change to that sentence): the criterion covers every Import card sentence that names a control; #426 rewrites that sentence later.
  • [MEDIUM] New onboardingPath.locales.test.ts, not in the plan's file list: the shared fake t never reads the locale files, so the quoting criterion had no test. It requires each {{x}} of onboarding.card.import.* to sit between « » / “ ”, and the preview sentence to carry button and flip.
  • [HIGH] QA list: a new section "Retours de la v0.17.1" that the next links of the stack can append to; case guided-import-folder now says "la carte d'un compte".
  • [HIGH] architecture.md: import components 15 → 16 (counted), empty-state table row names the box.

Acceptance criteria

  • Without a folder, and in a folder without sub-folder, the Import page shows the "Structure attendue" box (ImportEmptyState.test.tsx, both variants; ImportPage.render.test.tsx, both page states).
  • grep -n -P 'sous-dossier par banque|banque/source|sub-?folder per bank(?! account)|bank/source' src/i18n/locales/*.json docs/guide-utilisateur.md returns nothing (rc=1).
  • The preview card names « Inverser les signes » and « Vérifier les doublons », between quotation marks.
  • Every Import card sentence that names a control quotes it, FR and EN (onboardingPath.locales.test.ts, 9 interpolations per language).
  • npm run build and npm test green: 122 files, 2085 tests.

Proofs (mutations, one at a time, restored from the index)

# Mutation Result
M1 flip removed from the file-preview row red: onboardingPath.test.ts (7.), ImportPage.render.test.tsx (file-preview)
M2 importCardModel passes only { button } red: same two tests
M3 <ImportFolderStructure /> removed red: both #422 cases of ImportEmptyState.test.tsx, page render tests
M4 box kept only in the no-folder variant red: "folder without sources" case
M5 file list no longer a nested <ul> (div) red: ImportFolderStructure.test.tsx nesting test
M6 aria-hidden dropped on the file icons survives, equivalent: lucide-react renders aria-hidden="true" by default (checked by rendering <FileText/> alone); the explicit attribute stays
M7 rootNote not rendered red: structure and empty state tests
M8 FR filePreview: {{flip}} unquoted red: onboardingPath.locales.test.ts (fr)
M9 EN noSources: {{folder}} unquoted red: onboardingPath.locales.test.ts (en)
M10 EN filePreview without the flip red: both locale tests (en)

For the reviewer

  • The box sits inside EmptyState's children container (text-left max-w-xl), above the guide link; worth a look in both themes (bg-[var(--muted)]).
  • noSources quotes the folder name too (it is a value, not a label), as the issue's text does.

🤖 Generated with Claude Code

Generated autonomously by /autopilot run of 2026-10-13 (milestone `planned-2026-10-12-retours-onboarding-v0171`, link 2 of 8). Stacked on #438 (`issue-424-budget-row-order`). ## What - **Expected structure box** in the Import empty state, both variants (no folder, folder without sources): new `ImportFolderStructure` component, a nested `<ul>` (parent folder > two account folders > two CSV files under the first), lucide `Folder` / `FileText` icons with `aria-hidden`, and the note that files at the root of the parent folder are ignored. Rendered above the bank CSV guide link. - **Cards, per account instead of per bank**: `onboarding.card.import.noFolder`, `noSources`, `sourceList` rewritten with the issue's texts (FR + EN, word for word). - **Signs at the preview**: `onboarding.card.import.filePreview` says an expense must count among the outflows, as a negative amount, and names "Inverser les signes" before "Vérifier les doublons". `WIZARD_ROWS` rows now carry a `labels` record; the `file-preview` row has `{ button: "import.wizard.checkDuplicates", flip: "import.preview.flipSigns" }`, and `importCardModel` passes the whole record. - **Quotation marks** around every control label in the Import card sentences (« » FR, “ ” EN), including `duplicateCheck`, `confirm` and `sourceConfig` (see decisions). - Help tips (`import.help.tips[0]` rewritten, new tip at index 3 on the signs), `import.empty.noSources.description`, `docs.import.steps[1]`, `[5]`, `[6]` and the matching lines of `docs/guide-utilisateur.md`. The "Formats reconnus par banque" bullet is unchanged, as the issue says. No migration, no Rust, no network call, no Journal line. ## Files - `src/components/import/ImportFolderStructure.tsx` (new), `ImportFolderStructure.test.tsx` (new) - `src/components/import/ImportEmptyState.tsx`, `ImportEmptyState.test.tsx` - `src/utils/onboardingPath.ts`, `onboardingPath.test.ts`, `onboardingPath.locales.test.ts` (new) - `src/pages/ImportPage.render.test.tsx` - `src/i18n/locales/fr.json`, `en.json` - `docs/guide-utilisateur.md`, `docs/qa-onboarding.html` (new section `retours-v0171`, 4 cases), `docs/architecture.md` - `CHANGELOG.md`, `CHANGELOG.fr.md` (Changed / Modifié) ## Decisions - [MEDIUM] `sourceConfig` gets quotation marks around `{{button}}` too (only change to that sentence): the criterion covers every Import card sentence that names a control; #426 rewrites that sentence later. - [MEDIUM] New `onboardingPath.locales.test.ts`, not in the plan's file list: the shared fake `t` never reads the locale files, so the quoting criterion had no test. It requires each `{{x}}` of `onboarding.card.import.*` to sit between « » / “ ”, and the preview sentence to carry `button` and `flip`. - [HIGH] QA list: a new section "Retours de la v0.17.1" that the next links of the stack can append to; case `guided-import-folder` now says "la carte d'un compte". - [HIGH] architecture.md: import components 15 → 16 (counted), empty-state table row names the box. ## Acceptance criteria - [x] Without a folder, and in a folder without sub-folder, the Import page shows the "Structure attendue" box (`ImportEmptyState.test.tsx`, both variants; `ImportPage.render.test.tsx`, both page states). - [x] `grep -n -P 'sous-dossier par banque|banque/source|sub-?folder per bank(?! account)|bank/source' src/i18n/locales/*.json docs/guide-utilisateur.md` returns nothing (rc=1). - [x] The preview card names « Inverser les signes » and « Vérifier les doublons », between quotation marks. - [x] Every Import card sentence that names a control quotes it, FR and EN (`onboardingPath.locales.test.ts`, 9 interpolations per language). - [x] `npm run build` and `npm test` green: 122 files, 2085 tests. ## Proofs (mutations, one at a time, restored from the index) | # | Mutation | Result | |---|---|---| | M1 | `flip` removed from the `file-preview` row | red: `onboardingPath.test.ts` (7.), `ImportPage.render.test.tsx` (file-preview) | | M2 | `importCardModel` passes only `{ button }` | red: same two tests | | M3 | `<ImportFolderStructure />` removed | red: both `#422` cases of `ImportEmptyState.test.tsx`, page render tests | | M4 | box kept only in the no-folder variant | red: "folder without sources" case | | M5 | file list no longer a nested `<ul>` (`div`) | red: `ImportFolderStructure.test.tsx` nesting test | | M6 | `aria-hidden` dropped on the file icons | **survives, equivalent**: lucide-react renders `aria-hidden="true"` by default (checked by rendering `<FileText/>` alone); the explicit attribute stays | | M7 | `rootNote` not rendered | red: structure and empty state tests | | M8 | FR `filePreview`: `{{flip}}` unquoted | red: `onboardingPath.locales.test.ts` (fr) | | M9 | EN `noSources`: `{{folder}}` unquoted | red: `onboardingPath.locales.test.ts` (en) | | M10 | EN `filePreview` without the flip | red: both locale tests (en) | ## For the reviewer - The box sits inside `EmptyState`'s children container (`text-left max-w-xl`), above the guide link; worth a look in both themes (`bg-[var(--muted)]`). - `noSources` quotes the folder name too (it is a value, not a label), as the issue's text does. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
maximus added 1 commit 2026-10-14 00:54:10 +00:00
feat(import): show the expected folder structure, one subfolder per account, signs at the preview
All checks were successful
PR Check — Frontend / frontend (pull_request) Successful in 1m57s
af884ca13b
The Import empty state now shows an "Expected structure" box in both of its
variants (no folder, folder without sources): the parent folder to choose,
one subfolder per account, and files at its root ignored, as
scan_import_folder reads them. New ImportFolderStructure component, a nested
list with decorative icons.

The getting-started cards ask for the parent folder and one subfolder per
account instead of one per bank. The preview card says that an expense must
count among the outflows and names "Flip the signs" next to "Check
duplicates": WIZARD_ROWS rows now carry every label their sentence names.
Every Import card sentence quotes the controls it names, in French and in
English, pinned by a test that reads the locale files.

Help tips, docs.import.steps, the user guide, the QA list and architecture.md
follow; CHANGELOG entry under Changed.

Resolves #422

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

Self-review (autopilot worker, not independent) — verdict: APPROVE

Generated autonomously by /autopilot run of 2026-10-13. Diff read against issue-424-budget-row-order (15 files, all expected by the plan, plus one new locale test).

Acceptance criteria

  • Structure box in both empty-state variants: rendered through the shared details fragment, pinned by ImportEmptyState.test.tsx (both variants, box above the guide link) and ImportPage.render.test.tsx (no folder, folder without sources). OK.
  • Per-bank wording: the targeted grep -P of the issue returns nothing. A wider scan of "par banque / per bank / votre banque" only finds texts that are right (formats per bank, CSV export guide, EN "per bank account"). OK.
  • Preview card: labels = { button: checkDuplicates, flip: flipSigns }, both quoted in FR and EN. OK.
  • Quoting: the 9 interpolations of the Import cards are between « » / “ ” in both files, enforced by onboardingPath.locales.test.ts. OK.
  • Build and tests: 122 files, 2085 tests green; worktree clean.

Regressions: WIZARD_ROWS changed shape (single button → labels record); the only reader is importCardModel, and the other three rows keep exactly { button } (test 7 and its "...but only at rest" sibling unchanged and green).

i18n: every visible text goes through t, including the sample file names (FR releve-…, EN statement-…). No hard-coded UI string.

Security / privacy: static texts only; no Journal line, no path, no network, no migration, no Rust. The sample file names are fictitious.

Docs: guide lines found by their text (steps 2, 6, 7), docs.import.steps[1], [5], [6] in sync with it; architecture.md count and empty-state row; QA list has 4 new cases; CHANGELOG under Changed / Modifié in both files.

Known limit: mutation M6 (dropping aria-hidden on the icons) is an equivalent mutant, since lucide-react sets it by default. The explicit attribute stays.

Nothing to fix.

## Self-review (autopilot worker, not independent) — verdict: APPROVE Generated autonomously by /autopilot run of 2026-10-13. Diff read against `issue-424-budget-row-order` (15 files, all expected by the plan, plus one new locale test). **Acceptance criteria** - Structure box in both empty-state variants: rendered through the shared `details` fragment, pinned by `ImportEmptyState.test.tsx` (both variants, box above the guide link) and `ImportPage.render.test.tsx` (no folder, folder without sources). OK. - Per-bank wording: the targeted `grep -P` of the issue returns nothing. A wider scan of "par banque / per bank / votre banque" only finds texts that are right (formats per bank, CSV export guide, EN "per bank account"). OK. - Preview card: `labels = { button: checkDuplicates, flip: flipSigns }`, both quoted in FR and EN. OK. - Quoting: the 9 interpolations of the Import cards are between « » / “ ” in both files, enforced by `onboardingPath.locales.test.ts`. OK. - Build and tests: 122 files, 2085 tests green; worktree clean. **Regressions**: `WIZARD_ROWS` changed shape (single `button` → `labels` record); the only reader is `importCardModel`, and the other three rows keep exactly `{ button }` (test 7 and its "...but only at rest" sibling unchanged and green). **i18n**: every visible text goes through `t`, including the sample file names (FR `releve-…`, EN `statement-…`). No hard-coded UI string. **Security / privacy**: static texts only; no Journal line, no path, no network, no migration, no Rust. The sample file names are fictitious. **Docs**: guide lines found by their text (steps 2, 6, 7), `docs.import.steps[1]`, `[5]`, `[6]` in sync with it; architecture.md count and empty-state row; QA list has 4 new cases; CHANGELOG under Changed / Modifié in both files. **Known limit**: mutation M6 (dropping `aria-hidden` on the icons) is an equivalent mutant, since lucide-react sets it by default. The explicit attribute stays. Nothing to fix.
Author
Owner

CI lue par l'orchestrateur sur la tête af884ca : PR Check — Frontend run 544 vert (build OK, vitest 122 fichiers / 2085 tests, comptes égaux à ceux du worker). Pas de job Rust attendu.

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

CI lue par l'orchestrateur sur la tête `af884ca` : **PR Check — Frontend** run 544 vert (build OK, vitest 122 fichiers / 2085 tests, comptes égaux à ceux du worker). Pas de job Rust attendu. Generated autonomously by /autopilot run of 2026-10-13
Author
Owner

/pr-review — APPROVE

Résumé : la PR fait ce que #422 demande. Encadré « Structure attendue » dans les deux variantes de l'état vide, cartes « par compte », phrase de l'aperçu avec « Inverser les signes » puis « Vérifier les doublons », guillemets autour de chaque contrôle en FR et en EN. Tous les textes sont repris mot pour mot du corps de l'issue. Aucun bloquant.

Vérifications rejouées (tête af884ca, worktree détaché + npm ci propre)

  • npm run build vert ; vitest run : 122 fichiers, 2085 tests verts.
  • Critère grep de l'issue : rc=1 (rien). Seules restent « Formats reconnus par banque » et « one subfolder per bank account », justes et conservées comme l'issue le demande.
  • onFlipSigns est toujours passé par ImportPage (ImportPage.tsx:220) : la carte ne cite jamais un bouton absent de l'aperçu.
  • CI de la tête : success (statut agrégé).
  • Timeline de #422 : pull_ref de #439 en ref_action: none. La fermeture passe par Resolves #422 dans le message du commit, ce que lit un merge ff.

Mutations du relecteur (une à la fois, arbre restauré et vérifié propre après chacune)

# Mutation Résultat
R1 FR confirm : {{button}} sans « » rouge : 1 (onboardingPath.locales.test.ts, fr)
R2 encadré placé sous le lien du guide rouge : 2 (les deux variantes de ImportEmptyState.test.tsx)
R3 titre de l'encadré codé en dur (« Structure attendue ») rouge : 6 (structure, état vide, rendu de page)
R5 EN filePreview : “ ” remplacés par des guillemets droits \" rouge : 1 (locales, en)
R6 flip pointé sur import.preview.flipSignsHint rouge : 2 (onboardingPath.test.ts 7., ImportPage.render.test.tsx file-preview)
R4 import.empty.structure.account2 retiré de en.json survit, suite complète verte (2085)

Suggestions (non bloquantes)

  1. R4 : aucune garde de parité FR/EN sur les nouvelles clés. Une clé import.empty.structure.* manquante en anglais afficherait la clé brute (fallbackLng: "en"), et aucun test ne le verrait. Le trou existe dans tout le dépôt, pas seulement ici. Pour cette PR, un test de quelques lignes dans onboardingPath.locales.test.ts (mêmes clés structure dans les deux fichiers, valeurs non vides) suffirait. Sinon, une issue de suivi pour une garde de parité globale.
  2. Corps de la PR sans mot-clé de fermeture. Seul le commit porte Resolves #422. Ça suffit pour le merge ff de la pile, mais un merge par commit de merge s'appuierait sur le même commit. Ajouter Resolves #422 au corps, pour que la timeline soit cohérente avec les autres maillons.
  3. Remarque de texte, rien à changer ici (texte validé par Max) : la carte dit « « Créer un dossier d'exemple » dépose deux comptes fictifs », alors que les dossiers créés s'appellent « Exemple Desjardins » et « Exemple Tangerine », des noms de banque. Le lecteur peut se demander si un dossier correspond à une banque ou à un compte, ce que l'encadré vient justement de trancher. Nommer les exemples d'après un compte (« Exemple carte Desjardins »…) pourrait aller dans #426 ou dans un suivi.

Actions faites

  • Worktree de revue supprimé (git worktree remove + prune), dépôt principal propre.
  • Label de #422 : status:review → status:approved.
## /pr-review — APPROVE **Résumé** : la PR fait ce que #422 demande. Encadré « Structure attendue » dans les deux variantes de l'état vide, cartes « par compte », phrase de l'aperçu avec « Inverser les signes » puis « Vérifier les doublons », guillemets autour de chaque contrôle en FR et en EN. Tous les textes sont repris mot pour mot du corps de l'issue. Aucun bloquant. ### Vérifications rejouées (tête `af884ca`, worktree détaché + `npm ci` propre) - `npm run build` vert ; `vitest run` : **122 fichiers, 2085 tests verts**. - Critère grep de l'issue : rc=1 (rien). Seules restent « Formats reconnus par banque » et « one subfolder per bank account », justes et conservées comme l'issue le demande. - `onFlipSigns` est toujours passé par `ImportPage` (`ImportPage.tsx:220`) : la carte ne cite jamais un bouton absent de l'aperçu. - CI de la tête : `success` (statut agrégé). - Timeline de #422 : `pull_ref` de #439 en `ref_action: none`. La fermeture passe par `Resolves #422` dans le message du commit, ce que lit un merge ff. ### Mutations du relecteur (une à la fois, arbre restauré et vérifié propre après chacune) | # | Mutation | Résultat | |---|---|---| | R1 | FR `confirm` : `{{button}}` sans « » | rouge : 1 (`onboardingPath.locales.test.ts`, fr) | | R2 | encadré placé **sous** le lien du guide | rouge : 2 (les deux variantes de `ImportEmptyState.test.tsx`) | | R3 | titre de l'encadré codé en dur (« Structure attendue ») | rouge : 6 (structure, état vide, rendu de page) | | R5 | EN `filePreview` : “ ” remplacés par des guillemets droits `\"` | rouge : 1 (locales, en) | | R6 | `flip` pointé sur `import.preview.flipSignsHint` | rouge : 2 (`onboardingPath.test.ts` 7., `ImportPage.render.test.tsx` file-preview) | | R4 | `import.empty.structure.account2` retiré de `en.json` | **survit, suite complète verte (2085)** | ### Suggestions (non bloquantes) 1. **R4 : aucune garde de parité FR/EN sur les nouvelles clés.** Une clé `import.empty.structure.*` manquante en anglais afficherait la clé brute (`fallbackLng: "en"`), et aucun test ne le verrait. Le trou existe dans tout le dépôt, pas seulement ici. Pour cette PR, un test de quelques lignes dans `onboardingPath.locales.test.ts` (mêmes clés `structure` dans les deux fichiers, valeurs non vides) suffirait. Sinon, une issue de suivi pour une garde de parité globale. 2. **Corps de la PR sans mot-clé de fermeture.** Seul le commit porte `Resolves #422`. Ça suffit pour le merge ff de la pile, mais un merge par commit de merge s'appuierait sur le même commit. Ajouter `Resolves #422` au corps, pour que la timeline soit cohérente avec les autres maillons. 3. Remarque de texte, rien à changer ici (texte validé par Max) : la carte dit « « Créer un dossier d'exemple » dépose deux comptes fictifs », alors que les dossiers créés s'appellent « Exemple Desjardins » et « Exemple Tangerine », des noms de banque. Le lecteur peut se demander si un dossier correspond à une banque ou à un compte, ce que l'encadré vient justement de trancher. Nommer les exemples d'après un compte (« Exemple carte Desjardins »…) pourrait aller dans #426 ou dans un suivi. ### Actions faites - Worktree de revue supprimé (`git worktree remove` + `prune`), dépôt principal propre. - Label de #422 : `status:review` → `status:approved`.
maximus changed target branch from issue-424-budget-row-order to main 2026-10-16 00:36:50 +00:00
maximus merged commit af884ca13b into main 2026-10-16 00:36:59 +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#439
No description provided.