@adminforth/dashboard 1.7.0 → 1.9.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 +81 -55
- package/custom/model/dashboard.types.ts +17 -9
- package/custom/skills/adminforth-dashboard/SKILL.md +28 -12
- package/dist/custom/model/dashboard.types.d.ts +15 -8
- package/dist/custom/model/dashboard.types.ts +17 -9
- package/dist/custom/queries/useDashboardConfig.d.ts +222 -4
- package/dist/custom/queries/useWidgetData.d.ts +222 -4
- package/dist/custom/skills/adminforth-dashboard/SKILL.md +28 -12
- package/dist/schema/api.d.ts +5440 -941
- package/dist/schema/api.js +2 -2
- package/dist/schema/widget.d.ts +432 -23
- package/dist/schema/widget.js +1 -1
- package/dist/schema/widgets/charts.d.ts +558 -28
- package/dist/schema/widgets/charts.js +2 -2
- package/dist/schema/widgets/common.d.ts +17 -6
- package/dist/schema/widgets/common.js +16 -7
- package/dist/schema/widgets/gauge-card.d.ts +38 -2
- package/dist/schema/widgets/kpi-card.d.ts +38 -2
- package/dist/schema/widgets/pivot-table.d.ts +38 -2
- package/dist/schema/widgets/table.d.ts +38 -2
- package/dist/services/calc-evaluator.d.ts +1 -0
- package/dist/services/calc-evaluator.js +28 -0
- package/dist/services/dashboardFilterService.d.ts +5 -0
- package/dist/services/dashboardFilterService.js +125 -0
- package/dist/services/widgetDataService.js +53 -201
- package/package.json +2 -1
- package/schema/api.ts +1 -2
- package/schema/widget.ts +0 -1
- package/schema/widgets/charts.ts +1 -2
- package/schema/widgets/common.ts +16 -7
- package/services/calc-evaluator.ts +33 -0
- package/services/dashboardFilterService.ts +162 -0
- package/services/widgetDataService.ts +88 -263
package/README.md
CHANGED
|
@@ -29,7 +29,7 @@ Each widget has common fields:
|
|
|
29
29
|
| `label` | Optional widget title. |
|
|
30
30
|
| `target` | Widget type: `table`, `chart`, `kpi_card`, `pivot_table`, or `gauge_card`. |
|
|
31
31
|
| `order` | Widget order inside its group. |
|
|
32
|
-
| `variables` | Optional
|
|
32
|
+
| `variables` | Optional widget variables passed to widget data loading. Variables are not available inside `query.calcs`. |
|
|
33
33
|
| `size` | Preset width: `small`, `medium`, `large`, `wide`, or `full`. |
|
|
34
34
|
| `width`, `height`, `min_width`, `max_width` | Optional explicit layout constraints. |
|
|
35
35
|
| `query` | Data query definition. |
|
|
@@ -39,7 +39,7 @@ Each widget has common fields:
|
|
|
39
39
|
| Widget target | Config field | Main settings | Data usage |
|
|
40
40
|
| --- | --- | --- | --- |
|
|
41
41
|
| `table` | `table` | `pagination`, `page_size`, `columns` | Uses `query` to display raw or aggregate rows. |
|
|
42
|
-
| `chart` | `chart` | `type`, `x`, `y`, `label`, `value`, `series`, `buckets`, `color`, `colors` | Uses `query
|
|
42
|
+
| `chart` | `chart` | `type`, `x`, `y`, `label`, `value`, `series`, `buckets`, `color`, `colors` | Uses the same `query` shape for every chart type. Multi-resource charts use `query.source: steps`. |
|
|
43
43
|
| `kpi_card` | `card` | `value`, `subtitle`, `comparison`, `sparkline` | Reads the first returned query row. |
|
|
44
44
|
| `gauge_card` | `card` | `value`, `target`, `progress`, `color` | Reads the first returned query row. |
|
|
45
45
|
| `pivot_table` | `pivot` | `rows`, `columns`, `values` | Uses query rows to build a pivot table. |
|
|
@@ -52,13 +52,14 @@ Chart widget types:
|
|
|
52
52
|
| `pie` | Uses `label` and `value`. |
|
|
53
53
|
| `bar` | Uses `x` and `y`. |
|
|
54
54
|
| `stacked_bar` | Uses `x`, `y`, and `series`. |
|
|
55
|
-
| `funnel` | Uses `
|
|
55
|
+
| `funnel` | Uses `label`, `value`, and optional `colors`. Data comes from the same `query` shapes as every other chart. |
|
|
56
56
|
| `histogram` | Uses `x`, `y`, and optional `buckets`. |
|
|
57
57
|
|
|
58
58
|
## Query Shape
|
|
59
59
|
|
|
60
60
|
```ts
|
|
61
61
|
type QueryConfig = {
|
|
62
|
+
source?: 'resource'
|
|
62
63
|
resource: string
|
|
63
64
|
select?: Array<
|
|
64
65
|
| { field: string; as?: string; grain?: 'day' | 'week' | 'month' | 'year' }
|
|
@@ -70,6 +71,22 @@ type QueryConfig = {
|
|
|
70
71
|
order_by?: Array<{ field: string; direction?: 'asc' | 'desc' }>
|
|
71
72
|
limit?: number
|
|
72
73
|
offset?: number
|
|
74
|
+
bucket?: { field: string; buckets: Array<{ label: string; min?: number; max?: number }> }
|
|
75
|
+
calcs?: Array<{ calc: string; as: string }>
|
|
76
|
+
formatting?: Record<string, JsonValue>
|
|
77
|
+
} | {
|
|
78
|
+
source: 'steps'
|
|
79
|
+
steps: Array<{
|
|
80
|
+
name: string
|
|
81
|
+
resource: string
|
|
82
|
+
select: Array<{ agg: 'sum' | 'count' | 'count_distinct' | 'avg' | 'min' | 'max' | 'median'; field?: string; as: string; filters?: DashboardFilter | DashboardFilter[] }>
|
|
83
|
+
filters?: DashboardFilter | DashboardFilter[]
|
|
84
|
+
}>
|
|
85
|
+
calcs?: Array<{ calc: string; as: string }>
|
|
86
|
+
order_by?: Array<{ field: string; direction?: 'asc' | 'desc' }>
|
|
87
|
+
limit?: number
|
|
88
|
+
offset?: number
|
|
89
|
+
formatting?: Record<string, JsonValue>
|
|
73
90
|
}
|
|
74
91
|
|
|
75
92
|
type DashboardFilter =
|
|
@@ -77,91 +94,97 @@ type DashboardFilter =
|
|
|
77
94
|
| { or: DashboardFilter[] }
|
|
78
95
|
| {
|
|
79
96
|
field: string
|
|
80
|
-
eq?:
|
|
81
|
-
neq?:
|
|
82
|
-
gt?:
|
|
83
|
-
gte?:
|
|
84
|
-
lt?:
|
|
85
|
-
lte?:
|
|
86
|
-
in?:
|
|
87
|
-
not_in?:
|
|
88
|
-
like?:
|
|
89
|
-
ilike?:
|
|
97
|
+
eq?: FilterValue
|
|
98
|
+
neq?: FilterValue
|
|
99
|
+
gt?: FilterValue
|
|
100
|
+
gte?: FilterValue
|
|
101
|
+
lt?: FilterValue
|
|
102
|
+
lte?: FilterValue
|
|
103
|
+
in?: FilterValue[]
|
|
104
|
+
not_in?: FilterValue[]
|
|
105
|
+
like?: FilterValue
|
|
106
|
+
ilike?: FilterValue
|
|
90
107
|
}
|
|
91
108
|
|
|
92
109
|
type JsonValue = string | number | boolean | null | JsonValue[] | { [key: string]: JsonValue }
|
|
110
|
+
type RelativeDateValue = { now: true } | { now_minus: `${number}${'h' | 'd' | 'w' | 'mo' | 'y'}` }
|
|
111
|
+
type FilterValue = JsonValue | RelativeDateValue
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Use `filters` for rolling date ranges. Do not hard-code dates for dashboards that should move with time:
|
|
115
|
+
|
|
116
|
+
```yaml
|
|
117
|
+
query:
|
|
118
|
+
resource: orders
|
|
119
|
+
filters:
|
|
120
|
+
and:
|
|
121
|
+
- field: created_at
|
|
122
|
+
gte:
|
|
123
|
+
now_minus: 30d
|
|
124
|
+
- field: created_at
|
|
125
|
+
lt:
|
|
126
|
+
now: true
|
|
93
127
|
```
|
|
94
128
|
|
|
95
|
-
|
|
129
|
+
Multi-resource queries use `source: steps`. Each step uses `select`, even if it has only one aggregate:
|
|
96
130
|
|
|
97
131
|
```yaml
|
|
98
132
|
target: chart
|
|
99
133
|
label: Average price by database
|
|
100
|
-
variables:
|
|
101
|
-
price_multipliers:
|
|
102
|
-
cars_sl: 0.84
|
|
103
|
-
cars_mysql: 1.12
|
|
104
|
-
cars_pg: 0.91
|
|
105
|
-
cars_mongo: 1.07
|
|
106
|
-
cars_ch: 0.76
|
|
107
134
|
chart:
|
|
108
135
|
type: bar
|
|
109
136
|
title: Average price by database
|
|
110
137
|
x:
|
|
111
138
|
field: name
|
|
112
139
|
y:
|
|
113
|
-
field:
|
|
140
|
+
field: value
|
|
114
141
|
query:
|
|
142
|
+
source: steps
|
|
115
143
|
steps:
|
|
116
144
|
- name: SQLite
|
|
117
145
|
resource: cars_sl
|
|
118
|
-
|
|
119
|
-
agg: avg
|
|
120
|
-
|
|
121
|
-
|
|
146
|
+
select:
|
|
147
|
+
- agg: avg
|
|
148
|
+
field: price
|
|
149
|
+
as: value
|
|
122
150
|
- name: MySQL
|
|
123
151
|
resource: cars_mysql
|
|
124
|
-
|
|
125
|
-
agg: avg
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
calcs:
|
|
129
|
-
- calc: value * lookup($variables.price_multipliers, resource, 1)
|
|
130
|
-
as: adjusted_value
|
|
152
|
+
select:
|
|
153
|
+
- agg: avg
|
|
154
|
+
field: price
|
|
155
|
+
as: value
|
|
131
156
|
```
|
|
132
157
|
|
|
133
|
-
|
|
158
|
+
Cost calculation example:
|
|
134
159
|
|
|
135
160
|
```yaml
|
|
136
161
|
target: chart
|
|
137
162
|
label: Model costs
|
|
138
|
-
variables:
|
|
139
|
-
token_prices_per_1m:
|
|
140
|
-
input:
|
|
141
|
-
gpt-4.1: 2.00
|
|
142
|
-
gpt-4.1-mini: 0.40
|
|
143
|
-
gpt-4o-mini: 0.15
|
|
144
|
-
output:
|
|
145
|
-
gpt-4.1: 8.00
|
|
146
|
-
gpt-4.1-mini: 1.60
|
|
147
|
-
gpt-4o-mini: 0.60
|
|
148
|
-
cached:
|
|
149
|
-
gpt-4.1: 0.50
|
|
150
|
-
gpt-4.1-mini: 0.10
|
|
151
|
-
gpt-4o-mini: 0.075
|
|
152
163
|
chart:
|
|
153
164
|
type: stacked_bar
|
|
154
|
-
title:
|
|
165
|
+
title: GPT-5.4 costs by day
|
|
155
166
|
x:
|
|
156
|
-
field:
|
|
167
|
+
field: day
|
|
157
168
|
y:
|
|
158
169
|
- field: input_cost
|
|
159
170
|
- field: output_cost
|
|
160
171
|
- field: cached_cost
|
|
161
172
|
query:
|
|
162
173
|
resource: model_usage
|
|
174
|
+
filters:
|
|
175
|
+
and:
|
|
176
|
+
- field: model
|
|
177
|
+
eq: gpt-5.4
|
|
178
|
+
- field: used_at
|
|
179
|
+
gte:
|
|
180
|
+
now_minus: 7d
|
|
181
|
+
- field: used_at
|
|
182
|
+
lt:
|
|
183
|
+
now: true
|
|
163
184
|
select:
|
|
164
|
-
- field:
|
|
185
|
+
- field: used_at
|
|
186
|
+
as: day
|
|
187
|
+
grain: day
|
|
165
188
|
- agg: sum
|
|
166
189
|
field: input_tokens
|
|
167
190
|
as: input_tokens
|
|
@@ -172,16 +195,19 @@ query:
|
|
|
172
195
|
field: cached_tokens
|
|
173
196
|
as: cached_tokens
|
|
174
197
|
group_by:
|
|
175
|
-
-
|
|
198
|
+
- field: used_at
|
|
199
|
+
as: day
|
|
200
|
+
grain: day
|
|
176
201
|
calcs:
|
|
177
|
-
- calc: input_tokens / 1000000 *
|
|
202
|
+
- calc: input_tokens / 1000000 * 2.5
|
|
178
203
|
as: input_cost
|
|
179
|
-
- calc: output_tokens / 1000000 *
|
|
204
|
+
- calc: output_tokens / 1000000 * 15
|
|
180
205
|
as: output_cost
|
|
181
|
-
- calc: cached_tokens / 1000000 *
|
|
206
|
+
- calc: cached_tokens / 1000000 * 0.25
|
|
182
207
|
as: cached_cost
|
|
183
208
|
```
|
|
184
209
|
|
|
210
|
+
|
|
185
211
|
## Runtime Structure
|
|
186
212
|
|
|
187
213
|
```text
|
|
@@ -117,7 +117,8 @@ export type QueryOrderByItem = {
|
|
|
117
117
|
direction?: 'asc' | 'desc'
|
|
118
118
|
}
|
|
119
119
|
|
|
120
|
-
export type
|
|
120
|
+
export type ResourceQueryConfig = {
|
|
121
|
+
source?: 'resource'
|
|
121
122
|
resource: string
|
|
122
123
|
select?: QuerySelectItem[]
|
|
123
124
|
sparkline?: {
|
|
@@ -139,18 +140,25 @@ export type QueryConfig = {
|
|
|
139
140
|
formatting?: Record<string, JsonValue>
|
|
140
141
|
}
|
|
141
142
|
|
|
142
|
-
export type
|
|
143
|
-
steps: FunnelQueryStep[]
|
|
144
|
-
calcs?: QueryCalcSelectItem[]
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
export type FunnelQueryStep = {
|
|
143
|
+
export type StepsQueryStepConfig = {
|
|
148
144
|
name: string
|
|
149
145
|
resource: string
|
|
150
|
-
|
|
146
|
+
select: QueryAggregateSelectItem[]
|
|
151
147
|
filters?: FilterExpression
|
|
152
148
|
}
|
|
153
149
|
|
|
150
|
+
export type StepsQueryConfig = {
|
|
151
|
+
source: 'steps'
|
|
152
|
+
steps: StepsQueryStepConfig[]
|
|
153
|
+
calcs?: QueryCalcSelectItem[]
|
|
154
|
+
order_by?: QueryOrderByItem[]
|
|
155
|
+
limit?: number
|
|
156
|
+
offset?: number
|
|
157
|
+
formatting?: Record<string, JsonValue>
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export type QueryConfig = ResourceQueryConfig | StepsQueryConfig
|
|
161
|
+
|
|
154
162
|
export type FieldRef = string | {
|
|
155
163
|
field: string
|
|
156
164
|
label?: string
|
|
@@ -246,7 +254,7 @@ export type TableWidgetConfig = WidgetBaseConfig & {
|
|
|
246
254
|
export type ChartDashboardWidgetConfig = WidgetBaseConfig & {
|
|
247
255
|
target: 'chart'
|
|
248
256
|
chart: ChartWidgetConfig
|
|
249
|
-
query: QueryConfig
|
|
257
|
+
query: QueryConfig
|
|
250
258
|
}
|
|
251
259
|
|
|
252
260
|
export type KpiCardWidgetConfig = WidgetBaseConfig & {
|
|
@@ -151,8 +151,8 @@ Use resource, not resourceId.
|
|
|
151
151
|
|
|
152
152
|
## Query shape rules
|
|
153
153
|
|
|
154
|
-
|
|
155
|
-
|
|
154
|
+
All chart widgets, including funnel charts, use the same query shape.
|
|
155
|
+
Use a single-resource query by default.
|
|
156
156
|
|
|
157
157
|
For kpi_card and normal charts, use:
|
|
158
158
|
- query.resource
|
|
@@ -162,6 +162,26 @@ For kpi_card and normal charts, use:
|
|
|
162
162
|
- optional query.order_by
|
|
163
163
|
- optional query.calcs
|
|
164
164
|
|
|
165
|
+
For multi-resource charts or widgets, use the general steps source:
|
|
166
|
+
|
|
167
|
+
query:
|
|
168
|
+
source: steps
|
|
169
|
+
steps:
|
|
170
|
+
- name: Leads
|
|
171
|
+
resource: leads
|
|
172
|
+
select:
|
|
173
|
+
- agg: count
|
|
174
|
+
as: value
|
|
175
|
+
- name: Customers
|
|
176
|
+
resource: orders
|
|
177
|
+
select:
|
|
178
|
+
- agg: count_distinct
|
|
179
|
+
field: customer_id
|
|
180
|
+
as: value
|
|
181
|
+
|
|
182
|
+
Do not use bare query.steps without source: steps.
|
|
183
|
+
Do not use metric. Use select even when a step has only one aggregate.
|
|
184
|
+
|
|
165
185
|
## Date range rules
|
|
166
186
|
|
|
167
187
|
Use only query.filters for time ranges.
|
|
@@ -201,22 +221,18 @@ select raw token totals:
|
|
|
201
221
|
- sum output_tokens as output_tokens
|
|
202
222
|
|
|
203
223
|
then query.calcs:
|
|
204
|
-
- calculate total_spend from those aliases
|
|
224
|
+
- calculate total_spend from those aliases with explicit constants
|
|
205
225
|
|
|
206
|
-
For today vs yesterday KPI, use multiple aggregate select items with filters and distinct aliases, then calcs.
|
|
226
|
+
For today vs yesterday KPI, use multiple aggregate select items with filters and distinct aliases, then calcs.
|
|
207
227
|
|
|
208
|
-
## Calc
|
|
228
|
+
## Calc rules
|
|
209
229
|
|
|
210
|
-
|
|
211
|
-
Use
|
|
230
|
+
Calcs can reference only fields already present in the current row.
|
|
231
|
+
Use explicit constants for rates.
|
|
212
232
|
|
|
213
233
|
Minimal example:
|
|
214
234
|
|
|
215
|
-
variables:
|
|
216
|
-
prices:
|
|
217
|
-
gpt-5.4: 2.5
|
|
218
|
-
|
|
219
235
|
query:
|
|
220
236
|
calcs:
|
|
221
|
-
- calc: tokens / 1000000 *
|
|
237
|
+
- calc: tokens / 1000000 * 2.5
|
|
222
238
|
as: cost
|
|
@@ -87,7 +87,8 @@ export type QueryOrderByItem = {
|
|
|
87
87
|
field: string;
|
|
88
88
|
direction?: 'asc' | 'desc';
|
|
89
89
|
};
|
|
90
|
-
export type
|
|
90
|
+
export type ResourceQueryConfig = {
|
|
91
|
+
source?: 'resource';
|
|
91
92
|
resource: string;
|
|
92
93
|
select?: QuerySelectItem[];
|
|
93
94
|
sparkline?: {
|
|
@@ -112,16 +113,22 @@ export type QueryConfig = {
|
|
|
112
113
|
calcs?: QueryCalcSelectItem[];
|
|
113
114
|
formatting?: Record<string, JsonValue>;
|
|
114
115
|
};
|
|
115
|
-
export type
|
|
116
|
-
steps: FunnelQueryStep[];
|
|
117
|
-
calcs?: QueryCalcSelectItem[];
|
|
118
|
-
};
|
|
119
|
-
export type FunnelQueryStep = {
|
|
116
|
+
export type StepsQueryStepConfig = {
|
|
120
117
|
name: string;
|
|
121
118
|
resource: string;
|
|
122
|
-
|
|
119
|
+
select: QueryAggregateSelectItem[];
|
|
123
120
|
filters?: FilterExpression;
|
|
124
121
|
};
|
|
122
|
+
export type StepsQueryConfig = {
|
|
123
|
+
source: 'steps';
|
|
124
|
+
steps: StepsQueryStepConfig[];
|
|
125
|
+
calcs?: QueryCalcSelectItem[];
|
|
126
|
+
order_by?: QueryOrderByItem[];
|
|
127
|
+
limit?: number;
|
|
128
|
+
offset?: number;
|
|
129
|
+
formatting?: Record<string, JsonValue>;
|
|
130
|
+
};
|
|
131
|
+
export type QueryConfig = ResourceQueryConfig | StepsQueryConfig;
|
|
125
132
|
export type FieldRef = string | {
|
|
126
133
|
field: string;
|
|
127
134
|
label?: string;
|
|
@@ -210,7 +217,7 @@ export type TableWidgetConfig = WidgetBaseConfig & {
|
|
|
210
217
|
export type ChartDashboardWidgetConfig = WidgetBaseConfig & {
|
|
211
218
|
target: 'chart';
|
|
212
219
|
chart: ChartWidgetConfig;
|
|
213
|
-
query: QueryConfig
|
|
220
|
+
query: QueryConfig;
|
|
214
221
|
};
|
|
215
222
|
export type KpiCardWidgetConfig = WidgetBaseConfig & {
|
|
216
223
|
target: 'kpi_card';
|
|
@@ -117,7 +117,8 @@ export type QueryOrderByItem = {
|
|
|
117
117
|
direction?: 'asc' | 'desc'
|
|
118
118
|
}
|
|
119
119
|
|
|
120
|
-
export type
|
|
120
|
+
export type ResourceQueryConfig = {
|
|
121
|
+
source?: 'resource'
|
|
121
122
|
resource: string
|
|
122
123
|
select?: QuerySelectItem[]
|
|
123
124
|
sparkline?: {
|
|
@@ -139,18 +140,25 @@ export type QueryConfig = {
|
|
|
139
140
|
formatting?: Record<string, JsonValue>
|
|
140
141
|
}
|
|
141
142
|
|
|
142
|
-
export type
|
|
143
|
-
steps: FunnelQueryStep[]
|
|
144
|
-
calcs?: QueryCalcSelectItem[]
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
export type FunnelQueryStep = {
|
|
143
|
+
export type StepsQueryStepConfig = {
|
|
148
144
|
name: string
|
|
149
145
|
resource: string
|
|
150
|
-
|
|
146
|
+
select: QueryAggregateSelectItem[]
|
|
151
147
|
filters?: FilterExpression
|
|
152
148
|
}
|
|
153
149
|
|
|
150
|
+
export type StepsQueryConfig = {
|
|
151
|
+
source: 'steps'
|
|
152
|
+
steps: StepsQueryStepConfig[]
|
|
153
|
+
calcs?: QueryCalcSelectItem[]
|
|
154
|
+
order_by?: QueryOrderByItem[]
|
|
155
|
+
limit?: number
|
|
156
|
+
offset?: number
|
|
157
|
+
formatting?: Record<string, JsonValue>
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export type QueryConfig = ResourceQueryConfig | StepsQueryConfig
|
|
161
|
+
|
|
154
162
|
export type FieldRef = string | {
|
|
155
163
|
field: string
|
|
156
164
|
label?: string
|
|
@@ -246,7 +254,7 @@ export type TableWidgetConfig = WidgetBaseConfig & {
|
|
|
246
254
|
export type ChartDashboardWidgetConfig = WidgetBaseConfig & {
|
|
247
255
|
target: 'chart'
|
|
248
256
|
chart: ChartWidgetConfig
|
|
249
|
-
query: QueryConfig
|
|
257
|
+
query: QueryConfig
|
|
250
258
|
}
|
|
251
259
|
|
|
252
260
|
export type KpiCardWidgetConfig = WidgetBaseConfig & {
|