@adminforth/dashboard 1.8.0 → 1.10.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.
Files changed (39) hide show
  1. package/README.md +81 -55
  2. package/custom/api/dashboardApi.ts +73 -36
  3. package/custom/model/dashboard.types.ts +6 -13
  4. package/custom/runtime/DashboardRuntime.vue +26 -22
  5. package/custom/skills/adminforth-dashboard/SKILL.md +13 -20
  6. package/dist/custom/api/dashboardApi.d.ts +24 -18
  7. package/dist/custom/api/dashboardApi.js +42 -18
  8. package/dist/custom/api/dashboardApi.ts +73 -36
  9. package/dist/custom/model/dashboard.types.d.ts +0 -5
  10. package/dist/custom/model/dashboard.types.ts +6 -13
  11. package/dist/custom/queries/useDashboardConfig.d.ts +20 -120
  12. package/dist/custom/queries/useWidgetData.d.ts +20 -120
  13. package/dist/custom/runtime/DashboardRuntime.vue +26 -22
  14. package/dist/custom/skills/adminforth-dashboard/SKILL.md +13 -20
  15. package/dist/endpoint/groups.js +22 -20
  16. package/dist/endpoint/widgets.js +28 -26
  17. package/dist/schema/api.d.ts +230 -3936
  18. package/dist/schema/api.js +7 -12
  19. package/dist/schema/widget.d.ts +20 -200
  20. package/dist/schema/widgets/charts.d.ts +24 -240
  21. package/dist/schema/widgets/common.d.ts +2 -20
  22. package/dist/schema/widgets/common.js +1 -10
  23. package/dist/schema/widgets/gauge-card.d.ts +2 -20
  24. package/dist/schema/widgets/kpi-card.d.ts +2 -20
  25. package/dist/schema/widgets/pivot-table.d.ts +2 -20
  26. package/dist/schema/widgets/table.d.ts +2 -20
  27. package/dist/services/calc-evaluator.d.ts +2 -0
  28. package/dist/services/calc-evaluator.js +54 -0
  29. package/dist/services/dashboardFilterService.d.ts +5 -0
  30. package/dist/services/dashboardFilterService.js +125 -0
  31. package/dist/services/widgetDataService.js +15 -168
  32. package/endpoint/groups.ts +22 -20
  33. package/endpoint/widgets.ts +28 -26
  34. package/package.json +2 -1
  35. package/schema/api.ts +7 -12
  36. package/schema/widgets/common.ts +1 -11
  37. package/services/calc-evaluator.ts +71 -0
  38. package/services/dashboardFilterService.ts +162 -0
  39. package/services/widgetDataService.ts +26 -213
@@ -169,21 +169,18 @@ query:
169
169
  steps:
170
170
  - name: Leads
171
171
  resource: leads
172
- metric:
173
- agg: count
174
- as: value
172
+ select:
173
+ - agg: count
174
+ as: value
175
175
  - name: Customers
176
176
  resource: orders
177
- metric:
178
- agg: count_distinct
179
- field: customer_id
180
- as: value
181
-
182
- Each step may use either:
183
- - metric for one aggregate
184
- - select for multiple aggregate fields
177
+ select:
178
+ - agg: count_distinct
179
+ field: customer_id
180
+ as: value
185
181
 
186
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.
187
184
 
188
185
  ## Date range rules
189
186
 
@@ -224,22 +221,18 @@ select raw token totals:
224
221
  - sum output_tokens as output_tokens
225
222
 
226
223
  then query.calcs:
227
- - calculate total_spend from those aliases and lookup variables
224
+ - calculate total_spend from those aliases with explicit constants
228
225
 
229
226
  For today vs yesterday KPI, use multiple aggregate select items with filters and distinct aliases, then calcs.
230
227
 
231
- ## Calc variables
228
+ ## Calc rules
232
229
 
233
- Use variables for static maps/rates.
234
- Use lookup($variables.some.map, row_field, default_number) in query.calcs.
230
+ Calcs can reference only fields already present in the current row.
231
+ Use explicit constants for rates.
235
232
 
236
233
  Minimal example:
237
234
 
