@adminforth/dashboard 1.4.0 → 1.4.2

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 (86) hide show
  1. package/README.md +23 -4
  2. package/custom/api/dashboardApi.ts +6 -9
  3. package/custom/model/dashboard.types.ts +60 -275
  4. package/custom/model/dashboardTopics.ts +5 -0
  5. package/custom/package.json +1 -0
  6. package/custom/runtime/DashboardGroup.vue +2 -2
  7. package/custom/runtime/DashboardPage.vue +17 -7
  8. package/custom/runtime/DashboardRuntime.vue +20 -8
  9. package/custom/runtime/WidgetRenderer.vue +1 -2
  10. package/custom/runtime/WidgetShell.vue +3 -3
  11. package/custom/skills/adminforth-dashboard/SKILL.md +2 -2
  12. package/custom/widgets/{gauge-card/GaugeCardWidget.vue → GaugeCardWidget.vue} +63 -61
  13. package/custom/widgets/{kpi-card/KpiCardWidget.vue → KpiCardWidget.vue} +35 -33
  14. package/custom/widgets/{pivot-table/PivotTableWidget.vue → PivotTableWidget.vue} +71 -68
  15. package/custom/widgets/{table/TableWidget.vue → TableWidget.vue} +5 -5
  16. package/custom/widgets/chart/{bar/BarChart.vue → BarChart.vue} +2 -2
  17. package/custom/widgets/chart/ChartWidget.vue +4 -15
  18. package/{dist/custom/widgets/chart/funnel → custom/widgets/chart}/FunnelChart.vue +80 -78
  19. package/{dist/custom/widgets/chart/line → custom/widgets/chart}/LineChart.vue +2 -2
  20. package/custom/widgets/chart/{pie/PieChart.vue → PieChart.vue} +2 -2
  21. package/{dist/custom/widgets/chart/stacked-bar → custom/widgets/chart}/StackedBarChart.vue +97 -95
  22. package/custom/widgets/chart/chart.types.ts +0 -28
  23. package/dist/custom/api/dashboardApi.d.ts +4 -8
  24. package/dist/custom/api/dashboardApi.js +2 -6
  25. package/dist/custom/api/dashboardApi.ts +6 -9
  26. package/dist/custom/composables/useElementSize.js +7 -10
  27. package/dist/custom/model/dashboard.types.d.ts +38 -32
  28. package/dist/custom/model/dashboard.types.js +4 -161
  29. package/dist/custom/model/dashboard.types.ts +60 -275
  30. package/dist/custom/model/dashboardTopics.d.ts +2 -0
  31. package/dist/custom/model/dashboardTopics.js +4 -0
  32. package/dist/custom/model/dashboardTopics.ts +5 -0
  33. package/dist/custom/package.json +1 -0
  34. package/dist/custom/queries/useDashboardConfig.d.ts +96 -96
  35. package/dist/custom/queries/useDashboardConfig.js +9 -12
  36. package/dist/custom/queries/useWidgetData.d.ts +96 -96
  37. package/dist/custom/queries/useWidgetData.js +9 -12
  38. package/dist/custom/runtime/DashboardGroup.vue +2 -2
  39. package/dist/custom/runtime/DashboardPage.vue +17 -7
  40. package/dist/custom/runtime/DashboardRuntime.vue +20 -8
  41. package/dist/custom/runtime/WidgetRenderer.vue +1 -2
  42. package/dist/custom/runtime/WidgetShell.vue +3 -3
  43. package/dist/custom/skills/adminforth-dashboard/SKILL.md +2 -2
  44. package/dist/custom/widgets/{gauge-card/GaugeCardWidget.vue → GaugeCardWidget.vue} +63 -61
  45. package/dist/custom/widgets/{kpi-card/KpiCardWidget.vue → KpiCardWidget.vue} +35 -33
  46. package/dist/custom/widgets/{pivot-table/PivotTableWidget.vue → PivotTableWidget.vue} +71 -68
  47. package/dist/custom/widgets/{table/TableWidget.vue → TableWidget.vue} +5 -5
  48. package/dist/custom/widgets/chart/{bar/BarChart.vue → BarChart.vue} +2 -2
  49. package/dist/custom/widgets/chart/ChartWidget.vue +4 -15
  50. package/{custom/widgets/chart/funnel → dist/custom/widgets/chart}/FunnelChart.vue +80 -78
  51. package/{custom/widgets/chart/line → dist/custom/widgets/chart}/LineChart.vue +2 -2
  52. package/dist/custom/widgets/chart/{pie/PieChart.vue → PieChart.vue} +2 -2
  53. package/{custom/widgets/chart/stacked-bar → dist/custom/widgets/chart}/StackedBarChart.vue +97 -95
  54. package/dist/custom/widgets/chart/chart.types.d.ts +0 -2
  55. package/dist/custom/widgets/chart/chart.types.js +1 -25
  56. package/dist/custom/widgets/chart/chart.types.ts +0 -28
  57. package/dist/custom/widgets/chart/chart.utils.js +6 -14
  58. package/dist/custom/widgets/registry.js +14 -22
  59. package/dist/endpoint/dashboard.d.ts +2 -3
  60. package/dist/endpoint/dashboard.js +12 -32
  61. package/dist/endpoint/groups.d.ts +2 -21
  62. package/dist/endpoint/groups.js +18 -16
  63. package/dist/endpoint/widgets.d.ts +0 -3
  64. package/dist/endpoint/widgets.js +27 -74
  65. package/dist/index.js +1 -3
  66. package/dist/schema/api.d.ts +2090 -511
  67. package/dist/schema/api.js +18 -15
  68. package/dist/schema/widget.d.ts +1003 -250
  69. package/dist/schema/widget.js +102 -46
  70. package/dist/services/dashboardConfigService.d.ts +0 -10
  71. package/dist/services/dashboardConfigService.js +6 -21
  72. package/dist/services/widgetDataService.js +226 -196
  73. package/endpoint/dashboard.ts +13 -46
  74. package/endpoint/groups.ts +25 -42
  75. package/endpoint/widgets.ts +36 -95
  76. package/index.ts +0 -3
  77. package/package.json +3 -3
  78. package/schema/api.ts +19 -15
  79. package/schema/widget.ts +113 -52
  80. package/services/dashboardConfigService.ts +6 -25
  81. package/services/widgetDataService.ts +304 -229
  82. package/custom/widgets/chart/histogram/HistogramChart.vue +0 -21
  83. package/dist/custom/widgets/chart/histogram/HistogramChart.vue +0 -21
  84. package/dist/services/widgetConfigValidator.d.ts +0 -8
  85. package/dist/services/widgetConfigValidator.js +0 -27
  86. package/services/widgetConfigValidator.ts +0 -61
