Commit graph

218 commits

Author SHA1 Message Date
le king fu
43c5be0c84 docs(architecture): update for v0.7.3 OAuth2 and single-instance wiring
- Bump header date/version to 2026-04-13 / v0.7.3
- Correct Tauri command count (25 → 34) and add the missing commands
- Add `auth_commands.rs` section (5 commands) and expand `license_commands.rs`
  with the 4 activation commands that already existed
- New "Plugins Tauri" section documenting init order constraints
  (single-instance must be first, deep-link before setup)
- New "OAuth2 et deep-link" section explaining the end-to-end flow,
  why single-instance is required, and why `on_open_url` is used
  instead of `app.listen()`
- Note the temporary auto-update gate opening in entitlements
- Update CI/CD: GitHub Actions → Forgejo Actions, add check.yml

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 17:25:57 -04:00
le king fu
f5d74b4664 fix: use on_open_url for OAuth deep-link callback
All checks were successful
Release / build-and-release (push) Successful in 27m50s
The listener `app.listen("deep-link://new-url", ...)` did not reliably
fire when tauri-plugin-single-instance (deep-link feature) forwarded a
simpl-resultat://auth/callback URL to the running instance. The user
saw the browser complete the OAuth flow, the app regain focus, and
then sit in "loading" forever because the listener never received the
URL.

Switch to the canonical Tauri v2 API — `app.deep_link().on_open_url()`
via DeepLinkExt — which is directly coupled to the deep-link plugin
and catches URLs from both initial launch and single-instance forwards.

