Transactions can carry a `notes` field (set via the API — e.g. the
currency-conversion metadata on this user's imported historical data), but
there was no way to see it in the UI at all.
TransactionsTable now shows a note icon next to edit/delete, only when a
transaction actually has one, opening a dialog with the text. The edit
form's `notes` field was already wired end to end (state, payload) but had
no input in the JSX — added it, since a view-only note with no way to edit
or clear it would be a dead end.
Also fixed e2e/run.sh's readiness check, found while running the full
suite for this change: it polled `/` (nginx, answers almost immediately)
instead of `/api/health`, so the first test could start before the backend
finished migrating/seeding and fail trying to parse nginx's HTML error
page as JSON.
Picking a category at the top meant scrolling past the whole "top
categories" list before reaching its chart, then scrolling back up to
pick another one. Swapped the two so the chart renders immediately after
the category/range selector.
Language and Theme were two separate Cards stacked on top of each other —
both are pure client-side display preferences, so splitting them side by
side (language left, theme right) saves a card's worth of vertical space.
Collapses back to a single column on mobile.
Theme previously followed only the OS's prefers-color-scheme, which
switches automatically on this user's Mac at sunset/sunrise. Adds an
explicit override in Settings: Light/Dark/System, System being the
previous (default) auto-follow-the-OS behavior.
Persisted client-side in localStorage (lib/theme.ts, same pattern as
language) rather than server-side like currency — it's a pure display
preference, and synchronous localStorage reads are what make the
anti-FOUC inline script in index.html possible. index.css already had the
[data-theme] CSS variables laid in ahead of time; only the JS to actually
set the attribute, and the Settings UI, were missing.
Covered by 2 new Playwright specs (theme-switcher.spec.ts) run against the
isolated e2e stack: explicit choice overrides a dark OS and survives
reload; switching back to "system" doesn't leave a stale forced theme.
backend/tests/: 33 pytest tests against a real Postgres database
(aurum_test, created/migrated/dropped per run) exercised through the actual
HTTP API — not sqlite, not mocks, and the real `aurum` database is never
opened (app lifespan is skipped, get_session is overridden per test).
Covers transaction category-kind validation, year/month/date-range
filtering and pagination, dashboard summary arithmetic, budget status, cash
flow, and the /transactions/years picker.
e2e/: 6 Playwright specs driving a real browser against a fully separate
throwaway docker-compose stack (run.sh: separate project name, database,
port, and volume, torn down with `down -v` on exit) — regression coverage
for the Recent Transactions month bug, the Card grid-overflow bug, the
frozen-year bug, the Net Worth default range, and the Dashboard->
Transactions deep link, all fixed earlier this session.
Also synced the backend's APP_VERSION constant (stale at 0.15.2, embedded
in exported backups) with the frontend version.
Net Worth opened on a 30-day window by default, which frequently shows
a dip (short windows don't have enough recorded income relative to
spending) even though the long-term trend is a steady climb —
demotivating for no good reason. Defaults to 5y instead.
Dashboard's "All transactions" link always sent users to the current
month regardless of which month they had selected. It now carries
?year=&month= and TransactionsPage seeds its state from those params
(clamped/validated, falls back to the current month if absent).
"This year" and "12 months" were two different ways of showing roughly
the same recent window, which read as duplicated. Replaced the "12m"
preset with "custom": pick an arbitrary fromYear-toYear span via a new
YearRangeSelector (two dropdowns built on the same internal component
YearSelector already uses).
Also de-duplicated the RangePreset/computeRange logic that was copied
verbatim between CashFlowPage and ReportsPage into shared
lib/dateRange.ts — both pages had the identical bug/duplication.
The pill-row year switcher from the previous commit added a whole
extra row of buttons above the month pills, which read as cluttered.
Replaced it with a single dropdown button (current year + chevron)
placed to the right of MonthSelector on all three pages, matching how
a lot of finance-app date pickers separate month (frequent) from year
(occasional) navigation.
Year was previously hardcoded to the current year on all three pages
(useState with no setter) — only the month could be changed via
MonthSelector, making it impossible to look at or compare older years
even though transaction history goes back further than the current
year.
Adds GET /transactions/years (contiguous range from the earliest
transaction through the current year) and a YearSelector component
matching MonthSelector's style, wired into all three pages alongside
a real year useState.
Card had no min-width: 0, so as a direct CSS Grid child on the
Dashboard it would grow to fit nowrap (truncate) text instead of
letting the ellipsis kick in — classic grid blowout. Reproduced with
Playwright (scrollWidth 2143px vs 1440px viewport before the fix,
matches after). Fixing it on the shared Card primitive protects every
grid/flex layout that uses it, not just Dashboard.
RecentTransactionsCard queried transactions without year/month filters,
so it always showed the latest 6 transactions globally instead of the
ones for the month picked in the MonthSelector. DashboardPage now
passes its year/month state down to the card, which forwards it to
useTransactions.
Screenshots section at the top of README (dashboard and net worth
front and center, four supporting views in a grid below). Roadmap and
Community sections removed per request.
Aurum — a self-hosted personal finance operating system: cash flow,
net worth tracking, budgets, goals, recurring payments, reports,
proactive alerts, and a compound-interest returns calculator, all
bilingual (RU/EN) and mobile-first. Ships as a three-container Docker
Compose stack (Postgres + FastAPI + nginx-served React frontend).
See README.md for setup and CONTRIBUTING.md for development.