Customization
Add a page
- Create
src/app/your-page/page.tsx. - Add it to
navigationinsrc/config/navigation.ts(with an optionalfeatureflag andpermission). - If it should be gateable, add a
routeFeatureMapentry.
A typical page composes the same pieces every other page uses:
"use client";
export default function YourPage() {
const { params } = useGlobalFilters();
const query = useYourData(params);
return (
<>
<PageHeader title="Your page" description="What it shows" />
<FilterBar />
<QueryState query={query} skeleton={<TableSkeleton />}>
{(data) => <ChartCard title="Something">{/* … */}</ChartCard>}
</QueryState>
</>
);
}
QueryState handles pending, error and empty for you; ChartCard wraps each
widget in its own error boundary.
Add a data source method
- Add the method to
DataAdapterinsrc/services/adapters/types.ts. - Implement it in both
mock-adapter.tsandhttp-adapter.ts. - Add a facade entry in
src/services/index.ts. - Add a hook in
src/hooks/use-financial-data.ts.
TypeScript will point at every place you still need to touch.
Add a widget to the custom dashboard
Append to widgetRegistry in
src/components/dashboard/widget-registry.tsx:
{
key: "chart:headcount",
title: "Headcount",
description: "People by department",
defaultWidth: 1,
render: (data) => <BreakdownBarChart items={data.headcount} />,
}
It appears in the widget library immediately; no other file changes.
Add a table column
Columns are plain TanStack ColumnDefs declared in the page. Use
meta: { align: "right" } for numeric columns — it right-aligns the cell and
applies tabular figures.
{ accessorKey: "margin", header: "Margin", meta: { align: "right" },
cell: ({ row }) => formatPercent(row.original.margin, 1) }
Add a matching entry to exportColumns so the column appears in CSV output.
Make something drillable
const { drill } = useDrilldown();
<BreakdownBarChart
items={items}
onSelect={(item) => drill({ title: item.name, filters: { ...item.meta } })}
/>
Drill-downs stack; the drawer shows a breadcrumb and an "Open in Transactions" link that carries the filters into the full page.
Charts
Wrappers in src/components/charts/ cover line, area, bar, grouped bar,
composed, horizontal ranked bars and donut. All read var(--chart-N), so they
follow branding automatically. For something bespoke, build with Recharts
directly and use axisProps, gridProps and ChartTooltip from
chart-primitives.tsx to stay visually consistent.
Change the date presets
src/lib/dates.ts owns preset resolution and comparison ranges. Add a preset to
the DatePreset union, handle it in resolvePreset, and add a label in
src/components/filters/filter-bar.tsx.
Persist settings server-side
Every runtime setting flows through a provider backed by useLocalStorage.
Replace that hook with a TanStack Query read plus mutation and the UI keeps
working unchanged — it only consumes context.
Styling
Tokens live in src/app/globals.css (light and .dark). Change radius, surface
colours or the font stack there rather than in components. The template
deliberately uses a system font stack so it builds offline; to use a web font,
add next/font in src/app/layout.tsx and point --font-sans at its variable.