- 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
## What's changed
Show an image preview in the detail view when a field's `uiType` is `image`, instead of rendering it through the generic list cell renderer.
- Added `Avatar` from MUI and render a 120x120 rounded preview for `image` fields in `DetailFieldRenderer`
- All other field types keep their existing rendering via `ListCellRenderer`
## Files changed
- `react-openapi/src/components/fields/DetailFieldRenderer.tsx`
Reviewed-on: #14
Co-authored-by: Vishesh 'ironeagle' Bangotra <aetoskia@gmail.com>
Co-committed-by: Vishesh 'ironeagle' Bangotra <aetoskia@gmail.com>
## Summary
Align frontend with updated OpenAPI schema (rename `format` → `bank`, add `pipeline`) and introduce a dedicated `PATCH` operation for partial updates in react-openapi.
## Changes
### react-openapi: new `patch` operation support
- Add `patch` to `ResourceConfig.operations` type
- Separate PUT/PATCH detection in resource-config transformer
- `update()` now always calls PUT; new `patch()` method returned conditionally when spec defines PATCH
- Admin form still uses PUT for full replacements
### Fetch Request custom pages: schema alignment
- Rename `format` → `bank` in create form fields and TypeScript models (`FileSource`, `EmailSource`)
- Add `pipeline` field to create form and `FetchRequestCreate` model
- Export `PipelineType` from barrel index
### Fetch Request retry: use PATCH
- Retry handler now calls `patch(id, { status: "pending" })` instead of the generic `update()`
- Removes `useMutation` wrapper; uses simple local `retrying` state for loading
### Fix Account display in detail page
- Resolve FK displayFormat target in detail field rendering (was always using fetch-request format)
- Fix page title to use `applyDisplayFormat` with resolved FK objects instead of non-existent `account_name`
- Move `useMemo` before early returns to fix hooks ordering violation crash
## Testing
- Custom fetch request create form includes `bank` (select) and `pipeline` (select) fields
- Retry sends `PATCH` instead of `PUT` — no more 500 errors
- Detail page shows proper account name instead of `—`
Reviewed-on: #13
Co-authored-by: Vishesh 'ironeagle' Bangotra <aetoskia@gmail.com>
Co-committed-by: Vishesh 'ironeagle' Bangotra <aetoskia@gmail.com>
## Summary
Wire spec-driven auth into the frontend. Auth config (server URL, paths) is extracted from the served OpenAPI spec by `AppProvider`. `AuthProvider` receives the config as a prop. 401 responses from both the main API and the auth server dispatch an `auth:unauthorized` event that triggers redirect to `/login`. Fix `ProfileRoutes` URL duplication bug.
## Changes
### Auth config from spec
- **`main.jsx`** — `AppProvider` wraps everything, loads spec, exposes `authConfig`. `AuthProvider` receives `authConfig` from `useAppContext()`. `onUnauthorized` passed to both `AppProvider` and `AuthProvider` wires `navigate("/login")`.
### 401 handling
- **`AppProvider.tsx`** — remove `onUnauthorized` prop (handled by `AuthProvider`'s event listener instead, avoiding double-navigation).
- **`useApi.ts`** — 401 response interceptor dispatches `auth:unauthorized` CustomEvent on `window`.
### Profile routing fix
- **`Admin.tsx:ProfileRoutes`** — replace nested `<Routes>` (which caused `/profile/me/me` URL duplication with React Router v6) with `useLocation()`/`useNavigate()` conditional rendering. Only allows `/profile/me` and `/profile/me/edit`.
- **`Admin.tsx:ProfileComponentWrapper`** — replace `pushState() + reload()` with React Router `navigate()` for both `onEdit` and `handleSubmit`.
### Debug logging
- Temporary console logs at every navigation point for diagnosing remaining issues.
Reviewed-on: #12
Co-authored-by: Vishesh 'ironeagle' Bangotra <aetoskia@gmail.com>
Co-committed-by: Vishesh 'ironeagle' Bangotra <aetoskia@gmail.com>
### Summary of Changes:
1. **Spec-Driven Enums**:
- Updated `openapi_loader.ts` to collect all standalone enum schemas (e.g., `FetchRequestStatus`) into the `AppConfig.enums` map.
- Implemented `mergeProperties` and `oneOf`/`anyOf` resolution in `openapi_loader.ts` to ensure complex schemas like `FetchRequest` (using `allOf`) and `source` (using `oneOf`) are correctly parsed.
2. **Customizable Labeling**:
- Added `enumOption` (template-based) and `enumLabels` (mapping-based) to the config and field types.
- Implemented `resolveTemplate` in `utils/options.ts` to handle placeholders like `'{name} {number}'` or plain field names.
3. **UI Integration**:
- **`FormField.tsx`**: Updated relation and enum selects to use `getFieldOptions()` for correct key/value pairs and labels. Added value normalization to extract keys from API objects.
- **`EnhancedTable.tsx`**: Updated `valueOptions` to use key/value pairs for `singleSelect` and updated `FieldRenderer` to show the human-readable label for enums.
- **`FilterBar.tsx`**: Updated `extractOptions` to use spec-driven labels for enum filters.
- **`ResourceView.tsx`**: Centralized filter matching logic into a `getDisplayString` helper, ensuring filter comparisons use the same templates as the UI labels.
4. **App Fixes**:
- `FetchRequests.tsx` and `FetchRequestDetail.tsx` now derive status and format options from the OpenAPI spec via `useConfig()` instead of using hardcoded arrays.
Reviewed-on: #10
Co-authored-by: Vishesh 'ironeagle' Bangotra <aetoskia@gmail.com>
Co-committed-by: Vishesh 'ironeagle' Bangotra <aetoskia@gmail.com>