Dashboard
Dashboard protocol schemas
Color variant for dashboard widgets (e.g., KPI cards).
Source: packages/spec/src/ui/dashboard.zod.ts
TypeScript Usage
import { DashboardSchema, DashboardHeaderSchema, DashboardHeaderActionSchema, DashboardWidgetSchema, DashboardWidgetOptionsSchema, GlobalFilterSchema, GlobalFilterOptionsFromSchema, WidgetActionTypeSchema, WidgetColorVariantSchema } from '@objectstack/spec/ui';
import type { Dashboard, DashboardHeader, DashboardHeaderAction, DashboardWidget, DashboardWidgetOptions, GlobalFilter, GlobalFilterOptionsFrom, WidgetActionType, WidgetColorVariant } from '@objectstack/spec/ui';
// Validate data
const result = DashboardSchema.parse(data);Dashboard
Properties
| Property | Type | Required | Description |
|---|---|---|---|
| name | string | ✅ | Dashboard unique name |
| label | string | Record<string, string> | ✅ | Dashboard label |
| description | string | Record<string, string> | optional | Dashboard description |
| header | { showTitle: boolean; showDescription: boolean; actions?: object[] } | optional | Dashboard header configuration |
| widgets | { id: string; title?: string | Record<string, string>; description?: string | Record<string, string>; type: Enum<'bar' | 'horizontal-bar' | 'column' | 'line' | 'area' | 'pie' | 'donut' | … +13 more>; … }[] | ✅ | Widgets to display |
| columns | integer | optional | Number of grid columns (default 12) |
| gap | integer | optional | Grid gap in Tailwind spacing units |
| refreshInterval | number | optional | Auto-refresh interval in seconds |
| dateRange | { field?: string; defaultRange: Enum<'today' | 'yesterday' | 'this_week' | 'last_week' | 'this_month' | 'last_month' | … +8 more>; allowCustomRange: boolean } | optional | Global dashboard date range filter configuration |
| globalFilters | { name?: string; field: string; object?: string; label?: string | Record<string, string>; … }[] | optional | Global filters that apply to all widgets in the dashboard |
| aria | never | optional | [REMOVED] dashboard.aria was removed in @objectstack/spec 17.0.0 (#3896 audit close-out) — no dashboard renderer ever applied it, so declared ARIA attributes silently did not reach the DOM. Delete the key. Run os migrate meta --from 16 to rewrite existing sources automatically. |
| performance | never | optional | [REMOVED] dashboard.performance was removed in @objectstack/spec 17.0.0 (#3896 audit close-out) — no renderer or runtime read it; dashboard performance tuning was never implemented. Delete the key. Run os migrate meta --from 16 to rewrite existing sources automatically. |
| protection | { lock: Enum<'none' | 'no-overlay' | 'no-delete' | 'full'>; reason: string; docsUrl?: string } | optional | Package author protection block — lock policy for this dashboard. |
| _lock | Enum<'none' | 'no-overlay' | 'no-delete' | 'full'> | optional | Item-level lock — controls overlay & delete (ADR-0010). |
| _lockReason | string | optional | Human-readable reason shown when a write is refused by _lock. |
| _lockSource | Enum<'artifact' | 'package' | 'env-forced'> | optional | Layer that set _lock (artifact | package | env-forced). |
| _provenance | Enum<'package' | 'org' | 'env-forced'> | optional | Origin of the item (package | org | env-forced). |
| _packageId | string | optional | Owning package machine id. |
| _packageVersion | string | optional | Owning package version. |
| _lockDocsUrl | string | optional | Optional documentation link surfaced next to _lockReason. |
DashboardHeader
Dashboard header configuration
Properties
| Property | Type | Required | Description |
|---|---|---|---|
| showTitle | boolean | ✅ | Show dashboard title in header |
| showDescription | boolean | ✅ | Show dashboard description in header |
| actions | { label: string | Record<string, string>; actionUrl: string; actionType?: Enum<'script' | 'url' | 'modal' | 'flow' | 'api' | 'form'>; icon?: string }[] | optional | Header action buttons |
DashboardHeaderAction
Dashboard header action
Properties
| Property | Type | Required | Description |
|---|---|---|---|
| label | string | Record<string, string> | ✅ | Action button label |
| actionUrl | string | ✅ | URL or target for the action |
| actionType | Enum<'script' | 'url' | 'modal' | 'flow' | 'api' | 'form'> | optional | Type of action |
| icon | string | optional | Icon identifier for the action button |
DashboardWidget
Properties
| Property | Type | Required | Description |
|---|---|---|---|
| id | string | ✅ | Unique widget identifier (snake_case) |
| title | string | Record<string, string> | optional | Widget title |
| description | string | Record<string, string> | optional | Widget description text below the header |
| type | Enum<'bar' | 'horizontal-bar' | 'column' | 'line' | 'area' | 'pie' | 'donut' | 'funnel' | 'scatter' | 'treemap' | 'sankey' | 'combo' | 'gauge' | 'solid-gauge' | … +6 more> | ✅ | Visualization type |
| chartConfig | { type: Enum<'bar' | 'horizontal-bar' | 'column' | 'line' | 'area' | 'pie' | 'donut' | … +13 more>; title?: string | Record<string, string>; subtitle?: string | Record<string, string>; description?: string | Record<string, string>; … } | optional | Chart visualization configuration |
| colorVariant | Enum<'default' | 'blue' | 'teal' | 'orange' | 'purple' | 'success' | 'warning' | 'danger'> | optional | Widget color variant for theming |
| requiresObject | string | optional | Hide the widget unless the named object is registered |
| requiresService | string | optional | Hide the widget unless the named kernel service is registered |
| actionUrl | never | optional | [REMOVED] dashboard.widgets[].actionUrl was removed in @objectstack/spec 17.0.0 (#5010, ADR-0049 enforce-or-remove) — a dashboard widget has NO action button, and never had one. No renderer draws per-widget chrome for it: every action the dashboard dispatches comes from header.actions[]. The three keys actionUrl / actionType / actionIcon went together; delete all three. Put the affordance on the dashboard header instead — header: { actions: [{ label, actionUrl, actionType, icon }] } — which IS dispatched (DashboardHeaderAction, same vocabulary, and icon is the header spelling of actionIcon). For a per-ROW affordance, the widget to reach for is a table/pivot bound to a dataset: its rows are clickable and drill through the semantic layer. Run os migrate meta --from 16 to rewrite existing sources automatically. |
| actionType | never | optional | [REMOVED] dashboard.widgets[].actionType was removed in @objectstack/spec 17.0.0 (#5010, ADR-0049 enforce-or-remove) — a dashboard widget has NO action button, and never had one. No renderer draws per-widget chrome for it: every action the dashboard dispatches comes from header.actions[]. The three keys actionUrl / actionType / actionIcon went together; delete all three. Put the affordance on the dashboard header instead — header: { actions: [{ label, actionUrl, actionType, icon }] } — which IS dispatched (DashboardHeaderAction, same vocabulary, and icon is the header spelling of actionIcon). For a per-ROW affordance, the widget to reach for is a table/pivot bound to a dataset: its rows are clickable and drill through the semantic layer. Run os migrate meta --from 16 to rewrite existing sources automatically. |
| actionIcon | never | optional | [REMOVED] dashboard.widgets[].actionIcon was removed in @objectstack/spec 17.0.0 (#5010, ADR-0049 enforce-or-remove) — a dashboard widget has NO action button, and never had one. No renderer draws per-widget chrome for it: every action the dashboard dispatches comes from header.actions[]. The three keys actionUrl / actionType / actionIcon went together; delete all three. Put the affordance on the dashboard header instead — header: { actions: [{ label, actionUrl, actionType, icon }] } — which IS dispatched (DashboardHeaderAction, same vocabulary, and icon is the header spelling of actionIcon). For a per-ROW affordance, the widget to reach for is a table/pivot bound to a dataset: its rows are clickable and drill through the semantic layer. Run os migrate meta --from 16 to rewrite existing sources automatically. |
| filter | any | optional | Presentation-scope filter (runtimeFilter) |
| compareTo | { kind: Enum<'previousPeriod' | 'previousYear'>; dimension?: string } | optional | Period-over-period comparison window ({ kind, dimension? }) |
| dataset | string | ✅ | Dataset name to bind (ADR-0021) |
| dimensions | string[] | optional | Dimension names — X/group/split |
| values | string[] | ✅ | Measure names — Y (at least one) |
| layout | { x: number; y: number; w: number; h: number } | optional | Grid layout position (auto-flowed when omitted) |
| options | { dateGranularity?: Enum<'day' | 'week' | 'month' | 'quarter' | 'year'>; sortBy?: string; sortOrder?: Enum<'asc' | 'desc'>; limit?: integer; … } & Record<string, any> | optional | Widget specific configuration |
| filterBindings | Record<string, string | false> | optional | Per-widget dashboard-filter bindings: filter name → this widget's field, or false to opt out |
| suppressWarnings | string[] | optional | Build diagnostic rule ids suppressed on this widget |
| responsive | never | optional | [REMOVED] dashboard.widgets[].responsive was removed in @objectstack/spec 17.0.0 (#4876, ADR-0049 D2) — no renderer ever read it, so per-widget breakpoint overrides were never applied: the value parsed, validated, and then did nothing. The dashboard grid reflows by its own layout rules (columns + gap on the dashboard, the layout box on each widget). Delete the key. The shared ResponsiveConfig shape is NOT gone — it stays live on page.components[].responsive, which objectui useResponsiveConfig really does read; move the layout there if you need breakpoint behaviour today. Run os migrate meta --from 16 to rewrite existing sources automatically. |
| aria | never | optional | [REMOVED] dashboard.widgets[].aria was removed in @objectstack/spec 17.0.0 (#5010, ADR-0049 D2) — no renderer ever applied it, so ARIA attributes declared on a widget silently did not reach the DOM: the key promised accessibility compliance it did not deliver. This is the same removal the dashboard-level aria got in 17.0.0 (#3896). Delete the key. The dashboard renderer emits its own aria-* attributes for the widget grid; author a title (and description) on the widget instead — those ARE what the renderer labels the card with. The shared AriaProps shape is NOT gone: it stays live on page.aria, page.components[].aria and the list view aria. Run os migrate meta --from 16 to rewrite existing sources automatically. |
Allowed Values: DashboardWidget.type
barhorizontal-barcolumnlineareapiedonutfunnelscattertreemapsankeycombogaugesolid-gaugemetrickpibulletradartablepivot
DashboardWidgetOptions
Widget configuration — declared query keys + open renderer extras
Properties
| Property | Type | Required | Description |
|---|---|---|---|
| dateGranularity | Enum<'day' | 'week' | 'month' | 'quarter' | 'year'> | optional | Bucket selected date dimensions (day/week/month/quarter/year) |
| sortBy | string | optional | Dimension/measure name to order by |
| sortOrder | Enum<'asc' | 'desc'> | optional | Sort direction for sortBy |
| limit | integer | optional | Max rows (applied after ordering) |
| stageOrder | (string | number | boolean)[] | optional | Explicit category order for funnel/pyramid stages (stored values) |
GlobalFilter
Properties
| Property | Type | Required | Description |
|---|---|---|---|
| name | string | optional | Stable filter name (variable key); defaults to field |
| field | string | ✅ | Field name to filter on |
| object | string | optional | Object whose fields.<object>.<field> translation-bundle entry resolves this filter's field label and option labels (#7804) |
| label | string | Record<string, string> | optional | Display label for the filter |
| type | Enum<'text' | 'select' | 'date' | 'number' | 'lookup'> | optional | Filter input type |
| options | { value: string | number | boolean; label: string | Record<string, string> }[] | optional | Static filter options |
| optionsFrom | { object: string; valueField: string; labelField: string; filter?: any } | optional | Dynamic filter options from object |
| defaultValue | string | number | boolean | optional | Default filter value |
| scope | Enum<'dashboard' | 'widget'> | ✅ | Filter application scope |
| targetWidgets | string[] | optional | Widget IDs to apply this filter to |
GlobalFilterOptionsFrom
Dynamic filter options from object
Properties
| Property | Type | Required | Description |
|---|---|---|---|
| object | string | ✅ | Source object name |
| valueField | string | ✅ | Field to use as option value |
| labelField | string | ✅ | Field to use as option label |
| filter | any | optional | Filter to apply to source object |
WidgetActionType
Widget action type
Allowed Values
scripturlmodalflowapiform
WidgetColorVariant
Widget color variant
Allowed Values
defaultbluetealorangepurplesuccesswarningdanger