Onboarding : compteur de transactions, compteurs d'onboarding et reducer de la liste (socle) #351

Closed
opened 2026-09-26 12:24:29 +00:00 by maximus · 0 comments
Owner

Vague planned-2026-10-03-onboarding-phase1 (séance /plan-run du 2026-10-03, spec + /review-spec du 2026-09-25, verdict APPROUVÉ après intégration). Les fichiers spec-decisions-onboarding-phase1.md / spec-plan-onboarding-phase1.md sont ignorés par git : ce corps est auto-suffisant, ne pas chercher le spec. Analyse d'origine : docs/analyse-onboarding-2026-09.md.

Règles de la vague : pile linéaire (chaque PR part de la précédente) ; ne pas toucher CHANGELOG.md / CHANGELOG.fr.md avant #357 qui les centralise ; FR + EN pour toute clé i18n (.claude/rules/i18n.md) ; aucune migration DB ; aucune chaîne en dur dans les composants.

Objectif

Poser le socle sans UI : un compteur global de transactions, un service de compteurs d'onboarding en une requête, la fonction pure qui calcule les six cases de la liste de démarrage, et le hook à reducer exporté.

Tâches

  • transactionService.getTransactionCount(): Promise<number> — SELECT COUNT(*) FROM transactions, sans filtre
  • src/services/onboardingService.ts : getOnboardingCounters() qui lit en un Promise.all (a) la requête agrégée ci-dessous, (b) getImportFolder(), (c) getPreference("onboarding_checklist_hidden") ; hideChecklist() → setPreference("onboarding_checklist_hidden", "1") ; showChecklist() → deletePreference(...) ; logInfo au masquage et au réaffichage (logService)
  • src/utils/onboardingChecklist.ts : computeChecklist(counters, { importFolderSet, budgetAllowed }) pur → { items: ChecklistItem[], done, total, complete }, ChecklistItem = { key, done, locked, to } ; six cases dans cet ordre : profile (toujours cochée), importFolder (importFolderSet), source (sources ≥ 1), import (imports ≥ 1), manualCategory (manualCategorized ≥ 1), budget (budgetEntries ≥ 1, locked quand budgetAllowed est faux) ; total = 6, ou 5 quand la case budget est verrouillée ; complete = toutes les cases non verrouillées cochées ; to = /import, /import, /import, /transactions, /budget (/settings/users pour budget verrouillé)
  • src/hooks/useOnboardingChecklist.ts : onboardingChecklistReducer et initialOnboardingChecklistState exportés (nommage préfixé comme licenseReducer, feedbackReducer, migrationReducer) ; état { status: "loading" | "ready" | "error", counters, hidden, error } ; actions refresh(), hide(), show() ; composition avec useEntitlement("budget") (allowed → budgetAllowed, ready → budgetReady) ; rend { status, checklist, hidden, budgetReady, refresh, hide, show } ; une erreur de chargement pose status: "error" sans toucher hidden
  • Types OnboardingCounters et ChecklistItem dans src/shared/types/index.ts

Requête des compteurs :

SELECT
  (SELECT COUNT(*) FROM import_sources)                                 AS sources,
  (SELECT COUNT(*) FROM imported_files WHERE status = 'completed')      AS imports,
  (SELECT COUNT(*) FROM transactions WHERE is_manually_categorized = 1) AS manual_categorized,
  (SELECT COUNT(*) FROM budget_entries)                                 AS budget_entries,
  (SELECT COUNT(*) FROM transactions)                                   AS transactions;

Fichiers concernés

  • src/services/transactionService.ts (+ test dans src/services/transactionService.test.ts si existant, sinon créer)
  • src/services/onboardingService.ts (nouveau) + src/services/onboardingService.test.ts (nouveau, vi.mock("./db") comme categorizationService.test.ts:9)
  • src/utils/onboardingChecklist.ts (nouveau) + src/utils/onboardingChecklist.test.ts (nouveau)
  • src/hooks/useOnboardingChecklist.ts (nouveau) + src/hooks/useOnboardingChecklist.test.ts (nouveau, pattern src/hooks/useSnapshotEditor.test.ts : reducer testé sans renderHook)
  • src/shared/types/index.ts

