@adminforth/dashboard 1.1.0 → 1.2.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 +11 -28
  2. package/custom/model/dashboard.types.ts +236 -42
  3. package/custom/runtime/DashboardRuntime.vue +2 -1
  4. package/custom/runtime/WidgetRenderer.vue +2 -1
  5. package/custom/skills/adminforth-dashboard/SKILL.md +4 -4
  6. package/custom/widgets/chart/ChartWidget.vue +45 -12
  7. package/custom/widgets/chart/chart.types.ts +83 -0
  8. package/custom/widgets/gauge-card/GaugeCardWidget.vue +7 -38
  9. package/custom/widgets/kpi-card/KpiCardWidget.vue +6 -8
  10. package/custom/widgets/pivot-table/PivotTableWidget.vue +8 -10
  11. package/custom/widgets/table/TableWidget.vue +1 -1
  12. package/dist/custom/model/dashboard.types.d.ts +25 -1
  13. package/dist/custom/model/dashboard.types.js +133 -42
  14. package/dist/custom/model/dashboard.types.ts +236 -42
  15. package/dist/custom/queries/useDashboardConfig.d.ts +0 -2
  16. package/dist/custom/queries/useWidgetData.d.ts +0 -2
  17. package/dist/custom/runtime/DashboardRuntime.vue +2 -1
  18. package/dist/custom/runtime/WidgetRenderer.vue +2 -1
  19. package/dist/custom/skills/adminforth-dashboard/SKILL.md +4 -4
  20. package/dist/custom/widgets/chart/ChartWidget.vue +45 -12
  21. package/dist/custom/widgets/chart/chart.types.d.ts +15 -0
  22. package/dist/custom/widgets/chart/chart.types.js +46 -0
  23. package/dist/custom/widgets/chart/chart.types.ts +83 -0
  24. package/dist/custom/widgets/gauge-card/GaugeCardWidget.vue +7 -38
  25. package/dist/custom/widgets/kpi-card/KpiCardWidget.vue +6 -8
  26. package/dist/custom/widgets/pivot-table/PivotTableWidget.vue +8 -10
  27. package/dist/custom/widgets/table/TableWidget.vue +1 -1
  28. package/dist/endpoint/widgets.js +20 -3
  29. package/dist/schema/api.d.ts +0 -240
  30. package/dist/schema/widget.d.ts +0 -132
  31. package/dist/schema/widget.js +30 -16
  32. package/dist/services/widgetConfigValidator.js +9 -49
  33. package/dist/services/widgetDataService.d.ts +0 -9
  34. package/dist/services/widgetDataService.js +12 -30
  35. package/endpoint/widgets.ts +26 -3
  36. package/package.json +1 -1
  37. package/schema/widget.ts +34 -17
  38. package/services/widgetConfigValidator.ts +10 -57
  39. package/services/widgetDataService.ts +10 -45
@@ -163,18 +163,6 @@ export declare const ChartConfigSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
163
163
  value_field: z.ZodOptional<z.ZodString>;
164
164
  colors: z.ZodOptional<z.ZodArray<z.ZodString>>;
165
165
  }, z.core.$strip>], "type">;
166
- export declare const DashboardWidgetQuerySchema: z.ZodObject<{
167
- resource: z.ZodString;
168
- select: z.ZodOptional<z.ZodArray<z.ZodString>>;
169
- order: z.ZodOptional<z.ZodObject<{
170
- field: z.ZodString;
171
- direction: z.ZodEnum<{
172
- asc: "asc";
173
- desc: "desc";
174
- }>;
175
- }, z.core.$strip>>;
176
- limit: z.ZodOptional<z.ZodNumber>;
177
- }, z.core.$strip>;
178
166
  export declare const EmptyWidgetConfigSchema: z.ZodObject<{
179
167
  id: z.ZodOptional<z.ZodString>;
180
168
  group_id: z.ZodOptional<z.ZodString>;
@@ -283,18 +271,6 @@ export declare const WidgetConfigSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
283
271
  }, z.core.$strict>], "type">>;
284
272
  target: z.ZodLiteral<"table">;
285
273
  table: z.ZodOptional<z.ZodUnknown>;
