TotalBase
Widgets

Distribution and Relation

Heatmap, Calendar heatmap, and Scatter and bubbles.

In the admin: a Dashboard › a widget › Edit

These widgets show where values concentrate: on a grid of two categories, on the days of the year, or as points placed by two numbers.

Orders by weekday and hour as a heatmap, visits per day as a calendar, and technicians' orders against hours as a scatter.
Orders by weekday and hour as a heatmap, visits per day as a calendar, and technicians' orders against hours as a scatter.

Heatmap

A grid of two categories, such as weekday × hour or branch × month, each cell colored by its value.

RoleTakesUnset
Xthe columns, in the query's order (dates sorted)the first column that isn't a number; a number, such as an hour, only by hand
Ythe rows, in the query's order (dates sorted)the next column that isn't a number
Valueeach cell's valuethe first number column
Tooltipmore columns in a cell's tooltipnone

When Acme's customers report problems, over the last 90 days:

SELECT to_char(reported_at, 'Dy') AS weekday,
       to_char(reported_at, 'HH24') AS hour,
       count(*) AS orders
FROM work_orders
WHERE reported_at >= now() - interval '90 days'
GROUP BY 1, 2, extract(isodow FROM reported_at)
ORDER BY extract(isodow FROM reported_at), 2

A missing cell stays empty. For zeros, build the grid with generate_series and LEFT JOIN the data to it.

Setting
Colorthe scale's hue, from the background to it; unset, the theme's primary
Thresholdsfixed color bands instead of a scale, with their legend
Show valueseach cell's number written in it
Format, Compact number, Show legendhow values read; the scale's legend
On clickwhat a click on a cell does

HEATMAP

A grid of two categories (day × hour, branch × month), each cell colored by its value; a missing cell stays empty.

Size: 6 × 4 of the 12-column grid, at least 4 × 3 · Also: thresholds, events, export