Depends on

(aucune)

Critères d'acceptation

  • computeChecklist : tests pour chaque case isolée, budgetAllowed=false → total=5 et case budget.locked=true, complete vrai à 5/5 verrouillé et 6/6 ouvert, dossier choisi sans source → done=2, compteurs à zéro → done=1
  • Reducer : chaque action testée ; une erreur de chargement garde hidden et pose status="error" ; refresh après error repasse en loading puis ready
  • getTransactionCount testé avec db mocké (0 et N)
  • npm test et npm run build verts ; aucune UI touchée dans cette PR

Review caveats

  • (TECHNIQUE, résolu) Les hooks existants ont des reducers privés ; le nouveau est exporté dès le départ.

Décisions prises en planification

  • Case 5 de la liste = « une transaction classée à la main » (is_manually_categorized = 1) et non « règle créée » ni « transactions catégorisées » : le seed IPC met 180 mots-clés dans tout profil neuf et le wizard catégorise pendant l'import (useImportWizard.ts:1016).
  • Ordre des vagues : planned-2026-09-24-paiements s'exécute avant celle-ci (fichiers partagés : fr.json, en.json, CHANGELOG, architecture.md, guide-utilisateur.md) ; partir d'un main qui contient déjà ses clés.
  • Préférence onboarding_checklist_hidden, valeur "1", snake_case comme categories_v1_banner_dismissed.

Spec source

spec-plan-onboarding-phase1.md (local, ignoré par git)

