feat: reports hub + highlights panel + detailed highlights page (#71) #90

Merged
maximus merged 1 commit from issue-71-highlights-hub into main 2026-04-14 18:50:41 +00:00
Owner

Fixes #71

/reports devient un hub affichant un panneau de faits saillants et 4 cartes de navigation vers les sous-rapports.

Changements

Service

  • reportService.getHighlights(windowDays, referenceDate, topMoversLimit, topTransactionsLimit) — SQL strictement paramétré, referenceDate injectable pour tests déterministes
  • Retourne { currentMonth, netBalanceCurrent, netBalanceYtd, monthlyBalanceSeries[12], topMovers[], topTransactions[] }
  • Top movers = expenses (amount < 0), delta mois courant vs mois précédent, ORDER BY ABS(delta) DESC
  • Top transactions = fenêtre glissante 30/60/90 jours, ORDER BY ABS(amount) DESC

Hook

  • useHighlights complet avec reducer, loading/error, window-days switching

Composants hub

  • HubNetBalanceTile — montant formaté + sparkline coloré selon signe
  • HubTopMoversTile — top 3 avec toggle $/%, couleurs + icônes ArrowUp/Down
  • HubTopTransactionsTile — top 5 condensé
  • HubHighlightsPanel — layout 4-col responsive
  • HubReportNavCard — carte de navigation react-router Link, préserve la query string

Hub /reports

  • Refondu complètement : PageHelp + PeriodSelector global + HubHighlightsPanel + grille de 4 HubReportNavCard

Page détaillée /reports/highlights

  • HighlightsTopMoversTable triable (catégorie, précédent, courant, Δ$, Δ%)
  • HighlightsTopMoversChart — BarChart horizontal Recharts centré sur 0, ChartPatternDefs + getPatternFill
  • HighlightsTopTransactionsList — liste 10 items avec toggle 30/60/90 jours
  • ViewModeToggle avec storageKey=reports-viewmode-highlights

i18n

  • Nouvelles clés reports.hub.*Description, reports.highlights.* en FR et EN
  • Parité vérifiée (0 orpheline)

Tests

  • 5 tests vitest pour getHighlights : profil vide, paramétrisation SQL, fenêtre 60 jours, calcul deltas, zéro-previous
  • npm test 42/42
  • npm run build
  • cargo check
Fixes #71 `/reports` devient un **hub** affichant un panneau de faits saillants et 4 cartes de navigation vers les sous-rapports. ## Changements ### Service - `reportService.getHighlights(windowDays, referenceDate, topMoversLimit, topTransactionsLimit)` — SQL **strictement paramétré**, `referenceDate` injectable pour tests déterministes - Retourne `{ currentMonth, netBalanceCurrent, netBalanceYtd, monthlyBalanceSeries[12], topMovers[], topTransactions[] }` - Top movers = expenses (amount < 0), delta mois courant vs mois précédent, `ORDER BY ABS(delta) DESC` - Top transactions = fenêtre glissante 30/60/90 jours, `ORDER BY ABS(amount) DESC` ### Hook - `useHighlights` complet avec reducer, loading/error, window-days switching ### Composants hub - `HubNetBalanceTile` — montant formaté + sparkline coloré selon signe - `HubTopMoversTile` — top 3 avec toggle `$`/`%`, couleurs + icônes ArrowUp/Down - `HubTopTransactionsTile` — top 5 condensé - `HubHighlightsPanel` — layout 4-col responsive - `HubReportNavCard` — carte de navigation react-router `Link`, préserve la query string ### Hub `/reports` - Refondu complètement : `PageHelp` + `PeriodSelector` global + `HubHighlightsPanel` + grille de 4 `HubReportNavCard` ### Page détaillée `/reports/highlights` - `HighlightsTopMoversTable` triable (catégorie, précédent, courant, Δ$, Δ%) - `HighlightsTopMoversChart` — BarChart horizontal Recharts centré sur 0, `ChartPatternDefs` + `getPatternFill` - `HighlightsTopTransactionsList` — liste 10 items avec toggle 30/60/90 jours - `ViewModeToggle` avec `storageKey=reports-viewmode-highlights` ### i18n - Nouvelles clés `reports.hub.*Description`, `reports.highlights.*` en FR et EN - Parité vérifiée (0 orpheline) ## Tests - 5 tests vitest pour `getHighlights` : profil vide, paramétrisation SQL, fenêtre 60 jours, calcul deltas, zéro-previous - `npm test` ✅ 42/42 - `npm run build` ✅ - `cargo check` ✅
maximus added 1 commit 2026-04-14 18:48:19 +00:00
feat: reports hub with highlights panel and detailed highlights page (#71)
All checks were successful
PR Check / rust (pull_request) Successful in 24m54s
PR Check / frontend (pull_request) Successful in 2m32s
PR Check / rust (push) Successful in 24m14s
PR Check / frontend (push) Successful in 2m26s
ac9c8afc4a
- Transform /reports into a hub: highlights panel + 4 nav cards
- New service: reportService.getHighlights (parameterised SQL, deterministic
  via referenceDate argument for tests, computes current-month balance, YTD,
  12-month sparkline series, top expense movers vs previous month, top recent
  transactions within configurable 30/60/90 day window)
- Extended types: HighlightsData, HighlightMover, MonthBalance
- Wired useHighlights hook with reducer + window-days state
- Hub tiles (flat naming under src/components/reports):
  HubNetBalanceTile, HubTopMoversTile, HubTopTransactionsTile,
  HubHighlightsPanel, HubReportNavCard
- Detailed ReportsHighlightsPage: balance tiles, sortable top movers table,
  diverging bar chart (Recharts + patterns SVG), top transactions list with
  30/60/90 window toggle; ViewModeToggle persistence keyed as
  reports-viewmode-highlights
- New i18n keys: reports.hub.*, reports.highlights.*
- 5 new vitest cases: empty profile, parameterised queries, window sizing,
  delta computation, zero-previous divisor handling

Fixes #71

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

Review — APPROVE

Sécurité

  • Toutes les requêtes SQL strictement paramétrées (vérifié par test dédié parameterises every query with no inlined strings)
  • getHighlights accepte referenceDate comme string — mais injecté uniquement via db.select(sql, params) (jamais concaténé)
  • Pas de XSS : descriptions de transactions rendues comme enfants React ({tx.description}), troncature via truncate CSS
  • Intl.NumberFormat côté affichage, pas de concat manuelle

Correctness

  • Calcul de la plage 12 mois correct : shiftMonthStart(refIso, -11) → début inclusif, test couvre la borne 2026-052026-04
  • Delta mois courant vs précédent calculé sur ABS(amount) en filtrant amount < 0 (expenses only) — cohérent avec la sémantique "top hausse"
  • deltaPct = null quand previous = 0 — test dédié vérifie
  • Series remplie avec zéros pour les mois sans données (Map lookup par mois)
  • Fallback graceful : tuiles affichent empty-state reports.empty.noData si pas de données
  • Navigation Link to=/reports/...${preserveSearch}` → la query string de période est préservée entre hub et sous-pages

Qualité

  • npm run build
  • npm test 42/42 (5 nouveaux pour getHighlights)
  • cargo check
  • Parité i18n FR/EN : 0 clé orpheline
  • Convention flat respectée (préfixes Hub*, Highlights*)
  • Signature visuelle : patterns SVG + palette catégorie via chartPatterns.tsx

Points non-bloquants

  • HubReportNavCard utilise window.location.search pour préserver la query : dépend de SSR absent, OK pour Tauri desktop
  • Le label du solde netBalanceCurrent = "Ce mois-ci" est today-relative (ignore le sélecteur de période) — décision cohérente avec la spec mockup mais peut surprendre ; documenté implicitement par le nom des clés i18n
  • Le hub n appelle plus useReports → legacy vraiment orphelin en attente de #72/#73/#74 ; ok avec plan de sprint (suppression en #76)

Ready to merge.

## Review — APPROVE ### Sécurité ✅ - Toutes les requêtes SQL **strictement paramétrées** (vérifié par test dédié `parameterises every query with no inlined strings`) - `getHighlights` accepte `referenceDate` comme string — mais injecté uniquement via `db.select(sql, params)` (jamais concaténé) - Pas de XSS : descriptions de transactions rendues comme enfants React (`{tx.description}`), troncature via `truncate` CSS - `Intl.NumberFormat` côté affichage, pas de concat manuelle ### Correctness ✅ - Calcul de la plage 12 mois correct : `shiftMonthStart(refIso, -11)` → début inclusif, test couvre la borne `2026-05` → `2026-04` - Delta mois courant vs précédent calculé sur `ABS(amount)` en filtrant `amount < 0` (expenses only) — cohérent avec la sémantique "top hausse" - `deltaPct = null` quand `previous = 0` — test dédié vérifie - Series remplie avec zéros pour les mois sans données (Map lookup par mois) - Fallback graceful : tuiles affichent empty-state `reports.empty.noData` si pas de données - Navigation `Link to=`/reports/...${preserveSearch}` → la query string de période est préservée entre hub et sous-pages ✅ ### Qualité ✅ - `npm run build` ✅ - `npm test` ✅ 42/42 (5 nouveaux pour `getHighlights`) - `cargo check` ✅ - Parité i18n FR/EN : 0 clé orpheline - Convention flat respectée (préfixes `Hub*`, `Highlights*`) - Signature visuelle : patterns SVG + palette catégorie via `chartPatterns.tsx` ### Points non-bloquants - `HubReportNavCard` utilise `window.location.search` pour préserver la query : dépend de SSR absent, OK pour Tauri desktop - Le label du solde `netBalanceCurrent` = "Ce mois-ci" est today-relative (ignore le sélecteur de période) — décision cohérente avec la spec mockup mais peut surprendre ; documenté implicitement par le nom des clés i18n - Le hub n appelle plus `useReports` → legacy vraiment orphelin en attente de #72/#73/#74 ; ok avec plan de sprint (suppression en #76) Ready to merge.
maximus merged commit 5d206d5faf into main 2026-04-14 18:50:41 +00:00
maximus deleted branch issue-71-highlights-hub 2026-04-14 18:50:41 +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#90
No description provided.