← All guided builds

Guided build · 11

Build a live revenue dashboard

Filter, join, group, and present changing order data as KPI cells and a purpose-built Layout surface.

You will finish with: A live relational pipeline and dashboard that update together from one source-row change.

30 minIntermediateSource checked for Grid 0.61.0Reviewed 2026-08-24
Related canonical example21-live-revenue-kpi.grid
Get Grid
Starter modellive-revenue-starter.gridNamed source tables and dashboard bindings, ready for the relational pipeline.

Watch it in Grid

See the workflow before you build it.

Follow the finished interaction, then use the written steps below to build and inspect it yourself.

On this page

What you will build

A live revenue model whose source tables flow through filtering, two joins, margin calculation, grouping, KPI cells, and a Layout dashboard. One source-row change will update the entire result without rewriting the dashboard.

This build uses local sample data. A connector can populate the same tables later without changing the relational model.

1. Load the model and seed its sources

Open the Live Revenue KPI example in Grid. It declares three durable sources:

table Orders = { schema: "Orders" }
table Customers = { schema: "Customers" }
table Products = { schema: "Products" }

Use the table editor, an import, or one-time table mutation commands to add these rows:

The named schemas are runtime prerequisites rather than definitions contained in this canonical file. Create or map them with these exact fields before loading rows:

Schema Required fields
Orders order_id, customer_id, product_id, amount, status
Customers customer_id, segment
Products product_id, category, cost_rate
Table Sample rows
Customers c1 / SMB, c2 / Enterprise, c3 / Mid-market
Products p1 / software / 0.25, p2 / services / 0.40, p3 / hardware / 0.50
Orders o1 / c1 / p1 / 60000 / paid; o2 / c2 / p2 / 30000 / paid; o3 / c1 / p3 / 20000 / paid; o4 / c3 / p2 / 80000 / pending

The first value in every sample order is its order_id; that stable identity is how you locate o4 for the later mutation even though the derived query does not select it.

Checkpoint: Grid can read four orders, three customers, and three products.

2. Keep only paid orders

table PaidOrders = SELECT customer_id, product_id, amount AS revenue
  FROM Orders
  WHERE status = "paid"

The pending order does not enter the live result. Three paid rows remain.

This is a declared relationship, not a saved query result. When an order changes, Grid reevaluates the affected result under the documented Live Model contract.

3. Enrich the rows and calculate margin

The first join adds the customer segment. The second adds product cost and category, then computes margin:

table EnrichedPaidOrders = SELECT e.customer_id, e.revenue,
    e.revenue * (1 - COALESCE(p.cost_rate, 0)) AS margin,
    e.segment, p.category
  FROM CustomerEnrichedOrders AS e
  LEFT JOIN Products AS p ON e.product_id = p.product_id
  WHERE e.revenue > 0
    AND (p.category IN ("software", "services") OR p.category IS NULL)

The hardware order is deliberately filtered out. COALESCE makes the missing-cost policy explicit rather than letting a blank silently erase the result.

Checkpoint: two eligible rows remain—SMB revenue 60,000 with margin 45,000, and Enterprise revenue 30,000 with margin 18,000.

4. Group the result and expose KPI cells

table RevenueBySegment = SELECT segment, SUM(revenue) AS revenue,
    SUM(margin) AS margin
  FROM EnrichedPaidOrders
  GROUP BY segment

TotalRevenue IS currency = RevenueBySegment.sum("revenue")
TotalMargin IS currency = RevenueBySegment.sum("margin")
ActiveSegments = RevenueBySegment.count()
RevenueAlert = TotalRevenue > 100000
KPI Expected value
TotalRevenue 90,000
TotalMargin 63,000
ActiveSegments 2
RevenueAlert FALSE

5. Present the same state as a dashboard

The example’s Dashboard!config creates Layout tiles with formulas such as:

[[tiles.static]]
title = "Paid Revenue"
x = 0
y = 0
w = 4
h = 2
formula = "=TotalRevenue"

The surface does not recalculate revenue. It reads the named KPI that already belongs to the model.

Checkpoint: the Layout shows Paid Revenue 90,000, Paid Margin 63,000, Revenue Alert false, and Active Segments 2.

6. Make one source change

Change order o4 from pending to paid. It now passes the first filter, joins to Mid-market and services, and contributes 80,000 revenue with 48,000 margin.

KPI New value
TotalRevenue 170,000
TotalMargin 111,000
ActiveSegments 3
RevenueAlert TRUE

All four tiles should update without changing their configuration. If you inspect runtime.liveModels, freshness should settle back to current after the source revision is committed.

Try it yourself

Add a margin-rate KPI and a fifth tile:

MarginRate IS percentage = (TotalMargin / TotalRevenue) DEFAULT 0

After the order update, MarginRate should be approximately 65.29%. Change product p2’s cost_rate from 0.40 to 0.50: revenue remains 170,000, margin becomes 100,000, and the rate becomes approximately 58.82%.

You are done when

  • The source tables contain the sample rows.
  • Every baseline KPI matches the checkpoint.
  • One status change updates the relations, cells, and dashboard.
  • You can explain why the surface contains presentation configuration rather than business logic.
Build statusReached the expected checkpoint?