238
- variables:
239
- prices:
240
- gpt-5.4: 2.5
241
-
242
235
  query:
243
236
  calcs:
244
- - calc: tokens / 1000000 * lookup($variables.prices, model, 0)
237
+ - calc: tokens / 1000000 * 2.5
245
238
  as: cost
@@ -8,19 +8,20 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
8
8
  });
9
9
  };
10
10
  import { randomUUID } from 'crypto';
11
- import { DashboardApiResponseSchema, GroupIdRequestSchema, MoveGroupRequestSchema, SetGroupConfigRequestSchema, SlugRequestSchema, } from '../schema/api.js';
11
+ import { DashboardMutationResponseSchema, GroupIdRequestSchema, MoveGroupRequestSchema, SetGroupConfigRequestSchema, SlugRequestSchema, } from '../schema/api.js';
12
12
  export function registerGroupEndpoints(server, ctx) {
13
13
  server.endpoint({
14
14
  method: 'POST',
15
15
  path: '/dashboard/add_dashboard_group',
16
16
  description: 'Adds a new group to a dashboard configuration. Superadmin only.',
17
17
  request_schema: SlugRequestSchema,
18
- response_schema: DashboardApiResponseSchema,
18
+ response_schema: DashboardMutationResponseSchema,
19
19
  handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body, adminUser, response }) {
20
20
  if (!ctx.canEditDashboard(adminUser)) {
21
21
  response.setStatus(403);
22
- return { error: 'Dashboard edit is not allowed' };
22
+ return { ok: false, error: 'Dashboard edit is not allowed' };
23
23
  }
24
+ let groupId = null;
24
25
  const updatedDashboard = yield ctx.updateDashboardConfig(body.slug, (config) => {
25
26
  const nextOrder = config.groups.length + 1;
26
27
  const group = {
@@ -28,13 +29,14 @@ export function registerGroupEndpoints(server, ctx) {
28
29
  label: 'New group',
29
30
  order: nextOrder,
30
31
  };
32
+ groupId = group.id;
31
33
  return Object.assign(Object.assign({}, config), { groups: [...config.groups, group] });
32
34
  });
33
35
  if (!updatedDashboard) {
34
36
  response.setStatus(404);
35
- return { error: 'Dashboard not found' };
37
+ return { ok: false, error: 'Dashboard not found' };
36
38
  }
37
- return updatedDashboard;
39
+ return { ok: true, groupId };
38
40
  }),
39
41
  });
40
42
  server.endpoint({
@@ -42,11 +44,11 @@ export function registerGroupEndpoints(server, ctx) {
42
44
  path: '/dashboard/set_dashboard_group_config',
43
45
  description: 'Replaces editable JSON configuration for a dashboard group while preserving group id and order. Superadmin only.',
44
46
  request_schema: SetGroupConfigRequestSchema,
45
- response_schema: DashboardApiResponseSchema,
47
+ response_schema: DashboardMutationResponseSchema,
46
48
  handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body, adminUser, response }) {
47
49
  if (!ctx.canEditDashboard(adminUser)) {
48
50
  response.setStatus(403);
49
- return { error: 'Dashboard edit is not allowed' };
51
+ return { ok: false, error: 'Dashboard edit is not allowed' };
50
52
  }
51
53
  const groupId = body.groupId;
52
54
  let mutationError = null;
@@ -63,13 +65,13 @@ export function registerGroupEndpoints(server, ctx) {
63
65
  });
64
66
  if (!updatedDashboard) {
65
67
  response.setStatus(404);
66
- return { error: 'Dashboard not found' };
68
+ return { ok: false, error: 'Dashboard not found' };
67
69
  }
68
70
  if (mutationError) {
69
71
  response.setStatus(404);
70
- return { error: mutationError };
72
+ return { ok: false, error: mutationError };
71
73
  }
72
- return updatedDashboard;
74
+ return { ok: true };
73
75
  }),
74
76
  });
