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>
Collapse the three Compare tabs (MoM / YoY / Budget) into two modes. The
new "Actual vs actual" mode exposes an explicit reference-month dropdown
in the header (defaults to the previous month, wraps around January) and
a MoM/YoY sub-toggle. The chart is rewritten to a grouped side-by-side
BarChart with two bars per category (reference period vs comparison
period) so both values are visible at a glance instead of just the
delta. The URL PeriodSelector stays in sync with the reference month.
- useCompare: state splits into { mode: "actual"|"budget", subMode:
"mom"|"yoy", year, month }. Pure helpers previousMonth(),
defaultReferencePeriod(), comparisonMeta() extracted for tests
- CompareModeTabs: 2 modes instead of 3
- New CompareSubModeToggle and CompareReferenceMonthPicker components
- ComparePeriodChart: grouped bars via two <Bar dataKey="..."/> on a
vertical BarChart
- i18n: modeActual / subModeMoM / subModeYoY / referenceMonth (FR+EN),
retire modeMoM / modeYoY
- 9 new vitest cases covering the pure helpers (January wrap-around for
both MoM and YoY, default reference period, month/year arithmetic)
Closes#96
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Delete legacy src/hooks/useReports.ts (the monolithic hook is now fully
replaced by the per-domain hooks from #70)
- Delete src/components/reports/ReportFilterPanel.tsx (last caller was the
pre-refactor ReportsPage; no longer referenced anywhere)
- Update docs/architecture.md: reports hook list now lists the 5 per-domain
hooks, reports service entry lists every new endpoint, routing section
lists the 4 sub-routes, categorizationService entry mentions the new
keyword-editing helpers, components folder count + page count updated
- Update docs/guide-utilisateur.md section 9: rewrite around hub + 4
sub-reports, explain bookmarkable period via query string, walk through
the right-click keyword editing flow, remove stale pivot section
- Rewrite in-app docs.reports.* i18n in both FR and EN to match the new
UX (hub, sub-reports, contextual keywords)
- New ADR docs/adr/0007-reports-hub-refactor.md: context, decision (hub +
four routes, per-domain hooks, URL period, security guarantees on the
keyword dialog, bounded recursive CTE for category zoom), consequences,
alternatives considered
- CHANGELOG.md + CHANGELOG.fr.md: Unreleased entries describing the hub,
each sub-report, contextual keyword editing, bookmarkable period, view
mode persistence, useReports split, pivot removal, and the security
posture of AddKeywordDialog / getCategoryZoom
Fixes#76
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Service layer
- New reportService.getCategoryZoom(categoryId, from, to, includeChildren) —
bounded recursive CTE (WHERE ct.depth < 5) protects against parent_id cycles;
direct-only path skips the CTE; every binding is parameterised
- Export categorizationService helpers normalizeDescription / buildKeywordRegex /
compileKeywords so the dialog can reuse them
- New validateKeyword() enforces 2–64 char length (anti-ReDoS), whitespace-only
rejection, returns discriminated result
- New previewKeywordMatches(keyword, limit=50) uses parameterised LIKE + regex
filter in memory; caps candidate scan at 1000 rows to protect against
catastrophic backtracking
- New applyKeywordWithReassignment wraps INSERT (or UPDATE-reassign) +
per-transaction UPDATEs in an explicit BEGIN/COMMIT/ROLLBACK; rejects
existing keyword reassignment unless allowReplaceExisting is set; never
recategorises historical transactions beyond the ids the caller supplied
Hook
- Flesh out useCategoryZoom with reducer + fetch + refetch hook
Components (flat under src/components/reports/)
- CategoryZoomHeader — category combobox + include/direct toggle
- CategoryDonutChart — template'd from dashboard/CategoryPieChart with
innerRadius=55 and ChartPatternDefs for SVG patterns
- CategoryEvolutionChart — AreaChart with Intl-formatted axes
- CategoryTransactionsTable — sortable table with per-row onContextMenu
→ ContextMenu → "Add as keyword" action
AddKeywordDialog — src/components/categories/AddKeywordDialog.tsx
- Lives in categories/ (not reports/) because it is a keyword-editing widget
consumed from multiple sections
- Renders transaction descriptions as React children only (no
dangerouslySetInnerHTML); CSS truncation (CWE-79 safe)
- Per-row checkboxes for applying recategorisation; cap visible rows at 50;
explicit opt-in checkbox to extend to N-50 non-displayed matches
- Surfaces apply errors + "keyword already exists" replace prompt
- Re-runs category zoom fetch on success so the zoomed view updates
Page
- ReportsCategoryPage composes header + donut + evolution + transactions
+ AddKeywordDialog, fetches from useCategoryZoom, preserves query string
for back navigation
i18n
- New keys reports.category.* and reports.keyword.* in FR + EN
- Plural forms use i18next v25 _one / _other suffixes (nMatches)
Tests
- 3 reportService tests cover bounded CTE, cycle-guard depth check, direct-only fallthrough
- New categorizationService.test.ts: 13 tests covering validation boundaries,
parameterised LIKE preview, regex word-boundary filter, explicit BEGIN/COMMIT
wrapping, rollback on failure, existing keyword reassignment policy
- 62 total tests passing
Fixes#74
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Services: getCompareMonthOverMonth(year, month) and getCompareYearOverYear(year)
return CategoryDelta[] (expense-side, ABS aggregates, parameterised SQL only)
- Shared CategoryDelta type with HighlightMover now aliased to it
- Flesh out useCompare hook: reducer + fetch + automatic year/month inference
from the shared useReportsPeriod `to` date; budget mode skips fetch and
delegates to CompareBudgetView which wraps the existing BudgetVsActualTable
- Components: CompareModeTabs (MoM/YoY/Budget tabs), ComparePeriodTable (sortable
table with signed delta coloring), ComparePeriodChart (diverging horizontal
bar chart with ChartPatternDefs for SVG patterns), CompareBudgetView
(fetches budget rows for the current target year/month)
- ReportsComparePage wires everything with PeriodSelector + ViewModeToggle
(storage key reports-viewmode-compare); chart/table toggle is hidden in budget
mode since the budget table has its own presentation
- i18n keys: reports.compare.modeMoM / modeYoY / modeBudget in FR + EN
- 4 new vitest cases for the compare services: parameterised boundaries,
January wrap-around to December previous year, delta conversion with
previous=0 fallback to null pct, year-over-year spans
Fixes#73
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Flesh out ReportsTrendsPage with internal subview toggle
(global / byCategory) and ViewModeToggle (storage key reports-viewmode-trends)
- Reuse existing MonthlyTrendsChart/Table and CategoryOverTimeChart/Table
without modification; wire them through useTrends + useReportsPeriod so the
URL period is respected
- Add reports.trends.subviewGlobal / subviewByCategory keys in FR + EN
Fixes#72
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Delete DynamicReport* components and pivot types (PivotConfig, PivotResult, PivotFieldId, etc.)
- Remove getDynamicReportData/getDynamicFilterValues from reportService
- Strip pivotConfig/pivotResult from useReports hook and ReportsPage
- Drop "dynamic" from ReportTab union
- Remove reports.pivot.* and reports.dynamic i18n keys in FR and EN
- Add skeletons for /reports/highlights, /trends, /compare, /category pages
- Register the 4 new sub-routes in App.tsx
- Add reports.hub, reports.viewMode, reports.empty, common.underConstruction keys
- New shared ContextMenu component with click-outside + Escape handling
- Refactor ChartContextMenu to compose generic ContextMenu
- New ViewModeToggle with localStorage persistence via storageKey
- New Sparkline (Recharts LineChart) for compact trends
- Unit tests for readViewMode helper
Fixes#69
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds a close button and session-scoped dismissal flag so the banner
can be acknowledged for the current run but reappears on the next
app launch if the fallback is still active — matches the #81
acceptance criterion.
- sessionStorage key survives page navigation within the run, is
cleared on app restart.
- Graceful on storage quota errors.
- New `common.close` i18n key (FR: "Fermer", EN: "Close") used as
the aria-label of the close button.
Adds a visible warning in the Settings page when `token_store` has
landed in the file fallback instead of the OS keychain. Without this,
a user on a keychain-less system would silently lose the security
benefit introduced in #78 and never know.
- New `get_token_store_mode` service wrapper in authService.ts.
- New `TokenStoreFallbackBanner` component: fetches the mode on mount,
renders nothing when mode is `keychain` or null, renders an
amber warning card when mode is `file`.
- Mounted in SettingsPage right after AccountCard so it sits next to
the account state the user can fix (log out + log back in once the
keychain is available).
- i18n keys under `account.tokenStore.fallback.*` in fr/en.
Refs #66
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Both code paths that touch the updater now consult `check_entitlement`
from the Rust entitlements module before calling `check()`:
- `useUpdater.ts` adds a `notEntitled` status; on Free, the check
short-circuits and the Settings page displays an upgrade hint instead
of fetching update metadata.
- `ErrorPage.tsx` (recovery screen) does the same so the error path
matches the main path; users on Free no longer see network errors when
the updater would have run.
The gate name (`auto-update`) is the same string consumed by
`commands/entitlements.rs::FEATURE_TIERS`, so changing which tier
unlocks updates is a one-line edit in that file.
Bilingual i18n keys for the new messages are added to both `fr.json`
and `en.json`. CHANGELOG entries in both languages.
Adds the user-facing layer on top of the Rust license commands shipped
in #46.
- `licenseService.ts` thin wrapper around the new Tauri commands
- `useLicense` hook follows the project's useReducer pattern (idle,
loading, ready, validating, error) and exposes `submitKey`,
`refresh`, and `checkEntitlement` for cross-component use
- `LicenseCard` shows the current edition, the expiry date when set,
accepts a license key with inline validation feedback, and links to
the purchase page via `openUrl` from `@tauri-apps/plugin-opener`
- Card is inserted at the top of `SettingsPage` so the edition is the
first thing users see when looking for license-related actions
- i18n: new `license.*` keys in both `fr.json` and `en.json`
- Bilingual CHANGELOG entries
The Category Over Time report previously only showed expenses (t.amount < 0).
This removes that filter so all transaction types are shown by default,
and adds a type filter (expense/income/transfer) in the right filter panel.
Ref: simpl-resultat#41
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add sticky left-0 positioning to all category cells in BudgetVsActualTable
- Replace MonthNavigator arrows with inline title + dropdown month selector
- Default budget month to previous completed month instead of current
- Add i18n keys for new title prefix (FR/EN)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add a read-only column showing the annual budget total from the
previous year for each category. This serves as a baseline reference
when planning the current year's budget.
- Add previousYearTotal field to BudgetYearRow type
- Fetch previous year budget entries in useBudget hook
- Display column between Category and Annual in BudgetTable
- Propagate totals through parent/subtotal/section/grand total rows
- Add i18n keys for FR ("Année préc.") and EN ("Prev. Year")
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- CLAUDE.md: bump version to 0.6.3
- architecture.md: update version, component counts, dashboard description
- guide-utilisateur.md: rewrite dashboard section (budget table, chart, no recent transactions)
- i18n docs: update dashboard help text in both FR and EN
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Default period changed from month to year-to-date
- Remove recent transactions section
- Add expenses over time stacked bar chart (by category/month)
- Add budget vs actual table (current month)
- Reorganize layout: cards, pie + budget table, full-width chart
Closes#15
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add per-section subtotals (expenses, income, transfers) to budget table
and budget vs actual report. Fix category detail panel visibility when
scrolling through long category lists.
Closes#11, closes#12
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Create CHANGELOG.fr.md with full French translation of all versions
- Add ChangelogPage (/changelog) accessible from Settings
- Replace hardcoded i18n release notes with file-based approach:
loads CHANGELOG.md or CHANGELOG.fr.md based on user language
- CI copies changelogs to public/ during release build
- Update CLAUDE.md with bilingual changelog deployment process (fixes#10)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Change chart amount labels from white to black with white stroke outline
for better contrast on all bar colors (fixes#8)
- Improve budget table editable cells: add hover background, pointer cursor,
tooltip hint, and larger click target for clearer affordance (fixes#9)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add table/chart toggle for Trends, By Category, and Over Time reports
- Add "Show amounts" toggle to display values on chart elements
- Add filter panel with category checkboxes and source dropdown
- Add source filter at SQL level for all chart report queries
- Add sticky headers on Dynamic Report and Budget vs Actual tables
- Add interactive hover: dimmed non-hovered bars, filtered tooltip, legend hover
- Fix comment icon color to match split indicator (orange) (#7)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- #1: Sortable columns (date, description, amount) in the transaction
detail modal with click-to-toggle direction
- #2: Budget table headers stay fixed when scrolling vertically
- #3: Eye/EyeOff toggle to show/hide the amounts column in the
transaction detail modal
Closes#1, closes#2, closes#3
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Fix updater: strip v-prefix from version in latest.json, and delete
old package before re-uploading to avoid 409 conflicts.
Add frontend log capture (console intercept) with a log viewer card
in the settings page (filterable, copyable, clearable).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Prevent infinite spinner when DB connection fails at startup by adding
a 10s timeout on connectActiveProfile(). Add ErrorBoundary to catch
React crashes and ErrorPage with refresh, update check, and contact links.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Support up to 3 levels of categories (e.g., Dépenses récurrentes →
Assurances → Assurance-auto) while keeping SQL JOINs bounded and
existing 2-level branches fully compatible.
Changes across 14 files:
- Types: add "level3" pivot field, depth property on budget row types
- Reports: grandparent JOIN for 3-level resolution in dynamic reports
- Categories: depth validation (max 3), auto is_inputable management,
recursive tree operations, 3-level drag-drop with subtree validation
- Budget: 3-level grouping with intermediate subtotals, leaf-only
aggregation, depth-based indentation (pl-8/pl-14)
- Seed data: Assurances split into Assurance-auto/habitation/vie
- i18n: level3 translations for FR and EN
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Use i18n translations for release notes when available, falling back to
the GitHub release body. Adds FR/EN notes for versions 0.3.8–0.3.11.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Fields can now be assigned to multiple zones simultaneously (e.g. rows + filters).
Right-clicking a filter value excludes it (NOT IN), shown with strikethrough in red.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Overlay covers the full viewport while keeping the config panel
accessible. Press Escape or click the button to exit.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Implement a pivot table feature allowing users to compose custom reports
by assigning dimensions (Year, Month, Type, Level 1/2) to rows, columns,
and filters, with periodic and YTD measures as values. Includes a side
panel for configuration, a dynamic table with subtotals, and a stacked
bar chart visualization.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Extract changelog sections in CI to populate GitHub release bodies dynamically.
Expose the update body from tauri-plugin-updater and render it in the settings
page when an update is available. Add CHANGELOG discipline rule to CLAUDE.md.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add a toggle button to BudgetVsActualTable and BudgetTable that lets
users choose whether parent subtotal rows appear before or after their
children. The preference is persisted in localStorage and shared across
both tables.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add changelog entries for versions 0.2.5 through 0.3.7. Update FR/EN
user guide with profiles, transaction splits, drag-and-drop categories,
dark mode, SVG chart patterns, and context menus. Add Profiles section
to in-app docs page.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add a "Répartitions" section below manual adjustments listing all
split transactions. Clicking a split opens the existing modal to
view, edit, or delete it.
Fix CSV auto-detect failing on files with preamble lines (e.g.
Mastercard CSVs with metadata header). Three fixes:
- Delimiter detection uses mode of column counts instead of first-line
- Detect and skip preamble rows before header/data detection
- Exclude date-like columns from amount candidates and prefer columns
with decimal values when picking the amount column
Bumps version to 0.3.4.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Allow users to split a transaction across multiple categories directly
from the transactions table. Split children are hidden from the list
and automatically included in dashboard, report, and budget aggregates.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Auto-fix duplicate sort_order values on load, auto-assign sort_order on
category creation, and add drag-and-drop via @dnd-kit to reorder and
reparent categories in the tree (with 2-level nesting constraint).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Each profile gets its own SQLite database file for complete data isolation.
Profile selection screen at launch, sidebar switcher for quick switching,
and optional 4-6 digit PIN for privacy. Existing database becomes the
default profile with seamless upgrade.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add DocsPage with full user guide content, TOC sidebar with scroll spy,
and a print button that opens the OS print dialog for PDF export.
Print styles hide sidebars and remove layout constraints for clean output.
Link to user guide added on Settings page.
Bump version to 0.2.12.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
New tabular report showing actual vs budgeted amounts per category,
with dollar and percentage variations for both the selected month
and year-to-date. Includes parent/child hierarchy, type grouping,
variation coloring, and month navigation.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Parent categories now display as bold read-only subtotal rows with their
children indented below. Expenses show as negative (red), income as
positive (green). Inputable parents get a "(direct)" child row for their
own budget entries.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add period buttons (This month, 3 months, 6 months, This year, All)
above the transaction filters. "This year" is selected by default so
the page no longer shows all transactions since the beginning of time.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The template dropdown now stays on the selected value after applying,
and a new update button lets users save config changes back to the
selected template.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add export (JSON/CSV) and import (full replace) to the Settings page.
Export supports 3 modes (transactions+categories, transactions only,
categories only) with optional password encryption using Argon2id key
derivation. Import detects encrypted .sref files, prompts for password,
and shows a destructive confirmation modal before replacing data.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add reusable import config templates so users can save and apply CSV
parsing configurations across different import sources. Includes
database table, service, hook integration, and template UI in the
source config panel.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Budget page: replace single-month view with 12-month annual grid
with inline editing, split-evenly button, and year navigation
- Import: pre-select only new files and sort them first, show
"already imported" badge on previously imported files
- Add confirmation modals for category re-initialization and
import deletion (single and bulk), replacing native confirm()
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add SVG fill patterns to differentiate chart categories beyond color
- Add right-click context menu on charts to hide categories or view transactions
- Add transaction detail modal showing all transactions for a category
- Change import preview from wizard step to popup modal
- Add direct "Check Duplicates" button skipping preview step
- Bump version to 0.2.3
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds a toggle button to view all keywords across categories in a
searchable table with accent-insensitive filtering. Clicking a category
name navigates back to the normal view with that category selected.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>