Skip to main content

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.kindMeaning
datasetReference a dataset declared in the manifest
sqlArbitrary read-only SQL run through the KPI service
kpiAn existing KPI from the catalog
staticInline rows or select options
demoDeterministic 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, and abs, 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

ComponentNotes
echartsline, bar, pie, scatter, heatmap, gauge, radar; display.echarts.option is the base option
kpiSingle number with format (currency, percent, number), prefix, optional delta
tableSimple rows with page_size
tabulatorRich grid with grouping, frozen columns, and export
filtercontrol: select | radio | date | date_range | calendar
textMarkdown text block with alignment, color, sizing
imageurl, alt, object_fit
markdownRendered 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

  1. Publish the dashboard: POST /api/v1/custom-dashboards/{id}/publish.
  2. Generate an embed: POST /api/v1/custom-dashboards/{id}/embed returns 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 field named in filters, for example restaurant_id.
  • For SQL datasets, always set column on dimensions to the fully qualified name.
  • Formula metrics resolve in dependency order, so a share metric can depend on a sum metric.
  • layout.responsive is optional; the base layout applies to every breakpoint when omitted.
  • Preview mode renders exactly what an embed consumer sees, filters included.