@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.
- package/README.md +11 -28
- package/custom/model/dashboard.types.ts +236 -42
- package/custom/runtime/DashboardRuntime.vue +2 -1
- package/custom/runtime/WidgetRenderer.vue +2 -1
- package/custom/skills/adminforth-dashboard/SKILL.md +4 -4
- package/custom/widgets/chart/ChartWidget.vue +45 -12
- package/custom/widgets/chart/chart.types.ts +83 -0
- package/custom/widgets/gauge-card/GaugeCardWidget.vue +7 -38
- package/custom/widgets/kpi-card/KpiCardWidget.vue +6 -8
- package/custom/widgets/pivot-table/PivotTableWidget.vue +8 -10
- package/custom/widgets/table/TableWidget.vue +1 -1
- package/dist/custom/model/dashboard.types.d.ts +25 -1
- package/dist/custom/model/dashboard.types.js +133 -42
- package/dist/custom/model/dashboard.types.ts +236 -42
- package/dist/custom/queries/useDashboardConfig.d.ts +0 -2
- package/dist/custom/queries/useWidgetData.d.ts +0 -2
- package/dist/custom/runtime/DashboardRuntime.vue +2 -1
- package/dist/custom/runtime/WidgetRenderer.vue +2 -1
- package/dist/custom/skills/adminforth-dashboard/SKILL.md +4 -4
- package/dist/custom/widgets/chart/ChartWidget.vue +45 -12
- package/dist/custom/widgets/chart/chart.types.d.ts +15 -0
- package/dist/custom/widgets/chart/chart.types.js +46 -0
- package/dist/custom/widgets/chart/chart.types.ts +83 -0
- package/dist/custom/widgets/gauge-card/GaugeCardWidget.vue +7 -38
- package/dist/custom/widgets/kpi-card/KpiCardWidget.vue +6 -8
- package/dist/custom/widgets/pivot-table/PivotTableWidget.vue +8 -10
- package/dist/custom/widgets/table/TableWidget.vue +1 -1
- package/dist/endpoint/widgets.js +20 -3
- package/dist/schema/api.d.ts +0 -240
- package/dist/schema/widget.d.ts +0 -132
- package/dist/schema/widget.js +30 -16
- package/dist/services/widgetConfigValidator.js +9 -49
- package/dist/services/widgetDataService.d.ts +0 -9
- package/dist/services/widgetDataService.js +12 -30
- package/endpoint/widgets.ts +26 -3
- package/package.json +1 -1
- package/schema/widget.ts +34 -17
- package/services/widgetConfigValidator.ts +10 -57
- package/services/widgetDataService.ts +10 -45
package/dist/schema/widget.d.ts
CHANGED
|
@@ -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">;
|
package/dist/schema/widget.js
CHANGED
|
@@ -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.
|
|
161
|
+
if (!widget.dataSource) {
|
|
166
162
|
ctx.addIssue({
|
|
167
163
|
code: z.ZodIssueCode.custom,
|
|
168
164
|
path: ['dataSource'],
|
|
169
|
-
message: 'Chart widget must have
|
|
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
|
-
|
|
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: '
|
|
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: '
|
|
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
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
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
|
|
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,
|
|
20
|
+
return getResourceWidgetData(adminforth, dataSource, options);
|
|
22
21
|
});
|
|
23
22
|
}
|
|
24
|
-
function getResourceWidgetData(adminforth, dataSource,
|
|
23
|
+
function getResourceWidgetData(adminforth, dataSource, options) {
|
|
25
24
|
return __awaiter(this, void 0, void 0, function* () {
|
|
26
|
-
var _a, _b
|
|
25
|
+
var _a, _b;
|
|
27
26
|
const resource = adminforth.resource(dataSource.resourceId);
|
|
28
27
|
const filters = normalizeFilters(dataSource.filters);
|
|
29
|
-
const sort = normalizeSort(
|
|
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
|
|
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 = (
|
|
38
|
-
const total = pagination ?
|
|
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
|
|
75
|
-
if (
|
|
76
|
-
return
|
|
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
|
-
|
|
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)
|
package/endpoint/widgets.ts
CHANGED
|
@@ -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
|
|
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
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.
|
|
186
|
+
if (!widget.dataSource) {
|
|
191
187
|
ctx.addIssue({
|
|
192
188
|
code: z.ZodIssueCode.custom,
|
|
193
189
|
path: ['dataSource'],
|
|
194
|
-
message: 'Chart widget must have
|
|
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,
|