@adminforth/dashboard 1.4.0 → 1.4.1

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 (78) 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/runtime/DashboardGroup.vue +2 -2
  6. package/custom/runtime/DashboardPage.vue +17 -7
  7. package/custom/runtime/DashboardRuntime.vue +20 -8
  8. package/custom/runtime/WidgetRenderer.vue +1 -2
  9. package/custom/runtime/WidgetShell.vue +3 -3
  10. package/custom/skills/adminforth-dashboard/SKILL.md +2 -2
  11. package/custom/widgets/{gauge-card/GaugeCardWidget.vue → GaugeCardWidget.vue} +63 -61
  12. package/custom/widgets/{kpi-card/KpiCardWidget.vue → KpiCardWidget.vue} +35 -33
  13. package/custom/widgets/{pivot-table/PivotTableWidget.vue → PivotTableWidget.vue} +71 -68
  14. package/custom/widgets/{table/TableWidget.vue → TableWidget.vue} +5 -5
  15. package/custom/widgets/chart/{bar/BarChart.vue → BarChart.vue} +2 -2
  16. package/custom/widgets/chart/ChartWidget.vue +4 -15
  17. package/{dist/custom/widgets/chart/funnel → custom/widgets/chart}/FunnelChart.vue +80 -78
  18. package/{dist/custom/widgets/chart/line → custom/widgets/chart}/LineChart.vue +2 -2
  19. package/custom/widgets/chart/{pie/PieChart.vue → PieChart.vue} +2 -2
  20. package/{dist/custom/widgets/chart/stacked-bar → custom/widgets/chart}/StackedBarChart.vue +97 -95
  21. package/custom/widgets/chart/chart.types.ts +0 -28
  22. package/dist/custom/api/dashboardApi.d.ts +4 -8
  23. package/dist/custom/api/dashboardApi.ts +6 -9
  24. package/dist/custom/model/dashboard.types.d.ts +38 -32
  25. package/dist/custom/model/dashboard.types.js +2 -155
  26. package/dist/custom/model/dashboard.types.ts +60 -275
  27. package/dist/custom/model/dashboardTopics.d.ts +2 -0
  28. package/dist/custom/model/dashboardTopics.js +8 -0
  29. package/dist/custom/model/dashboardTopics.ts +5 -0
  30. package/dist/custom/queries/useDashboardConfig.d.ts +96 -96
  31. package/dist/custom/queries/useWidgetData.d.ts +96 -96
  32. package/dist/custom/runtime/DashboardGroup.vue +2 -2
  33. package/dist/custom/runtime/DashboardPage.vue +17 -7
  34. package/dist/custom/runtime/DashboardRuntime.vue +20 -8
  35. package/dist/custom/runtime/WidgetRenderer.vue +1 -2
  36. package/dist/custom/runtime/WidgetShell.vue +3 -3
  37. package/dist/custom/skills/adminforth-dashboard/SKILL.md +2 -2
  38. package/dist/custom/widgets/{gauge-card/GaugeCardWidget.vue → GaugeCardWidget.vue} +63 -61
  39. package/dist/custom/widgets/{kpi-card/KpiCardWidget.vue → KpiCardWidget.vue} +35 -33
  40. package/dist/custom/widgets/{pivot-table/PivotTableWidget.vue → PivotTableWidget.vue} +71 -68
  41. package/dist/custom/widgets/{table/TableWidget.vue → TableWidget.vue} +5 -5
  42. package/dist/custom/widgets/chart/{bar/BarChart.vue → BarChart.vue} +2 -2
  43. package/dist/custom/widgets/chart/ChartWidget.vue +4 -15
  44. package/{custom/widgets/chart/funnel → dist/custom/widgets/chart}/FunnelChart.vue +80 -78
  45. package/{custom/widgets/chart/line → dist/custom/widgets/chart}/LineChart.vue +2 -2
  46. package/dist/custom/widgets/chart/{pie/PieChart.vue → PieChart.vue} +2 -2
  47. package/{custom/widgets/chart/stacked-bar → dist/custom/widgets/chart}/StackedBarChart.vue +97 -95
  48. package/dist/custom/widgets/chart/chart.types.d.ts +0 -2
  49. package/dist/custom/widgets/chart/chart.types.js +0 -23
  50. package/dist/custom/widgets/chart/chart.types.ts +0 -28
  51. package/dist/endpoint/dashboard.d.ts +2 -3
  52. package/dist/endpoint/dashboard.js +12 -32
  53. package/dist/endpoint/groups.d.ts +2 -21
  54. package/dist/endpoint/groups.js +18 -16
  55. package/dist/endpoint/widgets.d.ts +0 -3
  56. package/dist/endpoint/widgets.js +27 -74
  57. package/dist/index.js +1 -3
  58. package/dist/schema/api.d.ts +2090 -511
  59. package/dist/schema/api.js +18 -15
  60. package/dist/schema/widget.d.ts +1003 -250
  61. package/dist/schema/widget.js +102 -46
  62. package/dist/services/dashboardConfigService.d.ts +0 -10
  63. package/dist/services/dashboardConfigService.js +6 -21
  64. package/dist/services/widgetDataService.js +226 -196
  65. package/endpoint/dashboard.ts +13 -46
  66. package/endpoint/groups.ts +25 -42
  67. package/endpoint/widgets.ts +36 -95
  68. package/index.ts +0 -3
  69. package/package.json +3 -3
  70. package/schema/api.ts +19 -15
  71. package/schema/widget.ts +113 -52
  72. package/services/dashboardConfigService.ts +6 -25
  73. package/services/widgetDataService.ts +304 -229
  74. package/custom/widgets/chart/histogram/HistogramChart.vue +0 -21
  75. package/dist/custom/widgets/chart/histogram/HistogramChart.vue +0 -21
  76. package/dist/services/widgetConfigValidator.d.ts +0 -8
  77. package/dist/services/widgetConfigValidator.js +0 -27
  78. package/services/widgetConfigValidator.ts +0 -61
