Import : avertir des fichiers CSV posés directement dans le dossier d'import (ignorés aujourd'hui sans message) #427

Closed
opened 2026-10-12 21:07:35 +00:00 by maximus · 1 comment
Owner

Un fichier posé directement dans le dossier d'import est ignoré sans laisser de trace : l'utilisateur qui y dépose ses relevés voit « Aucune source trouvée » sans savoir pourquoi. Le scan rend désormais ces fichiers, et la page Import les nomme dans un avertissement. Constat de l'analyse de #422, ouvert sur décision de Max.

Planifié le 2026-10-12 (/plan-run, jalon planned-2026-10-12-retours-onboarding-v0171), après /analyze et une revue /review-spec (sécurité, architecture, technique) dont les constats sont intégrés ci-dessous. Corps autosuffisant : la spec (spec-plan-retours-onboarding-v0171.md) est locale et ignorée par git. L'ancien corps est archivé en commentaire.

Préalable : ce jalon s'exécute après le merge de planned-2026-10-10-fiabilite-tests (#408, #409, #395, #389). Les numéros de ligne cités sont lus sur main à 8b5b3b1 et ont pu bouger : se fier aux noms de fonctions, aux clés et aux textes cités.

Contexte

  • scan_import_folder (src-tauri/src/commands/fs_commands.rs:26) rend Result<Vec<ScannedSource>, String> et saute toute entrée de la racine qui n'est pas un dossier (:40-42) ; ScannedSource (:18-23) n'a pas de champ pour ces fichiers. Dans chaque sous-dossier, seuls les .csv et .txt posés directement sont lus.
  • L'appelant TS est unique : src/hooks/useImportWizard.ts:381 (invoke<ScannedSource[]>), suivi de dispatch({ type: "SET_SCANNED_SOURCES", payload: sources }) (:384), épinglé par useImportWizard.mount.test.ts:93.
  • Le RESET du reducer garde le scan du dossier (:303-317, #384 : la lecture du montage ne se rejoue pas).
  • #422 ajoute l'explication de la structure attendue (dossier parent, un sous-dossier par compte, fichiers à la racine ignorés) ; cette issue ajoute l'avertissement à l'écran.

Travail à faire

  • Rust : scan_import_folder rend ImportFolderScan { sources: Vec<ScannedSource>, root_files: Vec<String> } (Serialize, noms de champs en snake_case comme ScannedSource). root_files : noms des fichiers non cachés posés directement à la racine dont l'extension est csv ou txt, avec la même règle de casse que les sous-dossiers, triés par nom. Mettre à jour le commentaire de la commande (règle du dépôt).
  • TS : type ImportFolderScan dans src/shared/types/index.ts. L'action SET_SCANNED_SOURCES reçoit l'ImportFolderScan entier et pose scannedSources et rootFiles ensemble ; RESET garde rootFiles comme il garde scannedSources ; un nouveau scan (changement de dossier, « Actualiser ») remplace les deux. useImportWizard expose rootFiles.
  • Fonction pure src/utils/rootFiles.ts : formatRootFiles(names, max = 5) rend { shown, more }.
  • Composant src/components/import/RootFilesWarning.tsx : icône AlertTriangle (aria-hidden) et le message ; pas de région live (affiché au chargement). Rendu par ImportPage quand rootFiles.length > 0 : au-dessus de la liste des sources s'il y en a, sous l'état vide « aucune source » sinon ; jamais sans dossier choisi. {{folder}} = folderNameFromPath(importFolder) (ImportEmptyState.tsx:31), comme dans la carte « aucune source ».
  • Avertissement à l'écran seulement : aucune ligne de Journal ne contient les noms des fichiers ni le chemin du dossier.
  • docs/architecture.md, ligne de scan_import_folder dans la liste des commandes (« Scan récursif de dossier pour fichiers CSV/TXT ») : « Liste les sous-dossiers directs du dossier d'import (une source chacun) avec leurs fichiers CSV/TXT directs, et les CSV/TXT posés à la racine, ignorés (root_files) ».

Textes (FR | EN)

{{names}} = les noms de shown joints par « , », suivis d'un espace et de import.rootFiles.more quand more > 0.

Clé FR EN
import.rootFiles.message_one {{count}} fichier est posé directement dans « {{folder}} » et est ignoré : {{names}}. Déplacez-le dans un sous-dossier par compte. {{count}} file sits directly in “{{folder}}” and is ignored: {{names}}. Move it into a subfolder per account.
import.rootFiles.message_other {{count}} fichiers sont posés directement dans « {{folder}} » et sont ignorés : {{names}}. Déplacez-les dans un sous-dossier par compte. {{count}} files sit directly in “{{folder}}” and are ignored: {{names}}. Move them into a subfolder per account.
import.rootFiles.more_one et {{count}} autre and {{count}} more
import.rootFiles.more_other et {{count}} autres and {{count}} more
docs.import.tips (nouvelle, en dernier) Si des fichiers CSV sont posés directement dans le dossier d'import, un avertissement les nomme : déplacez-les dans un sous-dossier par compte If CSV files sit directly in the import folder, a warning names them: move them into a subfolder per account

Guide Markdown (section Import, astuces) : la même astuce.

Fichiers concernés

  • src-tauri/src/commands/fs_commands.rs (commande et tests)
  • src/shared/types/index.ts
  • src/hooks/useImportWizard.ts, src/hooks/useImportWizard.test.ts, src/hooks/useImportWizard.mount.test.ts
  • src/utils/rootFiles.ts et son test (nouveaux)
  • src/components/import/RootFilesWarning.tsx et son test (nouveaux)
  • src/pages/ImportPage.tsx, src/pages/ImportPage.render.test.tsx
  • src/i18n/locales/fr.json, src/i18n/locales/en.json
  • docs/architecture.md, docs/guide-utilisateur.md, docs/qa-onboarding.html
  • CHANGELOG.md, CHANGELOG.fr.md

Depends on

#422 réécrit ImportEmptyState (encadré de structure) et les textes « par compte » que cet avertissement reprend.

Tests

  • Rust : fichiers de la racine listés et triés ; fichiers des sous-dossiers absents de root_files ; autres extensions et fichiers cachés ignorés ; racine sans fichier → [].
  • Reducer (useImportWizard.test.ts) : SET_SCANNED_SOURCES pose sources et rootFiles ; RESET garde les deux.
  • formatRootFiles : 0, 1, 5 et 7 noms (more = 2).
  • useImportWizard.mount.test.ts : le faux invoke rend la nouvelle forme, rootFiles est exposé.
  • RootFilesWarning : message au singulier et au pluriel, cinq noms puis « et 2 autres ».
  • ImportPage.render.test.tsx : l'avertissement au-dessus des sources, sous l'état vide « aucune source », absent sans dossier.

Critères d'acceptation

  • Un dossier d'import qui contient a.csv à la racine et un sous-dossier visa/ affiche la source visa et l'avertissement qui nomme a.csv.
  • Un dossier sans sous-dossier mais avec trois CSV à la racine affiche l'état « aucune source » et l'avertissement nommant les trois fichiers.
  • Sans fichier à la racine, aucun avertissement ; « Actualiser » relit et met l'avertissement à jour ; « Annuler » ou « Terminé » dans l'assistant le laisse en place.
  • Aucune ligne de Journal ne contient un nom de fichier ni le chemin du dossier.
  • cargo test, npm run build et npm test verts.

Review caveats

  • 🔴 Architecture, résolu : remettre rootFiles à [] par RESET aurait fait disparaître l'avertissement après « Annuler » ou « Terminé » alors que la liste des sources reste ; une seule action pose les deux et RESET garde les deux.
  • 🟡 Sécurité, résolu : noms de relevés (souvent porteurs d'un numéro de compte) et chemin (nom d'utilisateur) restent hors du Journal, qui part avec un rapport de feedback ; {{folder}} est le nom du dossier, pas le chemin (CWE-532).

Décisions prises en planification

  • Scan enrichi { sources, root_files } en un seul appel ; avertissement sur la page Import avec jusqu'à 5 noms puis « et N autres » (Max, 2026-10-12).

Conventions

  • Toute chaîne en FR et en EN (src/i18n/locales/fr.json, en.json) ; les textes de ce corps s'écrivent mot pour mot (FR validé par Max le 2026-10-12, EN rédigé sur le même contenu).
  • Phrase de carte : un contrôle cité passe par StepCardText.labels (src/utils/onboardingPath.ts), traduit par StepCard ; jamais recopié dans la phrase. Guillemets « » en FR et “ ” en EN autour de chaque libellé de contrôle interpolé.
  • CHANGELOG : une entrée sous ## [Unreleased] dans CHANGELOG.md et CHANGELOG.fr.md.
  • Tests de rendu : renderToStaticMarkup (pas de jsdom) et le faux t partagé livré par #389.
  • Journal : aucune ligne avec un chemin absolu, un nom de relevé ni un nom de dossier d'import (règle de useImportWizard.ts:423-425 : le Journal part avec un rapport de feedback).
  • Aucune migration ; npm run build (il vérifie aussi les types des fichiers de test) et npm test verts ; cargo test vert quand du Rust change.
  • Guide Markdown (docs/guide-utilisateur.md) : repérer une ligne par son texte cité, pas par son numéro.

CHANGELOG

Added / Ajouté : FR « Import : un avertissement nomme les fichiers CSV posés directement dans le dossier d'import, qui sont ignorés. » EN "Import: a warning names the CSV files placed directly in the import folder, which are ignored."

Spec source

spec-plan-retours-onboarding-v0171.md (local, ignoré par git) ; décisions dans spec-decisions-retours-onboarding-v0171.md.

Un fichier posé directement dans le dossier d'import est ignoré sans laisser de trace : l'utilisateur qui y dépose ses relevés voit « Aucune source trouvée » sans savoir pourquoi. Le scan rend désormais ces fichiers, et la page Import les nomme dans un avertissement. Constat de l'analyse de #422, ouvert sur décision de Max. > Planifié le 2026-10-12 (`/plan-run`, jalon `planned-2026-10-12-retours-onboarding-v0171`), après `/analyze` et une revue `/review-spec` (sécurité, architecture, technique) dont les constats sont intégrés ci-dessous. Corps autosuffisant : la spec (`spec-plan-retours-onboarding-v0171.md`) est locale et ignorée par git. L'ancien corps est archivé en commentaire. > > **Préalable :** ce jalon s'exécute après le merge de `planned-2026-10-10-fiabilite-tests` (#408, #409, #395, #389). Les numéros de ligne cités sont lus sur `main` à `8b5b3b1` et ont pu bouger : se fier aux noms de fonctions, aux clés et aux textes cités. ## Contexte - `scan_import_folder` (`src-tauri/src/commands/fs_commands.rs:26`) rend `Result<Vec<ScannedSource>, String>` et saute toute entrée de la racine qui n'est pas un dossier (`:40-42`) ; `ScannedSource` (`:18-23`) n'a pas de champ pour ces fichiers. Dans chaque sous-dossier, seuls les `.csv` et `.txt` posés directement sont lus. - L'appelant TS est unique : `src/hooks/useImportWizard.ts:381` (`invoke<ScannedSource[]>`), suivi de `dispatch({ type: "SET_SCANNED_SOURCES", payload: sources })` (`:384`), épinglé par `useImportWizard.mount.test.ts:93`. - Le `RESET` du reducer garde le scan du dossier (`:303-317`, #384 : la lecture du montage ne se rejoue pas). - #422 ajoute l'explication de la structure attendue (dossier parent, un sous-dossier par compte, fichiers à la racine ignorés) ; cette issue ajoute l'avertissement à l'écran. ## Travail à faire - [ ] Rust : `scan_import_folder` rend `ImportFolderScan { sources: Vec<ScannedSource>, root_files: Vec<String> }` (`Serialize`, noms de champs en snake_case comme `ScannedSource`). `root_files` : noms des fichiers non cachés posés directement à la racine dont l'extension est `csv` ou `txt`, avec la même règle de casse que les sous-dossiers, triés par nom. Mettre à jour le commentaire de la commande (règle du dépôt). - [ ] TS : type `ImportFolderScan` dans `src/shared/types/index.ts`. L'action `SET_SCANNED_SOURCES` reçoit l'`ImportFolderScan` entier et pose `scannedSources` et `rootFiles` ensemble ; `RESET` garde `rootFiles` comme il garde `scannedSources` ; un nouveau scan (changement de dossier, « Actualiser ») remplace les deux. `useImportWizard` expose `rootFiles`. - [ ] Fonction pure `src/utils/rootFiles.ts` : `formatRootFiles(names, max = 5)` rend `{ shown, more }`. - [ ] Composant `src/components/import/RootFilesWarning.tsx` : icône `AlertTriangle` (`aria-hidden`) et le message ; pas de région live (affiché au chargement). Rendu par `ImportPage` quand `rootFiles.length > 0` : au-dessus de la liste des sources s'il y en a, sous l'état vide « aucune source » sinon ; jamais sans dossier choisi. `{{folder}}` = `folderNameFromPath(importFolder)` (`ImportEmptyState.tsx:31`), comme dans la carte « aucune source ». - [ ] Avertissement à l'écran seulement : aucune ligne de Journal ne contient les noms des fichiers ni le chemin du dossier. - [ ] `docs/architecture.md`, ligne de `scan_import_folder` dans la liste des commandes (« Scan récursif de dossier pour fichiers CSV/TXT ») : « Liste les sous-dossiers directs du dossier d'import (une source chacun) avec leurs fichiers CSV/TXT directs, et les CSV/TXT posés à la racine, ignorés (`root_files`) ». ## Textes (FR | EN) `{{names}}` = les noms de `shown` joints par « , », suivis d'un espace et de `import.rootFiles.more` quand `more > 0`. | Clé | FR | EN | |---|---|---| | `import.rootFiles.message_one` | {{count}} fichier est posé directement dans « {{folder}} » et est ignoré : {{names}}. Déplacez-le dans un sous-dossier par compte. | {{count}} file sits directly in “{{folder}}” and is ignored: {{names}}. Move it into a subfolder per account. | | `import.rootFiles.message_other` | {{count}} fichiers sont posés directement dans « {{folder}} » et sont ignorés : {{names}}. Déplacez-les dans un sous-dossier par compte. | {{count}} files sit directly in “{{folder}}” and are ignored: {{names}}. Move them into a subfolder per account. | | `import.rootFiles.more_one` | et {{count}} autre | and {{count}} more | | `import.rootFiles.more_other` | et {{count}} autres | and {{count}} more | | `docs.import.tips` (nouvelle, en dernier) | Si des fichiers CSV sont posés directement dans le dossier d'import, un avertissement les nomme : déplacez-les dans un sous-dossier par compte | If CSV files sit directly in the import folder, a warning names them: move them into a subfolder per account | **Guide Markdown** (section Import, astuces) : la même astuce. ## Fichiers concernés - `src-tauri/src/commands/fs_commands.rs` (commande et tests) - `src/shared/types/index.ts` - `src/hooks/useImportWizard.ts`, `src/hooks/useImportWizard.test.ts`, `src/hooks/useImportWizard.mount.test.ts` - `src/utils/rootFiles.ts` et son test (nouveaux) - `src/components/import/RootFilesWarning.tsx` et son test (nouveaux) - `src/pages/ImportPage.tsx`, `src/pages/ImportPage.render.test.tsx` - `src/i18n/locales/fr.json`, `src/i18n/locales/en.json` - `docs/architecture.md`, `docs/guide-utilisateur.md`, `docs/qa-onboarding.html` - `CHANGELOG.md`, `CHANGELOG.fr.md` ## Depends on - #422 > #422 réécrit `ImportEmptyState` (encadré de structure) et les textes « par compte » que cet avertissement reprend. ## Tests - Rust : fichiers de la racine listés et triés ; fichiers des sous-dossiers absents de `root_files` ; autres extensions et fichiers cachés ignorés ; racine sans fichier → `[]`. - Reducer (`useImportWizard.test.ts`) : `SET_SCANNED_SOURCES` pose sources et `rootFiles` ; `RESET` garde les deux. - `formatRootFiles` : 0, 1, 5 et 7 noms (`more` = 2). - `useImportWizard.mount.test.ts` : le faux `invoke` rend la nouvelle forme, `rootFiles` est exposé. - `RootFilesWarning` : message au singulier et au pluriel, cinq noms puis « et 2 autres ». - `ImportPage.render.test.tsx` : l'avertissement au-dessus des sources, sous l'état vide « aucune source », absent sans dossier. ## Critères d'acceptation - [ ] Un dossier d'import qui contient `a.csv` à la racine et un sous-dossier `visa/` affiche la source `visa` et l'avertissement qui nomme `a.csv`. - [ ] Un dossier sans sous-dossier mais avec trois CSV à la racine affiche l'état « aucune source » et l'avertissement nommant les trois fichiers. - [ ] Sans fichier à la racine, aucun avertissement ; « Actualiser » relit et met l'avertissement à jour ; « Annuler » ou « Terminé » dans l'assistant le laisse en place. - [ ] Aucune ligne de Journal ne contient un nom de fichier ni le chemin du dossier. - [ ] `cargo test`, `npm run build` et `npm test` verts. ## Review caveats - 🔴 Architecture, résolu : remettre `rootFiles` à `[]` par `RESET` aurait fait disparaître l'avertissement après « Annuler » ou « Terminé » alors que la liste des sources reste ; une seule action pose les deux et `RESET` garde les deux. - 🟡 Sécurité, résolu : noms de relevés (souvent porteurs d'un numéro de compte) et chemin (nom d'utilisateur) restent hors du Journal, qui part avec un rapport de feedback ; `{{folder}}` est le nom du dossier, pas le chemin (CWE-532). ## Décisions prises en planification - Scan enrichi `{ sources, root_files }` en un seul appel ; avertissement sur la page Import avec jusqu'à 5 noms puis « et N autres » (Max, 2026-10-12). ## Conventions - Toute chaîne en FR et en EN (`src/i18n/locales/fr.json`, `en.json`) ; les textes de ce corps s'écrivent mot pour mot (FR validé par Max le 2026-10-12, EN rédigé sur le même contenu). - Phrase de carte : un contrôle cité passe par `StepCardText.labels` (`src/utils/onboardingPath.ts`), traduit par `StepCard` ; jamais recopié dans la phrase. Guillemets « » en FR et “ ” en EN autour de chaque libellé de contrôle interpolé. - CHANGELOG : une entrée sous `## [Unreleased]` dans `CHANGELOG.md` et `CHANGELOG.fr.md`. - Tests de rendu : `renderToStaticMarkup` (pas de jsdom) et le faux `t` partagé livré par #389. - Journal : aucune ligne avec un chemin absolu, un nom de relevé ni un nom de dossier d'import (règle de `useImportWizard.ts:423-425` : le Journal part avec un rapport de feedback). - Aucune migration ; `npm run build` (il vérifie aussi les types des fichiers de test) et `npm test` verts ; `cargo test` vert quand du Rust change. - Guide Markdown (`docs/guide-utilisateur.md`) : repérer une ligne par son texte cité, pas par son numéro. ## CHANGELOG Added / Ajouté : FR « Import : un avertissement nomme les fichiers CSV posés directement dans le dossier d'import, qui sont ignorés. » EN "Import: a warning names the CSV files placed directly in the import folder, which are ignored." ## Spec source `spec-plan-retours-onboarding-v0171.md` (local, ignoré par git) ; décisions dans `spec-decisions-retours-onboarding-v0171.md`.
maximus added the
status:triage
type:feature
source:human
labels 2026-10-12 21:07:35 +00:00
Author
Owner

Planification du 2026-10-12 (/plan-run, jalon planned-2026-10-12-retours-onboarding-v0171) : le corps est réécrit pour être autosuffisant (décisions de Max, constats de /review-spec intégrés). Corps précédent ci-dessous.

Corps précédent (titre : Import : avertir des fichiers CSV posés directement dans le dossier d'import (ignorés aujourd'hui sans message))

Contexte

Constat de l'analyse de #422 (2026-10-12, main à 1954502), ouvert sur décision de Max.

scan_import_folder (src-tauri/src/commands/fs_commands.rs:25-110) ne lit que les sous-dossiers du dossier d'import :

  • une entrée de la racine qui n'est pas un dossier est sautée (:40-42) sans laisser de trace, et ScannedSource (:18-23) n'a pas de champ pour elle ;
  • dans chaque sous-dossier, seuls les .csv et .txt posés directement sont lus (WalkDir::max_depth(1)).

Un utilisateur qui dépose ses relevés directement dans le dossier choisi voit donc « Aucune source trouvée » sans savoir pourquoi. #422 ajoute l'explication de la structure attendue (dossier parent, un sous-dossier par compte) ; cette issue ajoute l'avertissement à l'écran.

Travail à faire

  • Le scan rend aussi les .csv et .txt trouvés à la racine (leur nombre, ou leurs noms).
  • La page Import affiche un avertissement quand il y en a, par exemple : « 3 fichiers CSV sont posés directement dans le dossier d'import et sont ignorés : déplacez-les dans un sous-dossier par compte. » FR et EN.
  • Dans l'état « aucune source », avec ou sans parcours de démarrage, l'avertissement nomme ce cas.
  • Tests Rust du scan et tests TS du rendu ; CHANGELOG FR et EN ; guide, section Import.

Questions ouvertes

  • Forme du retour du scan : champ ajouté au résultat (la commande rend aujourd'hui la liste des sources) ou commande séparée. À trancher par /analyze.

Complexité estimée

Simple à moyenne (Rust et TS).

Planification du 2026-10-12 (`/plan-run`, jalon `planned-2026-10-12-retours-onboarding-v0171`) : le corps est réécrit pour être autosuffisant (décisions de Max, constats de `/review-spec` intégrés). Corps précédent ci-dessous. <details><summary>Corps précédent (titre : Import : avertir des fichiers CSV posés directement dans le dossier d'import (ignorés aujourd'hui sans message))</summary> ## Contexte Constat de l'analyse de #422 (2026-10-12, `main` à `1954502`), ouvert sur décision de Max. `scan_import_folder` (`src-tauri/src/commands/fs_commands.rs:25-110`) ne lit que les sous-dossiers du dossier d'import : - une entrée de la racine qui n'est pas un dossier est sautée (`:40-42`) sans laisser de trace, et `ScannedSource` (`:18-23`) n'a pas de champ pour elle ; - dans chaque sous-dossier, seuls les `.csv` et `.txt` posés directement sont lus (`WalkDir::max_depth(1)`). Un utilisateur qui dépose ses relevés directement dans le dossier choisi voit donc « Aucune source trouvée » sans savoir pourquoi. #422 ajoute l'explication de la structure attendue (dossier parent, un sous-dossier par compte) ; cette issue ajoute l'avertissement à l'écran. ## Travail à faire - [ ] Le scan rend aussi les `.csv` et `.txt` trouvés à la racine (leur nombre, ou leurs noms). - [ ] La page Import affiche un avertissement quand il y en a, par exemple : « 3 fichiers CSV sont posés directement dans le dossier d'import et sont ignorés : déplacez-les dans un sous-dossier par compte. » FR et EN. - [ ] Dans l'état « aucune source », avec ou sans parcours de démarrage, l'avertissement nomme ce cas. - [ ] Tests Rust du scan et tests TS du rendu ; CHANGELOG FR et EN ; guide, section Import. ## Questions ouvertes - Forme du retour du scan : champ ajouté au résultat (la commande rend aujourd'hui la liste des sources) ou commande séparée. À trancher par `/analyze`. ## Complexité estimée Simple à moyenne (Rust et TS). </details>
maximus added this to the planned-2026-10-12-retours-onboarding-v0171 milestone 2026-10-12 23:25:26 +00:00
maximus added
status:ready
and removed
status:triage
labels 2026-10-12 23:25:26 +00:00
maximus added
status:in-progress
and removed
status:ready
labels 2026-10-14 00:55:45 +00:00
maximus added
status:review
and removed
status:in-progress
labels 2026-10-14 01:02:52 +00:00
maximus added
status:approved
and removed
status:review
labels 2026-10-14 23:52:10 +00:00
maximus removed the
status:approved
label 2026-10-16 00:38:16 +00:00
Sign in to join this conversation.
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#427
No description provided.