286
- query: z.ZodOptional<z.ZodObject<{
287
- resource: z.ZodString;
288
- select: z.ZodOptional<z.ZodArray<z.ZodString>>;
289
- order: z.ZodOptional<z.ZodObject<{
290
- field: z.ZodString;
291
- direction: z.ZodEnum<{
292
- asc: "asc";
293
- desc: "desc";
294
- }>;
295
- }, z.core.$strip>>;
296
- limit: z.ZodOptional<z.ZodNumber>;
297
- }, z.core.$strip>>;
298
274
  }, z.core.$strip>, z.ZodObject<{
299
275
  id: z.ZodOptional<z.ZodString>;
300
276
  group_id: z.ZodOptional<z.ZodString>;
@@ -402,18 +378,6 @@ export declare const WidgetConfigSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
402
378
  value_field: z.ZodOptional<z.ZodString>;
403
379
  colors: z.ZodOptional<z.ZodArray<z.ZodString>>;
404
380
  }, z.core.$strip>], "type">;
405
- query: z.ZodOptional<z.ZodObject<{
406
- resource: z.ZodString;
407
- select: z.ZodOptional<z.ZodArray<z.ZodString>>;
408
- order: z.ZodOptional<z.ZodObject<{
409
- field: z.ZodString;
410
- direction: z.ZodEnum<{
411
- asc: "asc";
412
- desc: "desc";
413
- }>;
414
- }, z.core.$strip>>;
415
- limit: z.ZodOptional<z.ZodNumber>;
416
- }, z.core.$strip>>;
417
381
  }, z.core.$strip>, z.ZodObject<{
418
382
  id: z.ZodOptional<z.ZodString>;
419
383
  group_id: z.ZodOptional<z.ZodString>;
@@ -468,18 +432,6 @@ export declare const WidgetConfigSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
468
432
  }, z.core.$strict>], "type">>;
469
433
  target: z.ZodLiteral<"kpi_card">;
470
434
  kpi_card: z.ZodOptional<z.ZodUnknown>;
471
- query: z.ZodOptional<z.ZodObject<{
472
- resource: z.ZodString;
473
- select: z.ZodOptional<z.ZodArray<z.ZodString>>;
474
- order: z.ZodOptional<z.ZodObject<{
475
- field: z.ZodString;
476
- direction: z.ZodEnum<{
477
- asc: "asc";
478
- desc: "desc";
479
- }>;
480
- }, z.core.$strip>>;
481
- limit: z.ZodOptional<z.ZodNumber>;
482
- }, z.core.$strip>>;
483
435
  }, z.core.$strip>, z.ZodObject<{
484
436
  id: z.ZodOptional<z.ZodString>;
485
437
  group_id: z.ZodOptional<z.ZodString>;
@@ -534,18 +486,6 @@ export declare const WidgetConfigSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
534
486
  }, z.core.$strict>], "type">>;
535
487
  target: z.ZodLiteral<"gauge_card">;
536
488
  gauge_card: z.ZodOptional<z.ZodUnknown>;
537
- query: z.ZodOptional<z.ZodObject<{
538
- resource: z.ZodString;
539
- select: z.ZodOptional<z.ZodArray<z.ZodString>>;
540
- order: z.ZodOptional<z.ZodObject<{
541
- field: z.ZodString;
542
- direction: z.ZodEnum<{
543
- asc: "asc";
544
- desc: "desc";
545
- }>;
546
- }, z.core.$strip>>;
547
- limit: z.ZodOptional<z.ZodNumber>;
548
- }, z.core.$strip>>;
549
489
  }, z.core.$strip>, z.ZodObject<{
550
490
  id: z.ZodOptional<z.ZodString>;
551
491
  group_id: z.ZodOptional<z.ZodString>;
@@ -600,18 +540,6 @@ export declare const WidgetConfigSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
600
540
  }, z.core.$strict>], "type">>;
601
541
  target: z.ZodLiteral<"pivot_table">;
602
542
  pivot_table: z.ZodOptional<z.ZodUnknown>;
603
- query: z.ZodOptional<z.ZodObject<{
604
- resource: z.ZodString;
605
- select: z.ZodOptional<z.ZodArray<z.ZodString>>;
606
- order: z.ZodOptional<z.ZodObject<{
607
- field: z.ZodString;
608
- direction: z.ZodEnum<{
609
- asc: "asc";
610
- desc: "desc";
611
- }>;
612
- }, z.core.$strip>>;
613
- limit: z.ZodOptional<z.ZodNumber>;
614
- }, z.core.$strip>>;
615
543
  }, z.core.$strip>], "target">;
616
544
  export declare const StoredWidgetConfigSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
617
545
  id: z.ZodOptional<z.ZodString>;