75
77
  server.endpoint({
@@ -77,11 +79,11 @@ export function registerGroupEndpoints(server, ctx) {
77
79
  path: '/dashboard/move_dashboard_group',
78
80
  description: 'Moves a dashboard group up or down in its dashboard. Superadmin only.',
79
81
  request_schema: MoveGroupRequestSchema,
80
- response_schema: DashboardApiResponseSchema,
82
+ response_schema: DashboardMutationResponseSchema,
81
83
  handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body, adminUser, response }) {
82
84
  if (!ctx.canEditDashboard(adminUser)) {
83
85
  response.setStatus(403);
84
- return { error: 'Dashboard edit is not allowed' };
86
+ return { ok: false, error: 'Dashboard edit is not allowed' };
85
87
  }
86
88
  let mutationError = null;
87
89
  const updatedDashboard = yield ctx.updateDashboardConfig(body.slug, (config) => {
@@ -102,13 +104,13 @@ export function registerGroupEndpoints(server, ctx) {
102
104
  });
103
105
  if (!updatedDashboard) {
104
106
  response.setStatus(404);
105
- return { error: 'Dashboard not found' };
107
+ return { ok: false, error: 'Dashboard not found' };
106
108
  }
107
109
  if (mutationError) {
108
110
  response.setStatus(404);
109
- return { error: mutationError };
111
+ return { ok: false, error: mutationError };
110
112
  }
111
- return updatedDashboard;
113
+ return { ok: true };
112
114
  }),
113
115
  });
114
116
  server.endpoint({
@@ -116,11 +118,11 @@ export function registerGroupEndpoints(server, ctx) {
116
118
  path: '/dashboard/remove_dashboard_group',
117
119
  description: 'Removes a dashboard group and all widgets inside it. Superadmin only.',
118
120
  request_schema: GroupIdRequestSchema,
119
- response_schema: DashboardApiResponseSchema,
121
+ response_schema: DashboardMutationResponseSchema,
120
122
  handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body, adminUser, response }) {
121
123
  if (!ctx.canEditDashboard(adminUser)) {
122
124
  response.setStatus(403);
123
- return { error: 'Dashboard edit is not allowed' };
125
+ return { ok: false, error: 'Dashboard edit is not allowed' };
124
126
  }
125
127
  const groupId = body.groupId;
126
128
  let mutationError = null;
@@ -134,13 +136,13 @@ export function registerGroupEndpoints(server, ctx) {
134
136
  });
135
137
  if (!updatedDashboard) {
136
138
  response.setStatus(404);
137
- return { error: 'Dashboard not found' };
139
+ return { ok: false, error: 'Dashboard not found' };
138
140
  }
139
141
  if (mutationError) {
140
142
  response.setStatus(404);
141
- return { error: mutationError };
143
+ return { ok: false, error: mutationError };
142
144
  }
143
- return updatedDashboard;
145
+ return { ok: true };
144
146
  }),
145
147
  });
146
148
  }
@@ -8,7 +8,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
8
8
  });
9
9
  };
10
10
  import { randomUUID } from 'crypto';
