Commit graph

405 commits

Author SHA1 Message Date
le king fu
4c71eaca2d feat(balance): add i18n keys + CHANGELOG entry
All checks were successful
PR Check / rust (push) Successful in 22m25s
PR Check / frontend (push) Successful in 2m27s
PR Check / rust (pull_request) Successful in 22m44s
PR Check / frontend (pull_request) Successful in 2m21s
Adds the FR/EN translation namespace `balance.*` covering:
- balance.accountsPage.* (page chrome, tab labels, empty states)
- balance.account.* (table fields, status badges, action labels,
  full account form copy with priced/simple-aware hints)
- balance.category.* (intro text, table fields, kind labels, origin
  labels, action prompts, simple-only creation form, seeded labels
  for the 7 standard categories)
- balance.errors.* (one entry per BalanceErrorCode union member)

CHANGELOG.md and CHANGELOG.fr.md both gain a single entry under
`[Unreleased] / Added` summarising the schema migration v9, the new
balance.service CRUD section and the AccountsPage tabs.

Refs #138

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 14:38:09 -04:00
le king fu
fccc8e4fa2 feat(balance): add useBalanceAccounts hook + AccountsPage + AccountForm
Wires the AccountsPage end-to-end with a new scoped useReducer hook,
the page itself (accessible at /balance/accounts) and the account form.

useBalanceAccounts (src/hooks/useBalanceAccounts.ts):
- Loads accounts (excludes archived by default) + categories in parallel
- Surfaces typed errors from balance.service via state.errorCode so the
  UI can localize them (e.g. seed protection, currency rejection)
- CRUD operations on both domains: addAccount/editAccount/archive/
  unarchiveAccount + addCategory/editCategory/removeCategory

AccountsPage (src/pages/AccountsPage.tsx):
- Two tabs: Comptes + Catégories
- Accounts tab: archive toggle, table of (name, category, symbol,
  currency, status), inline edit/archive/restore
