Dashboard Templates & End-to-End Workflow
Skill: databricks-aibi-dashboards
What You Can Build
Section titled “What You Can Build”A complete dashboard from schema inspection to a published URL — following the mandatory validation workflow. Your AI coding assistant inspects table schemas, tests every SQL query via execute_sql, builds the dashboard JSON with datasets, pages, widgets, and filters, then deploys it through manage_dashboard. These templates show the full pattern you can adapt to your own tables.
In Action
Section titled “In Action”“Create a dashboard from the NYC taxi sample data with trip count, average fare, and average distance KPIs, a bar chart of top pickup ZIPs, and a detail table.”
{ "datasets": [ { "name": "summary", "displayName": "Summary Stats", "queryLines": [ "SELECT COUNT(*) as trips, AVG(fare_amount) as avg_fare, ", "AVG(trip_distance) as avg_distance ", "FROM samples.nyctaxi.trips" ] }, { "name": "by_zip", "displayName": "Trips by ZIP", "queryLines": [ "SELECT pickup_zip, COUNT(*) as trip_count ", "FROM samples.nyctaxi.trips ", "GROUP BY pickup_zip ", "ORDER BY trip_count DESC ", "LIMIT 10" ] } ], "pages": [{ "name": "overview", "displayName": "NYC Taxi Overview", "pageType": "PAGE_TYPE_CANVAS", "layout": [ { "widget": { "name": "title", "multilineTextboxSpec": {"lines": ["## NYC Taxi Dashboard"]} }, "position": {"x": 0, "y": 0, "width": 6, "height": 1} }, { "widget": { "name": "subtitle", "multilineTextboxSpec": {"lines": ["Trip statistics and analysis"]} }, "position": {"x": 0, "y": 1, "width": 6, "height": 1} }, { "widget": { "name": "total-trips", "queries": [{ "name": "main_query", "query": { "datasetName": "summary", "fields": [{"name": "trips", "expression": "`trips`"}], "disaggregated": true } }], "spec": { "version": 2, "widgetType": "counter", "encodings": { "value": {"fieldName": "trips", "displayName": "Total Trips"} }, "frame": {"title": "Total Trips", "showTitle": true} } }, "position": {"x": 0, "y": 2, "width": 2, "height": 3} }, { "widget": { "name": "avg-fare", "queries": [{ "name": "main_query", "query": { "datasetName": "summary", "fields": [{"name": "avg_fare", "expression": "`avg_fare`"}], "disaggregated": true } }], "spec": { "version": 2, "widgetType": "counter", "encodings": { "value": {"fieldName": "avg_fare", "displayName": "Avg Fare"} }, "frame": {"title": "Average Fare", "showTitle": true} } }, "position": {"x": 2, "y": 2, "width": 2, "height": 3} }, { "widget": { "name": "avg-distance", "queries": [{ "name": "main_query", "query": { "datasetName": "summary", "fields": [{"name": "avg_distance", "expression": "`avg_distance`"}], "disaggregated": true } }], "spec": { "version": 2, "widgetType": "counter", "encodings": { "value": {"fieldName": "avg_distance", "displayName": "Avg Distance"} }, "frame": {"title": "Average Distance", "showTitle": true} } }, "position": {"x": 4, "y": 2, "width": 2, "height": 3} }, { "widget": { "name": "trips-by-zip", "queries": [{ "name": "main_query", "query": { "datasetName": "by_zip", "fields": [ {"name": "pickup_zip", "expression": "`pickup_zip`"}, {"name": "trip_count", "expression": "`trip_count`"} ], "disaggregated": true } }], "spec": { "version": 3, "widgetType": "bar", "encodings": { "x": {"fieldName": "pickup_zip", "scale": {"type": "categorical"}, "displayName": "ZIP"}, "y": {"fieldName": "trip_count", "scale": {"type": "quantitative"}, "displayName": "Trips"} }, "frame": {"title": "Trips by Pickup ZIP", "showTitle": true} } }, "position": {"x": 0, "y": 5, "width": 6, "height": 5} }, { "widget": { "name": "zip-table", "queries": [{ "name": "main_query", "query": { "datasetName": "by_zip", "fields": [ {"name": "pickup_zip", "expression": "`pickup_zip`"}, {"name": "trip_count", "expression": "`trip_count`"} ], "disaggregated": true } }], "spec": { "version": 2, "widgetType": "table", "encodings": { "columns": [ {"fieldName": "pickup_zip", "displayName": "ZIP Code"}, {"fieldName": "trip_count", "displayName": "Trip Count"} ] }, "frame": {"title": "Top ZIP Codes", "showTitle": true} } }, "position": {"x": 0, "y": 10, "width": 6, "height": 5} } ] }]}Key decisions:
- One dataset per domain —
summaryfor aggregate KPIs,by_zipfor dimensional breakdown. Datasets own their SQL; widgets just reference columns. disaggregated: truefor pre-aggregated data — the summary dataset returns one row, so counters usedisaggregated: trueand reference the column directly. No widget-level aggregation needed.- LIMIT 10 in the dataset SQL — the bar chart and table both reference
by_zip. Capping at 10 keeps the chart readable and avoids rendering thousands of bars. - Layout fills width 6 at every row — three KPIs at width 2 (2+2+2=6), chart and table at full width 6. No gaps allowed in the grid.
Deploy this template:
manage_dashboard( action="create_or_update", display_name="NYC Taxi Dashboard", parent_path="/Workspace/Users/me/dashboards", serialized_dashboard='<the JSON above>', warehouse_id="<your_warehouse_id>")More Patterns
Section titled “More Patterns”Dashboard with global filters
Section titled “Dashboard with global filters”“Build a sales dashboard with revenue, orders, and margin KPIs, a revenue trend area chart, breakdowns by department and region, a product table, and global filters for date, region, and department.”
The full sales analytics template includes global filters that cross-cut all datasets:
{ "datasets": [ { "name": "ds_daily_sales", "displayName": "Daily Sales", "queryLines": [ "SELECT sale_date, region, department, total_orders, ", "total_units, total_revenue, total_cost, profit_margin ", "FROM catalog.schema.gold_daily_sales ", "ORDER BY sale_date" ] }, { "name": "ds_products", "displayName": "Product Performance", "queryLines": [ "SELECT product_name, department, region, ", "units_sold, revenue, cost, profit ", "FROM catalog.schema.gold_product_performance" ] } ], "pages": [ { "name": "sales_overview", "displayName": "Sales Overview", "pageType": "PAGE_TYPE_CANVAS", "layout": [] }, { "name": "global_filters", "displayName": "Filters", "pageType": "PAGE_TYPE_GLOBAL_FILTERS", "layout": [ { "widget": { "name": "filter_date_range", "queries": [{ "name": "ds_sales_date", "query": { "datasetName": "ds_daily_sales", "fields": [{"name": "sale_date", "expression": "`sale_date`"}], "disaggregated": false } }], "spec": { "version": 2, "widgetType": "filter-date-range-picker", "encodings": { "fields": [{"fieldName": "sale_date", "displayName": "Date", "queryName": "ds_sales_date"}] }, "frame": {"showTitle": true, "title": "Date Range"} } }, "position": {"x": 0, "y": 0, "width": 2, "height": 2} }, { "widget": { "name": "filter_region", "queries": [ {"name": "ds_sales_region", "query": {"datasetName": "ds_daily_sales", "fields": [{"name": "region", "expression": "`region`"}], "disaggregated": false}}, {"name": "ds_products_region", "query": {"datasetName": "ds_products", "fields": [{"name": "region", "expression": "`region`"}], "disaggregated": false}} ], "spec": { "version": 2, "widgetType": "filter-multi-select", "encodings": { "fields": [ {"fieldName": "region", "displayName": "Region", "queryName": "ds_sales_region"}, {"fieldName": "region", "displayName": "Region", "queryName": "ds_products_region"} ] }, "frame": {"showTitle": true, "title": "Region"} } }, "position": {"x": 2, "y": 0, "width": 2, "height": 2} }, { "widget": { "name": "filter_department", "queries": [ {"name": "ds_sales_dept", "query": {"datasetName": "ds_daily_sales", "fields": [{"name": "department", "expression": "`department`"}], "disaggregated": false}}, {"name": "ds_products_dept", "query": {"datasetName": "ds_products", "fields": [{"name": "department", "expression": "`department`"}], "disaggregated": false}} ], "spec": { "version": 2, "widgetType": "filter-multi-select", "encodings": { "fields": [ {"fieldName": "department", "displayName": "Department", "queryName": "ds_sales_dept"}, {"fieldName": "department", "displayName": "Department", "queryName": "ds_products_dept"} ] }, "frame": {"showTitle": true, "title": "Department"} } }, "position": {"x": 4, "y": 0, "width": 2, "height": 2} } ] } ]}The layout array on the canvas page holds KPIs, charts, and tables (shown in full in the JSON Reference page). The filter page binds region and department across both datasets. A filter only applies to datasets that include the filter column — if you add a dataset without a region column, the region filter ignores it automatically.
Deployment and lifecycle
Section titled “Deployment and lifecycle”“Deploy the dashboard, then publish it so the team can view it.”
# Create or updateresult = manage_dashboard( action="create_or_update", display_name="Sales Analytics", parent_path="/Workspace/Users/me/dashboards", serialized_dashboard=dashboard_json, warehouse_id=warehouse_id, publish=True)
# Later: get current statemanage_dashboard(action="get", dashboard_id=result["dashboard_id"])
# Unpublish for maintenancemanage_dashboard(action="unpublish", dashboard_id=result["dashboard_id"])
# Republish after updatesmanage_dashboard(action="publish", dashboard_id=result["dashboard_id"], warehouse_id=warehouse_id)Use publish=True on create_or_update to publish immediately. For updates to a published dashboard, call create_or_update again with the same display_name and parent_path — it overwrites the existing dashboard. Use action="list" to find dashboard IDs across your workspace.
Watch Out For
Section titled “Watch Out For”- Skipping query validation — always test every dataset SQL via
execute_sqlbefore building the JSON. Untested queries cause “Invalid widget definition” errors at render time, not at deploy time. The dashboard deploys successfully but shows broken widgets. queryLinesmust be an array — not a single string."queryLines": "SELECT ..."causes a parse failure. Use["SELECT ... ", "FROM ... "]with each line as an array element.- Grid gaps — every row must sum to width 6. Three KPIs at width 2 (6 total) is correct. Two KPIs at width 2 (4 total) leaves a gap and breaks the layout.
- Percent values in data — counter widgets with
number-percentformat expect values between 0 and 1, not 0 and 100. A profit margin of 0.45 displays as 45%. A value of 45 displays as 4,500%.