feat(reports/cartes): Mensuel/YTD toggle on KPI cards + user guide section (#102) #114

Merged
maximus merged 1 commit from issue-102-cartes-ytd-toggle-docs into main 2026-04-19 13:55:36 +00:00
Owner

Summary

  • New segmented Monthly / YTD toggle on /reports/cartes, placed next to the reference-month picker. Flips the four KPI cards (income, expenses, net balance, savings rate) between the current-month view (unchanged default) and a Year-to-Date cumulative view.
  • YTD deltas follow the #104 conventions: MoM = current YTD vs same-year Jan→(refMonth-1) window (null for January), YoY = current YTD vs Jan→refMonth of previous year. Savings rate recomputed from YTD totals, still null when YTD income is zero.
  • 13-month sparklines, top movers, seasonality and budget adherence stay monthly regardless of the toggle (by design).
  • Dynamic savings-rate tooltip mirrors the active mode. i18n nested under reports.cartes.savingsRateTooltip.{month,ytd}.
  • Mode persisted in localStorage under reports-cartes-period-mode, default month.
  • New Cartes section in docs/guide-utilisateur.md covering the four KPI formulas, the Monthly/YTD toggle and its effect on deltas, sparkline, top movers, seasonality, budget adherence, and the savings-rate edge case. In-app docs.reports tree extended in both FR and EN.
  • No SQL migration: YTD sums are derived from the already-fetched flowByMonth map.

Test plan

  • npm test -- --run — all 133 tests pass (5 new YTD service tests + hook persistence tests)
  • npm run build — tsc + vite build green
  • Manual: switch to /reports/cartes, toggle Monthly ↔ YTD; verify the four KPI values, deltas, and savings-rate tooltip update correctly
  • Manual: pick January as reference month in YTD mode; verify MoM delta shows on all four cards
  • Manual: reload the page after toggling to YTD; verify the mode persists

Closes #102

## Summary - New segmented **Monthly / YTD** toggle on `/reports/cartes`, placed next to the reference-month picker. Flips the four KPI cards (income, expenses, net balance, savings rate) between the current-month view (unchanged default) and a Year-to-Date cumulative view. - YTD deltas follow the #104 conventions: MoM = current YTD vs same-year Jan→(refMonth-1) window (null for January), YoY = current YTD vs Jan→refMonth of previous year. Savings rate recomputed from YTD totals, still null when YTD income is zero. - 13-month sparklines, top movers, seasonality and budget adherence stay monthly regardless of the toggle (by design). - Dynamic savings-rate tooltip mirrors the active mode. i18n nested under `reports.cartes.savingsRateTooltip.{month,ytd}`. - Mode persisted in `localStorage` under `reports-cartes-period-mode`, default `month`. - New **Cartes** section in `docs/guide-utilisateur.md` covering the four KPI formulas, the Monthly/YTD toggle and its effect on deltas, sparkline, top movers, seasonality, budget adherence, and the savings-rate edge case. In-app `docs.reports` tree extended in both FR and EN. - No SQL migration: YTD sums are derived from the already-fetched `flowByMonth` map. ## Test plan - [x] `npm test -- --run` — all 133 tests pass (5 new YTD service tests + hook persistence tests) - [x] `npm run build` — tsc + vite build green - [ ] Manual: switch to `/reports/cartes`, toggle Monthly ↔ YTD; verify the four KPI values, deltas, and savings-rate tooltip update correctly - [ ] Manual: pick January as reference month in YTD mode; verify MoM delta shows `—` on all four cards - [ ] Manual: reload the page after toggling to YTD; verify the mode persists Closes #102
maximus added 1 commit 2026-04-19 13:49:41 +00:00
feat(reports/cartes): Mensuel/YTD toggle on KPI cards + user guide section (#102)
All checks were successful
PR Check / rust (push) Successful in 21m48s
PR Check / frontend (push) Successful in 2m15s
PR Check / rust (pull_request) Successful in 21m44s
PR Check / frontend (pull_request) Successful in 2m16s
3be05db41a
Adds a segmented Monthly/YTD toggle next to the reference-month picker that
flips the four KPI cards (income, expenses, net, savings rate) between the
reference-month value (unchanged default) and a Year-to-Date cumulative view.

In YTD mode, the "current" value sums January to the reference month of the
reference year; MoM delta compares it to Jan to (refMonth - 1) of the same
year (null in January, since no prior YTD window exists); YoY delta compares
it to Jan to refMonth of the previous year; savings rate is recomputed from
YTD income and expenses, and stays null when YTD income is zero.

The 13-month sparkline, top movers, seasonality and budget adherence cards
remain monthly regardless of the toggle (by design). The savings-rate tooltip
is now dynamic and mirrors the active mode. The mode is persisted in
localStorage under `reports-cartes-period-mode`.

Also adds a dedicated Cartes section to `docs/guide-utilisateur.md` covering
the four KPI formulas, the Monthly/YTD toggle and its effect on deltas, the
sparkline, top movers, seasonality, budget adherence and the savings-rate
edge case. Mirrored in the in-app `docs.reports` i18n tree (features/steps/
tips extended) for both FR and EN.

No SQL migration: YTD sums are computed from the already-fetched
`flowByMonth` map, so no extra round trip.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Author
Owner

Revue — APPROVE

Verdict

Implementation propre qui respecte l'intégralité du scope revu post-sprint. Toutes les ACs sont adressées.

Points validés

  • Service : getCartesSnapshot accepte mode: CartesKpiPeriodMode = "month" (backward-compat). Calcule les 8 agrégats monthly + 8 YTD en parallèle et sélectionne via isYtd. Pas de SQL supplémentaire — la fonction sumYtd() itère sur le flowByMonth déjà fetché.
  • YTD sémantique : MoM-YTD = Jan→refMonth−1 même année (null si refMonth === 1), YoY-YTD = Jan→refMonth année précédente. Cohérent avec #104.
  • Savings YTD : null quand ytdIncome === 0, propagé proprement via buildKpi(current: number | null, ...).
  • Seasonality : referenceAmount rebinded sur monthRefExpenses (jamais refExpenses qui peut être YTD). La carte reste mensuelle quel que soit le toggle — conforme au "hors scope" de l'issue.
  • Toggle : nouveau CartesPeriodModeToggle.tsx (60 lignes, template TrendsChartTypeToggle). Placé à côté de CompareReferenceMonthPicker.
  • Persistance : localStorage sous reports-cartes-period-mode, readCartesPeriodMode() fallback sur "month". Écriture via useEffect sur changement de state.
  • Tooltip dynamique : i18n nested object savingsRateTooltip.{month,ytd}. Résolu au niveau page en suffixant la clé. KpiCard inchangé (prop tooltip?: string existant supporte une string dynamique).
  • Tests : 4 nouveaux tests service (computes YTD KPIs correctly, null when YTD income is zero, null when refMonth = January, YoY Jan→refMonth prev year), 2 tests hook (localStorage round-trip pour month et ytd), 1 test backward-compat (mode par défaut).
  • Docs : section standalone dans guide-utilisateur.md avec formules + toggle + règles Saisonnalité + Top Movers + Budget Adherence. In-app docs.reports.* enrichi plutôt que créer une nouvelle section Cartes (décision pragmatique du worker, documentée dans ses notes).
  • i18n : clés periodMode.{month,ytd,aria} et savingsRateTooltip.{month,ytd} en FR et EN.
  • CHANGELOG FR + EN sous Added / Ajouté.

Vérifications locales

  • npm test -- --run : 133/133 verts (+11 nouveaux)
  • npm run build : vert (5.57s)

Décisions notables du worker

  1. i18n nested au lieu de clés plates (savingsRateTooltip.month vs savingsRateTooltipMonth) — bon choix, scale mieux si on ajoute d'autres modes.
  2. Pas de nouvelle section cartes dans le composant DocsPage (qui a un SECTIONS hardcodé avec 10 entrées). À la place, enrichit la section reports existante. La doc standalone vit dans guide-utilisateur.md. Évite une refonte de DocsPage + sections i18n lourdes.
  3. mode en paramètre optionnel sur getCartesSnapshot — aucun caller existant ne casse.

Rien ne bloque le merge. Milestone spec-refonte-rapports est désormais à 14/14 done après merge.

## Revue — APPROVE ✅ ### Verdict Implementation propre qui respecte l'intégralité du scope revu post-sprint. Toutes les ACs sont adressées. ### Points validés - **Service** : `getCartesSnapshot` accepte `mode: CartesKpiPeriodMode = "month"` (backward-compat). Calcule les 8 agrégats monthly + 8 YTD en parallèle et sélectionne via `isYtd`. Pas de SQL supplémentaire — la fonction `sumYtd()` itère sur le `flowByMonth` déjà fetché. - **YTD sémantique** : MoM-YTD = Jan→refMonth−1 même année (null si refMonth === 1), YoY-YTD = Jan→refMonth année précédente. Cohérent avec #104. - **Savings YTD** : `null` quand `ytdIncome === 0`, propagé proprement via `buildKpi(current: number | null, ...)`. - **Seasonality** : `referenceAmount` rebinded sur `monthRefExpenses` (jamais `refExpenses` qui peut être YTD). La carte reste mensuelle quel que soit le toggle — conforme au "hors scope" de l'issue. - **Toggle** : nouveau `CartesPeriodModeToggle.tsx` (60 lignes, template `TrendsChartTypeToggle`). Placé à côté de `CompareReferenceMonthPicker`. - **Persistance** : `localStorage` sous `reports-cartes-period-mode`, `readCartesPeriodMode()` fallback sur `"month"`. Écriture via `useEffect` sur changement de state. - **Tooltip dynamique** : i18n nested object `savingsRateTooltip.{month,ytd}`. Résolu au niveau page en suffixant la clé. `KpiCard` inchangé (prop `tooltip?: string` existant supporte une string dynamique). - **Tests** : 4 nouveaux tests service (`computes YTD KPIs correctly`, `null when YTD income is zero`, `null when refMonth = January`, `YoY Jan→refMonth prev year`), 2 tests hook (`localStorage round-trip` pour month et ytd), 1 test backward-compat (mode par défaut). - **Docs** : section standalone dans `guide-utilisateur.md` avec formules + toggle + règles Saisonnalité + Top Movers + Budget Adherence. In-app `docs.reports.*` enrichi plutôt que créer une nouvelle section Cartes (décision pragmatique du worker, documentée dans ses notes). - **i18n** : clés `periodMode.{month,ytd,aria}` et `savingsRateTooltip.{month,ytd}` en FR et EN. - **CHANGELOG** FR + EN sous `Added` / `Ajouté`. ### Vérifications locales - `npm test -- --run` : **133/133** verts (+11 nouveaux) - `npm run build` : vert (5.57s) ### Décisions notables du worker 1. **i18n nested** au lieu de clés plates (`savingsRateTooltip.month` vs `savingsRateTooltipMonth`) — bon choix, scale mieux si on ajoute d'autres modes. 2. **Pas de nouvelle section `cartes`** dans le composant `DocsPage` (qui a un `SECTIONS` hardcodé avec 10 entrées). À la place, enrichit la section `reports` existante. La doc standalone vit dans `guide-utilisateur.md`. Évite une refonte de `DocsPage` + sections i18n lourdes. 3. **`mode` en paramètre optionnel** sur `getCartesSnapshot` — aucun caller existant ne casse. Rien ne bloque le merge. Milestone `spec-refonte-rapports` est désormais à 14/14 done après merge.
maximus merged commit f371ae3f7e into main 2026-04-19 13:55:36 +00:00
maximus deleted branch issue-102-cartes-ytd-toggle-docs 2026-04-19 13:55:36 +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#114
No description provided.