perf(reference-data): sweep the remaining raw reads onto the session cache, ratchet to 0 (#1941)

Final consumer migration of the responsiveness plan: the 35 files still
fetching fiscal periods, settings, accounts, cash accounts, dimensions or
templates on their own now read lib/reference-data, and every client
write site invalidates the shared cache instead of refetching locally.

Settings and registries: FiscalYearsManager, FiscalPeriodEditor (period
snapshotted once per company so a revalidation cannot reset dates being
edited), BookingTemplatesPanel, ChartOfAccounts, ChartOfAccountsManager,
EditAccountDialog, CorrectionEntryDialog, StrikeLinesDialog,
InvoicePaymentAccountsSettings; the dimensions registry (DimensionsManager,
DimensionCombobox, LineDimensionFields, DimensionFilter, bookkeeping/[id])
reads useDimensions and the ad-hoc fetchDimensions/fetchDimensionsCached
helpers are deleted.

Pages and pickers: CashAccountSelector (FyPicker-shaped restore, once per
company load), use-account-names, FiscalYearGapNotice,
OpeningBalancePeriodStep, BankFileConfirmStep, ImportReviewStep, the import
page (invalidates accounts + periods after a SIE execute), customers list,
invoices list + detail, pending, salary employee, asset dispose, year-end
and periodisering pages (invalidate periods after closing), reports
DimensionPnlView (its pivot picker read the wrong payload key and was
always empty; it now populates), SkatteverketPanel, TemplatePicker,
ArticleForm (vat_registered).

Invoice dialogs and extensions: SendInvoiceDialog, PaymentBookingDialog
(init reduced to the credit-note lookup + catalogue, proposal and voucher
preview fire on open when cached; a local getSession replaces the network
getUser for the fallback CC), InvoiceInboxWorkspace, TicWorkspace,
ArcimMigrationWorkspace (invalidates after each SIE import step),
enable-banking AccountPickerDialog.

raw-reference-fetch ratchet: 35 -> 0 files.

Co-authored-by: Jakob Wennberg <311770904+jakobwennberg-oss@users.noreply.github.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Jakob Wennberg
2026-08-26 14:56:37 +02:00
committed by GitHub
co-authored by Jakob Wennberg Claude Fable 5
parent 4560ccbfc9
commit 3ee3565d6d
41 changed files with 636 additions and 977 deletions
+4 -18
View File
@@ -1,6 +1,6 @@
'use client'
import { useEffect, useState } from 'react'
import { useState } from 'react'
import { X } from 'lucide-react'
import { Label } from '@/components/ui/label'
import { Badge } from '@/components/ui/badge'
@@ -14,7 +14,7 @@ import {
} from '@/components/ui/select'
import DimensionCombobox from '@/components/dimensions/DimensionCombobox'
import { useCompanySettings } from '@/components/settings/useSettings'
import { fetchDimensions, type DimensionDto } from '@/components/dimensions/types'
import { useDimensions } from '@/lib/reference-data/hooks'
export type DimensionFilterValue = {
/** SIE dimension number as a string ('1' kostnadsställe, '6' projekt). */
@@ -43,27 +43,13 @@ interface Props {
*/
export function DimensionFilter({ value, onChange }: Props) {
const { settings } = useCompanySettings()
const [dims, setDims] = useState<DimensionDto[]>([])
// Registry from the session cache (lib/reference-data).
const { dimensions: dims } = useDimensions()
// Which dimension the picker targets while no value is selected yet;
// once a value is picked, `value.dimNo` is the source of truth.
const [pendingDimNo, setPendingDimNo] = useState('6')
const enabled = settings?.dimensions_enabled === true
useEffect(() => {
if (!enabled) return
let cancelled = false
fetchDimensions()
.then((rows) => {
if (!cancelled) setDims(rows)
})
.catch(() => {
// Best-effort: without the registry the filter simply doesn't render.
})
return () => {
cancelled = true
}
}, [enabled])
if (!enabled || dims.length === 0) return null
const activeDimNo = value?.dimNo ?? pendingDimNo
+10 -7
View File
@@ -2,6 +2,7 @@
import { ENABLED_EXTENSION_IDS } from '@/lib/extensions/_generated/enabled-extensions'
import React, { useState, useEffect, useCallback } from 'react'
import { useCompanySettings } from '@/lib/reference-data/hooks'
import { Button } from '@/components/ui/button'
import { Skeleton } from '@/components/ui/skeleton'
import {
@@ -31,7 +32,7 @@ import {
} from 'lucide-react'
import type { VatPeriodType } from '@/types'
import { formatRedovisare, formatRedovisningsperiod } from '@/lib/skatteverket/format'
import { useCapability } from '@/contexts/CompanyContext'
import { useCapability, useCompanyOptional } from '@/contexts/CompanyContext'
import { CAPABILITY } from '@/lib/entitlements/keys'
import { UpgradeNote } from '@/components/billing/UpgradeNote'
import { InfoTooltip } from '@/components/ui/info-tooltip'
@@ -367,13 +368,15 @@ function SkatteverketPanelInner({
}
}
// Helper to get redovisare from settings
// Redovisare from the session-cached settings row (lib/reference-data).
// entity_type falls back to the company row, as /api/settings did.
const { settings: companySettings } = useCompanySettings()
const companyEntityType = useCompanyOptional()?.company?.entity_type ?? null
const getRedovisare = useCallback(async (): Promise<string> => {
const res = await fetch('/api/settings')
const { data } = await res.json()
if (!data?.org_number) throw new Error('Organisationsnummer saknas')
return formatRedovisare(data.org_number, data.entity_type)
}, [])
const orgNumber = companySettings?.org_number
if (!orgNumber) throw new Error('Organisationsnummer saknas')
return formatRedovisare(orgNumber, companySettings?.entity_type ?? companyEntityType)
}, [companySettings, companyEntityType])
const getRedovisningsperiod = useCallback((): string => {
return formatRedovisningsperiod(periodType, year, period, fiscalYearEnd)
+10 -14
View File
@@ -4,7 +4,8 @@
// Rendered by the focused /reports/[slug] route (see components/reports/FocusedReport.tsx).
// The regulated table/figure rendering is unchanged from the original monolith.
import React, { useState, useEffect, useCallback } from 'react'
import React, { useState, useEffect, useCallback, useMemo } from 'react'
import { useDimensions } from '@/lib/reference-data/hooks'
import Link from 'next/link'
import { useTranslations } from 'next-intl'
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
@@ -3426,22 +3427,17 @@ export function DimensionPnlView({ periodId, dateRange }: { periodId: string; da
data: DimensionPnlReport | null
error: string | null
} | null>(null)
const [dims, setDims] = useState<{ sie_dim_no: number; name: string }[]>([])
const [dimNo, setDimNo] = useState('6')
const reportQs = `${reportQuery(periodId, dateRange)}&dim_no=${encodeURIComponent(dimNo)}`
// Registered dimensions for the pivot picker (best-effort; the report
// defaults to projekt if the registry read fails).
useEffect(() => {
fetch('/api/dimensions')
.then((res) => res.json())
.then((payload) => {
if (Array.isArray(payload.data)) {
setDims(payload.data.map((d: { sie_dim_no: number; name: string }) => ({ sie_dim_no: d.sie_dim_no, name: d.name })))
}
})
.catch(() => {})
}, [])
// Registered dimensions for the pivot picker, from the session cache
// (lib/reference-data); best-effort, the report defaults to projekt while
// the registry is unavailable.
const { dimensions } = useDimensions()
const dims = useMemo(
() => dimensions.map((d) => ({ sie_dim_no: d.sie_dim_no, name: d.name })),
[dimensions],
)
useEffect(() => {
let cancelled = false