---
name: dashboard
description: >
  Transverse dashboard reference + scaffolder, consumed DURING development so
  every dashboard uses the right components, the right UI, and correct theming.
  It scaffolds editable, theme-compliant dashboard primitives (KpiCard, ChartCard,
  ListWidget, DashboardGrid, WidgetRenderer) into src/components/dashboard/ —
  because the package's own dashboard components are internal/not exported — and
  it defines the typed-widget config + the per-dashboard data contract that
  scaffold-component renders. Theme tokens come from scaffold-theme (--dataviz-*,
  --chart-*, --kpi-*).
argument-hint: "[scaffold the dashboard primitives, or ask for the dashboard conventions]"
allowed-tools: Read, Grep, Glob, Bash
---

# dashboard — the transverse dashboard contract

Use this skill whenever a dashboard is being built (a BA `SmartDashboard` screen,
or a `dashboard` view on an entity). It guarantees three things during development:
the **right components**, the **right UI**, and **correct theming**.

## Why a dedicated skill (the constraint)

The SmartStack package (`@atlashub/smartstack`) ships dashboard components
(`KpiCard`, `ChartContainer`, ~25 Recharts charts) but they are **internal — not
exported**, so a generated client app cannot import them. Exactly like the
EntityLookup paradox that `scaffold-ui-primitives` solved for form controls, the
answer is to **scaffold local, editable equivalents** the developer owns. This
skill is the dashboard counterpart of `ui-primitives`.

## The primitives (scaffolded into `src/components/dashboard/`)

All token-styled (zero hardcoded colors), editable, `// @customised`-aware:

| File | Role | Key tokens |
|------|------|-----------|
| `types.ts` | `DashboardWidget` + `WidgetResult` — the shared contract (also imported by the page) | — |
| `useDatavizPalette.ts` | reads `--dataviz-1..8` at runtime (Recharts needs real colors in SVG fills) | `--dataviz-*` |
| `KpiCard.tsx` | kpi / counter card (value + optional trend) | `--kpi-value/-label/-trend-up/-trend-down` |
| `ChartCard.tsx` | **the only Recharts wrapper** (`line`/`bar`/`pie`) | `--dataviz-*`, `--chart-grid/-axis/-tooltip-*` |
| `ListWidget.tsx` | top-N table | `--bg-card`, `--border-color`, `--text-*` |
| `DashboardGrid.tsx` | 12-col responsive grid | — |
| `WidgetRenderer.tsx` | switches a typed widget → the right primitive; unknown/no-data → muted placeholder | — |

Recharts must be a dependency of the consuming app (`npm i recharts`) — the CLI
warns if it's missing.

## The widget config (`DashboardWidget`)

```ts
type DashboardWidgetType = 'kpi' | 'counter' | 'chart-line' | 'chart-bar' | 'chart-pie' | 'list';
interface DashboardWidget {
  key: string;            // stable key + the data key
  label: string;          // already translated by the page
  type: DashboardWidgetType;
  col?: number;           // 1-12 span (defaults: kpi 3, charts 6, list 12)
  endpoint?: string;      // data source, e.g. 'widgets/active'
  permission?: string;    // gate visibility
  entity?: string; aggregation?: 'count'|'sum'|'avg'|'min'|'max'; field?: string; // BA hints
}
```
This mirrors the BA `SmartDashboard` widget vocabulary (see
`business-analyse/create-screen/references/smartcomponents.md` §SmartDashboard).

## The data contract (one endpoint per dashboard)

The page fetches **once** and hands `data[widget.key]` to each `<WidgetRenderer>`:

```
GET /api/screens/{dashboard-slug}/dashboard?from&to  →  { widgets: Record<widgetKey, WidgetResult> }

WidgetResult = {
  value?, delta?, unit?,                      // kpi / counter
  points?: { label, [series]: number }[],     // chart-line / chart-bar
  slices?: { name, value }[],                  // chart-pie
  rows?, columns?,                             // list
}
```

The backend has **no generic aggregation engine** — each dashboard is a dedicated
MediatR Query returning this DTO (one widget key per result). Until a key is
returned, that widget renders the muted "no data" placeholder, so the dashboard is
shippable before every endpoint exists. **Never** reintroduce the old
`/dashboard/consolidated` + flat `consolidated.metrics` contract (it had no
backend) — `audit-dev-frontend` DASH-001 flags it.

## Invocation (scaffold the primitives — idempotent)

```bash
npx --prefer-offline tsx skills/development/frontend/dashboard/cli/scaffold-dashboard-primitives/index.ts \
  --spec '{ "projectPath": "/abs/project", "appCode": "demo" }'
```
Add `--dry_run` to preview. `force:true` overwrites non-`@customised` files. Run it
once per app, before scaffolding any dashboard page (so `@/components/dashboard/*`
resolves). Deep-merges the `dashboard.*` i18n namespace (siblings preserved).

## Theming

Colors come exclusively from theme tokens emitted by **`/scaffold-theme`**:
`--dataviz-1..8` (categorical, hue-rotated from the accent — never status tokens,
R17), `--chart-grid/-axis/-tooltip-bg/-text`, `--kpi-value/-label/-trend-up/-trend-down`.
If a project needs custom category colors, pass `theme.dataviz: string[]` to
scaffold-theme — do NOT hardcode hex in a widget.

## How it's consumed during development

- **ba-develop Phase 3.0** runs `scaffold-dashboard-primitives` right after
  `scaffold-ui-primitives` (so the page's imports resolve).
- **scaffold-component** (`dashboard` view) renders `pageSpec.widgets[]` through
  `<DashboardGrid>` + `<WidgetRenderer>` — it does not hand-roll KPI markup.
- **audit**: `audit-dev-frontend` DASH-001..003 + ui-polish R28 enforce the
  primitives + dataviz tokens (no raw Recharts, no hardcoded chart colors).

## When NOT to use

- Form controls (datepicker, dropdown, FK lookup) → `/scaffold-ui-primitives`.
- App/module/section landing pages with quickLinks → the `SmartHome*` screen types
  (navigational), not `SmartDashboard` (free-form analytics).
- Restyle the theme → `/scaffold-theme`.
