# Dashboard page (many charts on one page)
> Put many charts on one page, each from its own query, with pickers such as $symbol at the top and an automatic refresh. Dashboards are saved in the database.
- Category: Web console
- Keywords: dashboard, dashboards, Dashboard page, panel, chart panel, text note, Add panel, Add to dashboard, Edit, Variables, $symbol, $from, variable, picker, Auto refresh, refresh, Save, \_dashboards, Export JSON, Import JSON, Copy JSON, Duplicate, example dashboards, Market overview, Stock view, Portfolio, today-30d
- See also: [Web console](web-console.md), [View data page (browse and edit rows)](console-data-page.md), [Monitor panel (memory, processor, caches)](console-monitor.md), [SELECT](select.md), [Users and roles](users-and-roles.md)
The **Dashboard** page of the [web console](web-console.md) (Home menu) shows many charts on one page. Each chart is a **panel**: one `SELECT` and the chart settings for it. The panels refresh by themselves, and pickers at the top (a symbol, a date range) change every panel at once.
- A dashboard belongs to the database chosen at the top. It is saved in the table `_dashboards` of that database, so backups keep it and roles apply.
- The list always holds three **example** dashboards: **Market overview**, **Stock view** and **Portfolio**. They are made for the [sample data](sample-data.md). **Save** on an example makes your own copy.
- A panel only reads: it runs its SQL with `GET /exec`, which refuses statements that change data. Each panel reads at most 10,000 rows; **Rows** in its editor picks 100, 1,000, 10,000 or 100,000.
## The screen
```text
+---------------------------------------------------------------------------------------------+
| [Stock view v] [Stock view ] Auto refresh [30 s v] Refresh Edit Add panel Variables Save More |
| Symbol [CITY v] From [2025-09-23] To [2026-09-23] |
| +------------------------------------------------------------+ |
| | Candles, volume, RSI and MACD ... | |
| | (chart) | |
| +------------------------------------------------------------+ |
| +------------------+ +---------------------------------------+ |
| | Last close ... | | Daily move % (calendar) ... | |
+---------------------------------------------------------------------------------------------+
```
| Control | What it does |
|---|---|
| Dashboard list | Opens another dashboard: yours, then the examples |
| Title box | The dashboard's name |
| **Auto refresh** | Runs every panel again every 5 s, 10 s, 30 s, 1 min, 5 min or 15 min, or **Off** |
| **Refresh** | Runs every panel again now |
| **Edit** | Turns on moving and resizing panels |
| **Add panel** | Opens the panel editor for a new panel |
| **Variables** | The pickers at the top ([variables](#variables)) |
| **Save** | Writes the dashboard to `_dashboards` |
| **More** | **New dashboard**, **Duplicate**, **Export JSON file**, **Copy JSON**, **Import JSON**, **Delete** |
The panels sit on a grid of 12 columns. In **Edit** mode, drag a panel by its move handle, or drag its bottom right corner to resize it. With the keyboard: arrow keys move it, `Shift` + arrow keys resize it.
## Panels
Each panel has a **...** menu:
| Item | What it does |
|---|---|
| **Edit** | Opens the panel editor |
| **Refresh** | Runs this panel again |
| **Open SQL in editor** | Puts the panel's SQL, with the variables filled in, into the SQL editor on Home |
| **Save PNG**, **Save CSV** | The chart as a picture, or its rows as a CSV file |
| **Full screen**, **Reset zoom** | As on the [chart](web-console.md#chart) of the SQL editor |
| **Duplicate** | A copy of the panel |
| **Remove** | Takes the panel off (in **Edit** mode only) |
The panel editor has a title, a kind and, for a chart, the SQL. **Chart from SQL** is one `SELECT` and a chart; **Text note** is a few lines of text, for example how to use the dashboard. **Run preview** (or `Ctrl+Enter`) runs the SQL and shows the chart, with the same chart types and settings as the SQL editor.
The quickest way to make a panel: run a query on Home, pick a chart, and click **Add to dashboard** on the chart bar.
**Example: A good panel query: one symbol, a time range** (sample data: sample)
```sql
SELECT time, open, high, low, close, volume
FROM ochl_day
WHERE symbol = 'CITY'
ORDER BY time;
```
Result:
| time (TIMESTAMP) | open (DOUBLE) | high (DOUBLE) | low (DOUBLE) | close (DOUBLE) | volume (DOUBLE) |
|---|---|---|---|---|---|
| 2025-12-28T00:00:00.000Z | 540.3 | 542.5 | 539.2 | 542.2 | 85290 |
| 2025-12-29T00:00:00.000Z | 540.6 | 542.1 | 538.8 | 539.7 | 52010 |
| 2025-12-30T00:00:00.000Z | 538.5 | 540.5 | 536.4 | 539.3 | 95460 |
| 2025-12-31T00:00:00.000Z | 540.4 | 546.2 | 538.9 | 542.4 | 22330 |
| 2026-08-23T00:00:00.000Z | 505 | 508.4 | 502 | 507.9 | 125050 |
(first 5 of 19 rows)
## Variables
A **variable** is a picker at the top of the dashboard. Write `$name` (or `${name}`) in a panel's SQL and the picker's value goes there. **Variables** opens the list: a name, a label, a type and a default value.
| Type | The picker | Goes into the SQL as |
|---|---|---|
| Text | a text box | a quoted text: `'CITY'` |
| List of values | a list of values you type (`CITY, COAST, VOLT`) | a quoted text |
| Values from SQL | a list filled by a query (`SELECT DISTINCT symbol FROM ochl_day ORDER BY symbol`) | a quoted text |
| Number | a number box | the number |
| Date | a date box; the default may be `today`, `today-30d`, `today-1y` | a quoted date: `'2026-09-11'` |
| Table or column name | a text box | the name as it is (letters, digits and `_` only) |
| Interval | a text box: `1d`, `15m`, `1M` | as it is, for `SAMPLE BY $step` |
- Text is always quoted, and a `'` in it is doubled, so a picker can never change what the statement does.
- A list can allow **several** values; they go in as `'CITY', 'COAST'`, for `WHERE symbol IN ($symbols)`.
- `$name` inside a quoted text or a comment is left alone.
- A value that does not fit its type (a word in a number box) stops the panel with a message; nothing is sent.
**Example: What a panel with $symbol, $from and $to runs** (sample data: sample)
```sql
-- The panel SQL: ... WHERE symbol = $symbol AND time >= $from AND time < $to
SELECT time, close
FROM ochl_day
WHERE symbol = 'CITY' AND time >= '2025-01-01' AND time < '2026-01-01'
ORDER BY time;
```
Result:
| time (TIMESTAMP) | close (DOUBLE) |
|---|---|
| 2025-12-28T00:00:00.000Z | 542.2 |
| 2025-12-29T00:00:00.000Z | 539.7 |
| 2025-12-30T00:00:00.000Z | 539.3 |
| 2025-12-31T00:00:00.000Z | 542.4 |
(4 rows)
## Saving, and who may do what
| Role on the database | Can |
|---|---|
| `read` | open and look at dashboards; export them as JSON |
| `write` | also save, change and delete them |
| `admin` | also save the **first** dashboard, which creates the table `_dashboards` |
- The first **Save** in a database runs `CREATE TABLE IF NOT EXISTS _dashboards (...)`. That needs the `admin` role. A `write` user who is first is asked to let an admin save one dashboard first, or to use **More > Export JSON file**.
- A changed dashboard says `Changed, not saved` next to its title. Opening another one first asks whether to drop the changes.
- **Export JSON file** and **Import JSON** move a dashboard to another database or another server. An imported dashboard opens at once; **Save** keeps it.
**Example: The table the first Save creates**
```sql
CREATE TABLE IF NOT EXISTS _dashboards (id STRING PRIMARY KEY, title STRING NOT NULL, body STRING NOT NULL, updated TIMESTAMP, updated_by STRING);
SHOW COLUMNS FROM _dashboards;
```
Result:
`CREATE TABLE IF NOT EXISTS _dashboards (`
```text
table '_dashboards' created
```
`SHOW COLUMNS FROM _dashboards`
| name (STRING) | type (STRING) | primary_key (BOOL) | not_null (BOOL) | computed (BOOL) | references (STRING) |
|---|---|---|---|---|---|
| id | STRING | true | false | false | NULL |
| title | STRING | false | true | false | NULL |
| body | STRING | false | true | false | NULL |
| updated | TIMESTAMP | false | false | false | NULL |
| updated_by | STRING | false | false | false | NULL |
(5 rows)