feat(reports): CategoryOverTimeTable hierarchique + collapse + resultat interleave (#265) #269

Closed
maximus wants to merge 1 commit from issue-265-overtime-hierarchy into issue-264-overtime-tree
Owner

Resolves #265

Generated autonomously by /autopilot run of 2026-07-07

Base: #264 (sidecar tree).

Renders the trends-by-category table (CategoryOverTimeTable) as a hierarchical, collapsible income statement — parity with ComparePeriodTable.

What

  • Hierarchy: parent/child rows indented by depth off the #264 id-keyed tree; top-level parents show a chevron + their own subtotal.
  • Collapse: useCollapsibleGroups + collapsibleRows (reused from #254), keyed by category id, collapsed by default, with an expand-all / collapse-all button. State persisted under a dedicated key reports-trends-expanded (distinct from reports-compare-expanded).
  • Result interleaved: Result before transfers now sits between the non-transfer sections and the transfers section (was at the bottom); Net result stays the bottom line.
  • Purely visual collapse: section subtotals, before/net results and totals come from computeOverTimeResults over the raw tree, never the visible rows.

How

  • New pure module overTimeTableModel.ts: groupOverTimeSections(analysis, tree) (full-hierarchy sections + transfer split) + OVERTIME_COLLAPSE_ACCESSORS + storage key. Keeps the component thin and unit-testable (this repo has no React render harness).
  • overTimeResults.ts left untouched (its 9 tests unchanged) — grouping happens in the component, mirroring ComparePeriodTable, rather than widening the reducer's leaves-only sections.

Tests / build

  • 8 new pure tests in overTimeTableModel.test.ts (hierarchy grouping, collapsed-by-default, Paie/revenue visible after expand, result interleave split, totals-invariant).
  • npm run build green, 728 vitest green (720 baseline + 8), no regressions.
  • No DB migration; no new i18n strings (reuses reports.collapse.* / reports.compare.*). CHANGELOG bilingual under Unreleased.

Decisions

  • [HIGH] Grouped the tree in the component (Option A2) instead of modifying the #264 reducer — keeps overTimeResults.ts + its tests byte-identical (zero baseline regression) and confines changes to #265's scoped files.
  • [MEDIUM] No subtotals-on-top/bottom toggle (unlike ComparePeriodTable) — out of #265 scope; parents render on top in natural tree order.
Resolves #265 Generated autonomously by /autopilot run of 2026-07-07 Base: #264 (sidecar tree). Renders the trends-by-category table (`CategoryOverTimeTable`) as a hierarchical, collapsible income statement — parity with `ComparePeriodTable`. ## What - **Hierarchy**: parent/child rows indented by `depth` off the #264 id-keyed tree; top-level parents show a chevron + their own subtotal. - **Collapse**: `useCollapsibleGroups` + `collapsibleRows` (reused from #254), keyed by category id, **collapsed by default**, with an expand-all / collapse-all button. State persisted under a dedicated key `reports-trends-expanded` (distinct from `reports-compare-expanded`). - **Result interleaved**: `Result before transfers` now sits between the non-transfer sections and the transfers section (was at the bottom); `Net result` stays the bottom line. - **Purely visual collapse**: section subtotals, before/net results and totals come from `computeOverTimeResults` over the raw tree, never the visible rows. ## How - New pure module `overTimeTableModel.ts`: `groupOverTimeSections(analysis, tree)` (full-hierarchy sections + transfer split) + `OVERTIME_COLLAPSE_ACCESSORS` + storage key. Keeps the component thin and unit-testable (this repo has no React render harness). - `overTimeResults.ts` left **untouched** (its 9 tests unchanged) — grouping happens in the component, mirroring `ComparePeriodTable`, rather than widening the reducer's leaves-only sections. ## Tests / build - 8 new pure tests in `overTimeTableModel.test.ts` (hierarchy grouping, collapsed-by-default, Paie/revenue visible after expand, result interleave split, totals-invariant). - `npm run build` green, **728 vitest** green (720 baseline + 8), no regressions. - No DB migration; no new i18n strings (reuses `reports.collapse.*` / `reports.compare.*`). CHANGELOG bilingual under Unreleased. ## Decisions - [HIGH] Grouped the tree in the component (Option A2) instead of modifying the #264 reducer — keeps `overTimeResults.ts` + its tests byte-identical (zero baseline regression) and confines changes to #265's scoped files. - [MEDIUM] No subtotals-on-top/bottom toggle (unlike `ComparePeriodTable`) — out of #265 scope; parents render on top in natural tree order.
maximus added 1 commit 2026-07-07 23:47:12 +00:00
Renders the trends-by-category table from the #264 id-keyed tree as an indented
parent/child hierarchy (parity with ComparePeriodTable): top-level parents carry
a chevron and fold their subtree down to a subtotal, with an "expand all /
collapse all" button. Collapsed by default; expansion state is persisted under a
trends-specific localStorage key (reports-trends-expanded), distinct from the
comparable tables. The "result before transfers" line is interleaved before the
transfers section instead of sitting at the bottom.

Collapse is purely visual: section subtotals, before/net results and totals come
from computeOverTimeResults over the raw tree, never the visible rows.

New pure module overTimeTableModel.ts (section grouping + collapse accessors)
keeps the component thin and unit-testable without a React render harness;
overTimeResults.ts is left untouched. 8 new tests; build + 728 vitest green.

Resolves #265

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
maximus added the
autopilot:pending-human
type:feature
labels 2026-07-07 23:47:21 +00:00
Author
Owner

/pr-review PR #269 — feat(reports): CategoryOverTimeTable hiérarchique + collapse + résultat interleave (#265)

Verdict : APPROVE

PR empilée (base issue-264-overtime-tree, review head-vs-base). Le tableau de tendances par catégorie reprend fidèlement le pattern hiérarchie + collapse déjà éprouvé sur ComparePeriodTable. Aucun blocage.

Ce qui est solide

  • Invariant « collapse purement visuel » respecté. Sous-totaux de section, Résultat avant transferts et Résultat net proviennent tous de computeOverTimeResults(data) sur l'arbre brut, jamais des lignes visibles. overTimeResults.ts reste inchangé (ses 9 tests intacts) — le regroupement full-hierarchy vit dans le nouveau module pur overTimeTableModel.ts, exactement comme le fait ComparePeriodTable.
  • Interleaving correct. Résultat avant transferts passe entre les sections non-transfert et la section transferts (avant : sous la section transferts), et n'apparaît que si hasTransfers — parité stricte avec le compare. Le net reste la ligne du bas. Les chiffres ne changent pas : seule la présentation (hiérarchie + collapse + déplacement du before-transfers) évolue vs l'ancien rendu.
  • Bucketisation cohérente. groupOverTimeSections pilote l'ordre et les sous-totaux par analysis.sections (donc identiques au réducteur), et n'ajoute que les lignes parentes depuis data.tree. L'arbre étant déjà trié income→expense→transfer en profondeur d'abord, visibleRows reçoit bien des sous-arbres contigus.
  • Tests justes. Les 8 tests purs de overTimeTableModel.test.ts sont arithmétiquement corrects (income 6500, expense 3300, before 3200, net 3600) et couvrent : split hiérarchique ordonné, sous-totaux leaf-summed non double-comptés, section vide droppée, collapsed-by-default, révélation de Paie après expand, invariance des résultats.
  • Clé localStorage dédiée reports-trends-expanded (distincte de reports-compare-expanded), hook appelé avant l'early-return (ordre stable), aucune string i18n nouvelle (toutes réutilisées et présentes dans en.json/fr.json), aucune migration DB, aucun artefact de debug (.only/.skip/console/TODO absents), CHANGELOG bilingue sous [Unreleased]/[Non publié].

Suggestions non bloquantes

  1. renderResultRow sans key (CategoryOverTimeTable.tsx). La version parité de ComparePeriodTable pose key={labelKey} sur le <tr>. Ici sans effet (ce sont des enfants standalone de <tbody>, pas des éléments d'un .map → pas de warning React), mais ajouter la clé alignerait exactement sur la référence.
  2. typeOf dupliqué verbatim entre overTimeResults.ts et overTimeTableModel.ts. Exporter le helper du réducteur éviterait la duplication. Trivial.

Notes (informatives, hors périmètre)

  • Gap CI : la base est un maillon empilé (issue-264-overtime-tree), donc check.yml n'a pas tourné sur cette PR. Valider le tip cumulé en local (build + vitest + cargo) avant de merger la pile. Le corps de PR rapporte build vert + 728 vitest localement.
  • Comme le compare, la contiguïté du collapse suppose qu'une catégorie parente et tout son sous-arbre partagent le même category_type — hypothèse pré-existante de l'ordre de l'arbre, non introduite ici.

Review générée via /pr-review.

## `/pr-review` PR #269 — feat(reports): CategoryOverTimeTable hiérarchique + collapse + résultat interleave (#265) **Verdict : APPROVE** ✅ PR empilée (base `issue-264-overtime-tree`, review head-vs-base). Le tableau de tendances par catégorie reprend fidèlement le pattern hiérarchie + collapse déjà éprouvé sur `ComparePeriodTable`. Aucun blocage. ### Ce qui est solide - **Invariant « collapse purement visuel » respecté.** Sous-totaux de section, `Résultat avant transferts` et `Résultat net` proviennent tous de `computeOverTimeResults(data)` sur l'arbre brut, jamais des lignes visibles. `overTimeResults.ts` reste **inchangé** (ses 9 tests intacts) — le regroupement full-hierarchy vit dans le nouveau module pur `overTimeTableModel.ts`, exactement comme le fait `ComparePeriodTable`. - **Interleaving correct.** `Résultat avant transferts` passe entre les sections non-transfert et la section transferts (avant : sous la section transferts), et n'apparaît que si `hasTransfers` — parité stricte avec le compare. Le net reste la ligne du bas. Les chiffres ne changent pas : seule la présentation (hiérarchie + collapse + déplacement du before-transfers) évolue vs l'ancien rendu. - **Bucketisation cohérente.** `groupOverTimeSections` pilote l'ordre et les sous-totaux par `analysis.sections` (donc identiques au réducteur), et n'ajoute que les lignes parentes depuis `data.tree`. L'arbre étant déjà trié income→expense→transfer en profondeur d'abord, `visibleRows` reçoit bien des sous-arbres contigus. - **Tests justes.** Les 8 tests purs de `overTimeTableModel.test.ts` sont arithmétiquement corrects (income 6500, expense 3300, before 3200, net 3600) et couvrent : split hiérarchique ordonné, sous-totaux leaf-summed non double-comptés, section vide droppée, collapsed-by-default, révélation de Paie après expand, invariance des résultats. - Clé localStorage dédiée `reports-trends-expanded` (distincte de `reports-compare-expanded`), hook appelé avant l'early-return (ordre stable), aucune string i18n nouvelle (toutes réutilisées et présentes dans `en.json`/`fr.json`), aucune migration DB, aucun artefact de debug (`.only`/`.skip`/`console`/`TODO` absents), CHANGELOG bilingue sous `[Unreleased]`/`[Non publié]`. ### Suggestions non bloquantes 1. **`renderResultRow` sans `key`** (`CategoryOverTimeTable.tsx`). La version parité de `ComparePeriodTable` pose `key={labelKey}` sur le `<tr>`. Ici sans effet (ce sont des enfants standalone de `<tbody>`, pas des éléments d'un `.map` → pas de warning React), mais ajouter la clé alignerait exactement sur la référence. 2. **`typeOf` dupliqué** verbatim entre `overTimeResults.ts` et `overTimeTableModel.ts`. Exporter le helper du réducteur éviterait la duplication. Trivial. ### Notes (informatives, hors périmètre) - **Gap CI :** la base est un maillon empilé (`issue-264-overtime-tree`), donc `check.yml` n'a pas tourné sur cette PR. Valider le tip cumulé en local (build + vitest + cargo) avant de merger la pile. Le corps de PR rapporte build vert + **728 vitest** localement. - Comme le compare, la contiguïté du collapse suppose qu'une catégorie parente et tout son sous-arbre partagent le même `category_type` — hypothèse pré-existante de l'ordre de l'arbre, non introduite ici. _Review générée via `/pr-review`._
Author
Owner

Mergée localement dans main (pile #266/#267/#268/#269, tip fe87313). Fermée manuellement — merge local d'une pile stackée (l'API merge Forgejo échoue sur les bases non-main), tip cumulé validé build + 728 vitest verts.

Mergée localement dans `main` (pile #266/#267/#268/#269, tip `fe87313`). Fermée manuellement — merge local d'une pile stackée (l'API merge Forgejo échoue sur les bases non-`main`), tip cumulé validé build + 728 vitest verts.
maximus closed this pull request 2026-07-08 00:34:04 +00:00

Pull request closed

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#269
No description provided.