DataCodeTakesRequiredUnset
Xxcategory, timeyesfirstThe columns - in the query's order (a date sorted). Defaults to the first column that isn't a number; a number (an hour) only by hand.
Yycategory, timeyesfirstThe rows - in the query's order (a date sorted). Defaults to the next column that isn't a number; a number (a weekday) only by hand.
ValuevaluenumberyesfirstEach cell's value. Defaults to the first number column.
Tooltiptooltipcategory, number, timenoneMore columns shown in a mark's tooltip, with their titles and formats. Optional.
PropertyCodeSectionValues
ColorcolorappearanceThe scale's hue, from the background to it - unset, the theme's primary. Thresholds draw fixed bands instead.
FormatdisplayFormatformatHow its numbers read - a displayFormat pattern: "$ #,##0", "#,##0.00", "0.0 %" (% never multiplies: 85 reads 85.0 %), "0.0 h", "CURRENCY:ARS". Unset: the value column's own (its presentation), else the locale's.
Compact numbercompactformatAbbreviates large numbers - 11.6M, 2.4K - keeping the format's text and decimals ("$ #,##0" + compact: $ 1.3M). Unset: on, unless a format applies (its own or its column's).
Show valuesshowValuesappearancedefault falseEach cell's number written in it.
Show legendshowLegendappearancedefault trueThe series' (or slices') names, beside the chart.
ThresholdsthresholdsthresholdsColors by value, in order: {color: "red"}, {from: 60, color: "amber"}, {from: 80, color: "green"} - the first, without from, the base. from: a number, {column} (the widget's row) or {variable} (a NUMBER or SELECT variable). Wins over Color. On a heatmap, bands of those colors with their legend.
On clickclickeventsWhat a click on a mark does: {action: "FILTER", variable, value} sets a variable of this dashboard; {action: "DATAVIEW", dataview, filter} opens a DataView filtered (the where grammar); {action: "DASHBOARD", dashboard, variables, tab}; {action: "RECORD", dataview, pk}; {action: "URL", url, newTab, inEmbeds}. Values are formulas over (_ columns too) and ; targets by id. hint: the tooltip's last line. On a heatmap, a cell.
SELECT (ARRAY['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'])[d] AS weekday, lpad(h::text, 2, '0') AS hour, (random() * 100)::int AS readings FROM generate_series(1, 7) AS d, generate_series(0, 23) AS h ORDER BY d, h

Calendar heatmap

Every day of a year colored by its value: a year per row, as many of the latest three as fit. On a phone, the latest months that fit, with a notice when some are left out. Day and month names follow the viewer's language, and the week starts on the viewer's first day.

RoleTakesUnset
Datethe daythe first date column
Valuethe day's valuethe first number column
Tooltipmore columns in a day's tooltipnone

Visits scheduled per day:

SELECT scheduled_start::date AS day, count(*) AS visits
FROM visits
WHERE scheduled_start >= now() - interval '1 year'
GROUP BY 1
ORDER BY 1

scheduled_start::date cuts days in the viewer's time zone. Its settings are a heatmap's: Color, Thresholds, Format, Compact number, Show legend and On click (a click on a day).

CALENDAR

Every day of a year colored by its value - a year per row, the latest three.

Size: 12 × 3 of the 12-column grid, at least 6 × 3 · Also: thresholds, events, export

DataCodeTakesRequiredUnset
DatedatetimeyesfirstThe day. Defaults to the first date column.
ValuevaluenumberyesfirstEach day's value. Defaults to the first number column.
Tooltiptooltipcategory, number, timenoneMore columns shown in a mark's tooltip, with their titles and formats. Optional.
PropertyCodeSectionValues
ColorcolorappearanceThe scale's hue, from the background to it - unset, the theme's primary. Thresholds draw fixed bands instead.
FormatdisplayFormatformatHow its numbers read - a displayFormat pattern: "$ #,##0", "#,##0.00", "0.0 %" (% never multiplies: 85 reads 85.0 %), "0.0 h", "CURRENCY:ARS". Unset: the value column's own (its presentation), else the locale's.
Compact numbercompactformatAbbreviates large numbers - 11.6M, 2.4K - keeping the format's text and decimals ("$ #,##0" + compact: $ 1.3M). Unset: on, unless a format applies (its own or its column's).
Show legendshowLegendappearancedefault trueThe series' (or slices') names, beside the chart.
ThresholdsthresholdsthresholdsColors by value, in order: {color: "red"}, {from: 60, color: "amber"}, {from: 80, color: "green"} - the first, without from, the base. from: a number, {column} (the widget's row) or {variable} (a NUMBER or SELECT variable). Wins over Color. On a calendar, bands of those colors with their legend.
On clickclickeventsWhat a click on a mark does: {action: "FILTER", variable, value} sets a variable of this dashboard; {action: "DATAVIEW", dataview, filter} opens a DataView filtered (the where grammar); {action: "DASHBOARD", dashboard, variables, tab}; {action: "RECORD", dataview, pk}; {action: "URL", url, newTab, inEmbeds}. Values are formulas over (_ columns too) and ; targets by id. hint: the tooltip's last line. On a calendar, a day.
SELECT d::date AS day, (random() * 20)::int AS visits FROM generate_series(date '2026-01-01', date '2026-12-31', interval '1 day') AS d

Scatter and bubbles

Points placed by two numbers. With Size, bubbles; with Group, a series per group.

RoleTakesUnset
Xa number; a date by handthe first number column
Ya numberthe next number column
Sizea bubble's size: its radius grows with the square root, from 6 to 40 pixelsnone
Labeleach point's name in its tooltipthe first column that isn't a number
Groupby hand: a series per value, colored by the column's presentationnone
Tooltipmore columns in a point's tooltipnone

Each technician's orders against hours worked this month, a series per branch: choose branch for Group.

SELECT u.name AS technician,
       count(DISTINCT te.work_order) AS orders,
       round(sum(te.minutes) / 60.0, 1) AS hours,
       b.code AS branch
FROM time_entries te
JOIN technicians t ON t.id = te.technician
JOIN totalbase.users u ON u.id = t."user"
JOIN branches b ON b.id = t.branch
WHERE te.started_at >= date_trunc('month', now())
GROUP BY u.name, b.code
Setting
Seriesper Group value: its name and color; unset, the Group column's presentation
Axesthe X and Y axes' title, minimum, maximum, logarithmic scale and format; unset minimum fits the data
Reference linesa line on the Y axis (or the X one) at a number, a statistic, a column or a variable; bands
Zoomsliders under and beside the chart, the wheel or a pinch
Format, Compact number, Color, Show legend, On clickas other charts'
A histogram has no widget of its own: bucket the values in SQL with width_bucket and draw them as a Bar chart.

SCATTER

Points by two numbers - with Size, bubbles; with Group, a series per group.

Size: 6 × 4 of the 12-column grid, at least 3 × 3 · Also: series, thresholds, events, zoom, export

DataCodeTakesRequiredUnset
XxnumberyesfirstDefaults to the first number column; a date chosen by hand.
YynumberyesfirstDefaults to the next number column.
SizesizenumbernoneEach point's size (radius by its square root, 6 to 40 px) - bubbles. Optional.
LabellabelcategoryfirstEach point's name in its tooltip. Defaults to the first column that isn't a number.
GroupgroupnoneA series per value of this column, colored by its presentation. Optional.
Tooltiptooltipcategory, number, timenoneMore columns shown in a mark's tooltip, with their titles and formats. Optional.
PropertyCodeSectionValues
FormatdisplayFormatformatHow its numbers read - a displayFormat pattern: "$ #,##0", "#,##0.00", "0.0 %" (% never multiplies: 85 reads 85.0 %), "0.0 h", "CURRENCY:ARS". Unset: the value column's own (its presentation), else the locale's.
Compact numbercompactformatAbbreviates large numbers - 11.6M, 2.4K - keeping the format's text and decimals ("$ #,##0" + compact: $ 1.3M). Unset: on, unless a format applies (its own or its column's).
ColorcolorappearanceA palette hue for its marks - unset, the theme's primary.
Show legendshowLegendappearancedefault trueThe series' (or slices') names, beside the chart.
SeriesseriesseriesPer Group value: {label, color}. Unset: the Group column's presentation (its CHECK list's labels and colors).
Axesaxesaxes{x: {title, min, max, log, displayFormat}, y: {...}} - unset min fits the data.
Reference linesreferencesthresholdsLines and bands on the value axis: {type: "line", value: a number | average | min | max | median | {column} | {variable}, label, color, series?} or {type: "band", from, to, label, color}. Labels read in the axis' format. On a scatter, axis: "x" or "y" (default y).
On clickclickeventsWhat a click on a mark does: {action: "FILTER", variable, value} sets a variable of this dashboard; {action: "DATAVIEW", dataview, filter} opens a DataView filtered (the where grammar); {action: "DASHBOARD", dashboard, variables, tab}; {action: "RECORD", dataview, pk}; {action: "URL", url, newTab, inEmbeds}. Values are formulas over (_ columns too) and ; targets by id. hint: the tooltip's last line. On a scatter, a point.
ZoomzoomzoomExportdefault falseBars under and beside the chart, and the wheel or a pinch, to zoom into a range of x and y.
SELECT * FROM (VALUES ('Ana', 12, 3500, 42000, 'GOLD'), ('Luis', 30, 2100, 63000, 'BLACK'), ('Marta', 7, 5200, 36400, 'GOLD'), ('Pedro', 18, 2900, 52200, 'PLATINUM')) AS t(customer, visits, avg_ticket, spent, level)

Next steps