HWHeat Waves
    DashboardUtforsk
    Analyse
    Data Kilder
      • Explore Slice
      • Read Path
      • Routes
      • Atlas Route
      • Charts Correlation
      • Chart Libraries
      • Dashboard Route
    • Design Rationale
    • Doc Map
    DocsSettings
    DashboardAtlasUtforsk
    Analyse
    Data Kilder
    1. Documentation
    2. Application Architecture
    3. Dashboard route — configurable block playground over fire_data

    Loading documentation...

    Dashboard route — configurable block playground over fire_data

    `/dashboard` is a drag-and-drop, snap-to-grid block canvas where users compose stat + chart blocks over the shared `FireDataQuery`. Layouts persist per user; blocks read from one snapshot (plus per-override re-reads).

    Dashboard route (/dashboard)

    This page owns the /dashboard surface: a spacious, Supabase-report-builder-style block playground. Sections hold a responsive grid of blocks (stat cards and charts) that drag, reflow, and snap into the nearest grid cell. Arrangements persist per user. It does not duplicate ETL contracts, the read path, or full DDL — use the links below.

    TopicOwner
    get_fire_data / catalogread-path-get-fire-data-and-catalog-caching.md
    Route inventory (nav)app-routes.md
    Postgres contract / RLS../fire-data-schema/postgres-contract-rls-and-keys.md
    Chart libraries by areacharts-libraries-by-area.md

    Flow

    1. app/(main)/dashboard/page.tsx — server page; searchParams read inside a Suspense child.
    2. fireDataQueryFromSearchParams + ConsoleShell (url="tier2", route="dashboard") — Tier-2 URL ↔ FireDataQuery; defaults from _lib/default-query.ts. The Atlas "Ditt utsnitt" CTA opens /dashboard with the same query.
    3. loadDashboardFireDataSnapshot → resolveFireDataRequest → getCachedFireData builds the base snapshot (one read per filter change). Multi-source defaults (sources 1–4).
    4. The user's saved layout loads via loadDashboardLayout (auth users); anonymous visitors get DEFAULT_DASHBOARD_LAYOUT_V1 read-only.
    5. resolveDashboardSnapshots resolves any per-section / per-block query overrides into extra snapshots, deduped by patch hash (see Query overrides).
    6. DashboardShell (client) holds the layout config + edit mode and renders DashboardCanvas (react-grid-layout).

    Access and entry

    • Landing (app/page.tsx): the Dashbord card is the primary deep-analysis entry from /.
    • Edge routing (proxy.ts): /dashboard is not auth-gated at the proxy; session cookies still refresh on every matched request.
    • Anonymous: default layout only; canEdit={false} hides edit chrome and shows Logg inn for å tilpasse.
    • Authenticated: saved row from public.dashboard_layouts when present; otherwise default until first edit.
    • Writes: layout upserts go through Server Actions (saveDashboardLayout) and RLS — not the edge proxy. Nav user_preferences.view_preference is separate from block layout storage (see Settings).

    Layout model

    The persisted shape is a single versioned JSON, DashboardLayoutConfigV1 (_lib/layout-config.ts), validated with Zod before any write:

    • A config is a list of sections. Each section has an optional timeRange override and a list of blocks.
    • Each block (DashboardBlock) carries instanceId, chartId (registry id), title, optional options (chart-specific, e.g. dim_code, sortBy), and an optional queryPatch.
    • Each section stores per-breakpoint react-grid-layout arrays (layouts.lg required; md/sm derived). GridItem is structurally compatible with RGL's Layout ({ i, x, y, w, h, minW?, minH? }).
    • Invariant (validated): for every section, the set of blocks[*].instanceId equals the set of layouts.lg[*].i.

    Pure, immutable mutations live in _lib/layout-mutations.ts (add/remove block, apply layouts, add/rename/remove section, set section time range, update block).

    Blocks and chart registry

    Every block type is one entry in FIRE_DATA_CHART_REGISTRY (components/fire-data/charts/registry.ts). An entry declares its kind (kpi | chart), Norwegian title/description, category, dataShape, lucide icon, defaultGrid footprint, and a lazy load() of a thin wrapper.

    Each wrapper takes FireDataChartProps ({ snapshot, block }), calls a pure adapter (components/fire-data/charts/adapters/) that pivots snapshot.rows via lib/data-model/chart-pivot.ts, and renders an existing chart component. Adding a chart = one registry entry (+ optional adapter); no canvas or picker edits.

    time_series_line (Utvikling over tid): default Recharts via SeriesChartCard. chartType: line_race (Linjeløp) switches the plot to ECharts multi-line with trailing end labels (build-series-chart-option.ts, LabelLayout in EChart.tsx). Configure: dimensjon, standard diagramtype, maks serier (topN, default 10). Adapter expandCategories splits the pinned dimension into per-category series for line race.

    Stat blocks

    KPI blocks (kpi_total, kpi_average) render the Supabase-style stat block chrome (charts/kpi/StatBlock.tsx): an uppercase muted label, a large tabular-nums value, and a faint ECharts mini bar sparkline. The kpi adapter computes grand total, mean per period, and the per-period sparkline series.

    Edit mode

    DashboardShell exposes a Rediger / Ferdig toggle (auth only). In edit mode RGL drag/resize is enabled and blocks snap to the nearest cell; BlockFrame overlays a drag handle and an overflow menu (configure / remove). Per section there is an Add block picker (AddBlockDialog, a Command palette grouped by registry category), a time-range selector, and a section menu (rename / remove). Empty sections show a centered call to action.

    Query overrides

    All blocks share the global ConsoleShell filter. Two optional overrides narrow a block's data:

    • Section timeRange — a Partial<FireDataQuery> applied to every block in the section.
    • Block queryPatch — a Partial<FireDataQuery> per block (e.g. the configure dialog sets p_dim_codes when the dimension changes).

    resolveDashboardSnapshots merges section + block patches, hashes each distinct merge (_lib/patch-hash.ts), and runs one extra getCachedFireData per distinct hash. The canvas selects the resolved snapshot for each block by the same hash; the base snapshot is the fallback. Because resolution runs on the server, changing a dimension or time range persists the config and triggers a router.refresh() so the new snapshot is fetched.

    The console Dimensjoner control adds a presentational categoryDims field (URL key cat_dims) that the time-series adapter reads to decide series shape: each selected dimension renders as one SUM series, or splits into category series when expanded. It is not part of get_fire_data args, so toggling it reshapes the existing snapshot without an extra read (see read-path-get-fire-data-and-catalog-caching.md).

    Hierarchy block (chartId: hierarchy)

    Registry entry hierarchy (dataShape: 'hierarchy') renders a sunburst ↔ treemap morph (HierarchyMorphBlock) with configurable breakdown steps and baseline (inner ring).

    ConcernOwner / path
    Step types, validation, query patchlib/data-model/calculations/hierarchy-steps.ts
    Estimated baseline modes + Rest mathlib/data-model/calculations/estimated-total.ts
    Tree pivot (buildHierarchyTree)lib/data-model/chart-pivot.ts
    ECharts adapter + morphcomponents/fire-data/charts/adapters/hierarchy-morph.ts
    Configure UIBreakdownStepPicker, EstimatedTotalControls, BlockConfigDialog

    Inner ring (baseline): always “Alle branner” — not a user step. User steps start at ring 2 (source / axis / dimension).

    Baseline modes (options.estimatedTotal):

    ModeInner ring value
    default3115 × yearsInRange per source
    datasourcemeasured sum in block snapshot
    overridecustom yearly rate × years (clamped below dataset total)

    When default/override budget is below measured total, the block auto-switches to datasource, shows a toast, and persists { mode: 'datasource' } (edit mode).

    Breakdown steps (options.breakdownSteps) drive both the tree and the RPC read. On every resolve, mergeHierarchyBlockQueryPatch() merges steps into p_dim_codes + p_breakdown_axes (see read-path doc). Block queryPatch is persisted on save; the merge also runs at snapshot resolve so stale patches cannot drop axes.

    Rest nodes: one gray Rest per non-leaf level (coverage gap at source/root; composition gap when children sum < parent). Leaf categories never get a spurious Rest child.

    Zod: block options remain z.record in layout-config.ts (v1). Step/baseline validation runs in hierarchy-steps.ts / EstimatedTotalControls at configure time, not in the layout schema.

    Bubble timeline block (chartId: bubble_timeline)

    Registry entry bubble_timeline (dataShape: time_series) renders an ECharts timeline + scatter (“Boble tidslinje”): one frame per period, bubbles positioned by user-chosen encodings.

    ConcernOwner / path
    Lens integrity + encode fieldscomponents/fire-data/charts/adapters/lens-contract.ts
    Snapshot → frames/pointscomponents/fire-data/charts/adapters/bubble-timeline.ts
    ECharts optioncomponents/fire-data/charts/echarts/build-bubble-timeline-option.ts, BubbleTimelineChart.tsx
    Configure UIBlockConfigDialog — Kilde, Dimensjon, Bruddaksler, X/Y/Boblestørrelse/Farge
    Defaults on addlayout-mutations.ts → defaultBubbleTimelineQueryPatch

    Configure semantics (short):

    • Kilde — single p_sources entry (R1: no cross-source sum).
    • Dimensjon — one p_dim_codes entry; categories become colored series.
    • Bruddaksler — optional p_breakdown_axes (fire_type, building_type, building_age, region); none = marginal totals per category per period.
    • Encode axes — options.encodeX/Y/Size/Color map lens fields (value, kr, cat_code, ordinal filter indices, derive:yoy, derive:share). Boblestørrelse should normally be Antall or Skade, not Kategori (cat_code).

    Dual metric (count + kr): get_fire_data returns one summed column per call (value = count or kr). When encodings need both (e.g. X = Skade, Y = Antall), resolveDashboardSnapshots issues a second cached read with p_metric: 'kr' and merges via mergeDualMetricRows onto the count rows (value_count, value_kr). BRASK yearly facts store both value and value_kr in Postgres; the merge exposes them to the adapter. See read-path-get-fire-data-and-catalog-caching.md (p_metric).

    Persistence

    Layouts are stored in public.dashboard_layouts (one is_default row per user) with RLS so each user only reads/writes their own rows — see ../fire-data-schema/postgres-contract-rls-and-keys.md. Server Actions in _lib/dashboard-layout-actions.ts load and (Zod-validated) upsert the config. The user layout is never cached. Edit changes autosave with a debounce; structural data changes save immediately and refresh.

    Code map

    AreaPath
    Pageapp/(main)/dashboard/page.tsx
    Shell + canvasapp/(main)/dashboard/_components/DashboardShell.tsx, DashboardCanvas.tsx, BlockFrame.tsx
    Dialogs_components/AddBlockDialog.tsx, BlockConfigDialog.tsx
    Layout schema + mutations_lib/layout-config.ts, _lib/layout-mutations.ts, _lib/default-layout.ts
    Override resolution_lib/resolve-snapshots.ts, _lib/patch-hash.ts, _lib/time-range-presets.ts
    Base snapshot read_lib/load-fire-data-snapshot.ts
    Persistence_lib/dashboard-layout-actions.ts, supabase/migrations/*_dashboard_layouts.sql
    Registry + adapters + wrapperscomponents/fire-data/charts/registry.ts, charts/adapters/, charts/wrappers/, charts/kpi/