Commit Graph
5 Commits
Author SHA1 Message Date
4ac7b45c8a perf(layout): dashboard layout in two waves, nav flags as one RPC, local JWT verification (#1946)
The dashboard layout runs on every hard load, hard refresh, company switch
and the 16 router.refresh() sites, and loading.tsx cannot paint until it
resolves. It cost ~20 network calls in 4 sequential waves: a third
getUser() round trip to Supabase Auth (after the proxy's and the route
guard's), the company resolution, then 16 reads including four limit-1
probes whose only job is to decide whether to render the Webshop and
Körjournal nav rows, and an entitlements read that itself ran two waves.

- lib/auth/claims.ts: claimsPinned/userFromClaims extracted from
  require-auth.ts (unchanged) so the dashboard request context shares the
  exact pinning + mapping. getDashboardAuthContext verifies the JWT locally
  and falls back to getUser() only when claims are missing, unpinned or
  unverifiable: the proxy already performed the per-request revocation
  check before the layout runs (same semantics approved for routes on
  2026-07-23).
- Wave 1 (user-keyed, parallel with the company resolution): team
  membership, profile, user preferences and the memberships join, which
  now also supplies the active company's row and role, so the separate
  companies and company_members reads are gone.
- Wave 2 (company-keyed): settings, agent profile, the switcher's settings
  names, entitlements in ONE wave (getCompanyEntitlements takes the
  team_id the join already carries and runs the grants read alongside
  config + subscription), and get_dashboard_nav_flags().
- supabase/migrations/20260826120000_get_dashboard_nav_flags.sql:
  SECURITY INVOKER, STABLE, EXECUTE for authenticated only; RLS applies
  inside. lib/dashboard/nav-flags.ts wraps it with the pre-RPC four-probe
  fallback on PGRST202/42883/42501 (self-hosted not yet migrated, deploy
  ordering) and degrades to hidden rows on any other error.
- tests/pg/dashboard-nav-flags-rpc.pg.test.ts (6): fresh company, active vs
  pending WooCommerce, active Shopify, mileage trips, RLS for a member of
  another company, EXECUTE grants. Unit tests for the wrapper (RPC row,
  single-object payload, each fallback code, other errors) and for the
  entitlements teamId option.

~20 calls / 4 waves -> ~12 calls / 2 waves, 0 auth network calls.

Co-authored-by: Jakob Wennberg <311770904+jakobwennberg-oss@users.noreply.github.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-08-26 15:14:49 +02:00
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>
2026-08-26 14:14:54 +02:00
MattssonandClaude Fable 5 45d7f1be4e feat(mileage): surface Körjournal in the nav behind a settings toggle (#1540)
* feat(mileage): surface Körjournal in the nav behind a settings toggle

The /mileage page shipped hidden: the route works but no nav row points at
it. Add company_settings.mileage_enabled (mirroring dimensions_enabled) with
a switch in Fönster -> Bokföring, and show the Arbeta nav row when the toggle
is on OR the company already has mileage_trips rows, the same hybrid gate as
webshop orders, so trips created via API/MCP can never become invisible
underlag. UI visibility only, never load-bearing for correctness.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(migrations): move mileage_enabled migration after already-applied 20260812153208

origin/main merged in 20260812153208 which prod has already applied; a new
file sorting before it risks an out-of-order db push abort.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-08-12 21:48:01 +02:00
44cff5e5e4 feat(onboarding): activation quick wins on the Hem checklist (#1461)
* feat(onboarding): activation quick wins on the Hem checklist

First implementation slice of the approved activation concept
(artifact de543d57, dev_docs/onboarding_activation_analysis.md §9):

- New "Kvitton och underlag" checklist step, gated on the invoice-inbox
  extension like the Skatteverket step; done once the company has ever
  received an inbox item (email/WhatsApp/upload). Non-AI companies route
  to billing, matching the assistant step.
- Personalized VAT line in the Skatteverket step: the company's real next
  momsdeklaration due date from the deadlines table, with an explicit
  "välj momsperiod" prompt when vat_registered is set but moms_period is
  null (that state silently generates zero VAT deadlines).
- Truthful Att göra empty state: while the setup checklist is open and no
  journal entry is posted, the all-clear reads "Bokföringen är tom än"
  instead of a false "Allt klart!".
- Activation funnel events (onboarding_setup_step_started / _completed /
  _dismissed) via posthog-js, mirroring the existing guarded capture
  pattern; sandbox never renders the block so no extra gate is needed.

Pure helpers live in lib/onboarding/checklist.ts with tests; step
numbering now adapts to both optional extensions.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* docs: record the receipts-signal and moms-period-guard decisions

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(onboarding): review triage: error-safe emptyLedger, stale-state guard, copy

- A failed posted-entries count no longer reads as an empty ledger.
- Confirming a suggested match books an entry, so the empty-ledger copy
  retires for the rest of the session (postedSinceLoad).
- 'Bokföringen är tom än så länge' reads naturally.

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>
2026-08-08 15:14:34 +02:00
Mattsson e11f70b347 Bug/gh issues fiz (#1103)
* refactor: optimize page loading and data fetching

* fix: resolve recurring production runtime errors

* feat: add MCP company and customer updates

* fix: handle year-end tax adjustments

* feat: harden annual report compliance

* fix: expand invoice logo and font support

* fix: sanitize API route error responses

* fix: sanitize user-facing error messages

* feat: persist onboarding and tax assessment notices

* fix: reduce cloud backup audit churn

* feat: refine invoice editor layout

* fix: show saved tax adjustments in INK2

* fix: complete annual report API mappings

* docs: record operational safeguards and decisions

* fix: harden annual report review findings

* fix: adjust column span for description based on VAT registration

* New css class name
2026-07-21 23:00:15 +02:00