|
|
|
|
@ -26,17 +26,18 @@
|
|
|
|
|
```
|
|
|
|
|
simpl-resultat/
|
|
|
|
|
├── src/ # Frontend React/TypeScript
|
|
|
|
|
│ ├── components/ # 61 composants organisés par domaine
|
|
|
|
|
│ ├── components/ # 138 composants organisés par domaine (fichiers .tsx hors tests, #446)
|
|
|
|
|
│ │ ├── adjustments/ # 3 composants
|
|
|
|
|
│ │ ├── balance/ # 8 composants Bilan (AccountForm, BalanceAccountsTable, BalanceEvolutionChart, BalanceOnboardingCard, BalanceOverviewCard, LinkTransfersModal, SnapshotEditor, SnapshotLineRow)
|
|
|
|
|
│ │ ├── balance/ # 12 composants Bilan (AccountForm, BalanceAccountsTable, BalanceEvolutionChart, BalanceOverviewCard, DetailAccountWizard, HoldingsCsvImportModal, LinkTransfersModal, PriceFetchControl, SecurityPicker, SnapshotEditor, SnapshotLineRow, StarterAccountsModal)
|
|
|
|
|
│ │ ├── budget/ # 5 composants
|
|
|
|
|
│ │ ├── categories/ # 5 composants
|
|
|
|
|
│ │ ├── categories/ # 8 composants (dont HierarchyCheckModal, #393)
|
|
|
|
|
│ │ ├── categories-migration/ # 5 composants (flux de migration des catégories v1→v2 de CategoriesMigrationPage : StepDiscover, StepSimulate, StepConsent, MappingRow, TransactionPreviewPanel)
|
|
|
|
|
│ │ ├── dashboard/ # 7 composants (dont DashboardEmptyState et OnboardingChecklist)
|
|
|
|
|
│ │ ├── import/ # 18 composants (wizard d'import, dont FormatDriftPanel, RepairPathNotice, ImportEmptyState, ImportFolderStructure, RootFilesWarning et SourceNameDisplay)
|
|
|
|
|
│ │ ├── layout/ # AppShell, Sidebar
|
|
|
|
|
│ │ ├── onboarding/ # OnboardingBanner (bandeau de démarrage, #397), StepCard et OnboardingStepCard (carte d'étape, #398)
|
|
|
|
|
│ │ ├── profile/ # 3 composants (PIN, formulaire, switcher)
|
|
|
|
|
│ │ ├── reports/ # ~25 composants (hub, faits saillants, tendances, comparables, zoom catégorie)
|
|
|
|
|
│ │ ├── reports/ # 35 composants, dont 7 sous cards/ (hub, faits saillants, tendances, comparables, analyse par catégorie, cartes)
|
|
|
|
|
│ │ ├── settings/ # 17 composants (dont LogViewerCard, LicenseCard, LicenseKeyForm, AccountCard, DocsContent et DocsSubsections)
|
|
|
|
|
│ │ ├── shared/ # 14 composants réutilisables (dont RequireFeature, UpsellGate, UpsellDialog, PurchaseButton, EmptyState)
|
|
|
|
|
│ │ └── transactions/ # 6 composants (dont SplitButton, #369)
|
|
|
|
|
@ -242,7 +243,7 @@ Les 29 fichiers `.ts` de `src/hooks/`, hors tests (`ls src/hooks/*.ts | grep -v
|
|
|
|
|
| `useCategoryTaxonomy` | Taxonomie standard v1 en lecture (`getTaxonomyV1`, `findById`, `findByPath`, `getLeaves`, `getParentById` de `categoryTaxonomyService`), mémorisée une fois : guide des catégories standard (`CategoriesStandardGuidePage`) et étapes de la migration |
|
|
|
|
|
| `useTransactions` | Transactions et filtrage. `useTransactions(initialFilters?)` part de filtres de départ posés sur les filtres par défaut et lus au montage seulement (`initTransactionsState`) : l'entrée guidée de l'étape « Classer une transaction à la main » passe `GUIDED_ENTRY_FILTERS` (non catégorisées, sans borne de date). Si sa première page revient vide, le reducer retire le filtre « non catégorisées » une seule fois (`guidedFallback`) et la page relit (#399) |
|
|
|
|
|
| `useDataImport` | Import de données |
|
|
|
|
|
| `useImportWizard` | Assistant d'import multi-étapes. Restaure le format enregistré **tel quel** via `formatFromRow` (aucune ré-inférence), déclenche la détection seule sur une source sans format (garde `!existing`), traverse l'étape `file-preview` à **chaque** import, et n'écrit la configuration qu'à `executeImport` (point d'écriture unique) — voir la section « Import CSV » et l'[ADR 0019](adr/0019-format-import-persiste.md). Expose `importFolderLoaded`, posé dans le `finally` de la lecture du dossier au montage (après son premier scan) et gardé par `RESET` : l'état vide de la page Import et sa carte de démarrage l'attendent (#384, #398). Expose aussi `rootFiles`, les CSV/TXT posés à la racine du dossier d'import que le scan (`ImportFolderScan`) rend à côté des sources : posé avec elles par la même action `SET_SCANNED_SOURCES`, gardé par `RESET` comme elles, nommé à l'écran par `RootFilesWarning` (au plus cinq noms, `formatRootFiles`) et jamais écrit au Journal (#427). `executeImport` enregistre la source sous `selectedSource.folder_name`, à la création comme à la mise à jour, jamais sous `config.name` : la source et ses fichiers importés sont retrouvés par le nom du sous-dossier, que `SourceNameDisplay` affiche en lecture seule (#426) |
|
|
|
|
|
| `useImportWizard` | Assistant d'import multi-étapes. Restaure le format enregistré **tel quel** via `formatFromRow` (aucune ré-inférence), déclenche la détection seule sur une source sans format (garde `!existing`), traverse l'étape `file-preview` à **chaque** import, et n'écrit la configuration qu'à `executeImport` (point d'écriture unique) — voir la section « Import CSV » et l'[ADR 0019](adr/0019-format-import-persiste.md). Expose `importFolderLoaded`, posé dans le `finally` de la lecture du dossier au montage (après son premier scan) et gardé par `RESET` : l'état vide de la page Import et sa carte de démarrage l'attendent (#384, #398). Expose aussi `rootFiles`, les CSV/TXT posés à la racine du dossier d'import que le scan (`ImportFolderScan`) rend à côté des sources : posé avec elles par la même action `SET_SCANNED_SOURCES`, gardé par `RESET` comme elles, vidé par `SET_IMPORT_FOLDER` jusqu'au scan du nouveau dossier, pour que l'avertissement ne nomme pas les fichiers de l'ancien dossier sous le nom du nouveau (les sources, `scannedSources`, restent celles du dernier scan jusque-là : comportement antérieur, #446), nommé à l'écran par `RootFilesWarning` (au plus cinq noms, `formatRootFiles`) et jamais écrit au Journal (#427). `executeImport` enregistre la source sous `selectedSource.folder_name`, à la création comme à la mise à jour, jamais sous `config.name` : la source et ses fichiers importés sont retrouvés par le nom du sous-dossier, que `SourceNameDisplay` affiche en lecture seule (#426) |
|
|
|
|
|
| `useImportHistory` | Historique des imports |
|
|
|
|
|
| `useAdjustments` | Ajustements |
|
|
|
|
|
| `useBudget` | Budget |
|
|
|
|
|
@ -410,7 +411,7 @@ Sur ces deux pages, le compte se lit au montage à côté des autres lectures de
|
|
|
|
|
|
|
|
|
|
**Les cartes d'étape.** `StepCard` (props seules) et `OnboardingStepCard` (connectée, seule pièce de la carte qui lit `useOnboarding()`). Chaque page qui porte une carte rend `OnboardingStepCard` en premier enfant, avant son en-tête, et ne lui passe que son modèle. La carte a un titre `h2` « Étape n sur N · <étape> », qui prend le focus après une arrivée par un lien d'étape (une fois par arrivée, garde sur `location.key`, puis l'état de navigation est consommé) ; la phrase du modèle, où les libellés des boutons viennent de leurs propres clés ; une zone `role="status"` toujours rendue, qui reçoit « Fait. » ; « J'ai compris » avant « Passer cette étape » pour une étape de visite (`onAcknowledge`, passé par `OnboardingStepCard` quand `VISIT_STEP_KEYS` contient l'étape de la carte ; `ACKNOWLEDGE` garde le verrou de carte, qui annonce « Fait. » puis l'étape suivante), « Passer cette étape » tant que l'étape n'est pas faite ; le rang « Étape n sur N » et la fin attendent `budgetReady && visitStepsInitialized` ; puis le lien vers l'étape suivante ou le message de fin. Sur la page Transactions, l'entrée guidée (`guided`) passe `GUIDED_ENTRY_FILTERS` à `useTransactions` : non catégorisées, sans borne de date, avec un repli unique sur toutes les transactions quand aucune ne reste à classer.
|
|
|
|
|
|
|
|
|
|
**La mise en évidence.** Elle passe par props, jamais par une requête DOM. La page tire la cible de son modèle et ne la transmet que pendant que la carte est affichée (`showsStepCard`) : `onboardingTarget` sur l'action de `EmptyState` (par `ImportEmptyState`), sur la première carte de `SourceList`, sur le bouton suivant de `WizardNavigation`, sur la catégorie de la première ligne de `TransactionTable`, sur « Tout déplier » ou la cellule du mois courant de `BudgetTable`, sur « Tous les mots-clés » de `CategoriesPage` et sur la carte Tendances du hub (`HubReportNavCard`), une seule cible à la fois. Sur le hub, la carte Tendances reçoit aussi `onNavigate`, appelé au clic avant la navigation, tant que la carte d'étape s'affiche et que l'étape n'est pas faite : il reconnaît l'étape des rapports (`acknowledge("reports")`, D17), car suivre l'anneau quitte la page et relâche le verrou de carte. Le contrôle rend `data-onboarding-target="<clé fixe>"` et `data-onboarding-highlight="true"` (`onboardingTargetAttributes`). `styles.css` dessine sur ce second attribut un contour `3px solid var(--accent)` décalé de 2 px et une pulsation `box-shadow` de 1,2 s jouée trois fois, retirée sous `prefers-reduced-motion: reduce`. `--accent` atteint 3:1 contre le fond de la page et contre les cartes, dans les deux thèmes (`styles.contrast.test.ts`). Une cible absente de l'écran laisse la carte sans anneau.
|
|
|
|
|
**La mise en évidence.** Elle passe par props, jamais par une requête DOM. La page tire la cible de son modèle et ne la transmet que pendant que la carte est affichée (`showsStepCard`) : `onboardingTarget` sur l'action de `EmptyState` (par `ImportEmptyState`), sur la première carte de `SourceList`, sur le bouton suivant de `WizardNavigation`, sur la catégorie de la première ligne de `TransactionTable`, sur « Tout déplier » ou la cellule du mois courant de `BudgetTable`, sur « Tous les mots-clés » de `CategoriesPage` et sur la carte Tendances du hub (`HubReportNavCard`), une seule cible à la fois. Sur le hub, la carte Tendances reçoit aussi `onNavigate`, appelé au clic avant la navigation, tant que la carte d'étape s'affiche et que l'étape n'est pas faite : il reconnaît l'étape des rapports (`acknowledge("reports")`, D17), car suivre l'anneau quitte la page et relâche le verrou de carte. `HubReportNavCard` le passe au `onClick` du `Link`, que React tire aussi sur un Ctrl+clic (pas sur le clic du milieu, qui passe par `onAuxClick`) : un Ctrl+clic sur la carte Tendances coche donc l'étape « Parcourir les rapports », alors que le routeur ne navigue pas sur un clic modifié. Le webview n'ayant pas d'onglets, le cas est jugé théorique : documenté, pas corrigé (#446). Le contrôle rend `data-onboarding-target="<clé fixe>"` et `data-onboarding-highlight="true"` (`onboardingTargetAttributes`). `styles.css` dessine sur ce second attribut un contour `3px solid var(--accent)` décalé de 2 px et une pulsation `box-shadow` de 1,2 s jouée trois fois, retirée sous `prefers-reduced-motion: reduce`. `--accent` atteint 3:1 contre le fond de la page et contre les cartes, dans les deux thèmes (`styles.contrast.test.ts`). Une cible absente de l'écran laisse la carte sans anneau.
|
|
|
|
|
|
|
|
|
|
**Paramètres → Utilisateurs** lit `hidden` dans `useOnboarding()` et montre « Afficher la liste de démarrage » tant que le parcours est masqué, y compris quand le bandeau de cette même page vient de le masquer. Le clic passe par `show()` du fournisseur, et une confirmation remplace le bouton (#397).
|
|
|
|
|
|
|
|
|
|
@ -603,7 +604,7 @@ Les modules payants sont enveloppés dans des **layout-routes pathless `RequireF
|
|
|
|
|
| `/categories` | `CategoriesPage` | Gestion hiérarchique ; le bouton « Vérifier la hiérarchie » ouvre `HierarchyCheckModal` (#393) |
|
|
|
|
|
| `/adjustments` | `AdjustmentsPage` | Ajustements manuels |
|
|
|
|
|
| `/budget` | `BudgetPage` | Planification budgétaire |
|
|
|
|
|
| `/reports` | `ReportsPage` | Hub des rapports : panneau faits saillants + 4 cartes de navigation |
|
|
|
|
|
| `/reports` | `ReportsPage` | Hub des rapports : panneau faits saillants + 5 cartes de navigation (Faits saillants, Tendances, Comparables, Analyse par catégorie, Cartes) |
|
|
|
|
|
| `/reports/highlights` | `ReportsHighlightsPage` | Faits saillants détaillés (soldes, top mouvements, top transactions) |
|
|
|
|
|
| `/reports/trends` | `ReportsTrendsPage` | Tendances (flux global + par catégorie) |
|
|
|
|
|
| `/reports/compare` | `ReportsComparePage` | Comparables (MoM / YoY / Réel vs budget) |
|
|
|
|
|
|