* fix(ux): update lists in place on actions instead of takeover spinners and jumps
Founder report: the app feels glitchy when clicking around, especially
when deleting a row or booking something. The repo-wide anti-pattern
behind it: single-row actions trigger whole-list skeleton/spinner
takeovers (layout collapse, scroll jump, full stagger-enter replay),
deletes give zero feedback then hard-jump, and the /transactions exit
"animation" was filter-only and never animated.
Per surface:
- Never take over a rendered list for a background refresh. The
skeleton/spinner swap is now reserved for an empty (or foreign) list
on /transactions (fetchTransactions), /pending (fetchOperations,
covering both listed Granskning findings, one file), kundfakturor
(fetchInvoices), leverantörsfakturor (fetchInvoices, plus
try/catch/finally so a failed fetch can no longer stick the skeleton
or masquerade as an empty register) and the verifikat list
(JournalEntryList now takes a refreshToken prop and refetches in
place; /bookkeeping no longer key-remounts it into a spinner, so
expansion/selection/pagination/scroll survive a created verifikat).
Quiet inline Loader2 cues near the list headers on /transactions and
/pending signal a background reconcile.
- /transactions row exit: exiting rows (booked/ignored/deleted) stay
rendered through the existing 350ms window with a real exit
transition (.row-exit: fast fade, then the space closes by
transitioning cell paddings/line metrics and a numeric max-height on
the fixed-height cell spans) and pointer-events off. Instant removal
under prefers-reduced-motion. Applied to the inbox cards, the
skattekonto card and the history rows.
- /transactions delete: routes through processingId (row spinner) and
the exitingIds path, and decrements totalUncategorizedCount when the
deleted row was pending (the realtime echo is not guaranteed for
DELETE on a filtered subscription).
- FyPicker double-fetch: the initial fetch now waits for FyPicker's
onReady (fires after its persisted-scope restore), so mount does one
correctly scoped fetch instead of racing an unscoped fetch against
the restore refetch (list -> skeleton -> list on every visit). Period
changes refetch background-only behind the client-filtered list.
- Pagination survives realtime echoes: background refreshes re-fetch
range(0, pagedCountRef) instead of resetting to the first 200 rows,
so "Visa fler" pages no longer collapse after any action.
Gates: full vitest suite green (14764 passed), tsc output byte-identical
to the origin/main baseline, eslint 0 errors on touched files,
check:guards green, package-lock untouched.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(ui): apply review round on action-feedback smoothness
- /pending: sequence-guard fetchOperations so a stale previous-tab
response can't overwrite the current tab's rows, counts, or loading cues
- /pending: check res.ok on the pending fetch and both history fetches
before applying payloads; failures keep current rows and surface the
existing error toast
- /transactions: reset fiscal scope (fyReady/fyPeriodId/fyPeriod) during
render on company switch so FyPicker re-runs its persisted restore and
stale bounds never scope a fetch for the wrong company
- /transactions: drop a deleted row's id from selectedIds so the bulk bar
can't act on a deleted row
- row exit: add the inert attribute on exiting row wrappers alongside
pointer-events so keyboard focus and activation are blocked too
- JournalEntryList: preserve selection on refreshToken background
refreshes (reconciled against the refreshed page); user-initiated
reloads still clear it
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Jakob Wennberg <311770904+jakobwennberg-oss@users.noreply.github.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>