@@ -6,7 +6,15 @@ export type DashboardConfig = {
6
6
  widgets: DashboardWidgetConfig[]
7
7
  }
8
8
 
9
- export type DashboardVariables = Record<string, unknown>
9
+ export type JsonValue =
10
+ | string
11
+ | number
12
+ | boolean
13
+ | null
14
+ | JsonValue[]
15
+ | { [key: string]: JsonValue }
16
+
17
+ export type DashboardVariables = Record<string, JsonValue>
10
18
 
11
19
  export type DashboardGroupConfig = {
12
20
  id: string
@@ -14,12 +22,18 @@ export type DashboardGroupConfig = {
14
22
  order: number
15
23
  }
16
24
 
25
+ export type EditableDashboardGroupConfig = Pick<DashboardGroupConfig, 'label'>
26
+
17
27
  export type DashboardGroupMoveDirection = 'up' | 'down'
18
28
  export type DashboardWidgetMoveDirection = 'up' | 'down'
19
29
  export type DashboardWidgetTarget = 'empty' | 'table' | 'chart' | 'kpi_card' | 'pivot_table' | 'gauge_card'
20
30
  export type DashboardWidgetSize = 'small' | 'medium' | 'large' | 'wide' | 'full'
31
+ export type DashboardWidgetConfigValidationError = {
32
+ field: string
33
+ message: string
34
+ }
21
35
  export type QueryAggregateOperation = 'sum' | 'count' | 'count_distinct' | 'avg' | 'min' | 'max' | 'median'
22
- export type TimeGrain = 'hour' | 'day' | 'week' | 'month' | 'quarter' | 'year'
36
+ export type TimeGrain = 'day' | 'week' | 'month' | 'year'
23
37
  export type ValueFormat =
24
38
  | 'number'
25
39
  | 'compact_number'
@@ -32,8 +46,8 @@ export type ValueFormat =
32
46
  export type WidgetLayout = {
33
47
  size?: DashboardWidgetSize
34
48
  width?: number
35
- minWidth?: number
36
- maxWidth?: number | null
49
+ min_width?: number
50
+ max_width?: number | null
37
51
  height?: number
38
52
  }
39
53
 
@@ -45,8 +59,8 @@ export type WidgetBaseConfig = {
45
59
  size?: DashboardWidgetSize
46
60
  width?: number
47
61
  height?: number
48
- minWidth?: number
49
- maxWidth?: number | null
62
+ min_width?: number
63
+ max_width?: number | null
50
64
  order: number
51
65
  }
52
66
 
@@ -56,16 +70,16 @@ export type FilterExpression =
56
70
  | Array<FilterExpression>
57
71
  | {
58
72
  field: string
59
- eq?: unknown
60
- neq?: unknown
61
- gt?: unknown
62
- gte?: unknown
63
- lt?: unknown
64
- lte?: unknown
65
- in?: unknown[]
66
- not_in?: unknown[]
67
- like?: unknown
68
- ilike?: unknown
73
+ eq?: JsonValue
74
+ neq?: JsonValue
75
+ gt?: JsonValue
76
+ gte?: JsonValue
77
+ lt?: JsonValue
78
+ lte?: JsonValue
79
+ in?: JsonValue[]
80
+ not_in?: JsonValue[]
81
+ like?: JsonValue
82
+ ilike?: JsonValue
69
83
  }
70
84
 
71
85
  export type QueryFieldSelectItem = {
@@ -106,26 +120,26 @@ export type QueryConfig = {
106
120
  resource: string
107
121
  select?: QuerySelectItem[]
108
122
  filters?: FilterExpression
109
- groupBy?: QueryGroupByItem[]
110
- orderBy?: QueryOrderByItem[]
123
+ group_by?: QueryGroupByItem[]
124
+ order_by?: QueryOrderByItem[]
111
125
  limit?: number
112
126
  offset?: number
113
- timeSeries?: {
127
+ time_series?: {
114
128
  field: string
115
129
  grain: TimeGrain
116
130
  timezone?: string
117
131
  }
118
132
  period?: {
119
133
  field: string
120
- gte?: unknown
121
- lt?: unknown
134
+ gte?: JsonValue
135
+ lt?: JsonValue
122
136
  }
123
137
  bucket?: {
124
138
  field: string
125
139
  buckets: Array<{ label: string, min?: number, max?: number }>
126
140
  }
127
141
  calcs?: QueryCalcSelectItem[]
128
- formatting?: Record<string, unknown>
142
+ formatting?: Record<string, JsonValue>
129
143
  }
130
144
 
131
145
  export type FunnelQueryConfig = {
@@ -149,7 +163,7 @@ export type FieldRef = string | {
149
163
  export type TableViewConfig = {
150
164
  columns?: FieldRef[]
151
165
  pagination?: boolean
152
- pageSize?: number
166
+ page_size?: number
153
167
  }
154
168
 
155
169
  export type KpiCardViewConfig = {
@@ -164,8 +178,8 @@ export type KpiCardViewConfig = {
164
178
  text?: string
165
179
  field?: string
166
180
  }
167
- comparison?: unknown
168
- sparkline?: unknown
181
+ comparison?: JsonValue
182
+ sparkline?: JsonValue
169
183
  }
170
184
 
171
185
  export type GaugeCardViewConfig = {
@@ -182,9 +196,9 @@ export type GaugeCardViewConfig = {
182
196
  label?: string
183
197
  }
184
198
  progress?: {
185
- valueField: string
186
- targetValue?: number
187
- targetField?: string
199
+ value_field: string
200
+ target_value?: number
201
+ target_field?: string
188
202
  format?: ValueFormat
189
203
  }
190
204
  color?: string
@@ -243,6 +257,14 @@ export type DashboardWidgetConfig =
243
257
  | GaugeCardWidgetConfig
244
258
  | PivotTableWidgetConfig
245
259
 
260
+ export type EditableDashboardWidgetConfig =
261
+ | Omit<EmptyWidgetConfig, 'id' | 'group_id' | 'order'>
262
+ | Omit<TableWidgetConfig, 'id' | 'group_id' | 'order'>
263
+ | Omit<ChartDashboardWidgetConfig, 'id' | 'group_id' | 'order'>
264
+ | Omit<KpiCardWidgetConfig, 'id' | 'group_id' | 'order'>
265
+ | Omit<GaugeCardWidgetConfig, 'id' | 'group_id' | 'order'>
266
+ | Omit<PivotTableWidgetConfig, 'id' | 'group_id' | 'order'>
267
+
246
268
  export type DashboardWidgetTableData = {
247
269
  kind?: 'table'
248
270
  columns: string[]
@@ -270,256 +292,19 @@ export type DashboardWidgetAggregateData = {
270
292
 
271
293
  export type DashboardWidgetData = DashboardWidgetTableData | DashboardWidgetAggregateData
272
294
 
273
- export function normalizeDashboardConfig(config: unknown): DashboardConfig {
274
- const value = isRecord(config) ? config : {}
275
-
276
- return {
277
- version: typeof value.version === 'number' ? value.version : 1,
278
- groups: Array.isArray(value.groups) ? (value.groups as DashboardGroupConfig[]) : [],
279
- widgets: Array.isArray(value.widgets)
280
- ? value.widgets.map((widget) => normalizeDashboardWidgetConfig(widget) as DashboardWidgetConfig)
281
- : [],
282
- }
283
- }
284
-
285
- export function normalizeDashboardWidgetConfig(config: unknown) {
286
- if (!isRecord(config)) {
287
- return config
288
- }
289
-
290
- const normalized: Record<string, unknown> = { ...config }
291
- normalizeWidgetLayoutConfig(normalized)
292
-
293
- if (normalized.query !== undefined) {
294
- normalized.query = normalizeQueryConfig(normalized.query)
295
- }
296
-
297
- if (normalized.table !== undefined) {
298
- normalized.table = normalizeTableConfig(normalized.table)
299
- }
300
-
301
- if (normalized.card !== undefined) {
302
- normalized.card = normalizeCardConfig(normalized.card)
303
- }
304
-
305
- if (normalized.pivot !== undefined) {
306
- normalized.pivot = normalizePivotConfig(normalized.pivot)
307
- }
308
-
309
- const target = normalizeDashboardWidgetTarget(normalized.target)
310
-
311
- if (target !== undefined) {
312
- normalized.target = target
313
- }
314
-
315
- return normalized
316
- }
317
-
318
- export function serializeDashboardWidgetConfigForEditor(widget: DashboardWidgetConfig) {
319
- const serialized: Record<string, unknown> = { ...widget }
320
-
321
- if (Object.prototype.hasOwnProperty.call(serialized, 'minWidth')) {
322
- serialized.min_width = widget.minWidth
323
- delete serialized.minWidth
324
- }
325
-
326
- if (Object.prototype.hasOwnProperty.call(serialized, 'maxWidth')) {
327
- serialized.max_width = widget.maxWidth
328
- delete serialized.maxWidth
329
- }
330
-
331
- if ('query' in widget) {
332
- serialized.query = serializeQueryConfigForEditor(widget.query)
333
- }
334
-
335
- if ('table' in widget && widget.table !== undefined) {
336
- serialized.table = serializeTableConfigForEditor(widget.table)
337
- }
338
-
339
- if ('card' in widget && widget.card !== undefined) {
340
- serialized.card = serializeCardConfigForEditor(widget.card)
341
- }
295
+ export function serializeDashboardWidgetConfigForEditor(
296
+ widget: DashboardWidgetConfig,
297
+ ): unknown {
298
+ const {
299
+ id: _id,
300
+ group_id: _groupId,
301
+ order: _order,
302
+ ...editableWidget
303
+ } = widget
342
304
 
343
- if ('pivot' in widget && widget.pivot !== undefined) {
344
- serialized.pivot = serializePivotConfigForEditor(widget.pivot)
345
- }
346
-
347
- return serialized
305
+ return editableWidget
348
306
  }
349
307
 
350
308
  export function getFieldRefField(value: FieldRef | undefined) {
351
309
  return typeof value === 'string' ? value : value?.field
352
310
  }
353
-
354
- export function getFieldRefLabel(value: FieldRef | undefined) {
355
- return typeof value === 'string' ? value : value?.label
356
- }
357
-
358
- function normalizeDashboardWidgetTarget(value: unknown): DashboardWidgetTarget | undefined {
359
- switch (value) {
360
- case 'empty':
361
- case 'table':
362
- case 'chart':
363
- case 'kpi_card':
364
- case 'pivot_table':
365
- case 'gauge_card':
366
- return value
367
- default:
368
- return undefined
369
- }
370
- }
371
-
372
- function normalizeWidgetLayoutConfig(value: Record<string, unknown>) {
373
- if (value.min_width !== undefined) {
374
- value.minWidth = value.min_width
375
- }
376
-
377
- if (value.max_width !== undefined) {
378
- value.maxWidth = value.max_width
379
- }
380
- }
381
-
382
- function normalizeQueryConfig(value: unknown): unknown {
383
- if (!isRecord(value)) {
384
- return value
385
- }
386
-
387
- if (Array.isArray(value.steps)) {
388
- return removeUndefinedFields({
389
- steps: value.steps.map((step) => normalizeFunnelQueryStep(step)),
390
- calcs: Array.isArray(value.calcs) ? value.calcs as QueryCalcSelectItem[] : undefined,
391
- })
392
- }
393
-
394
- return {
395
- ...value,
396
- ...(Array.isArray(value.group_by) ? { groupBy: value.group_by } : {}),
397
- ...(Array.isArray(value.order_by) ? { orderBy: value.order_by } : {}),
398
- ...(value.time_series !== undefined ? { timeSeries: value.time_series } : {}),
399
- }
400
- }
401
-
402
- function normalizeFunnelQueryStep(value: unknown) {
403
- if (!isRecord(value)) {
404
- return value
405
- }
406
-
407
- const { resource_id, ...rest } = value
408
-
409
- return removeUndefinedFields({
410
- ...rest,
411
- resource: typeof resource_id === 'string' ? resource_id : rest.resource,
412
- })
413
- }
414
-
415
- function normalizeTableConfig(value: unknown) {
416
- if (!isRecord(value)) {
417
- return value
418
- }
419
-
420
- return {
421
- ...value,
422
- ...(value.page_size !== undefined ? { pageSize: value.page_size } : {}),
423
- }
424
- }
425
-
426
- function normalizeCardConfig(value: unknown): unknown {
427
- if (!isRecord(value)) {
428
- return value
429
- }
430
-
431
- const normalized = { ...value }
432
-
433
- if (isRecord(normalized.progress)) {
434
- normalized.progress = {
435
- ...normalized.progress,
436
- ...(normalized.progress.value_field !== undefined ? { valueField: normalized.progress.value_field } : {}),
437
- ...(normalized.progress.target_value !== undefined ? { targetValue: normalized.progress.target_value } : {}),
438
- ...(normalized.progress.target_field !== undefined ? { targetField: normalized.progress.target_field } : {}),
439
- }
440
- }
441
-
442
- if (isRecord(normalized.comparison)) {
443
- normalized.comparison = {
444
- ...normalized.comparison,
445
- ...(normalized.comparison.positive_is_good !== undefined ? { positiveIsGood: normalized.comparison.positive_is_good } : {}),
446
- }
447
- }
448
-
449
- return normalized
450
- }
451
-
452
- function normalizePivotConfig(value: unknown): unknown {
453
- return value
454
- }
455
-
456
- function serializeQueryConfigForEditor(value: QueryConfig | FunnelQueryConfig) {
457
- if ('steps' in value) {
458
- return removeUndefinedFields({
459
- steps: value.steps.map((step) => ({
460
- ...step,
461
- resource_id: step.resource,
462
- resource: undefined,
463
- })).map((step) => removeUndefinedFields(step)),
464
- calcs: value.calcs,
465
- })
466
- }
467
-
468
- return removeUndefinedFields({
469
- ...value,
470
- group_by: value.groupBy,
471
- groupBy: undefined,
472
- order_by: value.orderBy,
473
- orderBy: undefined,
474
- time_series: value.timeSeries,
475
- timeSeries: undefined,
476
- })
477
- }
478
-
479
- function serializeTableConfigForEditor(value: TableViewConfig) {
480
- return removeUndefinedFields({
481
- ...value,
482
- page_size: value.pageSize,
483
- pageSize: undefined,
484
- })
485
- }
486
-
487
- function serializeCardConfigForEditor(value: KpiCardViewConfig | GaugeCardViewConfig) {
488
- const serialized: Record<string, unknown> = { ...value }
489
-
490
- if (isRecord(serialized.progress)) {
491
- serialized.progress = removeUndefinedFields({
492
- ...serialized.progress,
493
- value_field: serialized.progress.valueField,
494
- valueField: undefined,
495
- target_value: serialized.progress.targetValue,
496
- targetValue: undefined,
497
- target_field: serialized.progress.targetField,
498
- targetField: undefined,
499
- })
500
- }
501
-
502
- if (isRecord(serialized.comparison)) {
503
- serialized.comparison = removeUndefinedFields({
504
- ...serialized.comparison,
505
- positive_is_good: serialized.comparison.positiveIsGood,
506
- positiveIsGood: undefined,
507
- })
508
- }
509
-
510
- return removeUndefinedFields(serialized)
511
- }
512
-
513
- function serializePivotConfigForEditor(value: PivotTableViewConfig) {
514
- return value
515
- }
516
-
517
- function removeUndefinedFields<T extends Record<string, unknown>>(value: T) {
518
- return Object.fromEntries(
519
- Object.entries(value).filter(([, item]) => item !== undefined),
520
- )
521
- }
522
-
523
- function isRecord(value: unknown): value is Record<string, any> {
524
- return typeof value === 'object' && value !== null
525
- }
@@ -0,0 +1,2 @@
1
+ export declare const DASHBOARD_CONFIG_UPDATED_TOPIC_PREFIX = "/opentopic/dashboard-config-updated";
2
+ export declare function getDashboardConfigUpdatedTopic(slug: string): string;
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DASHBOARD_CONFIG_UPDATED_TOPIC_PREFIX = void 0;
4
+ exports.getDashboardConfigUpdatedTopic = getDashboardConfigUpdatedTopic;
5
+ exports.DASHBOARD_CONFIG_UPDATED_TOPIC_PREFIX = '/opentopic/dashboard-config-updated';
6
+ function getDashboardConfigUpdatedTopic(slug) {
7
+ return `${exports.DASHBOARD_CONFIG_UPDATED_TOPIC_PREFIX}/${slug}`;
8
+ }
@@ -0,0 +1,5 @@
1
+ export const DASHBOARD_CONFIG_UPDATED_TOPIC_PREFIX = '/opentopic/dashboard-config-updated'
2
+
3
+ export function getDashboardConfigUpdatedTopic(slug: string) {
4
+ return `${DASHBOARD_CONFIG_UPDATED_TOPIC_PREFIX}/${slug}`
5
+ }