package/schema/widget.ts CHANGED
@@ -1,10 +1,5 @@
1
1
  import { z } from 'zod'
2
-
3
- export type DashboardWidgetConfigValidationError = {
4
- field: string
5
- message: string
6
- }
7
-
2
+ export type { DashboardWidgetConfigValidationError } from '../custom/model/dashboard.types.js'
8
3
  const DashboardWidgetSizeSchema = z.enum([
9
4
  'small',
10
5
  'medium',
@@ -38,6 +33,15 @@ const FieldRefSchema = z.union([
38
33
  }).strict(),
39
34
  ])
40
35
 
36
+ const JsonValueSchema: z.ZodType = z.lazy(() => z.union([
37
+ z.string(),
38
+ z.number(),
39
+ z.boolean(),
40
+ z.null(),
41
+ z.array(JsonValueSchema),
42
+ z.record(z.string(), JsonValueSchema),
43
+ ]))
44
+
41
45
  const FilterExpressionSchema: z.ZodType = z.lazy(() => z.union([
42
46
  z.array(FilterExpressionSchema),
43
47
  z.object({
@@ -48,16 +52,16 @@ const FilterExpressionSchema: z.ZodType = z.lazy(() => z.union([
48
52
  }).strict(),
49
53
  z.object({
50
54
  field: z.string(),
51
- eq: z.unknown().optional(),
52
- neq: z.unknown().optional(),
53
- gt: z.unknown().optional(),
54
- gte: z.unknown().optional(),
55
- lt: z.unknown().optional(),
56
- lte: z.unknown().optional(),
57
- in: z.array(z.unknown()).optional(),
58
- not_in: z.array(z.unknown()).optional(),
59
- like: z.unknown().optional(),
60
- ilike: z.unknown().optional(),
55
+ eq: JsonValueSchema.optional(),
56
+ neq: JsonValueSchema.optional(),
57
+ gt: JsonValueSchema.optional(),
58
+ gte: JsonValueSchema.optional(),
59
+ lt: JsonValueSchema.optional(),
60
+ lte: JsonValueSchema.optional(),
61
+ in: z.array(JsonValueSchema).optional(),
62
+ not_in: z.array(JsonValueSchema).optional(),
63
+ like: JsonValueSchema.optional(),
64
+ ilike: JsonValueSchema.optional(),
61
65
  }).strict(),
62
66
  ]))
63
67
 
@@ -74,7 +78,7 @@ const QueryAggregateOperationSchema = z.enum([
74
78
  const QueryFieldSelectItemSchema = z.object({
75
79
  field: z.string(),
76
80
  as: z.string().optional(),
77
- grain: z.enum(['hour', 'day', 'week', 'month', 'quarter', 'year']).optional(),
81
+ grain: z.enum(['day', 'week', 'month', 'year']).optional(),
78
82
  }).strict()
79
83
 
80
84
  const QueryAggregateSelectItemSchema = z.object({
@@ -85,7 +89,7 @@ const QueryAggregateSelectItemSchema = z.object({
85
89
  }).strict().superRefine((item, ctx) => {
86
90
  if (!['count'].includes(item.agg) && !item.field) {
87
91
  ctx.addIssue({
88
- code: z.ZodIssueCode.custom,
92
+ code: 'custom',
89
93
  path: ['field'],
90
94
  message: `field is required for ${item.agg}`,
91
95
  })
@@ -108,7 +112,7 @@ const QueryGroupByItemSchema = z.union([
108
112
  z.object({
109
113
  field: z.string(),
110
114
  as: z.string().optional(),
111
- grain: z.enum(['hour', 'day', 'week', 'month', 'quarter', 'year']).optional(),
115
+ grain: z.enum(['day', 'week', 'month', 'year']).optional(),
112
116
  timezone: z.string().optional(),
113
117
  }).strict(),
114
118
  ])
@@ -120,14 +124,14 @@ const QueryOrderByItemSchema = z.object({
120
124
 
121
125
  const TimeSeriesConfigSchema = z.object({
122
126
  field: z.string(),
123
- grain: z.enum(['hour', 'day', 'week', 'month', 'quarter', 'year']),
127
+ grain: z.enum(['day', 'week', 'month', 'year']),
124
128
  timezone: z.string().optional(),
125
129
  }).strict()
126
130
 
127
131
  const PeriodConfigSchema = z.object({
128
132
  field: z.string(),
129
- gte: z.unknown().optional(),
130
- lt: z.unknown().optional(),
133
+ gte: JsonValueSchema.optional(),
134
+ lt: JsonValueSchema.optional(),
131
135
  }).strict()
132
136
 
133
137
  const BucketConfigSchema = z.object({
@@ -144,18 +148,18 @@ const QueryCalcItemSchema = z.object({
144
148
  as: z.string(),
145
149
  }).strict()
146
150
 
147
- const FormattingConfigSchema = z.record(z.string(), z.unknown())
148
- const VariablesConfigSchema = z.record(z.string(), z.unknown())
151
+ const FormattingConfigSchema = z.record(z.string(), JsonValueSchema)
152
+ const VariablesConfigSchema = z.record(z.string(), JsonValueSchema)
149
153
 
150
154
  export const QueryConfigSchema = z.object({
151
155
  resource: z.string(),
152
156
  select: z.array(QuerySelectItemSchema).optional(),
153
157
  filters: FilterExpressionSchema.optional(),
154
- groupBy: z.array(QueryGroupByItemSchema).optional(),
155
- orderBy: z.array(QueryOrderByItemSchema).optional(),
158
+ group_by: z.array(QueryGroupByItemSchema).optional(),
159
+ order_by: z.array(QueryOrderByItemSchema).optional(),
156
160
  limit: z.number().int().positive().optional(),
157
161
  offset: z.number().int().nonnegative().optional(),
158
- timeSeries: TimeSeriesConfigSchema.optional(),
162
+ time_series: TimeSeriesConfigSchema.optional(),
159
163
  period: PeriodConfigSchema.optional(),
160
164
  bucket: BucketConfigSchema.optional(),
161
165
  calcs: z.array(QueryCalcItemSchema).optional(),
@@ -174,28 +178,31 @@ export const FunnelQueryConfigSchema = z.object({
174
178
  calcs: z.array(QueryCalcItemSchema).optional(),
175
179
  }).strict()
176
180
 
177
- const WidgetBaseSchema = z.object({
178
- id: z.string().optional(),
179
- group_id: z.string().optional(),
181
+ const EditableWidgetBaseSchema = z.object({
180
182
  label: z.string().optional(),
181
183
  variables: VariablesConfigSchema.optional(),
182
184
  size: DashboardWidgetSizeSchema.optional(),
183
185
  width: z.number().positive('Width must be greater than 0').optional(),
184
186
  height: z.number().positive('Height must be greater than 0').optional(),
185
- minWidth: z.number().nonnegative('Min width must be a non-negative number').optional(),
186
- maxWidth: z.number().nonnegative('Max width must be a non-negative number').nullable().optional(),
187
- order: z.number().optional(),
187
+ min_width: z.number().nonnegative('Min width must be a non-negative number').optional(),
188
+ max_width: z.number().nonnegative('Max width must be a non-negative number').nullable().optional(),
189
+ }).strict()
190
+
191
+ const StoredWidgetBaseSchema = EditableWidgetBaseSchema.extend({
192
+ id: z.string(),
193
+ group_id: z.string(),
194
+ order: z.number(),
188
195
  })
189
196
 
190
197
  const TableViewConfigSchema = z.object({
191
198
  columns: z.array(FieldRefSchema).optional(),
192
199
  pagination: z.boolean().optional(),
193
- pageSize: z.number().int().positive().optional(),
200
+ page_size: z.number().int().positive().optional(),
194
201
  }).strict()
195
202
 
196
203
  const ChartBaseSchema = z.object({
197
204
  title: z.string().optional(),
198
- })
205
+ }).strict()
199
206
 
200
207
  const ChartBucketSchema = z.object({
201
208
  label: z.string().min(1, 'Bucket label is required'),
@@ -275,8 +282,8 @@ const KpiCardViewConfigSchema = z.object({
275
282
  text: z.string().optional(),
276
283
  field: z.string().optional(),
277
284
  }).strict().optional(),
278
- comparison: z.unknown().optional(),
279
- sparkline: z.unknown().optional(),
285
+ comparison: JsonValueSchema.optional(),
286
+ sparkline: JsonValueSchema.optional(),
280
287
  }).strict()
281
288
 
282
289
  const GaugeCardViewConfigSchema = z.object({
@@ -293,9 +300,9 @@ const GaugeCardViewConfigSchema = z.object({
293
300
  label: z.string().optional(),
294
301
  }).strict().optional(),
295
302
  progress: z.object({
296
- valueField: z.string(),
297
- targetValue: z.number().optional(),
298
- targetField: z.string().optional(),
303
+ value_field: z.string(),
304
+ target_value: z.number().optional(),
305
+ target_field: z.string().optional(),
299
306
  format: ValueFormatSchema,
300
307
  }).strict().optional(),
301
308
  color: z.string().optional(),
@@ -312,17 +319,44 @@ const PivotTableViewConfigSchema = z.object({
312
319
  }).strict()).min(1),
313
320
  }).strict()
314
321
 
315
- export const EmptyWidgetConfigSchema = WidgetBaseSchema.extend({
322
+ const EditableEmptyWidgetConfigSchema = EditableWidgetBaseSchema.extend({
316
323
  target: z.literal('empty'),
317
324
  })
318
325
 
319
- const TableWidgetConfigSchema = WidgetBaseSchema.extend({
326
+ export const EmptyWidgetConfigSchema = StoredWidgetBaseSchema.extend({
327
+ target: z.literal('empty'),
328
+ })
329
+
330
+ const EditableTableWidgetConfigSchema = EditableWidgetBaseSchema.extend({
320
331
  target: z.literal('table'),
321
332
  table: TableViewConfigSchema.optional(),
322
333
  query: QueryConfigSchema,
323
334
  })
324
335
 
325
- const ChartWidgetTargetConfigSchema = WidgetBaseSchema.extend({
336
+ const TableWidgetConfigSchema = StoredWidgetBaseSchema.extend({
337
+ target: z.literal('table'),
338
+ table: TableViewConfigSchema.optional(),
339
+ query: QueryConfigSchema,
340
+ })
341
+
342
+ const EditableChartWidgetTargetConfigSchema = EditableWidgetBaseSchema.extend({
343
+ target: z.literal('chart'),
344
+ chart: ChartConfigSchema,
345
+ query: z.union([QueryConfigSchema, FunnelQueryConfigSchema]),
346
+ }).superRefine((widget, ctx) => {
347
+ const isFunnelChart = widget.chart.type === 'funnel'
348
+ const isFunnelQuery = 'steps' in widget.query
349
+
350
+ if (isFunnelChart && !isFunnelQuery) {
351
+ ctx.addIssue({
352
+ code: 'custom',
353
+ path: ['query'],
354
+ message: 'Funnel charts must use steps query',
355
+ })
356
+ }
357
+ })
358
+
359
+ const ChartWidgetTargetConfigSchema = StoredWidgetBaseSchema.extend({
326
360
  target: z.literal('chart'),
327
361
  chart: ChartConfigSchema,
328
362
  query: z.union([QueryConfigSchema, FunnelQueryConfigSchema]),
@@ -332,37 +366,64 @@ const ChartWidgetTargetConfigSchema = WidgetBaseSchema.extend({
332
366
 
333
367
  if (isFunnelChart && !isFunnelQuery) {
334
368
  ctx.addIssue({
335
- code: z.ZodIssueCode.custom,
369
+ code: 'custom',
336
370
  path: ['query'],
337
371
  message: 'Funnel charts must use steps query',
338
372
  })
339
373
  }
340
374
  })
341
375
 
342
- const KpiCardWidgetConfigSchema = WidgetBaseSchema.extend({
376
+ const EditableKpiCardWidgetConfigSchema = EditableWidgetBaseSchema.extend({
377
+ target: z.literal('kpi_card'),
378
+ card: KpiCardViewConfigSchema,
379
+ query: QueryConfigSchema,
380
+ })
381
+
382
+ const KpiCardWidgetConfigSchema = StoredWidgetBaseSchema.extend({
343
383
  target: z.literal('kpi_card'),
344
384
  card: KpiCardViewConfigSchema,
345
385
  query: QueryConfigSchema,
346
386
  })
347
387
 
348
- const GaugeCardWidgetConfigSchema = WidgetBaseSchema.extend({
388
+ const EditableGaugeCardWidgetConfigSchema = EditableWidgetBaseSchema.extend({
349
389
  target: z.literal('gauge_card'),
350
390
  card: GaugeCardViewConfigSchema,
351
391
  query: QueryConfigSchema,
352
392
  })
353
393
 
354
- const PivotTableWidgetConfigSchema = WidgetBaseSchema.extend({
394
+ const GaugeCardWidgetConfigSchema = StoredWidgetBaseSchema.extend({
395
+ target: z.literal('gauge_card'),
396
+ card: GaugeCardViewConfigSchema,
397
+ query: QueryConfigSchema,
398
+ })
399
+
400
+ const EditablePivotTableWidgetConfigSchema = EditableWidgetBaseSchema.extend({
355
401
  target: z.literal('pivot_table'),
356
402
  pivot: PivotTableViewConfigSchema,
357
403
  query: QueryConfigSchema,
358
404
  })
359
405
 
406
+ const PivotTableWidgetConfigSchema = StoredWidgetBaseSchema.extend({
407
+ target: z.literal('pivot_table'),
408
+ pivot: PivotTableViewConfigSchema,
409
+ query: QueryConfigSchema,
410
+ })
411
+
412
+ export const EditableDashboardWidgetConfigSchema = z.discriminatedUnion('target', [
413
+ EditableEmptyWidgetConfigSchema,
414
+ EditableTableWidgetConfigSchema,
415
+ EditableChartWidgetTargetConfigSchema,
416
+ EditableKpiCardWidgetConfigSchema,
417
+ EditableGaugeCardWidgetConfigSchema,
418
+ EditablePivotTableWidgetConfigSchema,
419
+ ])
420
+
360
421
  export const WidgetConfigSchema = z.discriminatedUnion('target', [
361
- TableWidgetConfigSchema,
362
- ChartWidgetTargetConfigSchema,
363
- KpiCardWidgetConfigSchema,
364
- GaugeCardWidgetConfigSchema,
365
- PivotTableWidgetConfigSchema,
422
+ EditableTableWidgetConfigSchema,
423
+ EditableChartWidgetTargetConfigSchema,
424
+ EditableKpiCardWidgetConfigSchema,
425
+ EditableGaugeCardWidgetConfigSchema,
426
+ EditablePivotTableWidgetConfigSchema,
366
427
  ])
367
428
 
368
429
  export const StoredWidgetConfigSchema = z.discriminatedUnion('target', [
@@ -1,9 +1,8 @@
1
1
  import { Filters } from 'adminforth';
2
2
  import type { IAdminForth } from 'adminforth';
3
- import { normalizeDashboardConfig } from '../custom/model/dashboard.types.js';
4
3
  import type { DashboardConfig, DashboardWidgetConfig } from '../custom/model/dashboard.types.js';
5
-
6
- const DASHBOARD_CONFIG_UPDATED_TOPIC_PREFIX = '/opentopic/dashboard-config-updated';
4
+ import { getDashboardConfigUpdatedTopic } from '../custom/model/dashboardTopics.js';
5
+ import { DashboardConfigZodSchema } from '../schema/api.js';
7
6
 
8
7
  export type DashboardRecord = {
9
8
  id: string;
@@ -14,21 +13,9 @@ export type DashboardRecord = {
14
13
  };
15
14
 
16
15
  export function parseStoredDashboardConfig(config: unknown): DashboardConfig {
17
- if (typeof config === 'string') {
18
- return normalizeDashboardConfig(JSON.parse(config));
19
- }
16
+ const parsedConfig = typeof config === 'string' ? JSON.parse(config) : config;
20
17
 
21
- return normalizeDashboardConfig(config);
22
- }
23
-
24
- export function buildDashboardResponse(dashboard: DashboardRecord) {
25
- return {
26
- id: dashboard.id,
27
- slug: dashboard.slug,
28
- label: dashboard.label,
29
- revision: dashboard.revision,
30
- config: parseStoredDashboardConfig(dashboard.config),
31
- };
18
+ return DashboardConfigZodSchema.parse(parsedConfig) as DashboardConfig;
32
19
  }
33
20
 
34
21
  export type PersistedDashboardResponse = {
@@ -39,11 +26,7 @@ export type PersistedDashboardResponse = {
39
26
  config: DashboardConfig;
40
27
  };
41
28
 
42
- export function dashboardConfigUpdatedTopic(slug: string) {
43
- return `${DASHBOARD_CONFIG_UPDATED_TOPIC_PREFIX}/${slug}`;
44
- }
45
-
46
- export function normalizeDashboardOrder(config: DashboardConfig): DashboardConfig {
29
+ function normalizeDashboardOrder(config: DashboardConfig): DashboardConfig {
47
30
  const widgetsByGroupId = new Map<string, DashboardWidgetConfig[]>();
48
31
 
49
32
  for (const widget of config.widgets) {
@@ -93,7 +76,7 @@ export async function persistDashboardConfig(
93
76
  revision: dashboard.revision + 1,
94
77
  });
95
78
 
96
- await adminforth.websocket.publish(dashboardConfigUpdatedTopic(dashboard.slug), {
79
+ await adminforth.websocket.publish(getDashboardConfigUpdatedTopic(dashboard.slug), {
97
80
  id: dashboard.id,
98
81
  slug: dashboard.slug,
99
82
  revision: dashboard.revision + 1,
@@ -115,7 +98,6 @@ export type DashboardConfigService = {
115
98
  dashboard: DashboardRecord,
116
99
  config: DashboardConfig,
117
100
  ) => Promise<PersistedDashboardResponse>;
118
- buildDashboardResponse: typeof buildDashboardResponse;
119
101
  };
120
102
 
121
103
  export function createDashboardConfigService(
@@ -131,6 +113,5 @@ export function createDashboardConfigService(
131
113
  dashboard,
132
114
  config,
133
115
  ),
134
- buildDashboardResponse,
135
116
  };
136
117
  }