Vague `planned-2026-10-03-onboarding-phase1` (séance `/plan-run` du 2026-10-03, spec + `/review-spec` du 2026-09-25, verdict APPROUVÉ après intégration). Les fichiers `spec-decisions-onboarding-phase1.md` / `spec-plan-onboarding-phase1.md` sont **ignorés par git** : ce corps est auto-suffisant, ne pas chercher le spec. Analyse d'origine : `docs/analyse-onboarding-2026-09.md`. Règles de la vague : pile linéaire (chaque PR part de la précédente) ; **ne pas toucher `CHANGELOG.md` / `CHANGELOG.fr.md`** avant #357 qui les centralise ; FR + EN pour toute clé i18n (`.claude/rules/i18n.md`) ; aucune migration DB ; aucune chaîne en dur dans les composants. ## Objectif Poser le socle sans UI : un compteur global de transactions, un service de compteurs d'onboarding en une requête, la fonction pure qui calcule les six cases de la liste de démarrage, et le hook à reducer exporté. ## Tâches - [ ] `transactionService.getTransactionCount(): Promise<number>` — `SELECT COUNT(*) FROM transactions`, sans filtre - [ ] `src/services/onboardingService.ts` : `getOnboardingCounters()` qui lit en un `Promise.all` (a) la requête agrégée ci-dessous, (b) `getImportFolder()`, (c) `getPreference("onboarding_checklist_hidden")` ; `hideChecklist()` → `setPreference("onboarding_checklist_hidden", "1")` ; `showChecklist()` → `deletePreference(...)` ; `logInfo` au masquage et au réaffichage (`logService`) - [ ] `src/utils/onboardingChecklist.ts` : `computeChecklist(counters, { importFolderSet, budgetAllowed })` pur → `{ items: ChecklistItem[], done, total, complete }`, `ChecklistItem = { key, done, locked, to }` ; six cases dans cet ordre : `profile` (toujours cochée), `importFolder` (`importFolderSet`), `source` (`sources ≥ 1`), `import` (`imports ≥ 1`), `manualCategory` (`manualCategorized ≥ 1`), `budget` (`budgetEntries ≥ 1`, `locked` quand `budgetAllowed` est faux) ; `total` = 6, ou 5 quand la case budget est verrouillée ; `complete` = toutes les cases non verrouillées cochées ; `to` = `/import`, `/import`, `/import`, `/transactions`, `/budget` (`/settings/users` pour budget verrouillé) - [ ] `src/hooks/useOnboardingChecklist.ts` : `onboardingChecklistReducer` et `initialOnboardingChecklistState` **exportés** (nommage préfixé comme `licenseReducer`, `feedbackReducer`, `migrationReducer`) ; état `{ status: "loading" | "ready" | "error", counters, hidden, error }` ; actions `refresh()`, `hide()`, `show()` ; composition avec `useEntitlement("budget")` (`allowed` → `budgetAllowed`, `ready` → `budgetReady`) ; rend `{ status, checklist, hidden, budgetReady, refresh, hide, show }` ; une erreur de chargement pose `status: "error"` sans toucher `hidden` - [ ] Types `OnboardingCounters` et `ChecklistItem` dans `src/shared/types/index.ts` Requête des compteurs : ```sql SELECT (SELECT COUNT(*) FROM import_sources) AS sources, (SELECT COUNT(*) FROM imported_files WHERE status = 'completed') AS imports, (SELECT COUNT(*) FROM transactions WHERE is_manually_categorized = 1) AS manual_categorized, (SELECT COUNT(*) FROM budget_entries) AS budget_entries, (SELECT COUNT(*) FROM transactions) AS transactions; ``` ## Fichiers concernés - `src/services/transactionService.ts` (+ test dans `src/services/transactionService.test.ts` si existant, sinon créer) - `src/services/onboardingService.ts` (nouveau) + `src/services/onboardingService.test.ts` (nouveau, `vi.mock("./db")` comme `categorizationService.test.ts:9`) - `src/utils/onboardingChecklist.ts` (nouveau) + `src/utils/onboardingChecklist.test.ts` (nouveau) - `src/hooks/useOnboardingChecklist.ts` (nouveau) + `src/hooks/useOnboardingChecklist.test.ts` (nouveau, pattern `src/hooks/useSnapshotEditor.test.ts` : reducer testé sans `renderHook`) - `src/shared/types/index.ts` ## Depends on (aucune) ## Critères d'acceptation - [ ] `computeChecklist` : tests pour chaque case isolée, `budgetAllowed=false` → `total=5` et case `budget.locked=true`, `complete` vrai à 5/5 verrouillé et 6/6 ouvert, dossier choisi sans source → `done=2`, compteurs à zéro → `done=1` - [ ] Reducer : chaque action testée ; une erreur de chargement garde `hidden` et pose `status="error"` ; `refresh` après `error` repasse en `loading` puis `ready` - [ ] `getTransactionCount` testé avec `db` mocké (0 et N) - [ ] `npm test` et `npm run build` verts ; aucune UI touchée dans cette PR ## Review caveats - (TECHNIQUE, résolu) Les hooks existants ont des reducers privés ; le nouveau est exporté dès le départ. ## Décisions prises en planification - Case 5 de la liste = « une transaction classée à la main » (`is_manually_categorized = 1`) et non « règle créée » ni « transactions catégorisées » : le seed IPC met 180 mots-clés dans tout profil neuf et le wizard catégorise pendant l'import (`useImportWizard.ts:1016`). - Ordre des vagues : `planned-2026-09-24-paiements` s'exécute **avant** celle-ci (fichiers partagés : `fr.json`, `en.json`, CHANGELOG, `architecture.md`, `guide-utilisateur.md`) ; partir d'un `main` qui contient déjà ses clés. - Préférence `onboarding_checklist_hidden`, valeur `"1"`, `snake_case` comme `categories_v1_banner_dismissed`. ## Spec source spec-plan-onboarding-phase1.md (local, ignoré par git)
maximus added this to the planned-2026-10-03-onboarding-phase1 milestone 2026-09-26 12:24:29 +00:00
maximus added the
status:ready
type:feature
source:human
labels 2026-09-26 12:24:29 +00:00
maximus changed title from Onboarding: transaction count, onboarding counters and checklist reducer (socle) to Onboarding : compteur de transactions, compteurs d'onboarding et reducer de la liste (socle) 2026-10-03 14:09:44 +00:00
maximus added
status:in-progress
and removed
status:ready
labels 2026-10-03 23:16:48 +00:00
maximus added
status:review
and removed
status:in-progress
labels 2026-10-03 23:22:51 +00:00
maximus added
status:approved
and removed
status:review
labels 2026-10-03 23:24:14 +00:00
maximus removed the
status:approved
label 2026-10-04 12:52:51 +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#351
No description provided.