@@ -720,18 +648,6 @@ export declare const StoredWidgetConfigSchema: z.ZodDiscriminatedUnion<[z.ZodObj
720
648
  }, z.core.$strict>], "type">>;
721
649
  target: z.ZodLiteral<"table">;
722
650
  table: z.ZodOptional<z.ZodUnknown>;
723
- query: z.ZodOptional<z.ZodObject<{
724
- resource: z.ZodString;
725
- select: z.ZodOptional<z.ZodArray<z.ZodString>>;
726
- order: z.ZodOptional<z.ZodObject<{
727
- field: z.ZodString;
728
- direction: z.ZodEnum<{
729
- asc: "asc";
730
- desc: "desc";
731
- }>;
732
- }, z.core.$strip>>;
733
- limit: z.ZodOptional<z.ZodNumber>;
734
- }, z.core.$strip>>;
735
651
  }, z.core.$strip>, z.ZodObject<{
736
652
  id: z.ZodOptional<z.ZodString>;
737
653
  group_id: z.ZodOptional<z.ZodString>;
@@ -839,18 +755,6 @@ export declare const StoredWidgetConfigSchema: z.ZodDiscriminatedUnion<[z.ZodObj
839
755
  value_field: z.ZodOptional<z.ZodString>;
840
756
  colors: z.ZodOptional<z.ZodArray<z.ZodString>>;
841
757
  }, z.core.$strip>], "type">;
842
- query: z.ZodOptional<z.ZodObject<{
843
- resource: z.ZodString;
844
- select: z.ZodOptional<z.ZodArray<z.ZodString>>;
845
- order: z.ZodOptional<z.ZodObject<{
846
- field: z.ZodString;
847
- direction: z.ZodEnum<{
848
- asc: "asc";
849
- desc: "desc";
850
- }>;
851
- }, z.core.$strip>>;
852
- limit: z.ZodOptional<z.ZodNumber>;
853
- }, z.core.$strip>>;
854
758
  }, z.core.$strip>, z.ZodObject<{
855
759
  id: z.ZodOptional<z.ZodString>;
856
760
  group_id: z.ZodOptional<z.ZodString>;
@@ -905,18 +809,6 @@ export declare const StoredWidgetConfigSchema: z.ZodDiscriminatedUnion<[z.ZodObj
905
809
  }, z.core.$strict>], "type">>;
906
810
  target: z.ZodLiteral<"kpi_card">;
907
811
  kpi_card: z.ZodOptional<z.ZodUnknown>;
908
- query: z.ZodOptional<z.ZodObject<{
909
- resource: z.ZodString;
910
- select: z.ZodOptional<z.ZodArray<z.ZodString>>;
911
- order: z.ZodOptional<z.ZodObject<{
912
- field: z.ZodString;
913
- direction: z.ZodEnum<{
914
- asc: "asc";
915
- desc: "desc";
916
- }>;
917
- }, z.core.$strip>>;
918
- limit: z.ZodOptional<z.ZodNumber>;
919
- }, z.core.$strip>>;
920
812
  }, z.core.$strip>, z.ZodObject<{
921
813
  id: z.ZodOptional<z.ZodString>;
922
814
  group_id: z.ZodOptional<z.ZodString>;
@@ -971,18 +863,6 @@ export declare const StoredWidgetConfigSchema: z.ZodDiscriminatedUnion<[z.ZodObj
971
863
  }, z.core.$strict>], "type">>;
972
864
  target: z.ZodLiteral<"gauge_card">;
973
865
  gauge_card: z.ZodOptional<z.ZodUnknown>;
974
- query: z.ZodOptional<z.ZodObject<{
975
- resource: z.ZodString;
976
- select: z.ZodOptional<z.ZodArray<z.ZodString>>;
977
- order: z.ZodOptional<z.ZodObject<{
978
- field: z.ZodString;
979
- direction: z.ZodEnum<{
980
- asc: "asc";
981
- desc: "desc";
982
- }>;
983
- }, z.core.$strip>>;
984
- limit: z.ZodOptional<z.ZodNumber>;
985
- }, z.core.$strip>>;
986
866
  }, z.core.$strip>, z.ZodObject<{
987
867
  id: z.ZodOptional<z.ZodString>;
988
868
  group_id: z.ZodOptional<z.ZodString>;
@@ -1037,16 +917,4 @@ export declare const StoredWidgetConfigSchema: z.ZodDiscriminatedUnion<[z.ZodObj
1037
917
  }, z.core.$strict>], "type">>;