11
- import { ConfigureBarChartWidgetRequestSchema, ConfigureFunnelChartWidgetRequestSchema, ConfigureGaugeCardWidgetRequestSchema, ConfigureHistogramChartWidgetRequestSchema, ConfigureKpiCardWidgetRequestSchema, ConfigureLineChartWidgetRequestSchema, ConfigurePieChartWidgetRequestSchema, ConfigurePivotTableWidgetRequestSchema, ConfigureStackedBarChartWidgetRequestSchema, ConfigureTableWidgetRequestSchema, DashboardApiResponseSchema, DashboardWidgetDataResponseSchema, GroupIdRequestSchema, MoveWidgetRequestSchema, SetWidgetConfigRequestSchema, WidgetDataRequestSchema, WidgetIdRequestSchema, } from '../schema/api.js';
11
+ import { ConfigureBarChartWidgetRequestSchema, ConfigureFunnelChartWidgetRequestSchema, ConfigureGaugeCardWidgetRequestSchema, ConfigureHistogramChartWidgetRequestSchema, ConfigureKpiCardWidgetRequestSchema, ConfigureLineChartWidgetRequestSchema, ConfigurePieChartWidgetRequestSchema, ConfigurePivotTableWidgetRequestSchema, ConfigureStackedBarChartWidgetRequestSchema, ConfigureTableWidgetRequestSchema, DashboardMutationResponseSchema, DashboardWidgetDataResponseSchema, GroupIdRequestSchema, MoveWidgetRequestSchema, SetWidgetConfigRequestSchema, WidgetDataRequestSchema, WidgetIdRequestSchema, } from '../schema/api.js';
12
12
  function replaceWidgetConfig(ctx, slug, widgetId, widgetConfig) {
13
13
  return __awaiter(this, void 0, void 0, function* () {
14
14
  let mutationError = null;
@@ -35,23 +35,23 @@ function registerConfigureWidgetEndpoint(server, ctx, options) {
35
35
  path: options.path,
36
36
  description: options.description,
37
37
  request_schema: options.requestSchema,
38
- response_schema: DashboardApiResponseSchema,
38
+ response_schema: DashboardMutationResponseSchema,
39
39
  handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body, adminUser, response }) {
40
40
  if (!ctx.canEditDashboard(adminUser)) {
41
41
  response.setStatus(403);
42
- return { error: 'Dashboard edit is not allowed' };
42
+ return { ok: false, error: 'Dashboard edit is not allowed' };
43
43
  }
44
44
  const request = body;
45
45
  const { updatedDashboard, mutationError } = yield replaceWidgetConfig(ctx, request.slug, request.widgetId, request.config);
46
46
  if (!updatedDashboard) {
47
47
  response.setStatus(404);
48
- return { error: 'Dashboard not found' };
48
+ return { ok: false, error: 'Dashboard not found' };
49
49
  }
50
50
  if (mutationError) {
51
51
  response.setStatus(404);
52
- return { error: mutationError };
52
+ return { ok: false, error: mutationError };
53
53
  }
54
- return updatedDashboard;
54
+ return { ok: true };
55
55
  }),
56
56
  });
57
57
  }
@@ -61,13 +61,14 @@ export function registerWidgetEndpoints(server, ctx) {
61
61
  path: '/dashboard/add_dashboard_widget',
62
62
  description: 'Adds a new empty widget to a dashboard group. Superadmin only.',
63
63
  request_schema: GroupIdRequestSchema,
64
- response_schema: DashboardApiResponseSchema,
64
+ response_schema: DashboardMutationResponseSchema,
65
65
  handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body, adminUser, response }) {
66
66
  if (!ctx.canEditDashboard(adminUser)) {
67
67
  response.setStatus(403);
68
- return { error: 'Dashboard edit is not allowed' };
68
+ return { ok: false, error: 'Dashboard edit is not allowed' };
69
69
  }
70
70
  let mutationError = null;
71
+ let widgetId = null;
71
72
  const updatedDashboard = yield ctx.updateDashboardConfig(body.slug, (config) => {
72
73
  const group = config.groups.find((item) => item.id === body.groupId);
73
74
  if (!group) {
@@ -83,17 +84,18 @@ export function registerWidgetEndpoints(server, ctx) {
83
84
  order: nextOrder,
84
85
  target: 'empty',
85
86
  };
87
+ widgetId = widget.id;
86
88
  return Object.assign(Object.assign({}, config), { widgets: [...config.widgets, widget] });
87
89
  });
88
90
  if (!updatedDashboard) {
89
91
  response.setStatus(404);
90
- return { error: 'Dashboard not found' };
92
+ return { ok: false, error: 'Dashboard not found' };
91
93
  }
92
94
  if (mutationError) {
93
95
  response.setStatus(404);
94
- return { error: mutationError };
96
+ return { ok: false, error: mutationError };
95
97
  }
96
- return updatedDashboard;
98
+ return { ok: true, widgetId };
97
99
  }),
98
100
  });
