- make each month a card-sized accordion (single-open, first month
expanded by default) instead of a sticky header over flat rows
- turn the floating pill into a month-year selector: opens a menu of
all months and expands + scrolls to the chosen month
- remove tags from expense rows again until a placement is settled
- remove unused @mui/x-data-grid@7, which pulled @mui/system@7 over
material's v5 and caused the createTheme runtime crash
- drop the per-row accordion and delete ExpenseDetail (was only used
for the expanded view); rows are now static bordered cards with a
hover highlight
- row layout: logo | name/date + account chip | amount
- tag chips were added to the row during iteration but removed for
now until a better placement is settled
- add resolveMediaUrl() in fields/utils.ts: prefixes /uploads/... and
other relative paths with the configured baseApiUrl, leaves absolute
http/data/blob URLs untouched
- use it for every image render: ListCellRenderer, DetailFieldRenderer,
ImageField form preview, and FileUploadField preview/href (which
hardcoded /uploads/<value>)
- drop the Expense-side resolveLogoUrl hack and logo pre-normalization;
the Expenses page now renders logos through the same react-openapi
path as the admin entity list
- make the floating month pill interactive (button role, hover,
focus ring) and jump to the active month's header on click/Enter
- scroll via a non-sticky month anchor + window.scrollTo instead of
scrollIntoView, which no-ops on sticky headers that are always
already in view
- show spent and income totals separately per month header
(debits red, credits green) instead of a single net amount
- pin the current month header below the navbar while scrolling
its section (glass backdrop, divider), Stripe-style grouping
- add a floating month pill that updates via scroll-spy so the
active month stays visible across long feeds
occurred_at comes from the backend as a DD-MM-YYYY string, which
new Date() cannot parse (NaN), so most transactions fell into an
"unknown" bucket and month sorting was broken.
- add parseOccurredAt() that only accepts DD-MM-YYYY, day-first,
and throws on any other format or invalid date
- monthKey() now throws instead of returning "unknown"
- add currentMonthKey() for the "this month" stat
- sort groupByMonth/summary by parsed timestamp
- add CurrencyField renderer (inherits NumberField for editing) with
cached Intl formatter; route via uiType "currency" in list/detail/form
- add resourceConfig.fieldTypes override applied in AppProvider, so
amount becomes currency for both Admin and the Expenses page
- render Expenses page through react-openapi fields (ListCellRenderer,
DetailFieldRenderer, applyDisplayFormat) instead of custom fields
- resolve relative /uploads entity logos against the API base URL on
load so entity logos render on the expenses feed
- drop custom avatar, currency/date formatters from the Expense module
- memoize accordion cards and groupByMonth so toggling one
expense re-renders O(1) cards instead of the whole list
- cache Intl.NumberFormat and date formatters per key (was
~2-3 instantiations per item per render)
- unmount collapsed accordion details via unmountOnExit to cut
initial mount cost and input delay
- restructure Account row in ExpenseDetail so Chip is not nested
inside <p> (fixes validateDOMNesting warning)
- avoid mutating expenses state array when sorting