1038
918
  target: z.ZodLiteral<"pivot_table">;
1039
919
  pivot_table: z.ZodOptional<z.ZodUnknown>;
1040
- query: z.ZodOptional<z.ZodObject<{
1041
- resource: z.ZodString;
1042
- select: z.ZodOptional<z.ZodArray<z.ZodString>>;
1043
- order: z.ZodOptional<z.ZodObject<{
1044
- field: z.ZodString;
1045
- direction: z.ZodEnum<{
1046
- asc: "asc";
1047
- desc: "desc";
1048
- }>;
1049
- }, z.core.$strip>>;
1050
- limit: z.ZodOptional<z.ZodNumber>;
1051
- }, z.core.$strip>>;
1052
920
  }, z.core.$strip>], "target">;
@@ -130,24 +130,21 @@ export const ChartConfigSchema = z.discriminatedUnion('type', [
130
130
  HistogramChartSchema,
131
131
  FunnelChartSchema,
132
132
  ]);
133
- export const DashboardWidgetQuerySchema = z.object({
134
- resource: z.string().min(1, 'Query resource must be a non-empty string'),
135
- select: z.array(z.string()).optional(),
136
- order: z.object({
137
- field: z.string().min(1, 'Order field is required'),
138
- direction: z.enum(['asc', 'desc']),
139
- }).optional(),
140
- limit: z.number().optional(),
141
- });
142
133
  export const EmptyWidgetConfigSchema = WidgetBaseSchema.extend({
143
134
  target: z.literal('empty'),
144
135
  });