99
101
  server.endpoint({
@@ -101,11 +103,11 @@ export function registerWidgetEndpoints(server, ctx) {
101
103
  path: '/dashboard/move_dashboard_widget',
102
104
  description: 'Moves a dashboard widget up or down inside its group. Superadmin only.',
103
105
  request_schema: MoveWidgetRequestSchema,
104
- response_schema: DashboardApiResponseSchema,
106
+ response_schema: DashboardMutationResponseSchema,
105
107
  handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body, adminUser, response }) {
106
108
  if (!ctx.canEditDashboard(adminUser)) {
107
109
  response.setStatus(403);
108
- return { error: 'Dashboard edit is not allowed' };
110
+ return { ok: false, error: 'Dashboard edit is not allowed' };
109
111
  }
110
112
  let mutationError = null;
111
113
  const updatedDashboard = yield ctx.updateDashboardConfig(body.slug, (config) => {
@@ -133,13 +135,13 @@ export function registerWidgetEndpoints(server, ctx) {
133
135
  });
134
136
  if (!updatedDashboard) {
135
137
  response.setStatus(404);
136
- return { error: 'Dashboard not found' };
138
+ return { ok: false, error: 'Dashboard not found' };
137
139
  }
138
140
  if (mutationError) {
139
141
  response.setStatus(404);
140
- return { error: mutationError };
142
+ return { ok: false, error: mutationError };
141
143
  }
142
- return updatedDashboard;
144
+ return { ok: true };
143
145
  }),
144
146
  });
145
147
  server.endpoint({
@@ -147,11 +149,11 @@ export function registerWidgetEndpoints(server, ctx) {
147
149
  path: '/dashboard/remove_dashboard_widget',
148
150
  description: 'Removes one dashboard widget by id. Superadmin only.',
149
151
  request_schema: WidgetIdRequestSchema,
150
- response_schema: DashboardApiResponseSchema,
152
+ response_schema: DashboardMutationResponseSchema,
151
153
  handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body, adminUser, response }) {
152
154
  if (!ctx.canEditDashboard(adminUser)) {
153
155
  response.setStatus(403);
154
- return { error: 'Dashboard edit is not allowed' };
156
+ return { ok: false, error: 'Dashboard edit is not allowed' };
155
157
  }
156
158
  let mutationError = null;
157
159
  const updatedDashboard = yield ctx.updateDashboardConfig(body.slug, (config) => {
@@ -164,13 +166,13 @@ export function registerWidgetEndpoints(server, ctx) {
164
166
  });
165
167
  if (!updatedDashboard) {
166
168
  response.setStatus(404);
167
- return { error: 'Dashboard not found' };
169
+ return { ok: false, error: 'Dashboard not found' };
168
170
  }
169
171
  if (mutationError) {
170
172
  response.setStatus(404);
171
- return { error: mutationError };
173
+ return { ok: false, error: mutationError };
172
174
  }
173
- return updatedDashboard;
175
+ return { ok: true };
174
176
  }),
175
177
  });
176
178
  server.endpoint({
@@ -178,23 +180,23 @@ export function registerWidgetEndpoints(server, ctx) {
178
180
  path: '/dashboard/set_widget_config',
179
181
  description: 'Replaces editable JSON configuration for a dashboard widget while preserving widget id, group id, and order. Superadmin only.',
180
182
  request_schema: SetWidgetConfigRequestSchema,
181
- response_schema: DashboardApiResponseSchema,
183
+ response_schema: DashboardMutationResponseSchema,
182
184
  handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body, adminUser, response }) {
183
185
  if (!ctx.canEditDashboard(adminUser)) {
184
186
  response.setStatus(403);
185
- return { error: 'Dashboard edit is not allowed' };
187
+ return { ok: false, error: 'Dashboard edit is not allowed' };
186
188
  }
187
189
  const request = body;
188
190
  const { updatedDashboard, mutationError } = yield replaceWidgetConfig(ctx, request.slug, request.widgetId, request.config);
189
191
  if (!updatedDashboard) {
190
192
  response.setStatus(404);
191
- return { error: 'Dashboard not found' };
193
+ return { ok: false, error: 'Dashboard not found' };
192
194
  }
193
195
  if (mutationError) {
194
196
  response.setStatus(404);
195
- return { error: mutationError };
197
+ return { ok: false, error: mutationError };
196
198
  }
197
- return updatedDashboard;
199
+ return { ok: true };
198
200
  }),
199
201
  });
200
202
  registerConfigureWidgetEndpoint(server, ctx, {