3ee3565d6d2df3ebde453a4d537299a3415e5dd8
3 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
567fae654c |
perf(bookkeeping): booking dialogs render populated on open from the session cache (#1935)
The bookkeeping dialogs were the customer's "fields load late" in its purest form: Bokför (TransactionBookingDialog + the embedded JournalEntryForm) issued five requests on every open (fiscal periods, accounts, settings, cash accounts, then the voucher preview once the first two had landed), Nytt verifikat the same minus one, BookDirectlyDialog four, and the template dialogs two. Each Radix dialog unmounts on close, so every reopen paid the full price again, and several fields visibly flipped: the bank line seeded '1930' then rewrote itself, the series defaulted to 'A' until settings arrived, the period select was empty. All of them now read lib/reference-data (seeded by the dashboard layout): - JournalEntryForm: periods, accounts and settings from the hooks; dimensionsEnabled derived, not fetched; the voucher-number preview is keyed on the entry date (the route resolves the period from it) so it fires as soon as the series is known instead of after the period fetch; after activating accounts it invalidates the shared accounts cache; the create-period dialog callback invalidates the periods cache. - TransactionBookingDialog: settlement account and its name derived with useMemo from the cached cash accounts; the form mounts on the first paint. - BookDirectlyDialog: cash accounts, periods and accounts from the hooks; the '1930'-then-rewrite disappears because the resolved account is known on the first render. - TemplateBookDialog, BookingTemplatePicker, TemplatePicker: templates (and periods) from the hooks. - BookingTemplatesPanel (delete, import) and CreatePeriodDialog (create) invalidate the corresponding cache entries so every picker sees the change at once. - fetchers.ts: booking templates are booking_templates rows (BookingTemplateLibrary), not the static BookingTemplate shape. Per open: Bokför 5 requests -> 0 blocking (voucher preview is a non-blocking hint), Nytt verifikat 5 -> 1 non-blocking, BookDirectly 4 -> 0, Mall 2 -> 0, template pickers 1 -> 0. raw-reference-fetch ratchet: 51 -> 46 files. Co-authored-by: Jakob Wennberg <311770904+jakobwennberg-oss@users.noreply.github.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
9a56b7aff9 |
perf(bookkeeping): fiscal-year pickers and cash accounts read the session cache (#1934)
First consumer migration onto lib/reference-data. FyPicker and
FiscalYearSelector (14 consumer surfaces, 47 fiscal-period fetch sites
before this series) now read useFiscalPeriods(); with the layout seed the
restore of the persisted scope runs in the first effect tick and onReady
fires on mount instead of after a round trip. Their restore rules are
extracted into a pure resolveInitialFiscalScope() (lib/reference-data/
fiscal-scope.ts) so the two pickers cannot drift apart again, and the
restore runs once per company load, not on every background revalidation.
- /reports: the static catalog renders immediately; only the "no fiscal
year" empty state waits for the picker (previously six skeleton bars
until /api/bookkeeping/fiscal-periods resolved).
- JournalEntryList (/bookkeeping): resolves its initial scope from the
cached list instead of its own fetch; the saved-scope shortcut still
unblocks the entries fetch first when nothing is cached, and resolution
is guarded to once per company so a revalidation can never snap a
deep-link "all years" visit back to the stored year.
- /transactions: the account chooser reads useCashAccounts({ enabledOnly })
(seeded) instead of fetching /api/cash-accounts on every visit; the bank
sync button invalidates that entry after a sync.
- STORAGE_KEY_PREFIX / ALL_YEARS_VALUE move to a dependency-free
fiscal-year-storage.ts (re-exported from FiscalYearSelector) so lib/ code
can import them without a React component.
raw-reference-fetch ratchet: 55 -> 51 files.
Co-authored-by: Jakob Wennberg <311770904+jakobwennberg-oss@users.noreply.github.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
|
||
|
|
47fe193c48 |
feat(perf): session-cached reference data layer, server-seeded, with a raw-fetch ratchet (#1932)
* feat(perf): session-cached reference data layer, server-seeded, with a raw-fetch ratchet
Customer report (2026-08-26): "it takes time before all fields load when
clicking around". The cause is on the client: fiscal periods, settings,
accounts, cash accounts, dimensions and templates are fetched raw from 47 /
27 / 14 / 8 / 12 / 5 independent call sites, uncached, on every mount and
every dialog open, each request paying the auth proxy and route wrapper
before its own query. SWR was adopted for exactly this on 2026-07-13 but
reached only three files.
This PR adds the layer; consumers migrate in the follow-ups.
- lib/reference-data/keys.ts: one key builder per data set, company id in
position 1, null without a company; company_settings keeps the shape
useCompanySettings already uses so that hook is seeded without a change.
- lib/reference-data/fetchers.ts: browser Supabase for fiscal periods and
cash accounts (mirroring period.list and listForCompany ordering, pinned
by tests), /api for the lists whose routes do real work (accounts RPC,
dimensions ensure, template scoping, customer masking).
- lib/reference-data/hooks.ts: useFiscalPeriods, useCashAccounts,
useAccounts, useDimensions, useBookingTemplates, useCustomers,
useSuppliers, useArticles (+ re-exported useCompanySettings); one-minute
dedupe, keepPreviousData, background revalidation kept on so writes from
MCP/agents/other tabs surface.
- lib/reference-data/invalidate.ts: invalidateReferenceData(kind) for the
success path of every client write.
- lib/reference-data/seed.ts + components/providers/ReferenceDataSeed.tsx:
the dashboard layout fetches fiscal periods and cash accounts in its
existing batch and hands them, with the settings row it already had, to
SWR as fallback, so the first form of a session renders its period, bank
account and settings-driven fields on first paint. getDashboardSettings
now selects the full row for that (its other consumers read a subset).
The chart of accounts is not seeded (hundreds of KB for large charts).
- scripts/checks/raw-reference-fetch.mjs, wired into check:guards as a
per-file ratchet: GET-shaped fetch('/api/<reference path>') anywhere in
client-facing code and .from('<reference table>').select( in 'use client'
files. Baselined at 55 files; new sites fail CI; at 0 the entry goes.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(checks): anchor every optional whitespace run in the raw-reference-fetch regex
CodeQL js/redos flagged the `\s*,?\s*\)` tail: two adjacent optional
whitespace runs around an optional comma backtrack polynomially on a long
near-miss. The URL and init-object pieces are now named fragments and
every whitespace run is followed by a literal, so there is one way to
match. Behaviour unchanged (same 7 fixtures + baseline count of 55);
a worst-case timing test pins the linear scan.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(checks): make the use-client directive regex unambiguous (CodeQL js/redos)
An unclosed /* let the lazy comment body be re-split at every later /*.
The body is now (?:[^*]|\*(?!\/))* which cannot cross a */, so the outer
repetition has one parse. Pinned with a 3000-comment worst-case test.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* ci: re-trigger checks for the rebased head
No workflow ran for dd560a7af (nor after close/reopen); an empty commit
gives the pull_request event a fresh head. No code change.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(checks): single-character whitespace alternative in the use-client detector (CodeQL js/redos)
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>
|