145
136
  const TableWidgetConfigSchema = WidgetBaseSchema.extend({
146
137
  target: z.literal('table'),
147
138
  table: z.unknown().optional(),
148
- query: DashboardWidgetQuerySchema.optional(),
149
139
  }).superRefine((widget, ctx) => {
150
140
  var _a;
141
+ if (!widget.dataSource) {
142
+ ctx.addIssue({
143
+ code: z.ZodIssueCode.custom,
144
+ path: ['dataSource'],
145
+ message: 'Table widget must have dataSource config',
146
+ });
147
+ }
151
148
  if (((_a = widget.dataSource) === null || _a === void 0 ? void 0 : _a.type) === 'aggregate') {
152
149
  ctx.addIssue({
153
150
  code: z.ZodIssueCode.custom,
@@ -159,14 +156,13 @@ const TableWidgetConfigSchema = WidgetBaseSchema.extend({
159
156
  const ChartWidgetTargetConfigSchema = WidgetBaseSchema.extend({
160
157
  target: z.literal('chart'),
161
158
  chart: ChartConfigSchema,
162
- query: DashboardWidgetQuerySchema.optional(),
163
159
  }).superRefine((widget, ctx) => {
164
160
  var _a, _b;
165
- if (!widget.query && !widget.dataSource) {
161
+ if (!widget.dataSource) {
166
162
  ctx.addIssue({
167
163
  code: z.ZodIssueCode.custom,
168
164
  path: ['dataSource'],
169
- message: 'Chart widget must have query or dataSource config',
165
+ message: 'Chart widget must have dataSource config',
170
166
  });
171
167
  }
172
168
  if (((_a = widget.dataSource) === null || _a === void 0 ? void 0 : _a.type) === 'resource') {
@@ -187,9 +183,15 @@ const ChartWidgetTargetConfigSchema = WidgetBaseSchema.extend({
187
183
  const KpiCardWidgetConfigSchema = WidgetBaseSchema.extend({
188
184
  target: z.literal('kpi_card'),
189
185
  kpi_card: z.unknown().optional(),
190
- query: DashboardWidgetQuerySchema.optional(),
191
186
  }).superRefine((widget, ctx) => {
192
187
  var _a;
188
+ if (!widget.dataSource) {
189
+ ctx.addIssue({
190
+ code: z.ZodIssueCode.custom,
191
+ path: ['dataSource'],
192
+ message: 'KPI card widget must have dataSource config',
193
+ });
194
+ }
193
195
  if (((_a = widget.dataSource) === null || _a === void 0 ? void 0 : _a.type) === 'aggregate' && widget.dataSource.groupBy) {
194
196
  ctx.addIssue({
195
197
  code: z.ZodIssueCode.custom,
@@ -201,9 +203,15 @@ const KpiCardWidgetConfigSchema = WidgetBaseSchema.extend({
201
203
  const GaugeCardWidgetConfigSchema = WidgetBaseSchema.extend({
202
204
  target: z.literal('gauge_card'),
203
205
  gauge_card: z.unknown().optional(),
204
- query: DashboardWidgetQuerySchema.optional(),
205
206
  }).superRefine((widget, ctx) => {
206
207
  var _a;
208
+ if (!widget.dataSource) {
209
+ ctx.addIssue({
210
+ code: z.ZodIssueCode.custom,
211
+ path: ['dataSource'],
212
+ message: 'Gauge card widget must have dataSource config',
213
+ });
214
+ }
207
215
  if (((_a = widget.dataSource) === null || _a === void 0 ? void 0 : _a.type) === 'aggregate' && widget.dataSource.groupBy) {
208
216
  ctx.addIssue({
209
217
  code: z.ZodIssueCode.custom,
@@ -215,9 +223,15 @@ const GaugeCardWidgetConfigSchema = WidgetBaseSchema.extend({
215
223
  const PivotTableWidgetConfigSchema = WidgetBaseSchema.extend({
216
224
  target: z.literal('pivot_table'),
217
225
  pivot_table: z.unknown().optional(),
218
- query: DashboardWidgetQuerySchema.optional(),
219
226
  }).superRefine((widget, ctx) => {
220
227
  var _a;
228
+ if (!widget.dataSource) {
229
+ ctx.addIssue({
230
+ code: z.ZodIssueCode.custom,
231
+ path: ['dataSource'],
232
+ message: 'Pivot table widget must have dataSource config',
233
+ });
234
+ }
221
235
  if (((_a = widget.dataSource) === null || _a === void 0 ? void 0 : _a.type) === 'aggregate' && !widget.dataSource.groupBy) {
222
236
  ctx.addIssue({
223
237
  code: z.ZodIssueCode.custom,
@@ -1,10 +1,11 @@
1
+ import { normalizeChartWidgetConfig } from '../custom/widgets/chart/chart.types.js';
1
2
  export function validateDashboardWidgetApiConfig(adminforth, widget) {
2
- var _a, _b;
3
3
  if (widget.target !== 'chart') {
4
4
  return [];
5
5
  }
6
6
  const errors = [];
7
- if (!widget.chart) {
7
+ const chart = normalizeChartWidgetConfig(widget.chart);
8
+ if (!chart) {
8
9
  errors.push({
9
10
  field: 'chart',
10
11
  message: 'Chart widget must have chart config',
@@ -16,63 +17,22 @@ export function validateDashboardWidgetApiConfig(adminforth, widget) {
16
17
  const resource = adminforth.config.resources.find((item) => item.resourceId === aggregateDataSource.resourceId);
17
18
  if (!resource) {
18
19
  errors.push({
19
- field: 'dataSource.resourceId',
20
+ field: 'data_source.resource_id',
20
21
  message: `Resource "${aggregateDataSource.resourceId}" is not registered`,
21
22
  });
22
23
  }
23
24
  if (!aggregateDataSource.groupBy) {
24
25
  errors.push({
25
- field: 'dataSource.groupBy',
26
+ field: 'data_source.group_by',
26
27
  message: 'Chart aggregate dataSource must define groupBy',
27
28
  });
28
29
  }
29
30
  return errors;
30
31
  }
31
- if (!widget.query) {
32
- errors.push({
33
- field: 'query',
34
- message: 'Chart widget must have query or aggregate dataSource config',
35
- });
36
- return errors;
37
- }
38
- const query = widget.query;
39
- const chart = widget.chart;
40
- const resource = adminforth.config.resources.find((item) => item.resourceId === query.resource);
41
- if (!resource) {
42
- errors.push({
43
- field: 'query.resource',
44
- message: `Resource "${query.resource}" is not registered`,
45
- });
46
- return errors;
47
- }
48
- if (!query.select) {
49
- return errors;
50
- }
51
- const resourceFields = resource.columns.map((column) => column.name);
52
- for (const field of query.select) {
53
- if (!resourceFields.includes(field)) {
54
- errors.push({
55
- field: 'query.select',
56
- message: `Field "${field}" is not in resource "${query.resource}"`,
57
- });
58
- }
59
- }
60
- const chartFields = [
61
- chart.x_field,
62
- chart.y_field,
63
- chart.label_field,
64
- chart.value_field,
65
- chart.bucket_field,
66
- ...((_b = (_a = chart.series) === null || _a === void 0 ? void 0 : _a.map((series) => series.field)) !== null && _b !== void 0 ? _b : []),
67
- ].filter((field) => typeof field === 'string');
68
- for (const field of chartFields) {
69
- if (!query.select.includes(field)) {
70
- errors.push({
71
- field: 'query.select',
72
- message: `Query select must include chart field "${field}"`,
73
- });
74
- }
75
- }
32
+ errors.push({
33
+ field: 'data_source',
34
+ message: 'Chart widget must have aggregate dataSource config',
35
+ });
76
36
  return errors;
77
37
  }
78
38
  export function createWidgetConfigValidatorService(adminforth) {
@@ -1,14 +1,5 @@
1
1
  import type { IAdminForth } from 'adminforth';
2
2
  import type { DashboardWidgetConfig, DashboardWidgetData } from '../custom/model/dashboard.types.js';
3
- export type DashboardWidgetQueryConfig = {
4
- resource: string;
5
- select?: string[];
6
- order?: {
7
- field: string;
8
- direction: 'asc' | 'desc';
9
- };
10
- limit?: number;
11
- };
12
3
  export type DashboardWidgetDataOptions = {
13
4
  pagination?: {
14
5
  page: number;
@@ -10,32 +10,28 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
10
10
  import { Aggregates, GroupBy, Sorts } from 'adminforth';
11
11
  export function getWidgetData(adminforth_1, widget_1) {
12
12
  return __awaiter(this, arguments, void 0, function* (adminforth, widget, options = {}) {
13
- const legacyQuery = getLegacyQueryConfig(widget.query);
14
- const dataSource = getWidgetDataSource(widget, legacyQuery);
13
+ const dataSource = getWidgetDataSource(widget.dataSource);
15
14
  if (!dataSource) {
16
15
  return null;
17
16
  }
18
17
  if (dataSource.type === 'aggregate') {
19
18
  return getAggregateWidgetData(adminforth, dataSource);
20
19
  }
21
- return getResourceWidgetData(adminforth, dataSource, legacyQuery, options);
20
+ return getResourceWidgetData(adminforth, dataSource, options);
22
21
  });
23
22
  }
24
- function getResourceWidgetData(adminforth, dataSource, legacyQuery, options) {
23
+ function getResourceWidgetData(adminforth, dataSource, options) {
25
24
  return __awaiter(this, void 0, void 0, function* () {
26
- var _a, _b, _c, _d;
25
+ var _a, _b;
27
26
  const resource = adminforth.resource(dataSource.resourceId);
28
27
  const filters = normalizeFilters(dataSource.filters);
29
- const sort = normalizeSort((_a = dataSource.sort) !== null && _a !== void 0 ? _a : legacyQuery === null || legacyQuery === void 0 ? void 0 : legacyQuery.order);
28
+ const sort = normalizeSort(dataSource.sort);
30
29
  const pagination = options.pagination;
31
30
  const offset = pagination ? (pagination.page - 1) * pagination.pageSize : 0;
32
- const queryLimit = legacyQuery === null || legacyQuery === void 0 ? void 0 : legacyQuery.limit;
33
- const limit = pagination
34
- ? Math.max(Math.min(pagination.pageSize, (queryLimit !== null && queryLimit !== void 0 ? queryLimit : Infinity) - offset), 0)
35
- : queryLimit;
31
+ const limit = pagination ? pagination.pageSize : undefined;
36
32
  const rows = yield resource.list(filters, limit !== null && limit !== void 0 ? limit : undefined, offset, sort);
37
- const columns = (_c = (_b = dataSource.columns) !== null && _b !== void 0 ? _b : legacyQuery === null || legacyQuery === void 0 ? void 0 : legacyQuery.select) !== null && _c !== void 0 ? _c : Object.keys((_d = rows[0]) !== null && _d !== void 0 ? _d : {});
38
- const total = pagination ? Math.min(yield resource.count(filters), queryLimit !== null && queryLimit !== void 0 ? queryLimit : Infinity) : 0;
33
+ const columns = (_a = dataSource.columns) !== null && _a !== void 0 ? _a : Object.keys((_b = rows[0]) !== null && _b !== void 0 ? _b : {});
34
+ const total = pagination ? yield resource.count(filters) : 0;
39
35
  return Object.assign({ columns, rows: rows.map((row) => (Object.fromEntries(columns.map((column) => [column, row[column]])))) }, (pagination ? {
40
36
  pagination: {
41
37
  page: pagination.page,
@@ -71,25 +67,11 @@ function getAggregateWidgetData(adminforth, dataSource) {
71
67
  };
72
68
  });
73
69
  }
74
- function getLegacyQueryConfig(query) {
75
- if (!isRecord(query) || typeof query.resource !== 'string') {
76
- return undefined;
77
- }
78
- return query;
79
- }
80
- function getWidgetDataSource(widget, legacyQuery) {
81
- if (isWidgetDataSource(widget.dataSource)) {
82
- return widget.dataSource;
70
+ function getWidgetDataSource(dataSource) {
71
+ if (isWidgetDataSource(dataSource)) {
72
+ return dataSource;
83
73
  }
84
- if (!legacyQuery) {
85
- return undefined;
86
- }
87
- return {
88
- type: 'resource',
89
- resourceId: legacyQuery.resource,
90
- columns: legacyQuery.select,
91
- sort: legacyQuery.order,
92
- };
74
+ return undefined;
93
75
  }
94
76
  function isWidgetDataSource(value) {
95
77
  return isRecord(value)
@@ -37,11 +37,34 @@ type WidgetEndpointsContext = {
37
37
 
38
38
  function formatWidgetConfigValidationErrors(error: { issues: { path: PropertyKey[], message: string }[] }) {
39
39
  return error.issues.map((issue) => ({
40
- field: issue.path.length ? issue.path.map(String).join('.') : 'config',
40
+ field: issue.path.length ? formatWidgetConfigFieldPath(issue.path.map(String).join('.')) : 'config',
41
41
  message: issue.message,
42
42
  }));
43
43
  }
44
44
 
45
+ function formatWidgetConfigApiValidationErrors(errors: DashboardWidgetConfigValidationError[]) {
46
+ return errors.map((error) => ({
47
+ ...error,
48
+ field: formatWidgetConfigFieldPath(error.field),
49
+ }));
50
+ }
51
+
52
+ function formatWidgetConfigFieldPath(field: string) {
53
+ const fieldAliases = new Map([
54
+ ['minWidth', 'min_width'],
55
+ ['maxWidth', 'max_width'],
56
+ ['dataSource', 'data_source'],
57
+ ['resourceId', 'resource_id'],
58
+ ['groupBy', 'group_by'],
59
+ ['pageSize', 'page_size'],
60
+ ]);
61
+
62
+ return field
63
+ .split('.')
64
+ .map((segment) => fieldAliases.get(segment) ?? segment)
65
+ .join('.');
66
+ }
67
+
45
68
  export function registerWidgetEndpoints(
46
69
  server: IHttpServer,
47
70
  ctx: WidgetEndpointsContext,
@@ -229,7 +252,7 @@ export function registerWidgetEndpoints(
229
252
  response.setStatus(422);
230
253
  return {
231
254
  error: 'Invalid widget config',
232
- validationErrors: apiValidationErrors,
255
+ validationErrors: formatWidgetConfigApiValidationErrors(apiValidationErrors),
233
256
  };
234
257
  }
235
258
 
@@ -250,7 +273,7 @@ export function registerWidgetEndpoints(
250
273
  server.endpoint({
251
274
  method: 'POST',
252
275
  path: '/dashboard/get_dashboard_widget_data',
253
- description: 'Loads query result data for one dashboard widget by dashboard slug and widget id.',
276
+ description: 'Loads widget data for one dashboard widget by dashboard slug and widget id.',
254
277
  request_schema: WidgetDataRequestSchema,
255
278
  response_schema: DashboardWidgetDataResponseSchema,
256
279
  handler: async ({ body, response }) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adminforth/dashboard",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "type": "module",
package/schema/widget.ts CHANGED
@@ -154,16 +154,6 @@ export const ChartConfigSchema = z.discriminatedUnion('type', [
154
154
  FunnelChartSchema,
155
155
  ])
156
156
 
157
- export const DashboardWidgetQuerySchema = z.object({
158
- resource: z.string().min(1, 'Query resource must be a non-empty string'),
159
- select: z.array(z.string()).optional(),
160
- order: z.object({
161
- field: z.string().min(1, 'Order field is required'),
162
- direction: z.enum(['asc', 'desc']),
163
- }).optional(),
164
- limit: z.number().optional(),
165
- })
166
-
167
157
  export const EmptyWidgetConfigSchema = WidgetBaseSchema.extend({
168
158
  target: z.literal('empty'),
169
159
  })
@@ -171,8 +161,15 @@ export const EmptyWidgetConfigSchema = WidgetBaseSchema.extend({
171
161
  const TableWidgetConfigSchema = WidgetBaseSchema.extend({
172
162
  target: z.literal('table'),
173
163
  table: z.unknown().optional(),
174
- query: DashboardWidgetQuerySchema.optional(),
175
164
  }).superRefine((widget, ctx) => {
165
+ if (!widget.dataSource) {
166
+ ctx.addIssue({
167
+ code: z.ZodIssueCode.custom,
168
+ path: ['dataSource'],
169
+ message: 'Table widget must have dataSource config',
170
+ })
171
+ }
172
+
176
173
  if (widget.dataSource?.type === 'aggregate') {
177
174
  ctx.addIssue({
178
175
  code: z.ZodIssueCode.custom,
@@ -185,13 +182,12 @@ const TableWidgetConfigSchema = WidgetBaseSchema.extend({
185
182
  const ChartWidgetTargetConfigSchema = WidgetBaseSchema.extend({
186
183
  target: z.literal('chart'),
187
184
  chart: ChartConfigSchema,
188
- query: DashboardWidgetQuerySchema.optional(),
189
185
  }).superRefine((widget, ctx) => {
190
- if (!widget.query && !widget.dataSource) {
186
+ if (!widget.dataSource) {
191
187
  ctx.addIssue({
192
188
  code: z.ZodIssueCode.custom,
193
189
  path: ['dataSource'],
194
- message: 'Chart widget must have query or dataSource config',
190
+ message: 'Chart widget must have dataSource config',
195
191
  })
196
192
  }
197
193
 
@@ -215,8 +211,15 @@ const ChartWidgetTargetConfigSchema = WidgetBaseSchema.extend({
215
211
  const KpiCardWidgetConfigSchema = WidgetBaseSchema.extend({
216
212
  target: z.literal('kpi_card'),
217
213
  kpi_card: z.unknown().optional(),
218
- query: DashboardWidgetQuerySchema.optional(),
219
214
  }).superRefine((widget, ctx) => {
215
+ if (!widget.dataSource) {
216
+ ctx.addIssue({
217
+ code: z.ZodIssueCode.custom,
218
+ path: ['dataSource'],
219
+ message: 'KPI card widget must have dataSource config',
220
+ })
221
+ }
222
+
220
223
  if (widget.dataSource?.type === 'aggregate' && widget.dataSource.groupBy) {
221
224
  ctx.addIssue({
222
225
  code: z.ZodIssueCode.custom,
@@ -229,8 +232,15 @@ const KpiCardWidgetConfigSchema = WidgetBaseSchema.extend({
229
232
  const GaugeCardWidgetConfigSchema = WidgetBaseSchema.extend({
230
233
  target: z.literal('gauge_card'),
231
234
  gauge_card: z.unknown().optional(),
232
- query: DashboardWidgetQuerySchema.optional(),
233
235
  }).superRefine((widget, ctx) => {
236
+ if (!widget.dataSource) {
237
+ ctx.addIssue({
238
+ code: z.ZodIssueCode.custom,
239
+ path: ['dataSource'],
240
+ message: 'Gauge card widget must have dataSource config',
241
+ })
242
+ }
243
+
234
244
  if (widget.dataSource?.type === 'aggregate' && widget.dataSource.groupBy) {
235
245
  ctx.addIssue({
236
246
  code: z.ZodIssueCode.custom,
@@ -243,8 +253,15 @@ const GaugeCardWidgetConfigSchema = WidgetBaseSchema.extend({
243
253
  const PivotTableWidgetConfigSchema = WidgetBaseSchema.extend({
244
254
  target: z.literal('pivot_table'),
245
255
  pivot_table: z.unknown().optional(),
246
- query: DashboardWidgetQuerySchema.optional(),
247
256
  }).superRefine((widget, ctx) => {
257
+ if (!widget.dataSource) {
258
+ ctx.addIssue({
259
+ code: z.ZodIssueCode.custom,
260
+ path: ['dataSource'],
261
+ message: 'Pivot table widget must have dataSource config',
262
+ })
263
+ }
264
+
248
265
  if (widget.dataSource?.type === 'aggregate' && !widget.dataSource.groupBy) {
249
266
  ctx.addIssue({
250
267
  code: z.ZodIssueCode.custom,