@bonnard/cli 0.2.15 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -2
- package/dist/bin/bon.mjs +384 -32
- package/dist/docs/topics/dashboards.md +21 -24
- package/dist/docs/topics/sdk.apexcharts.md +281 -0
- package/dist/docs/topics/sdk.authentication.md +130 -0
- package/dist/docs/topics/sdk.browser.md +181 -0
- package/dist/docs/topics/sdk.chartjs.md +327 -0
- package/dist/docs/topics/sdk.echarts.md +297 -0
- package/dist/docs/topics/sdk.md +95 -0
- package/dist/docs/topics/sdk.query-reference.md +307 -0
- package/dist/templates/claude/skills/bonnard-build-dashboard/SKILL.md +145 -0
- package/dist/templates/claude/skills/bonnard-get-started/SKILL.md +1 -1
- package/dist/templates/claude/skills/bonnard-metabase-migrate/SKILL.md +1 -1
- package/dist/templates/cursor/rules/bonnard-build-dashboard.mdc +144 -0
- package/dist/templates/cursor/rules/bonnard-get-started.mdc +1 -1
- package/dist/templates/cursor/rules/bonnard-metabase-migrate.mdc +1 -1
- package/dist/templates/shared/bonnard.md +7 -1
- package/dist/viewer.html +261 -0
- package/package.json +11 -2
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# SDK
|
|
2
|
+
|
|
3
|
+
> Build data apps and dashboards on top of your semantic layer.
|
|
4
|
+
|
|
5
|
+
The Bonnard SDK (`@bonnard/sdk`) is a lightweight TypeScript/JavaScript client for querying your deployed semantic layer. Zero dependencies, ~3KB minified — works in Node.js, browsers, and edge runtimes.
|
|
6
|
+
|
|
7
|
+
## Two ways to use it
|
|
8
|
+
|
|
9
|
+
### npm (TypeScript / Node.js / React)
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install @bonnard/sdk
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
import { createClient } from '@bonnard/sdk';
|
|
17
|
+
|
|
18
|
+
const bon = createClient({ apiKey: 'bon_pk_...' });
|
|
19
|
+
const { data } = await bon.query({
|
|
20
|
+
measures: ['orders.revenue'],
|
|
21
|
+
dimensions: ['orders.city'],
|
|
22
|
+
});
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
### CDN (browser / HTML dashboards)
|
|
26
|
+
|
|
27
|
+
```html
|
|
28
|
+
<script src="https://cdn.jsdelivr.net/npm/@bonnard/sdk/dist/bonnard.iife.js"></script>
|
|
29
|
+
<script>
|
|
30
|
+
const bon = Bonnard.createClient({ apiKey: 'bon_pk_...' });
|
|
31
|
+
</script>
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Exposes `window.Bonnard` with `createClient` and `toCubeQuery`. See [sdk.browser](sdk.browser) for the full browser quickstart.
|
|
35
|
+
|
|
36
|
+
## Authentication
|
|
37
|
+
|
|
38
|
+
Two modes depending on your use case:
|
|
39
|
+
|
|
40
|
+
| Mode | Key type | Use case |
|
|
41
|
+
|------|----------|----------|
|
|
42
|
+
| **Publishable key** | `bon_pk_...` | Public dashboards, client-side apps — safe to expose in HTML |
|
|
43
|
+
| **Token exchange** | `bon_sk_...` → JWT | Multi-tenant / embedded analytics — server exchanges secret key for scoped token |
|
|
44
|
+
|
|
45
|
+
```typescript
|
|
46
|
+
// Simple: publishable key
|
|
47
|
+
const bon = createClient({ apiKey: 'bon_pk_...' });
|
|
48
|
+
|
|
49
|
+
// Multi-tenant: token exchange
|
|
50
|
+
const bon = createClient({
|
|
51
|
+
fetchToken: async () => {
|
|
52
|
+
const res = await fetch('/my-backend/bonnard-token');
|
|
53
|
+
const { token } = await res.json();
|
|
54
|
+
return token;
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
The SDK automatically caches tokens and refreshes them 60 seconds before expiry.
|
|
60
|
+
|
|
61
|
+
See [sdk.authentication](sdk.authentication) for the full auth guide.
|
|
62
|
+
|
|
63
|
+
## What you can query
|
|
64
|
+
|
|
65
|
+
| Method | Purpose |
|
|
66
|
+
|--------|---------|
|
|
67
|
+
| `query()` | JSON query — measures, dimensions, filters, time dimensions |
|
|
68
|
+
| `rawQuery()` | Pass a Cube-native query object directly |
|
|
69
|
+
| `sql()` | SQL with `MEASURE()` syntax |
|
|
70
|
+
| `explore()` | Discover available views, measures, and dimensions |
|
|
71
|
+
|
|
72
|
+
See [sdk.query-reference](sdk.query-reference) for the full API reference.
|
|
73
|
+
|
|
74
|
+
## Building HTML dashboards
|
|
75
|
+
|
|
76
|
+
The SDK pairs with any chart library for single-file HTML dashboards. No build step required — load both from CDN and start querying.
|
|
77
|
+
|
|
78
|
+
| Library | CDN size (gzip) | Best for |
|
|
79
|
+
|---------|----------------|----------|
|
|
80
|
+
| **Chart.js** | ~65 KB | Default choice — most LLM training data, smallest payload |
|
|
81
|
+
| **ECharts** | ~160 KB | Rich interactivity, built-in dark theme |
|
|
82
|
+
| **ApexCharts** | ~130 KB | Best defaults out of box, SVG rendering |
|
|
83
|
+
|
|
84
|
+
Each guide includes a complete, copy-pasteable HTML starter template:
|
|
85
|
+
|
|
86
|
+
- [sdk.chartjs](sdk.chartjs) — Chart.js + SDK
|
|
87
|
+
- [sdk.echarts](sdk.echarts) — ECharts + SDK
|
|
88
|
+
- [sdk.apexcharts](sdk.apexcharts) — ApexCharts + SDK
|
|
89
|
+
|
|
90
|
+
## See also
|
|
91
|
+
|
|
92
|
+
- [sdk.browser](sdk.browser) — Browser / CDN quickstart
|
|
93
|
+
- [sdk.query-reference](sdk.query-reference) — Full query API reference
|
|
94
|
+
- [sdk.authentication](sdk.authentication) — Auth patterns
|
|
95
|
+
- [security-context](security-context) — Row-level security for multi-tenant apps
|
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
# Query API Reference
|
|
2
|
+
|
|
3
|
+
> Complete reference for querying the Bonnard semantic layer via the SDK.
|
|
4
|
+
|
|
5
|
+
## query()
|
|
6
|
+
|
|
7
|
+
Execute a JSON query against the semantic layer. All field names must be fully qualified (e.g. `orders.revenue`, not `revenue`).
|
|
8
|
+
|
|
9
|
+
```javascript
|
|
10
|
+
const { data } = await bon.query({
|
|
11
|
+
measures: ['orders.revenue', 'orders.count'],
|
|
12
|
+
dimensions: ['orders.city'],
|
|
13
|
+
filters: [
|
|
14
|
+
{ dimension: 'orders.status', operator: 'equals', values: ['completed'] },
|
|
15
|
+
],
|
|
16
|
+
timeDimension: {
|
|
17
|
+
dimension: 'orders.created_at',
|
|
18
|
+
granularity: 'month',
|
|
19
|
+
dateRange: ['2025-01-01', '2025-12-31'],
|
|
20
|
+
},
|
|
21
|
+
orderBy: { 'orders.revenue': 'desc' },
|
|
22
|
+
limit: 10,
|
|
23
|
+
});
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### Parameters
|
|
27
|
+
|
|
28
|
+
| Parameter | Type | Required | Description |
|
|
29
|
+
|-----------|------|----------|-------------|
|
|
30
|
+
| `measures` | `string[]` | No | Numeric aggregations to compute (e.g. `['orders.revenue']`) |
|
|
31
|
+
| `dimensions` | `string[]` | No | Group-by columns (e.g. `['orders.city']`) |
|
|
32
|
+
| `filters` | `Filter[]` | No | Row-level filters |
|
|
33
|
+
| `timeDimension` | `TimeDimension` | No | Time-based grouping and date range |
|
|
34
|
+
| `orderBy` | `Record<string, 'asc' \| 'desc'>` | No | Sort order |
|
|
35
|
+
| `limit` | `number` | No | Max rows to return |
|
|
36
|
+
|
|
37
|
+
At least one of `measures` or `dimensions` is required.
|
|
38
|
+
|
|
39
|
+
### Response
|
|
40
|
+
|
|
41
|
+
```javascript
|
|
42
|
+
{
|
|
43
|
+
data: [
|
|
44
|
+
{ "orders.revenue": 125000, "orders.count": 340, "orders.city": "Berlin" },
|
|
45
|
+
{ "orders.revenue": 98000, "orders.count": 280, "orders.city": "Munich" },
|
|
46
|
+
],
|
|
47
|
+
annotation: {
|
|
48
|
+
measures: {
|
|
49
|
+
"orders.revenue": { title: "Revenue", type: "number" },
|
|
50
|
+
"orders.count": { title: "Count", type: "number" },
|
|
51
|
+
},
|
|
52
|
+
dimensions: {
|
|
53
|
+
"orders.city": { title: "City", type: "string" },
|
|
54
|
+
},
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
- `data` — array of result rows, keyed by fully qualified field names
|
|
60
|
+
- `annotation` — optional metadata with field titles and types
|
|
61
|
+
|
|
62
|
+
## Filters
|
|
63
|
+
|
|
64
|
+
```javascript
|
|
65
|
+
filters: [
|
|
66
|
+
{ dimension: 'orders.status', operator: 'equals', values: ['completed'] },
|
|
67
|
+
{ dimension: 'orders.revenue', operator: 'gt', values: [1000] },
|
|
68
|
+
]
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Filter operators
|
|
72
|
+
|
|
73
|
+
| Operator | Description | Example values |
|
|
74
|
+
|----------|-------------|---------------|
|
|
75
|
+
| `equals` | Exact match (any of values) | `['completed', 'shipped']` |
|
|
76
|
+
| `notEquals` | Exclude matches | `['cancelled']` |
|
|
77
|
+
| `contains` | Substring match | `['berlin']` |
|
|
78
|
+
| `gt` | Greater than | `[1000]` |
|
|
79
|
+
| `gte` | Greater than or equal | `[1000]` |
|
|
80
|
+
| `lt` | Less than | `[100]` |
|
|
81
|
+
| `lte` | Less than or equal | `[100]` |
|
|
82
|
+
|
|
83
|
+
## Time dimensions
|
|
84
|
+
|
|
85
|
+
Group data by time periods and filter by date range.
|
|
86
|
+
|
|
87
|
+
```javascript
|
|
88
|
+
timeDimension: {
|
|
89
|
+
dimension: 'orders.created_at',
|
|
90
|
+
granularity: 'month',
|
|
91
|
+
dateRange: ['2025-01-01', '2025-06-30'],
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Granularities
|
|
96
|
+
|
|
97
|
+
| Granularity | Groups by |
|
|
98
|
+
|-------------|-----------|
|
|
99
|
+
| `day` | Calendar day |
|
|
100
|
+
| `week` | ISO week (Monday start) |
|
|
101
|
+
| `month` | Calendar month |
|
|
102
|
+
| `quarter` | Calendar quarter |
|
|
103
|
+
| `year` | Calendar year |
|
|
104
|
+
|
|
105
|
+
Omit `granularity` to filter by date range without time grouping.
|
|
106
|
+
|
|
107
|
+
### Date range formats
|
|
108
|
+
|
|
109
|
+
```javascript
|
|
110
|
+
// ISO date strings (tuple)
|
|
111
|
+
dateRange: ['2025-01-01', '2025-12-31']
|
|
112
|
+
|
|
113
|
+
// Single string (relative)
|
|
114
|
+
dateRange: 'last 30 days'
|
|
115
|
+
dateRange: 'last 6 months'
|
|
116
|
+
dateRange: 'this year'
|
|
117
|
+
dateRange: 'last year'
|
|
118
|
+
dateRange: 'today'
|
|
119
|
+
dateRange: 'yesterday'
|
|
120
|
+
dateRange: 'last week'
|
|
121
|
+
dateRange: 'last month'
|
|
122
|
+
dateRange: 'last quarter'
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Ordering and pagination
|
|
126
|
+
|
|
127
|
+
```javascript
|
|
128
|
+
// Sort by revenue descending
|
|
129
|
+
orderBy: { 'orders.revenue': 'desc' }
|
|
130
|
+
|
|
131
|
+
// Multiple sort keys
|
|
132
|
+
orderBy: { 'orders.city': 'asc', 'orders.revenue': 'desc' }
|
|
133
|
+
|
|
134
|
+
// Limit results
|
|
135
|
+
limit: 10
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
## rawQuery()
|
|
139
|
+
|
|
140
|
+
Pass a Cube-native query object directly, bypassing the SDK's query format conversion. Use when you need features not exposed by `query()` (e.g. segments, offset).
|
|
141
|
+
|
|
142
|
+
```javascript
|
|
143
|
+
const { data } = await bon.rawQuery({
|
|
144
|
+
measures: ['orders.revenue'],
|
|
145
|
+
dimensions: ['orders.city'],
|
|
146
|
+
order: [['orders.revenue', 'desc']],
|
|
147
|
+
limit: 10,
|
|
148
|
+
offset: 20,
|
|
149
|
+
});
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## sql()
|
|
153
|
+
|
|
154
|
+
Execute a SQL query using Cube's SQL API. Use `MEASURE()` to reference semantic layer measures.
|
|
155
|
+
|
|
156
|
+
```javascript
|
|
157
|
+
const { data } = await bon.sql(
|
|
158
|
+
`SELECT city, MEASURE(revenue), MEASURE(count)
|
|
159
|
+
FROM orders
|
|
160
|
+
WHERE status = 'completed'
|
|
161
|
+
GROUP BY 1
|
|
162
|
+
ORDER BY 2 DESC
|
|
163
|
+
LIMIT 10`
|
|
164
|
+
);
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
Response includes an optional schema:
|
|
168
|
+
|
|
169
|
+
```javascript
|
|
170
|
+
{
|
|
171
|
+
data: [
|
|
172
|
+
{ city: "Berlin", revenue: 125000, count: 340 },
|
|
173
|
+
],
|
|
174
|
+
schema: [
|
|
175
|
+
{ name: "city", type: "string" },
|
|
176
|
+
{ name: "revenue", type: "number" },
|
|
177
|
+
{ name: "count", type: "number" },
|
|
178
|
+
]
|
|
179
|
+
}
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
## explore()
|
|
183
|
+
|
|
184
|
+
Discover available views, measures, dimensions, and segments.
|
|
185
|
+
|
|
186
|
+
```javascript
|
|
187
|
+
const meta = await bon.explore();
|
|
188
|
+
|
|
189
|
+
for (const view of meta.cubes) {
|
|
190
|
+
console.log(`${view.name}: ${view.description || ''}`);
|
|
191
|
+
for (const m of view.measures) {
|
|
192
|
+
console.log(` measure: ${m.name} (${m.type})`);
|
|
193
|
+
}
|
|
194
|
+
for (const d of view.dimensions) {
|
|
195
|
+
console.log(` dimension: ${d.name} (${d.type})`);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
By default returns only views (`viewsOnly: true`). To include underlying cubes:
|
|
201
|
+
|
|
202
|
+
```javascript
|
|
203
|
+
const meta = await bon.explore({ viewsOnly: false });
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
### Response shape
|
|
207
|
+
|
|
208
|
+
```javascript
|
|
209
|
+
{
|
|
210
|
+
cubes: [
|
|
211
|
+
{
|
|
212
|
+
name: "orders",
|
|
213
|
+
title: "Orders",
|
|
214
|
+
description: "Customer orders",
|
|
215
|
+
type: "view",
|
|
216
|
+
measures: [
|
|
217
|
+
{ name: "orders.revenue", title: "Revenue", type: "number" },
|
|
218
|
+
{ name: "orders.count", title: "Count", type: "number" },
|
|
219
|
+
],
|
|
220
|
+
dimensions: [
|
|
221
|
+
{ name: "orders.city", title: "City", type: "string" },
|
|
222
|
+
{ name: "orders.created_at", title: "Created At", type: "time" },
|
|
223
|
+
],
|
|
224
|
+
segments: [],
|
|
225
|
+
}
|
|
226
|
+
]
|
|
227
|
+
}
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
## toCubeQuery()
|
|
231
|
+
|
|
232
|
+
Utility function that converts SDK `QueryOptions` into a Cube-native query object. Useful for debugging or when you need to inspect the query before sending.
|
|
233
|
+
|
|
234
|
+
```javascript
|
|
235
|
+
import { toCubeQuery } from '@bonnard/sdk';
|
|
236
|
+
// or in browser: Bonnard.toCubeQuery(...)
|
|
237
|
+
|
|
238
|
+
const cubeQuery = Bonnard.toCubeQuery({
|
|
239
|
+
measures: ['orders.revenue'],
|
|
240
|
+
dimensions: ['orders.city'],
|
|
241
|
+
orderBy: { 'orders.revenue': 'desc' },
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
console.log(JSON.stringify(cubeQuery, null, 2));
|
|
245
|
+
// {
|
|
246
|
+
// "measures": ["orders.revenue"],
|
|
247
|
+
// "dimensions": ["orders.city"],
|
|
248
|
+
// "order": [["orders.revenue", "desc"]]
|
|
249
|
+
// }
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
## Common patterns
|
|
253
|
+
|
|
254
|
+
### KPI query (single row, no dimensions)
|
|
255
|
+
|
|
256
|
+
```javascript
|
|
257
|
+
const { data } = await bon.query({
|
|
258
|
+
measures: ['orders.revenue', 'orders.count', 'orders.avg_value'],
|
|
259
|
+
});
|
|
260
|
+
const kpis = data[0];
|
|
261
|
+
// { "orders.revenue": 1250000, "orders.count": 3400, "orders.avg_value": 367.6 }
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
### Top N with dimension
|
|
265
|
+
|
|
266
|
+
```javascript
|
|
267
|
+
const { data } = await bon.query({
|
|
268
|
+
measures: ['orders.revenue'],
|
|
269
|
+
dimensions: ['orders.city'],
|
|
270
|
+
orderBy: { 'orders.revenue': 'desc' },
|
|
271
|
+
limit: 10,
|
|
272
|
+
});
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
### Time series
|
|
276
|
+
|
|
277
|
+
```javascript
|
|
278
|
+
const { data } = await bon.query({
|
|
279
|
+
measures: ['orders.revenue'],
|
|
280
|
+
timeDimension: {
|
|
281
|
+
dimension: 'orders.created_at',
|
|
282
|
+
granularity: 'month',
|
|
283
|
+
dateRange: 'last 12 months',
|
|
284
|
+
},
|
|
285
|
+
});
|
|
286
|
+
// data[0]["orders.created_at"] is an ISO date string like "2025-01-01T00:00:00.000"
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
### Filtered breakdown
|
|
290
|
+
|
|
291
|
+
```javascript
|
|
292
|
+
const { data } = await bon.query({
|
|
293
|
+
measures: ['orders.revenue'],
|
|
294
|
+
dimensions: ['orders.product_category'],
|
|
295
|
+
filters: [
|
|
296
|
+
{ dimension: 'orders.city', operator: 'equals', values: ['Berlin'] },
|
|
297
|
+
{ dimension: 'orders.revenue', operator: 'gt', values: [100] },
|
|
298
|
+
],
|
|
299
|
+
orderBy: { 'orders.revenue': 'desc' },
|
|
300
|
+
});
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
## See also
|
|
304
|
+
|
|
305
|
+
- [sdk.browser](sdk.browser) — Browser / CDN quickstart
|
|
306
|
+
- [sdk.authentication](sdk.authentication) — Auth patterns
|
|
307
|
+
- [sdk.chartjs](sdk.chartjs) — Building dashboards with Chart.js
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: bonnard-build-dashboard
|
|
3
|
+
description: Guide a user through building and deploying a markdown dashboard. Use when user says "build a dashboard", "create a chart", "visualize data", or wants to create a dashboard.
|
|
4
|
+
allowed-tools: Bash(bon *), Write, Edit, Read
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Build & Deploy a Markdown Dashboard
|
|
8
|
+
|
|
9
|
+
This skill guides you through creating a markdown dashboard with built-in
|
|
10
|
+
chart components and deploying it to Bonnard.
|
|
11
|
+
|
|
12
|
+
## Phase 1: Explore Available Data
|
|
13
|
+
|
|
14
|
+
Discover what measures and dimensions are available to query:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
# List all views and their fields
|
|
18
|
+
bon docs schema
|
|
19
|
+
|
|
20
|
+
# Query a specific view to see what data looks like
|
|
21
|
+
bon query '{"measures": ["view_name.measure"], "dimensions": ["view_name.dimension"], "limit": 5}'
|
|
22
|
+
|
|
23
|
+
# Or use SQL format
|
|
24
|
+
bon query --sql "SELECT MEASURE(total_revenue), date FROM sales_performance LIMIT 5"
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Ask the user what data they want to visualize. Match their request to
|
|
28
|
+
available views and measures.
|
|
29
|
+
|
|
30
|
+
## Phase 2: Learn the Format
|
|
31
|
+
|
|
32
|
+
Review the dashboard format docs for reference:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
bon docs dashboards # Overview + format
|
|
36
|
+
bon docs dashboards.components # Chart components (BigValue, LineChart, BarChart, etc.)
|
|
37
|
+
bon docs dashboards.queries # Query block syntax
|
|
38
|
+
bon docs dashboards.inputs # Interactive filters (DateRange, Dropdown)
|
|
39
|
+
bon docs dashboards.examples # Complete examples
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Phase 3: Build the Markdown File
|
|
43
|
+
|
|
44
|
+
Create a `.md` file with three parts:
|
|
45
|
+
|
|
46
|
+
1. **YAML frontmatter** — title and optional description
|
|
47
|
+
2. **Query blocks** — ` ```query name ` code fences with YAML query options
|
|
48
|
+
3. **Components** — `<BigValue />`, `<LineChart />`, `<BarChart />`, etc.
|
|
49
|
+
|
|
50
|
+
Key points:
|
|
51
|
+
- All field names must be fully qualified: `orders.total_revenue`, not `total_revenue`
|
|
52
|
+
- Each component references a query by name: `data={query_name}`
|
|
53
|
+
- Consecutive `<BigValue>` components auto-group into a row
|
|
54
|
+
- Use `<Grid cols="2">` to place charts side by side
|
|
55
|
+
- Use `<DateRange>` and `<Dropdown>` for interactive filters
|
|
56
|
+
|
|
57
|
+
Example structure:
|
|
58
|
+
|
|
59
|
+
```markdown
|
|
60
|
+
---
|
|
61
|
+
title: Revenue Dashboard
|
|
62
|
+
description: Key revenue metrics and trends
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
` ``query total_revenue
|
|
66
|
+
measures: [orders.total_revenue]
|
|
67
|
+
` ``
|
|
68
|
+
|
|
69
|
+
` ``query order_count
|
|
70
|
+
measures: [orders.count]
|
|
71
|
+
` ``
|
|
72
|
+
|
|
73
|
+
<BigValue data={total_revenue} value="orders.total_revenue" title="Revenue" fmt="eur2" />
|
|
74
|
+
<BigValue data={order_count} value="orders.count" title="Orders" />
|
|
75
|
+
|
|
76
|
+
## Trend
|
|
77
|
+
|
|
78
|
+
` ``query monthly
|
|
79
|
+
measures: [orders.total_revenue]
|
|
80
|
+
timeDimension:
|
|
81
|
+
dimension: orders.created_at
|
|
82
|
+
granularity: month
|
|
83
|
+
` ``
|
|
84
|
+
|
|
85
|
+
<LineChart data={monthly} x="orders.created_at" y="orders.total_revenue" title="Monthly Revenue" yFmt="eur" />
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Save the file (e.g., `dashboard.md`).
|
|
89
|
+
|
|
90
|
+
## Phase 4: Preview Locally
|
|
91
|
+
|
|
92
|
+
Preview the dashboard with live reload:
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
bon dashboard dev dashboard.md
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
This opens a browser with the rendered dashboard. Edit the `.md` file and
|
|
99
|
+
the preview updates automatically. Queries run against the deployed
|
|
100
|
+
semantic layer using the user's credentials.
|
|
101
|
+
|
|
102
|
+
Requires `bon login` — no API key needed.
|
|
103
|
+
|
|
104
|
+
## Phase 5: Deploy
|
|
105
|
+
|
|
106
|
+
Deploy the dashboard to Bonnard:
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
bon dashboard deploy dashboard.md
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
This will:
|
|
113
|
+
- Upload the markdown content
|
|
114
|
+
- Assign a slug (derived from filename, or use `--slug`)
|
|
115
|
+
- Extract the title from frontmatter
|
|
116
|
+
- Print the URL where the dashboard is accessible
|
|
117
|
+
|
|
118
|
+
Options:
|
|
119
|
+
- `--slug <slug>` — custom URL slug (default: derived from filename)
|
|
120
|
+
- `--title <title>` — override frontmatter title
|
|
121
|
+
|
|
122
|
+
## Phase 6: View Live
|
|
123
|
+
|
|
124
|
+
Open the deployed dashboard in the browser:
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
bon dashboard open dashboard
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## Iteration
|
|
131
|
+
|
|
132
|
+
To update, edit the `.md` file and redeploy:
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
bon dashboard deploy dashboard.md
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Each deploy increments the version. Use `bon dashboard list` to see all
|
|
139
|
+
deployed dashboards with their versions and URLs.
|
|
140
|
+
|
|
141
|
+
To remove a dashboard:
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
bon dashboard remove dashboard
|
|
145
|
+
```
|
|
@@ -30,7 +30,7 @@ bon datasource add --name my_warehouse --type postgres \
|
|
|
30
30
|
bon datasource add
|
|
31
31
|
```
|
|
32
32
|
|
|
33
|
-
Supported types: `postgres`, `redshift`, `snowflake`, `bigquery`, `databricks`.
|
|
33
|
+
Supported types: `postgres`, `redshift`, `snowflake`, `bigquery`, `databricks`, `duckdb`.
|
|
34
34
|
|
|
35
35
|
The demo option adds a read-only Contoso retail dataset with tables like
|
|
36
36
|
`fact_sales`, `dim_product`, `dim_store`, and `dim_customer`.
|
|
@@ -65,7 +65,7 @@ bon datasource add --from-dbt
|
|
|
65
65
|
bon datasource add
|
|
66
66
|
```
|
|
67
67
|
|
|
68
|
-
Supported types: `postgres`, `redshift`, `snowflake`, `bigquery`, `databricks`.
|
|
68
|
+
Supported types: `postgres`, `redshift`, `snowflake`, `bigquery`, `databricks`, `duckdb`.
|
|
69
69
|
|
|
70
70
|
The connection will be tested automatically during `bon deploy`.
|
|
71
71
|
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "Guide for building and deploying markdown dashboards with built-in chart components. Use when user says 'build a dashboard', 'create a chart', 'visualize data', or wants to create a dashboard."
|
|
3
|
+
alwaysApply: false
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Build & Deploy a Markdown Dashboard
|
|
7
|
+
|
|
8
|
+
This guide walks through creating a markdown dashboard with built-in
|
|
9
|
+
chart components and deploying it to Bonnard.
|
|
10
|
+
|
|
11
|
+
## Phase 1: Explore Available Data
|
|
12
|
+
|
|
13
|
+
Discover what measures and dimensions are available to query:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
# List all views and their fields
|
|
17
|
+
bon docs schema
|
|
18
|
+
|
|
19
|
+
# Query a specific view to see what data looks like
|
|
20
|
+
bon query '{"measures": ["view_name.measure"], "dimensions": ["view_name.dimension"], "limit": 5}'
|
|
21
|
+
|
|
22
|
+
# Or use SQL format
|
|
23
|
+
bon query --sql "SELECT MEASURE(total_revenue), date FROM sales_performance LIMIT 5"
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Ask the user what data they want to visualize. Match their request to
|
|
27
|
+
available views and measures.
|
|
28
|
+
|
|
29
|
+
## Phase 2: Learn the Format
|
|
30
|
+
|
|
31
|
+
Review the dashboard format docs for reference:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
bon docs dashboards # Overview + format
|
|
35
|
+
bon docs dashboards.components # Chart components (BigValue, LineChart, BarChart, etc.)
|
|
36
|
+
bon docs dashboards.queries # Query block syntax
|
|
37
|
+
bon docs dashboards.inputs # Interactive filters (DateRange, Dropdown)
|
|
38
|
+
bon docs dashboards.examples # Complete examples
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Phase 3: Build the Markdown File
|
|
42
|
+
|
|
43
|
+
Create a `.md` file with three parts:
|
|
44
|
+
|
|
45
|
+
1. **YAML frontmatter** — title and optional description
|
|
46
|
+
2. **Query blocks** — ` ```query name ` code fences with YAML query options
|
|
47
|
+
3. **Components** — `<BigValue />`, `<LineChart />`, `<BarChart />`, etc.
|
|
48
|
+
|
|
49
|
+
Key points:
|
|
50
|
+
- All field names must be fully qualified: `orders.total_revenue`, not `total_revenue`
|
|
51
|
+
- Each component references a query by name: `data={query_name}`
|
|
52
|
+
- Consecutive `<BigValue>` components auto-group into a row
|
|
53
|
+
- Use `<Grid cols="2">` to place charts side by side
|
|
54
|
+
- Use `<DateRange>` and `<Dropdown>` for interactive filters
|
|
55
|
+
|
|
56
|
+
Example structure:
|
|
57
|
+
|
|
58
|
+
```markdown
|
|
59
|
+
---
|
|
60
|
+
title: Revenue Dashboard
|
|
61
|
+
description: Key revenue metrics and trends
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
` ``query total_revenue
|
|
65
|
+
measures: [orders.total_revenue]
|
|
66
|
+
` ``
|
|
67
|
+
|
|
68
|
+
` ``query order_count
|
|
69
|
+
measures: [orders.count]
|
|
70
|
+
` ``
|
|
71
|
+
|
|
72
|
+
<BigValue data={total_revenue} value="orders.total_revenue" title="Revenue" fmt="eur2" />
|
|
73
|
+
<BigValue data={order_count} value="orders.count" title="Orders" />
|
|
74
|
+
|
|
75
|
+
## Trend
|
|
76
|
+
|
|
77
|
+
` ``query monthly
|
|
78
|
+
measures: [orders.total_revenue]
|
|
79
|
+
timeDimension:
|
|
80
|
+
dimension: orders.created_at
|
|
81
|
+
granularity: month
|
|
82
|
+
` ``
|
|
83
|
+
|
|
84
|
+
<LineChart data={monthly} x="orders.created_at" y="orders.total_revenue" title="Monthly Revenue" yFmt="eur" />
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Save the file (e.g., `dashboard.md`).
|
|
88
|
+
|
|
89
|
+
## Phase 4: Preview Locally
|
|
90
|
+
|
|
91
|
+
Preview the dashboard with live reload:
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
bon dashboard dev dashboard.md
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
This opens a browser with the rendered dashboard. Edit the `.md` file and
|
|
98
|
+
the preview updates automatically. Queries run against the deployed
|
|
99
|
+
semantic layer using the user's credentials.
|
|
100
|
+
|
|
101
|
+
Requires `bon login` — no API key needed.
|
|
102
|
+
|
|
103
|
+
## Phase 5: Deploy
|
|
104
|
+
|
|
105
|
+
Deploy the dashboard to Bonnard:
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
bon dashboard deploy dashboard.md
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
This will:
|
|
112
|
+
- Upload the markdown content
|
|
113
|
+
- Assign a slug (derived from filename, or use `--slug`)
|
|
114
|
+
- Extract the title from frontmatter
|
|
115
|
+
- Print the URL where the dashboard is accessible
|
|
116
|
+
|
|
117
|
+
Options:
|
|
118
|
+
- `--slug <slug>` — custom URL slug (default: derived from filename)
|
|
119
|
+
- `--title <title>` — override frontmatter title
|
|
120
|
+
|
|
121
|
+
## Phase 6: View Live
|
|
122
|
+
|
|
123
|
+
Open the deployed dashboard in the browser:
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
bon dashboard open dashboard
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## Iteration
|
|
130
|
+
|
|
131
|
+
To update, edit the `.md` file and redeploy:
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
bon dashboard deploy dashboard.md
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Each deploy increments the version. Use `bon dashboard list` to see all
|
|
138
|
+
deployed dashboards with their versions and URLs.
|
|
139
|
+
|
|
140
|
+
To remove a dashboard:
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
bon dashboard remove dashboard
|
|
144
|
+
```
|