- Categories tab: full list of seeded + user categories. Add new
  simple-kind category (priced creation lands in #140). Rename via
  inline prompt; delete disabled on seeded rows. Errors surfaced via
  i18n keys keyed on BalanceErrorCode.

AccountForm (src/components/balance/AccountForm.tsx):
- Variant=account only (category variant lands in #140)
- Auto-detects priced category to hint the symbol field
- Full FR/EN coverage of labels and validation messages

Per spec-plan-bilan.md v2 the sidebar entry "Bilan" is intentionally
not added in this issue — it lands in #141 (Bilan #3) when the
/balance overview becomes navigable. Until then the route is reachable
directly via URL.

Refs #138

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 14:37:30 -04:00
le king fu
58d3c86336 feat(balance): add balance.service CRUD section + tests
Adds the TypeScript service layer for the Bilan feature, scoped to
Issue #138 (Bilan #1a) — categories + accounts CRUD only. Snapshots,
snapshot lines, transfers and price-fetching land in subsequent issues.

The service uses `getDb()` + tauri-plugin-sql directly per project
convention (96 occurrences across 15 services). No new Tauri commands
introduced — the only future Rust commands are `compute_account_return`
(Issue #142) and `fetch_price` (Issue #144).

API surface:
- listBalanceCategories / getBalanceCategory / createBalanceCategory /
  updateBalanceCategory / deleteBalanceCategory (with seed + has-accounts
  guards)
- listBalanceAccounts (excludes archived by default) / getBalanceAccount
  / createBalanceAccount (CAD-only at MVP) / updateBalanceAccount /
  archiveBalanceAccount / unarchiveBalanceAccount (soft delete)

Typed errors via BalanceServiceError + BalanceErrorCode union so the UI
can render distinct i18n messages. Domain types added under
`src/shared/types/index.ts`: BalanceCategoryKind, BalanceCategory,
BalanceAccount, BalanceAccountWithCategory, BALANCE_CURRENCY_CAD.

19 vitest cases cover: ordering, kind validation, seed protection,
linked-account guard, currency rejection, missing-category lookup,
soft delete + restore round-trip, symbol/notes normalization.

Refs #138

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 14:33:39 -04:00
le king fu
a6787adef0 feat(balance): add migration v9 schema (5 tables, 7 indexes, seed)
Adds the SQL foundation for the Bilan (balance sheet) feature:

- 5 new tables: balance_categories, balance_accounts, balance_snapshots,
  balance_snapshot_lines, balance_account_transfers
- 7 indexes (category, active partial, snapshot, accounts x2, transaction,
  snapshot_date)
- Seed of 7 standard categories (5 simple + 2 priced) marked is_seed=1
- CHECK(currency = 'CAD') on balance_accounts (MVP — v2 lifts the constraint
  with a multi-currency rate table)
- CHECK kind invariants on balance_snapshot_lines (quantity/unit_price both
  NULL OR both NOT NULL)
- FK transaction_id ON DELETE RESTRICT to preserve reproducibility of
  Modified Dietz returns calculated on past periods

Migration v9 is added inline to the lib.rs Vec<Migration> via a new
constant database::BALANCE_SCHEMA backed by balance_schema.sql. The
schema is mirrored in consolidated_schema.sql so brand-new profiles
get the feature preinstalled without replaying v9.

13 new co-located rusqlite tests validate the migration on a fresh
in-memory DB: schema applies cleanly, 7 categories seeded with correct
kinds, CHECK rejects invalid currency/kind/direction, UNIQUE rejects
duplicate snapshot_date / (snapshot_id,account_id) / (transaction_id,
account_id), FK CASCADE on snapshot delete, FK RESTRICT on transaction
delete and on category with linked accounts, seed idempotent on replay.

Refs #138

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 14:31:50 -04:00
1f506fb171 Merge pull request 'fix(reports): render category combobox in hierarchical DFS order (#126)' (#134) from issue-126-category-combobox-presentation into main 2026-04-22 01:09:35 +00:00
le king fu
871768593d fix(reports): render category combobox in hierarchical DFS order (#126)
All checks were successful
PR Check / rust (push) Successful in 22m7s
PR Check / frontend (push) Successful in 2m19s
PR Check / rust (pull_request) Successful in 21m37s
PR Check / frontend (pull_request) Successful in 2m14s
The by-category report combobox (`/reports/category`) was showing its full
category list with scrambled indentation — parents from one sub-tree
interleaved with children from another. Root cause: `getAllCategoriesWithCounts`
returns rows via `ORDER BY sort_order, name`, which is a *global* sort; two
different roots with sort_order=1 would be followed by their respective
children in the same bucket, mixing depths together.

Add a pure `sortHierarchical(categories, resolveName)` helper in
`CategoryCombobox.tsx` that rebuilds the display order as a DFS walk of the
tree: each parent is emitted immediately followed by its descendants, with
siblings within a group sorted by `sort_order` then localized display name.
Orphans (parent filtered out or missing) are appended at the end so nothing
disappears. The helper runs client-side inside the combobox's `useMemo`, so
the fix is scoped to this component and doesn't affect other consumers of
`getAllCategoriesWithCounts`. Filtering on the input query remains unchanged.

Covered by 7 unit tests on the helper (empty list, single root, the exact
bug-reproducing scrambled case, sort_order + name tiebreak, 3-level
hierarchy, orphans, idempotence).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-21 20:58:53 -04:00
le king fu
1c9eebb78c chore: release v0.8.4
All checks were successful
Release / build-and-release (push) Successful in 24m1s
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-21 19:32:44 -04:00
95f708c8fd Merge pull request 'test(categories): complete test coverage for migration flow (#123)' (#133) from issue-123-complete-tests into main 2026-04-21 23:28:08 +00:00
le king fu
12d1877870 test(categories): complete test coverage for migration flow (#123)
All checks were successful
PR Check / rust (push) Successful in 22m48s
PR Check / frontend (push) Successful in 2m20s
PR Check / rust (pull_request) Successful in 22m51s
PR Check / frontend (pull_request) Successful in 2m21s
Adds unit + integration + regression tests and a QA checklist for the
v2→v1 seed migration feature.

- Fixtures: src/__fixtures__/profiles.ts (makeV2Profile, makeV1Profile,
  makeV2ProfileWithCustom) with realistic categories, keywords,
  suppliers, transactions, budgets.
- Unit: categoryMappingService (100 cases covering every DEFAULT_MAPPINGS
  entry, 4-pass priority, splits, preserved/custom detection),
  categoryBackupService (23 cases — Tauri mocks: success, write error,
  integrity check, PIN-encrypted profile), categoryMigrationService (16
  cases — BEGIN/COMMIT/ROLLBACK flow, backup-missing abort, journaling,
  custom parent creation).
- Integration: full plan→backup→migrate→verify flow; rollback via SREF
  import; backup failure → no DB write; migration SQL failure → ROLLBACK
  + intact state.
- Regression: parameterised v2/v1 fixtures covering auto-categorisation,
  budget aggregation, splits preservation.
- Docs: docs/qa-refonte-seed-categories-ipc.md — manual checklist for UX,
  system errors, encrypted profile, custom preservation, 90-day banner,
  restore flow.

331 vitest tests pass (up from 193 baseline).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-21 19:25:13 -04:00
4c7f3d09e1 Merge pull request 'feat(categories): restore backup banner and permanent restore action (#122)' (#132) from issue-122-restore-backup-banner into main 2026-04-21 01:52:08 +00:00
le king fu
0132e6e164 feat(categories): add restore backup banner and permanent restore action (#122)
All checks were successful
PR Check / rust (push) Successful in 21m45s
PR Check / frontend (push) Successful in 2m17s
PR Check / rust (pull_request) Successful in 21m1s
PR Check / frontend (pull_request) Successful in 2m13s
Surfaces the pre-migration SREF backup to the user so they can roll back a
category migration without digging into the filesystem:

- 90-day dismissable banner at the top of Settings > Categories pointing to
  the automatic backup (hidden once reverted, once dismissed, or past 90d).
- Permanent "Restore a backup" entry in Settings > Categories, available as
  long as a migration journal exists (even past the 90-day window).
- Confirmation modal with two-step consent, red Restore button, fallback
  file picker when the recorded path is missing, PIN prompt for encrypted
  SREF files, full-page reload on success.

Internals:
- New `categoryRestoreService` wrapping `read_import_file` +
  `importTransactionsWithCategories` with stable error codes
  (file_missing, read_failed, parse_failed, wrong_envelope_type,
  needs_password, wrong_password, import_failed).
- New `file_exists` Tauri command for the pre-flight presence check.
- On success: `categories_schema_version=v2` + merge `reverted_at` into
  `last_categories_migration`.
- Pure `shouldShowBanner` / `isWithinBannerWindow` helpers with tests.
- FR/EN i18n keys under `settings.categoriesCard.restore*`.
- CHANGELOG entries in both locales.

Closes #122
2026-04-20 21:47:43 -04:00
b9734acd93 Merge pull request 'feat(categories): 3-step migration page + categoryMigrationService (#121)' (#131) from issue-121-categories-migration-page into main 2026-04-21 01:33:55 +00:00
le king fu
0646875327 feat(categories): add 3-step migration page + categoryMigrationService (#121)
All checks were successful
PR Check / rust (push) Successful in 21m39s
PR Check / frontend (push) Successful in 2m21s
PR Check / rust (pull_request) Successful in 21m49s
PR Check / frontend (pull_request) Successful in 2m15s
New user-facing 3-step migration flow at /settings/categories/migrate that
allows legacy v2 profiles to opt in to the v1 IPC taxonomy.

Step 1 Discover — read-only taxonomy tree (reuses CategoryTaxonomyTree from
Livraison 1, #117).
Step 2 Simulate — 3-column dry-run table with confidence badges (high /
medium / low / needs-review), transaction preview side panel, inline target
picker for unresolved rows. The "next" button is blocked until every row is
resolved.
Step 3 Consent — checklist + optional PIN field for PIN-protected profiles +
4-step loader (backup created / verified / SQL applied / committed).

Success and error screens surface the SREF backup path and the counts of
rows migrated. Errors never leave the profile in a partial state — the new
categoryMigrationService wraps the entire SQL writeover in a
BEGIN/COMMIT/ROLLBACK atomic transaction and aborts up-front if the backup
is not present / verified.

New code:
- src/services/categoryMigrationService.ts — applyMigration(plan, backup)
  atomic writer (INSERT v1 → UPDATE transactions/budgets/budget_templates/
  keywords/suppliers → reparent preserved customs → deactivate v2 seed →
  bump categories_schema_version=v1 → journal last_categories_migration).
- src/hooks/useCategoryMigration.ts — useReducer state machine
  (discover → simulate → consent → running → success | error).
- src/hooks/useCategoryMigration.test.ts — 13 pure reducer tests.
- src/components/categories-migration/{StepDiscover,StepSimulate,StepConsent,
  MappingRow,TransactionPreviewPanel}.tsx — UI per the mockup.
- src/pages/CategoriesMigrationPage.tsx — wrapper with internal router,
  stepper, backup/migrate orchestration, success/error screens.

Tweaks:
- src/App.tsx — new /settings/categories/migrate route.
- src/components/settings/CategoriesCard.tsx — additional card surfacing
  the migrate entry for v2 profiles only.
- src/i18n/locales/{fr,en}.json — categoriesSeed.migration.* namespace
  (page / stepper / 3 steps / running / success / error / backup error codes).
- CHANGELOG.{md,fr.md} — [Unreleased] / Added entry.

Scope limits respected: no SQL migration modified, no new migration added,
no unit tests of the applyMigration writer (covered by #123 in wave 4),
no restore-backup button (#122 in wave 4), no post-migration banner (#122).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 21:31:21 -04:00
084b621506 Merge pull request 'feat(categories): dashboard v1 discovery banner (#118)' (#130) from issue-118-dashboard-discovery-banner into main 2026-04-21 01:16:20 +00:00
le king fu
0ded5a1ac6 feat(categories): add dashboard v1 discovery banner (#118)
All checks were successful
PR Check / rust (push) Successful in 21m42s
PR Check / frontend (push) Successful in 2m14s
PR Check / rust (pull_request) Successful in 21m50s
PR Check / frontend (pull_request) Successful in 2m17s
Adds a non-destructive, dismissable banner on the Dashboard that invites
profiles still on the legacy v2 category seed to discover the new v1 IPC
taxonomy. The banner links to the standard categories guide page
(/settings/categories/standard, shipped in #117).

Visibility rules:
- Only rendered when `categories_schema_version='v2'` in `user_preferences`.
- Hidden once the user dismisses it — dismissal is persisted in the same
  `user_preferences` table under the key `categories_v1_banner_dismissed`
  (value '1'), so the banner never reappears across app restarts.
- New v1-seeded profiles never see it.

No DB schema change: reuses the existing key/value `user_preferences`
table via the existing `getPreference`/`setPreference` helpers. No
migration added.

i18n keys under `dashboard.categoriesBanner.*` (FR + EN).
Changelog entry added under [Unreleased] in both CHANGELOG.md and
CHANGELOG.fr.md.
2026-04-20 21:11:56 -04:00
1640a73499 Merge pull request 'feat(categories): categoryMappingService 4-pass algo (#119)' (#128) from issue-119-category-mapping-service into main 2026-04-21 01:07:16 +00:00
115f707823 Merge pull request 'feat(categories): categories standard guide page (#117)' (#129) from issue-117-categories-standard-guide into main 2026-04-21 01:07:05 +00:00
le king fu
defa63a063 feat(categories): add categories standard guide page (#117)
All checks were successful
PR Check / rust (push) Successful in 21m42s
PR Check / frontend (push) Successful in 2m17s
PR Check / rust (pull_request) Successful in 22m37s
PR Check / frontend (pull_request) Successful in 2m12s
Adds a read-only Settings subpage at /settings/categories/standard that
exposes the full v1 IPC category taxonomy:

- Recursive tree with per-root expand/collapse (chevron buttons), clickable
  only via the disclosure caret — no destructive actions anywhere on the
  page.
- Live counter banner: roots / subcategories / leaves / total, computed
  from the bundled categoryTaxonomyV1 JSON.
- Accent- and case-insensitive full-text search over translated names;
  matching nodes keep their ancestor chain visible, non-matching branches
  are pruned from the visible tree.
- Hover tooltips (native `title`) showing i18n_key, type (income /
  expense / transfer — translated) and numeric id of each node — useful
  for power-users cross-referencing the consolidated schema.
- Export as PDF button that triggers window.print(); a dedicated
  @media print rule in styles.css forces every branch to render fully
  expanded during printing regardless of the on-screen collapse state,
  and hides the toolbar / back-link.
- All labels resolve via t(node.i18n_key, { defaultValue: node.name })
  to be forward-compatible with future user-created taxonomy rows that
  have no i18n_key.

Also:
- New CategoriesCard in Settings that links to the page (FolderTree
  icon, consistent with the userGuide / changelog card pattern).
- i18n keys added under categoriesSeed.guidePage.* and
  settings.categoriesCard.* (FR + EN).
- CHANGELOG.md + CHANGELOG.fr.md updated under [Unreleased] / Added.

Route uses the English-style `/settings/categories/standard` to match
the rest of the app (/settings, /categories, /changelog, ...). The
original spec mentions a French-accented path but the existing router
is English-only; documenting here so reviewers can see the decision.

No SQL migration, no schema change, no write to the database — this
is strictly a read-only view on the TS-side taxonomy bundle.

Type-check clean (tsc --noEmit), 148/148 vitest tests pass, vite build
succeeds.
2026-04-20 21:02:38 -04:00
le king fu
be3cda1556 feat(categories): add categoryMappingService (4-pass algo) (#119)
All checks were successful
PR Check / rust (push) Successful in 22m33s
PR Check / frontend (push) Successful in 2m18s
PR Check / rust (pull_request) Successful in 21m36s
PR Check / frontend (pull_request) Successful in 2m13s
Pure function that computes a v2 → v1 category migration plan from a
snapshot of the profile data. The 4-pass algorithm (keyword → supplier
propagation → default fallback → needs review) produces a MigrationPlan
with confidence badges (high/medium/low/none) per row, exposes split
targets for categories that ventilate across multiple v1 leaves (e.g.
Transport en commun → Autobus + Train), and preserves user-custom
categories in a dedicated bucket for later placement under
"Catégories personnalisées (migration)".

- Mapping tables encoded from
  .spikes/archived/seed-standard/code/mapping-old-to-new.md
- No DB I/O: the caller hands us categories, keywords, transactions and
  optional suppliers; the service stays testable and side-effect-free.
- 20 unit tests cover every pass, custom preservation, split exposure,
  stats aggregation and pass priority.

Prepares the ground for #121 (migration writer UI).
2026-04-20 21:01:55 -04:00
b8fa089c5f Merge pull request 'feat(categories): categoryTaxonomyService + useCategoryTaxonomy (#116)' (#127) from issue-116-category-taxonomy-service into main 2026-04-21 00:54:58 +00:00
le king fu
742aa9ec3c feat(categories): add categoryTaxonomyService + useCategoryTaxonomy hook (#116)
All checks were successful
PR Check / rust (push) Successful in 22m44s
PR Check / frontend (push) Successful in 2m17s
PR Check / rust (pull_request) Successful in 22m4s
PR Check / frontend (pull_request) Successful in 2m15s
Source of truth for the v1 IPC taxonomy on the TS side. Loads the bundled
JSON, exposes typed helpers (findById, findByPath, getLeaves, getParentById)
used by the upcoming Guide (#117) and Migration (#121) pages.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 20:53:15 -04:00
le king fu
b37be36ddc chore(claude): add project rules and release skill, ignore local state
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 20:44:49 -04:00
le king fu
4912ae39b0 docs: add WIP specs for OAuth keychain, monetisation, reports, and web
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 20:41:00 -04:00
f3af3d7c1b Merge pull request 'feat(categories): v1 IPC seed + i18n keys + migration v8 (#115)' (#125) from issue-115-seed-v1-i18n into main 2026-04-19 20:51:16 +00:00
63feebefc8 Merge pull request 'feat(categories): categoryBackupService pre-migration SREF wrapper (#120)' (#124) from issue-120-category-backup-service into main 2026-04-19 20:48:51 +00:00
le king fu
bd992f2f94 feat(categories): add v1 IPC seed, i18n keys, and migration v8 (#115)
All checks were successful
PR Check / rust (push) Successful in 22m29s
PR Check / frontend (push) Successful in 2m18s
PR Check / rust (pull_request) Successful in 22m39s
PR Check / frontend (pull_request) Successful in 2m18s
Livraison 1 du milestone spec-refonte-seed-categories-ipc. Applies the
new v1 IPC (Indice des prix à la consommation) taxonomy to freshly
created profiles while leaving existing v2 profiles untouched until the
migration wizard (upcoming issue #121) prompts them to move.

- Migration v8 (additive only):
    - ALTER TABLE categories ADD COLUMN i18n_key TEXT
    - INSERT OR IGNORE user_preferences.categories_schema_version=v2
      (existing profiles tagged as v2 for later migration)
- consolidated_schema.sql rewritten with the full v1 seed and
  categories_schema_version='v1' default for brand-new profiles
- src/data/categoryTaxonomyV1.json bundled as the TS-side source of
  truth (consumed by #116 categoryTaxonomyService next)
- categoriesSeed.* i18n namespace (FR/EN) — 150 entries each
- CategoryTree and CategoryCombobox fall back to the raw `name` when
  i18n_key is null (user-created categories stay literal)
- CategoryTreeNode and CategoryRow gain the i18n_key field end-to-end

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 16:41:55 -04:00
le king fu
3c628d4cd1 feat(categories): add categoryBackupService for pre-migration SREF backup (#120)
All checks were successful
PR Check / rust (push) Successful in 22m20s
PR Check / frontend (push) Successful in 2m18s
PR Check / rust (pull_request) Successful in 22m1s
PR Check / frontend (pull_request) Successful in 2m16s
Wrapper around dataExportService that creates and verifies a full SREF
backup before the v2->v1 categories migration. Throws on any failure to
ensure migration aborts cleanly.

- Generates filename <ProfileName>_avant-migration-<ISO8601>.sref
- Writes to ~/Documents/Simpl-Resultat/backups/ (creates dir if missing)
- Verifies integrity via re-read + SHA-256 checksum
- Reuses profile PIN for encryption when protected
- Adds two minimal Tauri commands: ensure_backup_dir, get_file_size
- Stable error codes (BackupError) to map to i18n keys in the UI layer

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 15:46:23 -04:00
le king fu
0e2078088a docs: add spec decisions and plan for categories IPC seed refactor
Source of truth for milestone spec-refonte-seed-categories-ipc
(issues #115-#123).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 15:35:15 -04:00
le king fu
0af5dd95cc chore: release v0.8.3
All checks were successful
Release / build-and-release (push) Successful in 23m6s
2026-04-19 10:01:29 -04:00
f371ae3f7e Merge pull request 'feat(reports/cartes): Mensuel/YTD toggle on KPI cards + user guide section (#102)' (#114) from issue-102-cartes-ytd-toggle-docs into main 2026-04-19 13:55:35 +00:00
le king fu
3be05db41a 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
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>
2026-04-19 09:49:21 -04:00
d834ffeb9c Merge pull request 'fix(reports/cartes): Budget Adherence card filtered out all expense categories' (#113) from issue-112-budget-adherence-signed-fix into main 2026-04-19 13:39:11 +00:00
le king fu
2ec48d4e90 fix(reports/cartes): Budget Adherence card was filtering out all expense categories
All checks were successful
PR Check / rust (push) Successful in 21m31s
PR Check / frontend (push) Successful in 2m13s
PR Check / rust (pull_request) Successful in 22m0s
PR Check / frontend (pull_request) Successful in 2m14s
Expense budgets are stored signed-negative by budgetService. The Cartes
budget-adherence card used raw values in its filter (monthBudget > 0),
its in-target comparison (|actual| <= monthBudget), and its overrun
calculation — all of which silently rejected every expense row. Route
every amount through Math.abs() so the card reflects real budget data.

Test: regression fixture with a signed-negative monthBudget that must
pass the filter and count as in-target or overrun based on absolute
values.

Fixes #112
2026-04-19 09:35:26 -04:00
da6041dc45 Merge pull request 'Highlights: default reference month to previous + YTD current year, user-changeable (#106)' (#111) from issue-106-highlights-default-ref-month into main 2026-04-19 12:34:37 +00:00
le king fu
8b90cb6489 feat(reports/highlights): default reference month to previous month + YTD current year, user-changeable (#106)
All checks were successful
PR Check / rust (push) Successful in 21m14s
PR Check / frontend (push) Successful in 2m16s
PR Check / rust (pull_request) Successful in 21m31s
PR Check / frontend (pull_request) Successful in 2m14s
- Extract shared defaultReferencePeriod helper (src/utils/referencePeriod.ts)
- useHighlights now reads ?refY=YYYY&refM=MM, defaults to previous month
- getHighlights signature: (referenceYear, referenceMonth, ytdYear, windowDays, ...)
- YTD tile pinned to Jan 1 of current civil year, independent of reference month
- CompareReferenceMonthPicker surfaced on /reports/highlights
- Hub highlights panel inherits the same default via useHighlights
- useCartes and useCompare now delegate their default-period helpers to the shared util
2026-04-19 08:28:30 -04:00
3842a1102a Merge pull request 'feat(reports/trends): add stacked-area chart option for category view (#105)' (#110) from issue-105-trends-stacked-area into main 2026-04-19 12:22:03 +00:00
le king fu
94104c4223 refactor(reports/trends): rename chart type from 'line' to 'bar' to match actual rendering
All checks were successful
PR Check / rust (push) Successful in 21m44s
PR Check / frontend (push) Successful in 2m18s
PR Check / rust (pull_request) Successful in 21m27s
PR Check / frontend (pull_request) Successful in 2m11s
The legacy chart was a stacked BarChart, not a LineChart — the initial 'line'
naming was misleading. Rename internal type, i18n key (chartLine -> chartBar,
Lignes -> Barres, Lines -> Bars) and icon. Legacy 'line' in localStorage is
migrated to 'bar' on read.
2026-04-19 07:26:22 -04:00
le king fu
02efc75542 feat(reports/trends): add stacked-area chart option for category view (#105)
Some checks failed
PR Check / rust (push) Has been cancelled
PR Check / frontend (push) Has been cancelled
PR Check / rust (pull_request) Successful in 21m22s
PR Check / frontend (pull_request) Successful in 2m15s
Adds a segmented chart-type toggle to the /reports/trends "By category"
sub-view that switches between the existing stacked bars (default,
unchanged) and a new Recharts AreaChart with stackId="1" showing total
composition over time. Both modes share the same category palette and
SVG grayscale patterns so the visual signature is preserved.

- CategoryOverTimeChart gains a chartType: 'line' | 'area' prop
  (default 'line' for backward compatibility with the dashboard usage).
- New TrendsChartTypeToggle component, persisted in localStorage under
  "reports-trends-category-charttype".
- Toggle only renders in the "By category" sub-view with chart view
  mode selected; hidden otherwise.
- i18n keys reports.trends.chartLine / chartArea / chartTypeAria in
  FR and EN.
- CHANGELOG entries in both languages.
2026-04-19 07:23:49 -04:00
e95612a55d Merge pull request 'feat(reports/compare): 8-column table with monthly + cumulative YTD blocks (#104)' (#109) from issue-104-compare-eight-col-table into main 2026-04-19 11:19:21 +00:00
le king fu
bd8a5732c6 feat(reports/compare): 8-column table with monthly + cumulative YTD blocks (#104)
All checks were successful
PR Check / rust (push) Successful in 21m21s
PR Check / frontend (push) Successful in 2m11s
PR Check / rust (pull_request) Successful in 21m30s
PR Check / frontend (pull_request) Successful in 2m8s
Mirror the BudgetVsActualTable structure in the Actual-vs-Actual compare
mode so MoM and YoY both surface a Monthly block (reference month vs
comparison month) and a Cumulative YTD block (progress through the
reference month vs progress through the previous window).

- CategoryDelta gains cumulative{Previous,Current}Amount and
  cumulativeDelta{Abs,Pct}. Legacy previousAmount / currentAmount /
  deltaAbs / deltaPct are kept as aliases of the monthly block so the
  Highlights hub, Cartes dashboard and ComparePeriodChart keep working
  unchanged.
- getCompareMonthOverMonth: cumulative-previous window ends at the end
  of the previous month within the SAME year; when the reference month
  is January, the previous window sits entirely in the prior calendar
  year (Jan → Dec).
- getCompareYearOverYear: now takes an optional reference month
  (defaults to December for backward compatibility). Monthly block
  compares the single reference month across years; cumulative block
  compares Jan → refMonth across years.
- ComparePeriodTable rebuilt with two colspan header groups, four
  sub-columns each, a totals row and month/year boundary sub-labels.
- ComparePeriodChart unchanged: still reads the monthly primary fields.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 21:17:32 -04:00
8d916a1283 Merge pull request 'feat(reports/category): searchable combobox for category zoom (#103)' (#108) from issue-103-category-combobox into main 2026-04-19 01:10:25 +00:00
le king fu
01869462f4 feat(reports/category): replace select with searchable combobox (#103)
All checks were successful
PR Check / rust (push) Successful in 21m2s
PR Check / frontend (push) Successful in 2m13s
PR Check / rust (pull_request) Successful in 21m14s
PR Check / frontend (pull_request) Successful in 2m9s
Swap the native <select> in CategoryZoomHeader for the reusable
CategoryCombobox. Enhances the combobox with ARIA compliance
(combobox, listbox, option roles + aria-expanded, aria-controls,
aria-activedescendant) and hierarchy indentation based on parent_id
depth. Adds reports.category.searchPlaceholder in FR/EN.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 21:07:47 -04:00
49a4ef2171 Merge pull request 'fix(reports/cartes): remove broken period selector + add savings-rate tooltip' (#107) from issue-101-cartes-period-savings-tooltip into main 2026-04-19 01:04:38 +00:00
le king fu
b258e2b80a fix(reports/cartes): remove broken period selector + add savings-rate tooltip (#101)
All checks were successful
PR Check / rust (push) Successful in 21m51s
PR Check / frontend (push) Successful in 2m29s
PR Check / rust (pull_request) Successful in 21m3s
PR Check / frontend (pull_request) Successful in 2m11s
- Remove the non-functional PeriodSelector from /reports/cartes — the Cartes
  report is by design a "month X vs X-1 vs X-12" snapshot, so the
  reference-month picker is the only control needed.
- Simplify useCartes to drop its useReportsPeriod dependency; the hook now
  only exposes the reference year/month and its setter.
- Add a (?) help bubble (lucide HelpCircle) next to the savings-rate KPI
  title, wired up via a new `tooltip?: string` prop on KpiCard.
- Propagate `number | null` through CartesKpi.current and buildKpi so
  savings rate is now null (rendered as "—") when reference-month income
  is 0 instead of a misleading "0 %". Use refExpenses directly for
  seasonality.referenceAmount since it is always numeric.
- Update the cartes snapshot tests to expect null for the zero-income case.
- Add FR/EN strings reports.cartes.savingsRateTooltip + CHANGELOG entries
  in both locales.
2026-04-18 20:50:18 -04:00
le king fu
4416457c22 chore: release v0.8.2
All checks were successful
Release / build-and-release (push) Successful in 24m6s
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 10:41:48 -04:00
4f4ab87bea feat: feedback hub widget in Settings Logs card (#67)
Closes #67

Add opt-in Feedback Hub widget integrated into the Settings Logs card. Routes through a Rust command to bypass CORS and centralize privacy audit. First submission triggers a one-time consent dialog; three opt-in checkboxes (context, logs, identify with Maximus account) all unchecked by default. Wording and payload follow the cross-app conventions in la-compagnie-maximus/docs/feedback-hub-ops.md.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 14:36:26 +00:00
le king fu
3b2587d843 chore: bump version to 0.8.1
All checks were successful
Release / build-and-release (push) Successful in 24m56s
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 22:07:53 -04:00
89b69f325e feat: new Cartes dashboard report — KPI cards, sparklines, top movers (#97)
Closes #97
2026-04-15 23:53:37 +00:00
le king fu
4c58b8bab8 feat(reports/cartes): new KPI dashboard sub-report with sparklines, top movers, budget adherence and seasonality (#97)
All checks were successful
PR Check / rust (push) Successful in 23m21s
PR Check / frontend (push) Successful in 2m24s
PR Check / rust (pull_request) Successful in 23m12s
PR Check / frontend (pull_request) Successful in 2m20s
New /reports/cartes page surfaces a dashboard-style snapshot of the
reference month:

- 4 KPI cards (income / expenses / net / savings rate) showing MoM and
  YoY deltas simultaneously, each with a 13-month sparkline highlighting
  the reference month
- 12-month income vs expenses overlay chart (bars + net balance line)
- Top 5 category increases + top 5 decreases MoM, clickable through to
  the category zoom report
- Budget adherence card: on-target count + 3 worst overruns with
  progress bars
- Seasonality card: reference month vs same calendar month averaged
  over the two previous years, with deviation indicator

All data is fetched in a single getCartesSnapshot() service call that
runs four queries concurrently (25-month flow, MoM category deltas,
budget-vs-actual, seasonality). Missing months are filled with zeroes
in the sparklines but preserved as null in the MoM/YoY deltas so the UI
can distinguish "no data" from "zero spend".

- Exported pure helpers: shiftMonth, defaultCartesReferencePeriod
- 13 vitest cases covering zero data, MoM/YoY computation, January
  wrap-around, missing-month handling, division by zero for the
  savings rate, seasonality with and without history, top mover sign
  splitting and 5-cap

Note: src/components/reports/CompareReferenceMonthPicker.tsx is a
temporary duplicate — the canonical copy lives on the issue-96 branch
(refactor: compare report). Once both branches merge the content is
identical and git will dedupe. Keeping the local copy here means the
Cartes branch builds cleanly on main without depending on #96.

Closes #97

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 19:44:58 -04:00
5fd2108d07 refactor: compare report — Actual-vs-actual mode with reference month picker (#96)
Closes #96
2026-04-15 23:10:23 +00:00