Also surface OAuth error responses: if the callback URL contains an
`error` parameter instead of a `code`, emit `auth-callback-error` so
the UI can show the error instead of staying stuck in "loading".

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:26:17 -04:00
le king fu
f14ac3c6f8 fix: temporarily open auto-update to Free edition
All checks were successful
Release / build-and-release (push) Successful in 25m59s
The auto-update gate added in #48 requires the Base edition, but the
license server (#49) needed to grant Base does not exist yet. This
chicken-and-egg left the only current user — myself — unable to
receive the critical v0.7.1 OAuth callback fix via auto-update.

Add EDITION_FREE to the auto-update feature tiers as a temporary
measure. The gate will be restored to [BASE, PREMIUM] once paid
activation works end-to-end via the Phase 2 license server.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 14:11:16 -04:00
le king fu
88e1fff253 fix: wire single-instance plugin for OAuth deep-link callback
All checks were successful
Release / build-and-release (push) Successful in 26m52s
The Maximus Account sign-in flow was broken in v0.7.0: clicking "Sign in"
opened Logto in the browser, but when the OAuth2 callback fired
simpl-resultat://auth/callback?code=..., the OS launched a second app
instance instead of routing the URL to the running one. The second
instance had no PKCE verifier in memory, and the original instance
never received the deep-link event, leaving it stuck in "loading".

Fix: register tauri-plugin-single-instance (with the deep-link feature)
as the first plugin. It forwards the callback URL to the existing
process, which triggers the existing deep-link://new-url listener and
completes the token exchange.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 12:37:09 -04:00
le king fu
93fd60bf41 chore: release v0.7.0
All checks were successful
Release / build-and-release (push) Successful in 27m50s
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 11:58:34 -04:00
le king fu
324436c0f1 fix: set Logto app ID to sr-desktop-native
Update the default LOGTO_APP_ID to match the Native App registered
in the Logto instance at auth.lacompagniemaximus.com.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 11:53:53 -04:00
4b42d53659 Merge pull request 'feat: Maximus Account OAuth2 + machine activation (#51, #53)' (#65) from issue-51-compte-maximus-oauth into main 2026-04-10 19:38:27 +00:00
le king fu
e314bbe1e3 fix: remove handle_auth_callback from invoke_handler
All checks were successful
PR Check / rust (push) Successful in 17m12s
PR Check / frontend (push) Successful in 2m12s
PR Check / rust (pull_request) Successful in 16m56s
PR Check / frontend (pull_request) Successful in 2m14s
The auth callback is handled exclusively via the deep-link handler in
lib.rs — exposing it as a JS-invocable command is unnecessary attack
surface. The frontend listens for auth-callback-success/error events
instead.

Plaintext token storage documented as known limitation (see #66).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 15:35:10 -04:00
le king fu
60b995394e fix: tighten CSP img-src, show initials instead of external avatar
Some checks are pending
PR Check / rust (push) Waiting to run
PR Check / frontend (push) Waiting to run
PR Check / rust (pull_request) Successful in 17m9s
PR Check / frontend (pull_request) Successful in 2m15s
Privacy-first: remove 'https:' from img-src CSP directive to prevent
IP leaks via external avatar URLs (Google/Gravatar). AccountCard now
shows user initials instead of loading a remote image.

Also remove .keys-temp/ from .gitignore (not relevant to this PR).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 15:24:52 -04:00
le king fu
4e92882724 fix: restrict last_check file perms + add useAuth to architecture docs
Some checks are pending
PR Check / rust (push) Waiting to run
PR Check / frontend (push) Waiting to run
PR Check / rust (pull_request) Successful in 17m24s
PR Check / frontend (pull_request) Successful in 2m14s
- Use write_restricted() for auth/last_check file (consistent 0600)
- Add useAuth hook to the hooks table in docs/architecture.md

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 15:14:31 -04:00
le king fu
ca3005bc0e fix: use write_restricted for account.json (0600 perms)
Some checks are pending
PR Check / rust (push) Waiting to run
PR Check / frontend (push) Waiting to run
PR Check / rust (pull_request) Successful in 17m0s
PR Check / frontend (pull_request) Successful in 2m12s
account.json contains PII and subscription_status — apply the same
restricted file permissions as tokens.json.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 15:04:05 -04:00
le king fu
9e26ad58d1 fix: use base64 crate, restrict token file perms, safer chrono_now
Some checks are pending
PR Check / rust (push) Waiting to run
PR Check / frontend (push) Waiting to run
PR Check / rust (pull_request) Successful in 17m32s
PR Check / frontend (pull_request) Successful in 2m15s
- Replace hand-rolled base64 encoder with base64::URL_SAFE_NO_PAD crate
- Set 0600 permissions on tokens.json via write_restricted() helper (Unix)
- Replace chrono_now() .unwrap() with .unwrap_or_default()

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 14:58:10 -04:00
le king fu
be5f6a55c5 fix: URL-decode auth code + replace Mutex unwrap with map_err
Some checks failed
PR Check / rust (push) Has been cancelled
PR Check / frontend (push) Has been cancelled
PR Check / rust (pull_request) Successful in 17m21s
PR Check / frontend (pull_request) Successful in 2m21s
- extract_auth_code now URL-decodes the code parameter to handle
  percent-encoded characters from the OAuth provider
- Replace Mutex::lock().unwrap() with .lock().map_err() in start_oauth
  and handle_auth_callback to avoid panics on poisoned mutex

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 14:43:18 -04:00
le king fu
b53a902f11 feat: Maximus Account OAuth2 PKCE + machine activation + subscription check (#51, #53)
All checks were successful
PR Check / rust (push) Successful in 16m34s
PR Check / frontend (push) Successful in 2m14s
PR Check / rust (pull_request) Successful in 16m31s
PR Check / frontend (pull_request) Successful in 2m13s
- Add auth_commands.rs: OAuth2 PKCE flow (start_oauth, handle_auth_callback,
  refresh_auth_token, get_account_info, check_subscription_status, logout)
- Add deep-link handler in lib.rs for simpl-resultat://auth/callback
- Add AccountCard.tsx + useAuth hook + authService.ts
- Add machine activation commands (activate, deactivate, list, get_activation_status)
- Extend LicenseCard with machine management UI
- get_edition() now checks account subscription for Premium detection
- Daily subscription status check (refresh token if last check > 24h)
- Configure CSP for API/auth endpoints
- Configure tauri-plugin-deep-link for desktop
- Update i18n (FR/EN), changelogs, and architecture docs

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 14:18:51 -04:00
877ace370f Merge pull request 'feat: license system (UI card + auto-update gating) (#47, #48)' (#64) from issue-46-license-commands-entitlements into main 2026-04-10 14:28:27 +00:00
dd106a1df6 Merge pull request 'feat: gate auto-updates behind license entitlement (#48)' (#58) from issue-48-gate-auto-updates into issue-46-license-commands-entitlements
All checks were successful
PR Check / rust (push) Successful in 16m9s
PR Check / frontend (push) Successful in 2m11s
PR Check / rust (pull_request) Successful in 16m10s
PR Check / frontend (pull_request) Successful in 2m14s
2026-04-10 13:55:39 +00:00
44f98549b5 Merge pull request 'feat: license UI card in settings (#47)' (#57) from issue-47-license-ui-card into issue-46-license-commands-entitlements
Some checks failed
PR Check / rust (push) Has been cancelled
PR Check / frontend (push) Has been cancelled
2026-04-10 13:54:59 +00:00
le king fu
6d67ab8935 feat: gate auto-updates behind license entitlement (#48)
All checks were successful
PR Check / rust (push) Successful in 16m6s
PR Check / frontend (push) Successful in 2m15s
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.
2026-04-09 15:52:59 -04:00
le king fu
2da2de183a feat: license card in settings (#47)
All checks were successful
PR Check / rust (push) Successful in 16m19s
PR Check / frontend (push) Successful in 2m14s
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
2026-04-09 15:47:04 -04:00
59cefe8435 feat: license validation commands + entitlements system (#46)
Merge PR #56

Closes #46
2026-04-09 19:35:32 +00:00
le king fu
2e9df1c0b9 fix(rust): pass raw public key bytes to DecodingKey::from_ed_der
All checks were successful
PR Check / rust (push) Successful in 15m54s
PR Check / frontend (push) Successful in 2m15s
PR Check / rust (pull_request) Successful in 16m7s
PR Check / frontend (pull_request) Successful in 2m15s
Previous test refactor wrapped both keys in their respective DER
envelopes. CI surfaced the asymmetry: jsonwebtoken's two from_ed_der
constructors expect different inputs.

- EncodingKey::from_ed_der → PKCS#8 v1 wrapped (ring's
  Ed25519KeyPair::from_pkcs8 path). The 16-byte prefix + 32-byte seed
  blob is correct.
- DecodingKey::from_ed_der → raw 32-byte public key. Internally it
  becomes ring's UnparsedPublicKey::new(&ED25519, key_bytes), which
  takes the bare bytes, NOT a SubjectPublicKeyInfo wrapper.

The test was building an SPKI DER for the public key, so verification
saw a malformed key and failed every signature with InvalidSignature
(`accepts_well_formed_base_license` and `activation_token_matches_machine`).

Drop the SPKI helper, pass `signing_key.verifying_key().to_bytes()`
straight into DecodingKey::from_ed_der. Inline doc-comment captures
the asymmetry so the next person doesn't fall in the same hole.
2026-04-09 11:12:10 -04:00
le king fu
69e136cab0 fix(rust): use DER-built keys in license tests, drop ed25519-dalek pem feature
Some checks failed
PR Check / rust (push) Failing after 10m20s
PR Check / frontend (push) Successful in 2m15s
PR Check / rust (pull_request) Failing after 9m30s
PR Check / frontend (pull_request) Successful in 2m7s
cargo CI flagged: `unresolved import ed25519_dalek::pkcs8::LineEnding`. The
`LineEnding` re-export path varies between pkcs8/spki/der versions, so the
test code that called `to_pkcs8_pem(LineEnding::LF)` won't compile against
the dependency tree we get with ed25519-dalek 2.2 + pkcs8 0.10.

Fix:
- Drop the `pem` feature from the ed25519-dalek dev-dependency.
- In tests, build the PKCS#8 v1 PrivateKeyInfo and SubjectPublicKeyInfo
  DER blobs manually from the raw 32-byte Ed25519 seed/public key. The
  Ed25519 layout is fixed (16-byte prefix + 32-byte key) so this is short
  and stable.
- Pass the resulting DER bytes to `EncodingKey::from_ed_der` /
  `DecodingKey::from_ed_der`.

Refactor:
- Extract `strict_validation()` and `embedded_decoding_key()` helpers so
  the validation config (mandatory exp/iat for CWE-613) lives in one
  place and production callers all share the same DecodingKey constructor.
- `validate_with_key` and `validate_activation_with_key` now take a
  `&DecodingKey` instead of raw PEM bytes; production builds the key
  once via `embedded_decoding_key()`.
- New canary test `embedded_public_key_pem_parses` fails fast if the
  embedded PEM constant ever becomes malformed.
2026-04-09 10:59:12 -04:00
le king fu
99fef19a6b feat: add license validation and entitlements (Rust) (#46)
Some checks failed
PR Check / rust (push) Failing after 5m50s
PR Check / frontend (push) Successful in 2m9s
PR Check / rust (pull_request) Failing after 6m1s
PR Check / frontend (pull_request) Successful in 2m12s
Introduces the offline license infrastructure for the Base/Premium editions.

- jsonwebtoken (EdDSA) verifies license JWTs against an embedded Ed25519
  public key. The exp claim is mandatory (CWE-613) and is enforced via
  Validation::set_required_spec_claims.
- Activation tokens (server-issued, machine-bound) prevent license.key
  copying between machines. Storage is wired up; the actual issuance flow
  ships with Issue #49.
- get_edition() fails closed to "free" when the license is missing,
  invalid, expired, or activated for a different machine.
- New commands/entitlements module centralizes feature → tier mapping so
  Issue #48 (and any future gate) reads from a single source of truth.
- machine-uid provides the cross-platform machine identifier; OS reinstall
  invalidates the activation token by design.
- Tests cover happy path, expiry, wrong-key signature, malformed JWT,
  unknown edition, and machine_id matching for activation tokens.

The embedded PUBLIC_KEY_PEM is the RFC 8410 §10.3 test vector, clearly
labelled as a development placeholder; replacing it with the production
public key is a release-time task.
2026-04-09 10:02:02 -04:00
8afcafe890 Merge pull request 'fix(ci): install Node.js in the rust job' (#62) from fix/check-workflow-rust-node into main 2026-04-09 14:01:39 +00:00
le king fu
60bf43fd65 fix(ci): install Node.js in the rust job
All checks were successful
PR Check / rust (push) Successful in 15m32s
PR Check / frontend (push) Successful in 2m24s
PR Check / rust (pull_request) Successful in 15m44s
PR Check / frontend (pull_request) Successful in 2m33s
actions/checkout@v4 and actions/cache@v4 are JavaScript actions and
require `node` in the container PATH. The rust job in check.yml only
installed system libs and the Rust toolchain, so the post-checkout
cleanup failed with `exec: "node": executable file not found in $PATH`
on every Forgejo run.

The frontend job already installed Node, which is why it succeeded.
The GitHub mirror is unaffected because ubuntu-latest ships with Node
preinstalled.

Validated against the failed run https://git.lacompagniemaximus.com/maximus/Simpl-Resultat/actions/runs/122
2026-04-09 09:44:24 -04:00
b5c81b2a01 Merge pull request 'ci: add PR validation workflow (cargo check/test + npm build) (#60)' (#61) from issue-60-pr-check-workflow into main 2026-04-09 13:31:15 +00:00
le king fu
8e5228e61c ci: add PR validation workflow (#60)
Some checks failed
PR Check / rust (push) Failing after 2m2s
PR Check / frontend (push) Successful in 2m10s
PR Check / rust (pull_request) Failing after 1m32s
PR Check / frontend (pull_request) Successful in 2m8s
Adds .forgejo/workflows/check.yml (and a GitHub mirror) that runs on
every branch push (except main) and on every PR targeting main.

Two parallel jobs:
- rust: cargo check + cargo test, with cargo registry/git/target caches
  keyed on Cargo.lock. Installs the minimal Rust toolchain and the
  webkit2gtk system deps that the tauri build script needs.
- frontend: npm ci + npm run build (tsc + vite) + npm test (vitest),
  with the npm cache keyed on package-lock.json.

The Forgejo workflow uses the ubuntu:22.04 container pattern from
release.yml. The GitHub mirror uses native runners (ubuntu-latest)
since the GitHub mirror exists for portability and uses GitHub-native
actions.

Documents the new workflow in CLAUDE.md alongside release.yml so future
contributors know what CI runs before merge.
2026-04-09 09:21:20 -04:00
le king fu
198897cbba chore: release v0.6.7
All checks were successful
Release / build-and-release (push) Successful in 22m49s
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-29 21:16:35 -04:00
7f6126f305 Merge pull request 'fix: update picomatch 4.0.3 → 4.0.4 (#43)' (#45) from issue-43-update-picomatch into main 2026-03-30 01:14:18 +00:00
b9bdab8b88 Merge pull request 'fix: remove expense filter from Category Over Time report (#41)' (#42) from fix/simpl-resultat-41-category-time-report-filter into main 2026-03-30 01:14:11 +00:00
le king fu
99dc78ab15 fix: update picomatch 4.0.3 → 4.0.4 to resolve HIGH vulnerabilities (#43)
Fixes GHSA-3v7f-55p6-f55p (method injection) and GHSA-c2c7-rcm5-vvqj (ReDoS).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-29 21:09:17 -04:00
escouade-bot
97da6f9f71 fix: address reviewer feedback (#41)
- Correct CHANGELOG to reflect default type is expense, not all types
- Validate select onChange value against allowed CategoryTypeFilter values

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 04:01:43 -04:00
escouade-bot
d099678ddf fix: address reviewer feedback (#41)
- Default categoryType filter to "expense" so top-N ranking is not
  skewed by mixing income and expense amounts
- Remove redundant showFilterPanel condition (hasCategories already
  covers the overTime tab)
- Add unit tests for getCategoryOverTime query construction and output

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 02:02:41 -04:00
escouade-bot
56b46f1dfa Remove hard-coded expense filter from Category Over Time report
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>
2026-03-18 00:05:27 -04:00
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
52faa017f3 chore: release v0.6.5
All checks were successful
Release / build-and-release (push) Successful in 27m1s
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 20:14:52 -04:00