Custom dashboards
Custom dashboards let you build your own analytics pages from a YAML manifest called UAS (UnitOps Analytics Specification). A dashboard references widget files: charts, KPIs, tables, filters, images, and markdown notes.
Page: Dashboards -> Custom (/dashboards/custom)
For viewers
Open a dashboard
Go to /dashboards/custom. The list shows every dashboard in your organization. Click one to open it.
Use filters
A dashboard with a filter widget re-queries the connected widgets as soon as you change the value. Filter controls can be a select, radio group, single date, date range, or calendar. Date filters accept absolute dates (2024-01-15), ranges (2024-01-01 to 2024-01-31), and relative expressions such as today, yesterday, 3 days ago, this week, last month, before 2024-01-15, and after 2024-01-15.
Export data
tabulator grids support pagination, frozen columns, grouping, sorting, and CSV or Excel export.
For builders
Anatomy of a dashboard
version: "1.0"
kind: dashboard
metadata:
id: store_performance
name: Store Performance
platform:
scope: org
roles: [admin, general_manager]
allow_embed: true
public: false
spec:
responsive:
breakpoints: [mobile, tablet, desktop, wide]
datasets:
- id: orders
from: dw_mart.orders
dimensions:
- id: business_date
column: dw_mart.orders.business_date
- id: restaurant_id
column: dw_mart.orders.restaurant_id
metrics:
- id: net_sales
type: sql
expression: sum(net_sales)
tabs:
- id: overview
name: Overview
widgets: [total_sales_kpi, sales_by_day, location_filter]
A widget
version: "1.0"
kind: widget
metadata:
id: total_sales_kpi
name: Total Sales
spec:
component: kpi
layout:
x: 8
y: 0
w: 2
h: 2
responsive:
mobile: {x: 0, y: 4, w: 2, h: 2}
desktop: {x: 8, y: 0, w: 2, h: 2}
data:
kind: dataset
dataset: orders
columns: [net_sales]
mappings:
value: net_sales
label: Total Sales
display:
kpi:
format: currency
prefix: $
filters:
- filter_id: location_filter
field: restaurant_id
param: restaurant_ids
operator: in
Data sources
data.kind | Meaning |
|---|---|
dataset | Reference a dataset declared in the manifest |
sql | Arbitrary read-only SQL run through the KPI service |
kpi | An existing KPI from the catalog |
static | Inline rows or select options |
demo | Deterministic generated data for prototyping |
Start with demo and the sample_orders dataset to lay out the page, then switch to dataset or sql.
Datasets avoid ambiguous columns
Declare a Dataset once and reference it by id. Dimensions carry an id (used in mappings and metrics) and a column (the fully qualified SQL expression). That prevents business_date from being ambiguous when several tables share the name.
Dataset sources: from (table or subquery), query (arbitrary SQL), kpi (catalog KPI), or none for demo mode.
Calculated fields
Metrics live inside a dataset and are referenced by id:
type: formula- a row-level expression evaluated by a safe interpreter. Supports arithmetic, comparisons,and/or,if ... else, andabs,round,float,int,len,min,max. Metrics may reference other metrics; dependencies resolve automatically.type: sql- an expression injected into the generated SQL, best for warehouse-side aggregates.
Filter wiring
The filter widget declares what it produces:
bindings:
- target: "*"
field: restaurant_id
param: restaurant_ids
operator: in
Each consuming widget declares what it accepts:
filters:
- filter_id: location_filter
field: restaurant_id
param: restaurant_ids
operator: in
Operators: eq, in, gt, lt, gte, lte, contains, date, between. The filter_id must match the source widget's metadata.id.
Components
| Component | Notes |
|---|---|
echarts | line, bar, pie, scatter, heatmap, gauge, radar; display.echarts.option is the base option |
kpi | Single number with format (currency, percent, number), prefix, optional delta |
table | Simple rows with page_size |
tabulator | Rich grid with grouping, frozen columns, and export |
filter | control: select | radio | date | date_range | calendar |
text | Markdown text block with alignment, color, sizing |
image | url, alt, object_fit |
markdown | Rendered markdown |
Layout
spec.config.filterPanel positions a top or left filter panel (position, collapsible, defaultCollapsed, width, gap, padding, background). spec.config.header and spec.config.footer add banded sections with their own text widgets.
Publish and share
- Publish the dashboard:
POST /api/v1/custom-dashboards/{id}/publish. - Generate an embed:
POST /api/v1/custom-dashboards/{id}/embedreturns an iframe snippet and a signed token. See Embedding dashboards.
Create a dashboard through the API:
curl -X POST https://api.example.com/api/v1/custom-dashboards \
-H "Content-Type: application/json" \
-d '{"name":"My Dashboard","slug":"my-dashboard","spec_yaml":"..."}'
Load the bundled sample dashboard for a working reference (admin only):
curl -X POST https://api.example.com/api/v1/custom-dashboards/_seed
Tips and troubleshooting
- A widget goes empty after filtering: its data does not contain the
fieldnamed infilters, for examplerestaurant_id. - For SQL datasets, always set
columnon dimensions to the fully qualified name. - Formula metrics resolve in dependency order, so a share metric can depend on a sum metric.
layout.responsiveis optional; the base layout applies to every breakpoint when omitted.- Preview mode renders exactly what an embed consumer sees, filters included.