Compare commits

...

15 commits
v0.6.5 ... main

Author SHA1 Message Date
99fdf4f9ea Merge pull request 'refactor: inline buildPrevYearTotalMap and simplify tests (#39)' (#40) from fix/simpl-resultat-39-simplify-budget into main
All checks were successful
Release / build-and-release (push) Successful in 28m8s
2026-03-11 21:34:35 +00:00
le king fu
003f456203 chore: bump version to 0.6.6
Some checks failed
Release / build-and-release (push) Has been cancelled
Includes fixes #34, #37, #39: budget prev year actuals, changelog sync via Vite, inline buildPrevYearTotalMap.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 17:29:04 -04:00
61a5b2e40d Merge pull request 'fix: sync changelog to public/ and automate on build/dev (#37)' (#38) from fix/simpl-resultat-37-changelog into main 2026-03-11 21:26:06 +00:00
c7caab0ef4 fix: address reviewer feedback (#37)
- Restore Unreleased entries in root CHANGELOG files (were incorrectly removed)
- Add public/CHANGELOG*.md to .gitignore (auto-generated by syncChangelogs)
- Remove public/CHANGELOG*.md from git tracking
- Fix indentation in vite.config.ts return block

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 14:04:34 -04:00
03c5f2538f refactor: inline buildPrevYearTotalMap and remove disproportionate tests (#39)
The 3-line helper was exported solely for testing. Inlining it removes the
export-for-test pattern and eliminates 50 lines of tests that were
disproportionate for a trivial filter-and-set loop.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 13:02:47 -04:00
5493e0c4e2 fix: address reviewer feedback (#37)
- Revert public/ changelog changes (syncChangelogs copies from root)
- Replace manual __dirname with import.meta.dirname

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 13:01:51 -04:00
ecbcf44f86 Merge pull request 'fix: show actual transactions in budget previous year column (#34)' (#35) from fix/simpl-resultat-34-budget-previous-year-actual into main 2026-03-11 16:16:19 +00:00
9afe23180f fix: remove redundant prebuild script (#37)
syncChangelogs() in vite.config.ts already handles copying changelogs
to public/ on dev/build start. The prebuild npm script was redundant
and introduced a platform dependency (cp command).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 12:03:48 -04:00
21bf1173ea fix: remove double sign negation for previous year actuals (#34)
Transaction amounts are already signed in the DB (expenses negative,
income positive). Remove Math.abs() normalization and stop multiplying
by sign at display time to avoid double negation.

Extract buildPrevYearTotalMap as a testable exported function and
rewrite tests to import the real function instead of reimplementing it.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 12:02:58 -04:00
a764ae0d38 fix: address reviewer feedback (#34)
Fix sign bug in previous year actuals column: transaction amounts are
stored with sign in the DB (expenses negative) but budget entries are
always positive. Apply Math.abs() when building the previousYearTotal
map so the display-time sign multiplier works correctly.

Add unit tests for the normalization logic verifying that both expense
(negative in DB) and income (positive in DB) amounts are correctly
handled.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 08:05:34 -04:00
fd88ba41ba Merge branch 'main' into fix/simpl-resultat-34-budget-previous-year-actual 2026-03-11 08:05:24 -04:00
4b6b4d96ef fix: sync public/ changelogs and automate copy on build/dev (#37)
The in-app changelog page reads from public/CHANGELOG*.md which were
stale (last synced at 0.6.3). Add automatic sync via Vite config
(runs on dev/build start) and npm prebuild script so public/ copies
are always up to date without manual intervention.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 08:03:08 -04:00
501051f9ed Merge pull request 'test: add unit tests for dateRange.ts (#33)' (#36) from fix/simpl-resultat-33-daterange-tests into main 2026-03-11 11:12:25 +00:00
2a18d9be2d test: add unit tests for dateRange.ts utility
Add vitest and 17 tests covering computeDateRange (all periods
including January rollover) and buildMonthOptions (length, order,
rollover, label formatting). Add test/test:watch npm scripts.

Ref #33

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 23:05:32 -04:00
4e70eee0a8 feat: show actual transactions in budget previous year column
Replace planned budget data with actual transaction totals for the
previous year column in the budget table. Add getActualTotalsForYear
helper to budgetService.

Ref #34

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 23:03:26 -04:00
16 changed files with 558 additions and 507 deletions

4
.gitignore vendored
View file

@ -45,5 +45,9 @@ imports/*.csv
.DS_Store .DS_Store
Thumbs.db Thumbs.db
# Auto-generated changelogs (synced from root by vite.config.ts)
public/CHANGELOG.md
public/CHANGELOG.fr.md
# Tauri generated # Tauri generated
src-tauri/gen/ src-tauri/gen/

View file

@ -2,6 +2,15 @@
## [Non publié] ## [Non publié]
## [0.6.6]
### Modifié
- Tableau de budget : la colonne année précédente affiche maintenant le réel (transactions) au lieu du budget planifié (#34)
- Refactorisation de `buildPrevYearTotalMap` en inline et simplification des tests (#39)
### Corrigé
- Fichiers changelog synchronisés automatiquement via plugin Vite, copies obsolètes dans public/ supprimées (#37)
## [0.6.5] ## [0.6.5]
### Ajouté ### Ajouté

View file

@ -2,6 +2,15 @@
## [Unreleased] ## [Unreleased]
## [0.6.6]
### Changed
- Budget table: previous year column now shows actual transactions instead of planned budget (#34)
- Refactored `buildPrevYearTotalMap` inline and simplified tests (#39)
### Fixed
- Changelog files synced automatically via Vite plugin, removed stale public/ copies (#37)
## [0.6.5] ## [0.6.5]
### Added ### Added

View file

@ -141,7 +141,7 @@ La documentation technique est centralisée dans `docs/` :
- `CHANGELOG.fr.md` (français) — traduction - `CHANGELOG.fr.md` (français) — traduction
- Catégories : Added/Ajouté, Changed/Modifié, Fixed/Corrigé, Removed/Supprimé - Catégories : Added/Ajouté, Changed/Modifié, Fixed/Corrigé, Removed/Supprimé
- Format [Keep a Changelog](https://keepachangelog.com/). Le contenu est extrait automatiquement par le CI pour les release notes et affiché dans l'app selon la langue de l'utilisateur. - Format [Keep a Changelog](https://keepachangelog.com/). Le contenu est extrait automatiquement par le CI pour les release notes et affiché dans l'app selon la langue de l'utilisateur.
- Les deux fichiers sont copiés dans `public/` par le CI (`release.yml`). En dev, synchroniser manuellement : `cp CHANGELOG*.md public/` - The `public/` copies are synced automatically: Vite copies them on `dev`/`build` start via `syncChangelogs()` in `vite.config.ts`. No manual sync needed.
--- ---

340
package-lock.json generated
View file

@ -35,7 +35,8 @@
"@vitejs/plugin-react": "^4.7.0", "@vitejs/plugin-react": "^4.7.0",
"tailwindcss": "^4.1.18", "tailwindcss": "^4.1.18",
"typescript": "~5.8.3", "typescript": "~5.8.3",
"vite": "^6.4.1" "vite": "^6.4.1",
"vitest": "^4.0.18"
} }
}, },
"node_modules/@babel/code-frame": { "node_modules/@babel/code-frame": {
@ -1763,6 +1764,17 @@
"@babel/types": "^7.28.2" "@babel/types": "^7.28.2"
} }
}, },
"node_modules/@types/chai": {
"version": "5.2.3",
"resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz",
"integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/deep-eql": "*",
"assertion-error": "^2.0.1"
}
},
"node_modules/@types/d3-array": { "node_modules/@types/d3-array": {
"version": "3.2.2", "version": "3.2.2",
"resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.2.tgz", "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.2.tgz",
@ -1817,6 +1829,13 @@
"resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz", "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz",
"integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==" "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw=="
}, },
"node_modules/@types/deep-eql": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz",
"integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/estree": { "node_modules/@types/estree": {
"version": "1.0.8", "version": "1.0.8",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
@ -1884,6 +1903,127 @@
"vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0"
} }
}, },
"node_modules/@vitest/expect": {
"version": "4.0.18",
"resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.0.18.tgz",
"integrity": "sha512-8sCWUyckXXYvx4opfzVY03EOiYVxyNrHS5QxX3DAIi5dpJAAkyJezHCP77VMX4HKA2LDT/Jpfo8i2r5BE3GnQQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@standard-schema/spec": "^1.0.0",
"@types/chai": "^5.2.2",
"@vitest/spy": "4.0.18",
"@vitest/utils": "4.0.18",
"chai": "^6.2.1",
"tinyrainbow": "^3.0.3"
},
"funding": {
"url": "https://opencollective.com/vitest"
}
},
"node_modules/@vitest/mocker": {
"version": "4.0.18",
"resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.0.18.tgz",
"integrity": "sha512-HhVd0MDnzzsgevnOWCBj5Otnzobjy5wLBe4EdeeFGv8luMsGcYqDuFRMcttKWZA5vVO8RFjexVovXvAM4JoJDQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@vitest/spy": "4.0.18",
"estree-walker": "^3.0.3",
"magic-string": "^0.30.21"
},
"funding": {
"url": "https://opencollective.com/vitest"
},
"peerDependencies": {
"msw": "^2.4.9",
"vite": "^6.0.0 || ^7.0.0-0"
},
"peerDependenciesMeta": {
"msw": {
"optional": true
},
"vite": {
"optional": true
}
}
},
"node_modules/@vitest/pretty-format": {
"version": "4.0.18",
"resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.0.18.tgz",
"integrity": "sha512-P24GK3GulZWC5tz87ux0m8OADrQIUVDPIjjj65vBXYG17ZeU3qD7r+MNZ1RNv4l8CGU2vtTRqixrOi9fYk/yKw==",
"dev": true,
"license": "MIT",
"dependencies": {
"tinyrainbow": "^3.0.3"
},
"funding": {
"url": "https://opencollective.com/vitest"
}
},
"node_modules/@vitest/runner": {
"version": "4.0.18",
"resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.0.18.tgz",
"integrity": "sha512-rpk9y12PGa22Jg6g5M3UVVnTS7+zycIGk9ZNGN+m6tZHKQb7jrP7/77WfZy13Y/EUDd52NDsLRQhYKtv7XfPQw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@vitest/utils": "4.0.18",
"pathe": "^2.0.3"
},
"funding": {
"url": "https://opencollective.com/vitest"
}
},
"node_modules/@vitest/snapshot": {
"version": "4.0.18",
"resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.0.18.tgz",
"integrity": "sha512-PCiV0rcl7jKQjbgYqjtakly6T1uwv/5BQ9SwBLekVg/EaYeQFPiXcgrC2Y7vDMA8dM1SUEAEV82kgSQIlXNMvA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@vitest/pretty-format": "4.0.18",
"magic-string": "^0.30.21",
"pathe": "^2.0.3"
},
"funding": {
"url": "https://opencollective.com/vitest"
}
},
"node_modules/@vitest/spy": {
"version": "4.0.18",
"resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.0.18.tgz",
"integrity": "sha512-cbQt3PTSD7P2OARdVW3qWER5EGq7PHlvE+QfzSC0lbwO+xnt7+XH06ZzFjFRgzUX//JmpxrCu92VdwvEPlWSNw==",
"dev": true,
"license": "MIT",
"funding": {
"url": "https://opencollective.com/vitest"
}
},
"node_modules/@vitest/utils": {
"version": "4.0.18",
"resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.0.18.tgz",
"integrity": "sha512-msMRKLMVLWygpK3u2Hybgi4MNjcYJvwTb0Ru09+fOyCXIgT5raYP041DRRdiJiI3k/2U6SEbAETB3YtBrUkCFA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@vitest/pretty-format": "4.0.18",
"tinyrainbow": "^3.0.3"
},
"funding": {
"url": "https://opencollective.com/vitest"
}
},
"node_modules/assertion-error": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz",
"integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=12"
}
},
"node_modules/baseline-browser-mapping": { "node_modules/baseline-browser-mapping": {
"version": "2.9.19", "version": "2.9.19",
"resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.19.tgz", "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.19.tgz",
@ -1946,6 +2086,16 @@
} }
] ]
}, },
"node_modules/chai": {
"version": "6.2.2",
"resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz",
"integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=18"
}
},
"node_modules/clsx": { "node_modules/clsx": {
"version": "2.1.1", "version": "2.1.1",
"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
@ -2138,6 +2288,13 @@
"node": ">=10.13.0" "node": ">=10.13.0"
} }
}, },
"node_modules/es-module-lexer": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz",
"integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==",
"dev": true,
"license": "MIT"
},
"node_modules/es-toolkit": { "node_modules/es-toolkit": {
"version": "1.44.0", "version": "1.44.0",
"resolved": "https://registry.npmjs.org/es-toolkit/-/es-toolkit-1.44.0.tgz", "resolved": "https://registry.npmjs.org/es-toolkit/-/es-toolkit-1.44.0.tgz",
@ -2193,11 +2350,31 @@
"node": ">=6" "node": ">=6"
} }
}, },
"node_modules/estree-walker": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz",
"integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/estree": "^1.0.0"
}
},
"node_modules/eventemitter3": { "node_modules/eventemitter3": {
"version": "5.0.4", "version": "5.0.4",
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz",
"integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==" "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw=="
}, },
"node_modules/expect-type": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz",
"integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==",
"dev": true,
"license": "Apache-2.0",
"engines": {
"node": ">=12.0.0"
}
},
"node_modules/fdir": { "node_modules/fdir": {
"version": "6.5.0", "version": "6.5.0",
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
@ -2643,11 +2820,29 @@
"integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==",
"dev": true "dev": true
}, },
"node_modules/obug": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/obug/-/obug-2.1.1.tgz",
"integrity": "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==",
"dev": true,
"funding": [
"https://github.com/sponsors/sxzz",
"https://opencollective.com/debug"
],
"license": "MIT"
},
"node_modules/papaparse": { "node_modules/papaparse": {
"version": "5.5.3", "version": "5.5.3",
"resolved": "https://registry.npmjs.org/papaparse/-/papaparse-5.5.3.tgz", "resolved": "https://registry.npmjs.org/papaparse/-/papaparse-5.5.3.tgz",
"integrity": "sha512-5QvjGxYVjxO59MGU2lHVYpRWBBtKHnlIAcSe1uNFCkkptUh63NFRj0FJQm7nR67puEruUci/ZkjmEFrjCAyP4A==" "integrity": "sha512-5QvjGxYVjxO59MGU2lHVYpRWBBtKHnlIAcSe1uNFCkkptUh63NFRj0FJQm7nR67puEruUci/ZkjmEFrjCAyP4A=="
}, },
"node_modules/pathe": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz",
"integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==",
"dev": true,
"license": "MIT"
},
"node_modules/picocolors": { "node_modules/picocolors": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
@ -2920,6 +3115,13 @@
"resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz", "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz",
"integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==" "integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw=="
}, },
"node_modules/siginfo": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz",
"integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==",
"dev": true,
"license": "ISC"
},
"node_modules/source-map-js": { "node_modules/source-map-js": {
"version": "1.2.1", "version": "1.2.1",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
@ -2929,6 +3131,20 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/stackback": {
"version": "0.0.2",
"resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz",
"integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==",
"dev": true,
"license": "MIT"
},
"node_modules/std-env": {
"version": "3.10.0",
"resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz",
"integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==",
"dev": true,
"license": "MIT"
},
"node_modules/tailwindcss": { "node_modules/tailwindcss": {
"version": "4.1.18", "version": "4.1.18",
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.18.tgz", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.18.tgz",
@ -2953,6 +3169,23 @@
"resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz", "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz",
"integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==" "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg=="
}, },
"node_modules/tinybench": {
"version": "2.9.0",
"resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz",
"integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==",
"dev": true,
"license": "MIT"
},
"node_modules/tinyexec": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.2.tgz",
"integrity": "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=18"
}
},
"node_modules/tinyglobby": { "node_modules/tinyglobby": {
"version": "0.2.15", "version": "0.2.15",
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz",
@ -2969,6 +3202,16 @@
"url": "https://github.com/sponsors/SuperchupuDev" "url": "https://github.com/sponsors/SuperchupuDev"
} }
}, },
"node_modules/tinyrainbow": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.0.3.tgz",
"integrity": "sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/tslib": { "node_modules/tslib": {
"version": "2.8.1", "version": "2.8.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
@ -3126,6 +3369,84 @@
} }
} }
}, },
"node_modules/vitest": {
"version": "4.0.18",
"resolved": "https://registry.npmjs.org/vitest/-/vitest-4.0.18.tgz",
"integrity": "sha512-hOQuK7h0FGKgBAas7v0mSAsnvrIgAvWmRFjmzpJ7SwFHH3g1k2u37JtYwOwmEKhK6ZO3v9ggDBBm0La1LCK4uQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@vitest/expect": "4.0.18",
"@vitest/mocker": "4.0.18",
"@vitest/pretty-format": "4.0.18",
"@vitest/runner": "4.0.18",
"@vitest/snapshot": "4.0.18",
"@vitest/spy": "4.0.18",
"@vitest/utils": "4.0.18",
"es-module-lexer": "^1.7.0",
"expect-type": "^1.2.2",
"magic-string": "^0.30.21",
"obug": "^2.1.1",
"pathe": "^2.0.3",
"picomatch": "^4.0.3",
"std-env": "^3.10.0",
"tinybench": "^2.9.0",
"tinyexec": "^1.0.2",
"tinyglobby": "^0.2.15",
"tinyrainbow": "^3.0.3",
"vite": "^6.0.0 || ^7.0.0",
"why-is-node-running": "^2.3.0"
},
"bin": {
"vitest": "vitest.mjs"
},
"engines": {
"node": "^20.0.0 || ^22.0.0 || >=24.0.0"
},
"funding": {
"url": "https://opencollective.com/vitest"
},
"peerDependencies": {
"@edge-runtime/vm": "*",
"@opentelemetry/api": "^1.9.0",
"@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0",
"@vitest/browser-playwright": "4.0.18",
"@vitest/browser-preview": "4.0.18",
"@vitest/browser-webdriverio": "4.0.18",
"@vitest/ui": "4.0.18",
"happy-dom": "*",
"jsdom": "*"
},
"peerDependenciesMeta": {
"@edge-runtime/vm": {
"optional": true
},
"@opentelemetry/api": {
"optional": true
},
"@types/node": {
"optional": true
},
"@vitest/browser-playwright": {
"optional": true
},
"@vitest/browser-preview": {
"optional": true
},
"@vitest/browser-webdriverio": {
"optional": true
},
"@vitest/ui": {
"optional": true
},
"happy-dom": {
"optional": true
},
"jsdom": {
"optional": true
}
}
},
"node_modules/void-elements": { "node_modules/void-elements": {
"version": "3.1.0", "version": "3.1.0",
"resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz", "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz",
@ -3134,6 +3455,23 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/why-is-node-running": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz",
"integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==",
"dev": true,
"license": "MIT",
"dependencies": {
"siginfo": "^2.0.0",
"stackback": "0.0.2"
},
"bin": {
"why-is-node-running": "cli.js"
},
"engines": {
"node": ">=8"
}
},
"node_modules/yallist": { "node_modules/yallist": {
"version": "3.1.1", "version": "3.1.1",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",

View file

@ -1,14 +1,16 @@
{ {
"name": "simpl_result_scaffold", "name": "simpl_result_scaffold",
"private": true, "private": true,
"version": "0.6.5", "version": "0.6.6",
"license": "GPL-3.0-only", "license": "GPL-3.0-only",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",
"build": "tsc && vite build", "build": "tsc && vite build",
"preview": "vite preview", "preview": "vite preview",
"tauri": "tauri" "tauri": "tauri",
"test": "vitest run",
"test:watch": "vitest"
}, },
"dependencies": { "dependencies": {
"@dnd-kit/core": "^6.3.1", "@dnd-kit/core": "^6.3.1",
@ -37,6 +39,7 @@
"@vitejs/plugin-react": "^4.7.0", "@vitejs/plugin-react": "^4.7.0",
"tailwindcss": "^4.1.18", "tailwindcss": "^4.1.18",
"typescript": "~5.8.3", "typescript": "~5.8.3",
"vite": "^6.4.1" "vite": "^6.4.1",
"vitest": "^4.0.18"
} }
} }

View file

@ -1,233 +0,0 @@
# Journal des modifications
## [Non publié]
## [0.6.3]
### Ajouté
- Tableau de bord : histogramme empilé des dépenses par catégorie et par mois (#15)
- Tableau de bord : tableau budget vs réel du mois courant avec écart en $ et % (#15)
- Tableau de budget et rapport Budget vs Réel : formatage des sous-totaux de section avec poids visuel croissant (#14)
### Modifié
- Tableau de bord : période par défaut changée de « mois » à « année à ce jour » (#15)
- Tableau de bord : section des transactions récentes supprimée (#15)
- Tous les rapports tabulaires : les lignes de grand total utilisent maintenant une police plus grande (text-sm), gras et bordure supérieure plus épaisse pour une meilleure hiérarchie visuelle (#14)
### Corrigé
- Rapport catégories dans le temps : toutes les catégories sont maintenant affichées (limite passée de 8 à 50) (#13)
- Graphique par catégorie : les noms sur l'axe Y utilisent maintenant la couleur du texte principal au lieu du gris (#13)
- Graphique catégories dans le temps : le texte de la légende utilise maintenant la couleur du texte principal au lieu d'hériter la couleur de la catégorie (#13)
## [0.6.2]
### Ajouté
- Tableau de budget : sous-totaux par section (dépenses, revenus, transferts) affichés après chaque groupe (#11)
- Rapport Budget vs Réel : sous-totaux par section avec réel, prévu, écart ($) et écart (%) par type (#11)
### Corrigé
- Page catégories : le panneau de détail reste maintenant visible lors du défilement d'une longue liste de catégories (#12)
## [0.6.1]
### Ajouté
- Page historique des versions : historique complet accessible depuis les Paramètres à tout moment
- Changelog bilingue : les notes de version s'affichent dans la langue choisie par l'utilisateur (FR/EN)
### Corrigé
- Visibilité des labels de graphiques : les montants sur les barres empilées utilisent maintenant du texte noir avec contour blanc pour un meilleur contraste (#8)
- Tableau de budget : les cellules éditables affichent maintenant un fond au survol, un curseur pointeur et une info-bulle pour clarifier l'interaction (#9)
## [0.6.0]
### Ajouté
- Rapports : bascule entre vue tableau et graphique pour les onglets Tendances, Par catégorie et Évolution
- Rapports : option « Afficher les montants » pour afficher les valeurs directement sur les barres et courbes
- Rapports : panneau de filtres avec cases à cocher par catégorie (recherche, tout sélectionner/désélectionner) et filtre par source
- Rapports : le filtre source s'applique au niveau SQL pour des totaux filtrés précis
- Rapports : en-têtes de tableau fixes sur tous les tableaux de rapports (Rapport dynamique, Budget vs Réel)
- Rapports : survol interactif — barres non survolées estompées, info-bulle filtrée sur la catégorie survolée
- Rapports : le survol de la légende met en évidence la catégorie sur tous les mois (graphique Évolution)
### Corrigé
- Tableau des transactions : l'icône de commentaire devient orange (comme l'icône de ventilation) quand une note est présente (#7)
## [0.5.0]
### Ajouté
- Gestion d'erreurs : intercepte les plantages React et affiche une page d'erreur au lieu d'un écran blanc
- Délai de démarrage (10 s) sur la connexion à la base de données — affiche une page d'erreur au lieu d'un indicateur de chargement infini
- Page d'erreur avec « Rafraîchir », « Vérifier les mises à jour » et liens de contact
- Visionneuse de journaux dans les paramètres — capture la sortie console, filtrable par niveau, copiable, persiste entre les rafraîchissements
- Licence GPL-3.0 — le projet est maintenant open source
### Modifié
- Modale détail de rapport : colonnes triables — cliquez sur les en-têtes pour trier par date, description ou montant (#1)
- Modale détail de rapport : bascule pour afficher/masquer la colonne des montants (#3)
- Tableau de budget : les en-têtes de colonnes restent fixes lors du défilement vertical (#2)
### Corrigé
- Mise à jour automatique sur Linux : le champ version de `latest.json` n'a plus le préfixe `v`, téléversement au registre de paquets plus robuste
- Nouvelle tentative au démarrage : la connexion BD réessaie jusqu'à 3 fois avant d'afficher la page d'erreur (corrige l'échec au premier lancement sur Windows)
- Somme de contrôle de migration : répare automatiquement la somme de contrôle obsolète de la migration 1 au démarrage
## [0.4.4]
### Corrigé
- Le binaire Linux est maintenant compatible avec glibc 2.35+ (Ubuntu 22.04 / Pop!_OS) — le CI compile dans un conteneur Ubuntu 22.04
## [0.4.3]
### Corrigé
- Le point de terminaison de mise à jour automatique utilise maintenant le registre de paquets Forgejo pour une URL stable
- Les signatures Linux (.AppImage.sig) sont maintenant correctement collectées dans le CI
- Toutes les signatures de plateforme (.deb.sig, .rpm.sig) sont maintenant incluses dans les assets de la release
## [0.4.2]
### Modifié
- La mise à jour automatique pointe maintenant vers l'instance Forgejo auto-hébergée
- Les builds Windows sont maintenant compilés en croisé via cargo-xwin
## [0.4.1]
### Corrigé
- Application bloquée sur un indicateur de chargement infini après mise à jour depuis v0.3.x (somme de contrôle de migration incompatible sur seed_categories.sql)
- Les erreurs de connexion BD sont maintenant journalisées dans la console au lieu d'échouer silencieusement
## [0.4.0]
### Ajouté
- Catégories : support de 3 niveaux de hiérarchie (ex : Dépenses récurrentes → Assurances → Assurance-auto)
- Rapport dynamique : nouveau champ « Catégorie (Niveau 3) »
- Budget : sous-totaux intermédiaires et indentation 3 niveaux pour les catégories imbriquées
- Catégories : gestion automatique de `is_inputable` à la création/suppression de sous-catégories
- Catégories : la validation de profondeur empêche la création d'un 4e niveau
- Données initiales : Assurances divisées en Assurance-auto, Assurance-habitation, Assurance-vie
### Corrigé
- Auto-catégorisation : les mots-clés commençant/finissant par des caractères spéciaux (`[`, `]`, `(`, `)`, `-`, etc.) sont maintenant reconnus
- Auto-catégorisation : pré-compilation des regex pour de meilleures performances en lot
## [0.3.11]
### Ajouté
- Rapport dynamique : support de plusieurs dimensions en colonnes (clés composites)
### Corrigé
- Rapport dynamique : n'est plus affecté par les filtres de date globaux — utilise uniquement ses propres filtres du panneau
## [0.3.10]
### Ajouté
- Rapport dynamique : les champs peuvent maintenant être utilisés dans plusieurs zones simultanément (lignes + filtres, colonnes + filtres)
- Rapport dynamique : clic-droit sur une valeur de filtre pour l'exclure (affiché barré en rouge)
- Option de période « Cette année » dans les rapports et le tableau de bord (du 1er janvier à aujourd'hui)
## [0.3.9]
### Ajouté
- Rapport dynamique (tableau croisé) : composez des rapports personnalisés en assignant des dimensions aux lignes, colonnes, filtres et mesures aux valeurs
- Suppression de mots-clés depuis la vue « Tous les mots-clés »
## [0.3.8]
### Ajouté
- Sélecteur de plage de dates personnalisée pour les rapports et le tableau de bord
- Bascule pour positionner les sous-totaux au-dessus ou en dessous des lignes de détail
- Affichage des notes de version du CHANGELOG dans les releases et le système de mise à jour
## [0.3.7]
### Corrigé
- Suppression du bundle MSI pour éviter le conflit de chemin d'installation du système de mise à jour
- Changement du mode d'installation Windows à basicUi
- Amélioration de la visibilité de l'indicateur de ventilation et de la mise en page des ajustements
## 0.3.2
### Nouvelles fonctionnalités
- **Support Linux** : ajout des builds Linux (`.deb`, `.rpm`, `.AppImage`) au workflow de release
- **Ventilations sur la page Ajustements** : visualisez les ajustements de ventilation des transactions dans une section dédiée
### Corrigé
- Correction de cas limites de détection automatique CSV
- Suppression de l'accent dans productName pour la compatibilité `.deb` Linux
## 0.3.1
### Corrigé
- Toujours afficher le sélecteur de profil dans la barre latérale (#2)
## 0.3.0
### Nouvelles fonctionnalités
- **Profils multiples** : créez plusieurs profils avec des bases de données séparées, des noms et couleurs personnalisés
- **Protection par NIP** : protégez les profils avec un NIP numérique optionnel
- **Sélecteur de profil** : changement rapide de profil depuis la barre latérale
- **Glisser-déposer les catégories** : réordonnez les catégories ou changez le parent par glisser-déposer dans l'arborescence
- **Ventilation des transactions** : ventilez une transaction sur plusieurs catégories avec des montants ajustables
## 0.2.10
### Nouvelles fonctionnalités
- **Sélection rapide de période** : boutons de filtre rapide (Ce mois, Mois dernier, etc.) sur la page Transactions
- **Rapport Budget vs Réel** : tableau comparatif mensuel et cumulatif annuel dans les Rapports
- **Sous-totaux de catégories parentes** : la page Budget affiche les sous-totaux agrégés pour les catégories parentes
- **Guide utilisateur** : documentation complète accessible depuis les Paramètres, imprimable en PDF
### Améliorations
- Persistance de la sélection de modèle et ajout du bouton Mettre à jour le modèle
- Ne plus pré-sélectionner les fichiers déjà importés à l'entrée de la configuration source
- Rendre les imports de données de paramètres visibles dans l'historique d'import
- Remplacer les boutons de suppression par modèle par un seul bouton sur la sélection
- Remplacer l'icône de rafraîchissement par une icône de sauvegarde sur le bouton de mise à jour du modèle
- Ajout de la convention de signe à la page budget
## 0.2.9
### Corrigé
- Permettre les fichiers à contenu identique avec des noms différents (#1)
## 0.2.8
### Nouvelles fonctionnalités
- **Export/import de données** : exportez et importez vos données (transactions, catégories, ou les deux) avec chiffrement AES-256-GCM optionnel (#3)
### Corrigé
- Détection de doublons inter-fichiers et suivi d'import par fichier
## 0.2.5
### Nouvelles fonctionnalités
- **Modèles de configuration d'import** : sauvegardez et chargez les configurations de source d'import comme modèles réutilisables
- **Grille de budget 12 mois** : vue budgétaire annuelle complète avec cellules mensuelles et totaux annuels
### Corrigé
- Corrections du budget et des catégories
- Problème de somme de contrôle de migration (schema.sql ne doit pas être modifié après la release initiale)
## 0.2.3
### Nouvelles fonctionnalités
- **Motifs de graphiques** : ajout de motifs de remplissage SVG (lignes diagonales, points, hachures, etc.) pour différencier les catégories dans les graphiques au-delà de la couleur
- **Menu contextuel des graphiques** : clic-droit sur une catégorie dans un graphique pour la masquer ou voir ses transactions dans une fenêtre de détail
- **Catégories masquées** : les catégories masquées apparaissent comme des puces au-dessus des graphiques avec un bouton « Tout afficher »
- **Modale de détail des transactions** : visualisez toutes les transactions composant le total d'une catégorie directement depuis n'importe quel graphique
- **Aperçu d'import en popup** : l'aperçu des données est maintenant une modale popup au lieu d'une étape séparée de l'assistant
- **Vérification directe des doublons** : nouveau bouton « Vérifier les doublons » sur la page de configuration d'import
### Améliorations
- Flux de l'assistant d'import simplifié : configuration source → vérification des doublons (l'aperçu est optionnel via popup)
- Le bouton retour de la vérification des doublons retourne maintenant à la configuration source
## 0.2.2
- Mise à jour de version
## 0.2.1
- Ajout de la vue « Tous les mots-clés » sur la page Catégories
- Ajout du mode sombre avec palette de gris chauds
- Correction des catégories orphelines, persistance de has_header pour les imports, ajout de la réinitialisation
- Ajout des pages Budget et Ajustements

View file

@ -1,234 +0,0 @@
# Changelog
## [Unreleased]
## [0.6.3]
### Added
- Dashboard: expenses over time stacked bar chart by category and month (#15)
- Dashboard: budget vs actual table for current month with variance in $ and % (#15)
- Budget table and Budget vs Actual report: section subtotal formatting with increasing visual weight (#14)
### Changed
- Dashboard: default period changed from "month" to "year to date" (#15)
- Dashboard: removed recent transactions section (#15)
- All report tables: grand total rows now use larger font (text-sm), bold weight, and thicker top border for better visual hierarchy (#14)
### Fixed
- Category over time report: all categories now displayed (limit increased from 8 to 50) (#13)
- Category bar chart: Y-axis labels now use foreground color instead of muted gray (#13)
- Category over time chart: legend text now uses foreground color instead of inheriting category color (#13)
## [0.6.2]
### Added
- Budget table: section subtotals for expenses, income, and transfers displayed after each group (#11)
- Budget vs Actual report: section subtotals with actual, planned, variation ($) and variation (%) per type (#11)
### Fixed
- Category page: detail panel now stays visible when scrolling through a long category list (#12)
## [0.6.1]
### Added
- Changelog page: full version history accessible from Settings at any time
- Bilingual changelog: release notes displayed in the user's selected language (EN/FR)
### Fixed
- Chart label visibility: amount labels on stacked bar charts now use black text with white outline for better contrast (#8)
- Budget table: editable cells now show hover background, pointer cursor, and tooltip hint for clearer affordance (#9)
## [0.6.0]
### Added
- Reports: toggle between table and chart view for Trends, By Category, and Over Time tabs
- Reports: "Show amounts" toggle displays values directly on chart bars and area curves
- Reports: filter panel with category checkboxes (search, select all/none) and source dropdown
- Reports: source filter applies at SQL level for accurate filtered totals
- Reports: sticky table headers on all report tables (Dynamic Report, Budget vs Actual)
- Reports: interactive hover — dimmed non-hovered bars, tooltip filtered to hovered category
- Reports: legend hover highlights category across all months (Over Time chart)
### Fixed
- Transaction table: comment icon now turns orange (like split icon) when a note is present (#7)
## [0.5.0]
### Added
- Error boundary catches React crashes and displays an error page instead of a white screen
- Startup timeout (10s) on database connection — shows error page instead of infinite spinner
- Error page with "Refresh", "Check for updates", and contact/issue links
- Log viewer in settings page — captures console output, filterable by level, copyable, persists across refresh
- GPL-3.0 license — project is now open source
### Changed
- Report detail modal: sortable columns — click headers to sort by date, description, or amount (#1)
- Report detail modal: toggle to show/hide amounts column (#3)
- Budget table: column headers stay fixed when scrolling vertically (#2)
### Fixed
- Auto-updater on Linux: `latest.json` version field no longer has `v` prefix, package registry upload is more robust
- Startup retry: DB connection retries up to 3 times before showing error page (fixes first-launch failure on Windows)
- Migration checksum mismatch: automatically repairs stale migration 1 checksum on startup
## [0.4.4]
### Fixed
- Linux binary now compatible with glibc 2.35+ (Ubuntu 22.04 / Pop!_OS) — CI builds in Ubuntu 22.04 container
## [0.4.3]
### Fixed
- Auto-updater endpoint now uses Forgejo package registry for stable URL
- Linux updater signatures (.AppImage.sig) now correctly collected in CI
- All platform signatures (.deb.sig, .rpm.sig) now included in release assets
## [0.4.2]
### Changed
- Auto-updater now points to self-hosted Forgejo instance
- Windows builds now cross-compiled via cargo-xwin
## [0.4.1]
### Fixed
- App stuck on infinite spinner after updating from v0.3.x (migration checksum mismatch on seed_categories.sql)
- DB connection errors now logged to console instead of silently failing
## [0.4.0]
### Added
- Categories: support for 3 levels of hierarchy (e.g., Dépenses récurrentes → Assurances → Assurance-auto)
- Dynamic Report: new "Category (Level 3)" pivot field
- Budget: intermediate subtotals and 3-level indentation for nested categories
- Categories: automatic `is_inputable` management when creating/deleting subcategories
- Categories: depth validation prevents creating a 4th level
- Seed data: Assurances split into Assurance-auto, Assurance-habitation, Assurance-vie
### Fixed
- Auto-categorization: keywords starting/ending with special characters (`[`, `]`, `(`, `)`, `-`, etc.) now match correctly
- Auto-categorization: pre-compile regex patterns for better batch performance
## [0.3.11]
### Added
- Dynamic Report: support multiple column dimensions (composite column keys)
### Fixed
- Dynamic Report: no longer affected by global page date filters — uses only its own panel filters
## [0.3.10]
### Added
- Dynamic Report: fields can now be used in multiple zones simultaneously (rows + filters, columns + filters)
- Dynamic Report: right-click on a filter value to exclude it (shown with strikethrough in red)
- "This year" period option in reports and dashboard (Jan 1 to today)
## [0.3.9]
### Added
- Dynamic Report (pivot table): compose custom reports by assigning dimensions to rows, columns, filters and measures to values
- Delete keywords from the "All Keywords" view
## [0.3.8]
### Added
- Custom date range picker for reports and dashboard
- Toggle to position subtotals above or below detail rows
- Display release notes from CHANGELOG in GitHub releases and in-app updater
## [0.3.7]
### Fixes
- Remove MSI bundle to prevent updater install path conflict
- Change Windows updater installMode to basicUi
- Improve split indicator visibility and adjustments layout
## 0.3.2
### New Features
- **Linux support**: Add Linux build (`.deb`, `.rpm`, `.AppImage`) to release workflow
- **Transaction splits on Adjustments page**: View transaction split adjustments in a dedicated section on the Adjustments page
### Fixes
- Fix CSV auto-detect edge cases
- Remove accent from productName for Linux `.deb` compatibility
## 0.3.1
### Fixes
- Always show profile switcher in sidebar (#2)
## 0.3.0
### New Features
- **Multiple profiles**: Create multiple profiles with separate databases, custom names, and colors
- **PIN protection**: Protect profiles with an optional numeric PIN
- **Profile switcher**: Quick profile switching from the sidebar
- **Drag-and-drop categories**: Reorder categories or change parent via drag-and-drop in the category tree
- **Transaction splits**: Split a transaction across multiple categories with adjustable amounts
## 0.2.10
### New Features
- **Period quick-select**: Add quick period filter buttons (This month, Last month, etc.) on the Transactions page
- **Budget vs Actual report**: Monthly and year-to-date comparison table in Reports
- **Parent category subtotals**: Budget page shows aggregated subtotals for parent categories
- **User guide**: Complete documentation page accessible from Settings, printable to PDF
### Improvements
- Persist template selection and add Update template button
- Don't pre-select already-imported files when entering source config
- Make settings data imports visible in Import History
- Replace per-template delete buttons with single delete on selection
- Replace refresh icon with save icon on update template button
- Add sign convention to budget page
## 0.2.9
### Fixes
- Allow duplicate-content files with different names (#1)
## 0.2.8
### New Features
- **Data export/import**: Export and import your data (transactions, categories, or both) with optional AES-256-GCM encryption (#3)
### Fixes
- Cross-file duplicate detection and per-file import tracking
## 0.2.5
### New Features
- **Import config templates**: Save and load import source configurations as reusable templates
- **12-month budget grid**: Full year budget view with monthly cells and annual totals
### Fixes
- Budget and category fixes
- Migration checksum issue (schema.sql must not be modified after initial release)
## 0.2.3
### New Features
- **Chart patterns**: Added SVG fill patterns (diagonal lines, dots, crosshatch, etc.) to differentiate categories in bar charts, pie chart, and stacked bar charts beyond just color
- **Chart context menu**: Right-click any category in a chart to hide it or view its transactions in a detail popup
- **Hidden categories**: Hidden categories appear as dismissible chips above charts with a "Show all" button to restore them
- **Transaction detail modal**: View all transactions composing a category's total directly from any chart
- **Import preview popup**: The data preview is now a popup modal instead of a separate wizard step, allowing quick inspection without leaving the configuration page
- **Direct duplicate check**: New "Check Duplicates" button on the import configuration page skips directly to duplicate validation without requiring a preview first
### Improvements
- Import wizard flow simplified: source-config → duplicate-check (preview is optional via popup)
- Duplicate-check back button now returns to source configuration instead of the removed preview step
- Added `categoryIds` map to `CategoryOverTimeData` for proper category resolution in the over-time chart
## 0.2.2
- Bump version
## 0.2.1
- Add "All Keywords" view on Categories page
- Add dark mode with warm gray palette
- Fix orphan categories, persist has_header for imports, add re-initialize
- Add Budget and Adjustments pages

View file

@ -1,6 +1,6 @@
[package] [package]
name = "simpl-result" name = "simpl-result"
version = "0.6.5" version = "0.6.6"
description = "Personal finance management app" description = "Personal finance management app"
license = "GPL-3.0-only" license = "GPL-3.0-only"
authors = ["you"] authors = ["you"]

View file

@ -1,7 +1,7 @@
{ {
"$schema": "https://schema.tauri.app/config/2", "$schema": "https://schema.tauri.app/config/2",
"productName": "Simpl Resultat", "productName": "Simpl Resultat",
"version": "0.6.5", "version": "0.6.6",
"identifier": "com.simpl.resultat", "identifier": "com.simpl.resultat",
"build": { "build": {
"beforeDevCommand": "npm run dev", "beforeDevCommand": "npm run dev",

View file

@ -150,7 +150,7 @@ export default function BudgetTable({ rows, onUpdatePlanned, onSplitEvenly }: Bu
monthTotals[m] += row.months[m] * sign; monthTotals[m] += row.months[m] * sign;
} }
annualTotal += row.annual * sign; annualTotal += row.annual * sign;
prevYearTotal += row.previousYearTotal * sign; prevYearTotal += row.previousYearTotal; // actuals are already signed in the DB
} }
const totalCols = 15; // category + prev year + annual + 12 months const totalCols = 15; // category + prev year + annual + 12 months
@ -197,7 +197,7 @@ export default function BudgetTable({ rows, onUpdatePlanned, onSplitEvenly }: Bu
</div> </div>
</td> </td>
<td className={`py-2 px-2 text-right text-xs ${isIntermediateParent ? "font-medium" : "font-semibold"} text-[var(--muted-foreground)]`}> <td className={`py-2 px-2 text-right text-xs ${isIntermediateParent ? "font-medium" : "font-semibold"} text-[var(--muted-foreground)]`}>
{formatSigned(row.previousYearTotal * sign)} {formatSigned(row.previousYearTotal)}
</td> </td>
<td className={`py-2 px-2 text-right text-xs ${isIntermediateParent ? "font-medium" : "font-semibold"}`}> <td className={`py-2 px-2 text-right text-xs ${isIntermediateParent ? "font-medium" : "font-semibold"}`}>
{formatSigned(row.annual * sign)} {formatSigned(row.annual * sign)}
@ -230,7 +230,7 @@ export default function BudgetTable({ rows, onUpdatePlanned, onSplitEvenly }: Bu
{/* Previous year total — read-only */} {/* Previous year total — read-only */}
<td className="py-2 px-2 text-right text-[var(--muted-foreground)]"> <td className="py-2 px-2 text-right text-[var(--muted-foreground)]">
<span className="text-xs px-1 py-0.5"> <span className="text-xs px-1 py-0.5">
{formatSigned(row.previousYearTotal * sign)} {formatSigned(row.previousYearTotal)}
</span> </span>
</td> </td>
{/* Annual total — editable */} {/* Annual total — editable */}
@ -340,7 +340,7 @@ export default function BudgetTable({ rows, onUpdatePlanned, onSplitEvenly }: Bu
sectionMonthTotals[m] += row.months[m] * sign; sectionMonthTotals[m] += row.months[m] * sign;
} }
sectionAnnualTotal += row.annual * sign; sectionAnnualTotal += row.annual * sign;
sectionPrevYearTotal += row.previousYearTotal * sign; sectionPrevYearTotal += row.previousYearTotal; // actuals are already signed in the DB
} }
return ( return (
<Fragment key={type}> <Fragment key={type}>

View file

@ -3,6 +3,7 @@ import type { BudgetYearRow, BudgetTemplate } from "../shared/types";
import { import {
getAllActiveCategories, getAllActiveCategories,
getBudgetEntriesForYear, getBudgetEntriesForYear,
getActualTotalsForYear,
upsertBudgetEntry, upsertBudgetEntry,
upsertBudgetEntriesForYear, upsertBudgetEntriesForYear,
getAllTemplates, getAllTemplates,
@ -72,10 +73,10 @@ export function useBudget() {
dispatch({ type: "SET_ERROR", payload: null }); dispatch({ type: "SET_ERROR", payload: null });
try { try {
const [allCategories, entries, prevYearEntries, templates] = await Promise.all([ const [allCategories, entries, prevYearActuals, templates] = await Promise.all([
getAllActiveCategories(), getAllActiveCategories(),
getBudgetEntriesForYear(year), getBudgetEntriesForYear(year),
getBudgetEntriesForYear(year - 1), getActualTotalsForYear(year - 1),
getAllTemplates(), getAllTemplates(),
]); ]);
@ -88,10 +89,11 @@ export function useBudget() {
entryMap.get(e.category_id)!.set(e.month, e.amount); entryMap.get(e.category_id)!.set(e.month, e.amount);
} }
// Build a map for previous year totals: categoryId -> annual total // Build a map for previous year actuals: categoryId -> annual actual total
// Amounts are already signed (expenses negative, income positive) — stored as-is.
const prevYearTotalMap = new Map<number, number>(); const prevYearTotalMap = new Map<number, number>();
for (const e of prevYearEntries) { for (const a of prevYearActuals) {
prevYearTotalMap.set(e.category_id, (prevYearTotalMap.get(e.category_id) ?? 0) + e.amount); if (a.category_id != null) prevYearTotalMap.set(a.category_id, a.actual);
} }
// Helper: build months array from entryMap // Helper: build months array from entryMap

View file

@ -178,6 +178,16 @@ export async function deleteTemplate(templateId: number): Promise<void> {
await db.execute("DELETE FROM budget_templates WHERE id = $1", [templateId]); await db.execute("DELETE FROM budget_templates WHERE id = $1", [templateId]);
} }
// --- Actuals helpers ---
export async function getActualTotalsForYear(
year: number
): Promise<Array<{ category_id: number | null; actual: number }>> {
const dateFrom = `${year}-01-01`;
const dateTo = `${year}-12-31`;
return getActualsByCategoryRange(dateFrom, dateTo);
}
// --- Budget vs Actual --- // --- Budget vs Actual ---
async function getActualsByCategoryRange( async function getActualsByCategoryRange(

View file

@ -142,7 +142,7 @@ export interface BudgetYearRow {
depth?: number; depth?: number;
months: number[]; // index 0-11 = Jan-Dec planned amounts months: number[]; // index 0-11 = Jan-Dec planned amounts
annual: number; // computed sum annual: number; // computed sum
previousYearTotal: number; // total budget from the previous year previousYearTotal: number; // actual (transactions) total from the previous year
} }
export interface ImportConfigTemplate { export interface ImportConfigTemplate {

123
src/utils/dateRange.test.ts Normal file
View file

@ -0,0 +1,123 @@
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
import { computeDateRange, buildMonthOptions } from "./dateRange";
describe("computeDateRange", () => {
beforeEach(() => {
// Fix "now" to 2025-07-15 for deterministic tests
vi.useFakeTimers();
vi.setSystemTime(new Date(2025, 6, 15)); // July 15, 2025
});
afterEach(() => {
vi.useRealTimers();
});
it('returns empty object for "all" period', () => {
expect(computeDateRange("all")).toEqual({});
});
it('returns custom range for "custom" period', () => {
expect(computeDateRange("custom", "2025-01-01", "2025-06-30")).toEqual({
dateFrom: "2025-01-01",
dateTo: "2025-06-30",
});
});
it('falls back to default when "custom" has missing dates', () => {
const result = computeDateRange("custom");
// Should fall through to default (same as "month")
expect(result.dateFrom).toBe("2025-07-01");
expect(result.dateTo).toBe("2025-07-15");
});
it('computes "month" period (first of current month to today)', () => {
const result = computeDateRange("month");
expect(result).toEqual({ dateFrom: "2025-07-01", dateTo: "2025-07-15" });
});
it('computes "3months" period (3 months back)', () => {
const result = computeDateRange("3months");
expect(result).toEqual({ dateFrom: "2025-05-01", dateTo: "2025-07-15" });
});
it('computes "6months" period (6 months back)', () => {
const result = computeDateRange("6months");
expect(result).toEqual({ dateFrom: "2025-02-01", dateTo: "2025-07-15" });
});
it('computes "year" period (Jan 1st of current year)', () => {
const result = computeDateRange("year");
expect(result).toEqual({ dateFrom: "2025-01-01", dateTo: "2025-07-15" });
});
it('computes "12months" period (12 months back)', () => {
const result = computeDateRange("12months");
expect(result).toEqual({ dateFrom: "2024-08-01", dateTo: "2025-07-15" });
});
it("handles January rollover for 3months period", () => {
vi.setSystemTime(new Date(2025, 1, 10)); // Feb 10, 2025
const result = computeDateRange("3months");
expect(result).toEqual({ dateFrom: "2024-12-01", dateTo: "2025-02-10" });
});
it("handles January rollover for 6months period", () => {
vi.setSystemTime(new Date(2025, 0, 20)); // Jan 20, 2025
const result = computeDateRange("6months");
expect(result).toEqual({ dateFrom: "2024-08-01", dateTo: "2025-01-20" });
});
it("handles January rollover for 12months period", () => {
vi.setSystemTime(new Date(2025, 0, 5)); // Jan 5, 2025
const result = computeDateRange("12months");
expect(result).toEqual({ dateFrom: "2024-02-01", dateTo: "2025-01-05" });
});
});
describe("buildMonthOptions", () => {
beforeEach(() => {
vi.useFakeTimers();
vi.setSystemTime(new Date(2025, 6, 15)); // July 15, 2025
});
afterEach(() => {
vi.useRealTimers();
});
it("returns 24 month options", () => {
const options = buildMonthOptions("fr");
expect(options).toHaveLength(24);
});
it("starts with the current month", () => {
const options = buildMonthOptions("en");
expect(options[0].key).toBe("2025-7");
expect(options[0].value).toBe("2025-7");
});
it("ends 23 months ago", () => {
const options = buildMonthOptions("en");
expect(options[23].key).toBe("2023-8");
expect(options[23].value).toBe("2023-8");
});
it("handles January rollover correctly", () => {
vi.setSystemTime(new Date(2025, 0, 15)); // Jan 15, 2025
const options = buildMonthOptions("en");
expect(options[0].key).toBe("2025-1");
expect(options[1].key).toBe("2024-12");
expect(options[12].key).toBe("2024-1");
});
it("capitalizes the first letter of labels", () => {
const options = buildMonthOptions("fr");
for (const opt of options) {
expect(opt.label[0]).toBe(opt.label[0].toUpperCase());
}
});
it("labels contain year information", () => {
const options = buildMonthOptions("en");
expect(options[0].label).toContain("2025");
});
});

View file

@ -1,33 +1,53 @@
import { defineConfig } from "vite"; import { defineConfig } from "vite";
import react from "@vitejs/plugin-react"; import react from "@vitejs/plugin-react";
import tailwindcss from "@tailwindcss/vite"; import tailwindcss from "@tailwindcss/vite";
import { copyFileSync } from "fs";
import { resolve } from "path";
// Sync root CHANGELOG files to public/ so the app always shows the latest version history
function syncChangelogs() {
const root = import.meta.dirname;
const files = ["CHANGELOG.md", "CHANGELOG.fr.md"];
for (const file of files) {
try {
copyFileSync(resolve(root, file), resolve(root, "public", file));
} catch {
// Ignore if source file doesn't exist
}
}
}
// @ts-expect-error process is a nodejs global // @ts-expect-error process is a nodejs global
const host = process.env.TAURI_DEV_HOST; const host = process.env.TAURI_DEV_HOST;
// https://vite.dev/config/ // https://vite.dev/config/
export default defineConfig(async () => ({ export default defineConfig(async () => {
plugins: [react(), tailwindcss()], // Sync changelogs before starting dev server or building
syncChangelogs();
// Vite options tailored for Tauri development and only applied in `tauri dev` or `tauri build` return {
// plugins: [react(), tailwindcss()],
// 1. prevent Vite from obscuring rust errors
clearScreen: false, // Vite options tailored for Tauri development and only applied in `tauri dev` or `tauri build`
// 2. tauri expects a fixed port, fail if that port is not available //
server: { // 1. prevent Vite from obscuring rust errors
port: 1420, clearScreen: false,
strictPort: true, // 2. tauri expects a fixed port, fail if that port is not available
host: host || false, server: {
hmr: host port: 1420,
? { strictPort: true,
protocol: "ws", host: host || false,
host, hmr: host
port: 1421, ? {
} protocol: "ws",
: undefined, host,
watch: { port: 1421,
// 3. tell Vite to ignore watching `src-tauri` }
ignored: ["**/src-tauri/**"], : undefined,
watch: {
// 3. tell Vite to ignore watching `src-tauri`
ignored: ["**/src-tauri/**"],
},
}, },
}, };
})); });