@adminforth/dashboard 1.2.0 → 1.3.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 +47 -39
- package/custom/composables/useElementSize.ts +17 -2
- package/custom/model/dashboard.types.ts +327 -234
- package/custom/skills/adminforth-dashboard/SKILL.md +6 -2
- package/custom/widgets/chart/ChartWidget.vue +23 -55
- package/custom/widgets/chart/bar/BarChart.vue +20 -12
- package/custom/widgets/chart/chart.types.ts +17 -66
- package/custom/widgets/chart/chart.utils.ts +11 -0
- package/custom/widgets/chart/funnel/FunnelChart.vue +6 -4
- package/custom/widgets/chart/line/LineChart.vue +23 -15
- package/custom/widgets/chart/stacked-bar/StackedBarChart.vue +28 -43
- package/custom/widgets/gauge-card/GaugeCardWidget.vue +7 -12
- package/custom/widgets/kpi-card/KpiCardWidget.vue +6 -8
- package/custom/widgets/pivot-table/PivotTableWidget.vue +8 -7
- package/custom/widgets/table/TableWidget.vue +8 -3
- package/dist/custom/composables/useElementSize.js +14 -2
- package/dist/custom/composables/useElementSize.ts +17 -2
- package/dist/custom/model/dashboard.types.d.ts +178 -61
- package/dist/custom/model/dashboard.types.js +67 -92
- package/dist/custom/model/dashboard.types.ts +327 -234
- package/dist/custom/queries/useDashboardConfig.d.ts +832 -66
- package/dist/custom/queries/useWidgetData.d.ts +828 -62
- package/dist/custom/skills/adminforth-dashboard/SKILL.md +6 -2
- package/dist/custom/widgets/chart/ChartWidget.vue +23 -55
- package/dist/custom/widgets/chart/bar/BarChart.vue +20 -12
- package/dist/custom/widgets/chart/chart.types.d.ts +13 -22
- package/dist/custom/widgets/chart/chart.types.js +2 -25
- package/dist/custom/widgets/chart/chart.types.ts +17 -66
- package/dist/custom/widgets/chart/chart.utils.d.ts +1 -0
- package/dist/custom/widgets/chart/chart.utils.js +7 -0
- package/dist/custom/widgets/chart/chart.utils.ts +11 -0
- package/dist/custom/widgets/chart/funnel/FunnelChart.vue +6 -4
- package/dist/custom/widgets/chart/line/LineChart.vue +23 -15
- package/dist/custom/widgets/chart/stacked-bar/StackedBarChart.vue +28 -43
- package/dist/custom/widgets/gauge-card/GaugeCardWidget.vue +7 -12
- package/dist/custom/widgets/kpi-card/KpiCardWidget.vue +6 -8
- package/dist/custom/widgets/pivot-table/PivotTableWidget.vue +8 -7
- package/dist/custom/widgets/table/TableWidget.vue +8 -3
- package/dist/endpoint/widgets.js +5 -2
- package/dist/schema/api.d.ts +2680 -736
- package/dist/schema/widget.d.ts +1588 -476
- package/dist/schema/widget.js +205 -139
- package/dist/services/widgetConfigValidator.js +16 -40
- package/dist/services/widgetDataService.js +359 -82
- package/endpoint/dashboard.ts +1 -1
- package/endpoint/widgets.ts +5 -2
- package/package.json +1 -1
- package/schema/widget.ts +222 -139
- package/services/widgetConfigValidator.ts +29 -53
- package/services/widgetDataService.ts +484 -100
package/dist/schema/widget.js
CHANGED
|
@@ -6,56 +6,146 @@ const DashboardWidgetSizeSchema = z.enum([
|
|
|
6
6
|
'wide',
|
|
7
7
|
'full',
|
|
8
8
|
]);
|
|
9
|
-
|
|
9
|
+
const ValueFormatSchema = z.enum([
|
|
10
|
+
'number',
|
|
11
|
+
'compact_number',
|
|
12
|
+
'currency',
|
|
13
|
+
'percent',
|
|
14
|
+
'percent_delta',
|
|
15
|
+
'number_delta',
|
|
16
|
+
'currency_delta',
|
|
17
|
+
]).optional();
|
|
18
|
+
const ChartFieldRefSchema = z.object({
|
|
19
|
+
field: z.string(),
|
|
20
|
+
label: z.string().optional(),
|
|
21
|
+
format: ValueFormatSchema,
|
|
22
|
+
}).strict();
|
|
23
|
+
const FieldRefSchema = z.union([
|
|
24
|
+
z.string(),
|
|
25
|
+
z.object({
|
|
26
|
+
field: z.string(),
|
|
27
|
+
label: z.string().optional(),
|
|
28
|
+
format: ValueFormatSchema,
|
|
29
|
+
}).strict(),
|
|
30
|
+
]);
|
|
31
|
+
const FilterExpressionSchema = z.lazy(() => z.union([
|
|
32
|
+
z.array(FilterExpressionSchema),
|
|
33
|
+
z.object({
|
|
34
|
+
and: z.array(FilterExpressionSchema),
|
|
35
|
+
}).strict(),
|
|
36
|
+
z.object({
|
|
37
|
+
or: z.array(FilterExpressionSchema),
|
|
38
|
+
}).strict(),
|
|
39
|
+
z.object({
|
|
40
|
+
field: z.string(),
|
|
41
|
+
eq: z.unknown().optional(),
|
|
42
|
+
neq: z.unknown().optional(),
|
|
43
|
+
gt: z.unknown().optional(),
|
|
44
|
+
gte: z.unknown().optional(),
|
|
45
|
+
lt: z.unknown().optional(),
|
|
46
|
+
lte: z.unknown().optional(),
|
|
47
|
+
in: z.array(z.unknown()).optional(),
|
|
48
|
+
not_in: z.array(z.unknown()).optional(),
|
|
49
|
+
like: z.unknown().optional(),
|
|
50
|
+
ilike: z.unknown().optional(),
|
|
51
|
+
}).strict(),
|
|
52
|
+
]));
|
|
53
|
+
const QueryAggregateOperationSchema = z.enum([
|
|
10
54
|
'sum',
|
|
11
55
|
'count',
|
|
56
|
+
'count_distinct',
|
|
12
57
|
'avg',
|
|
13
58
|
'min',
|
|
14
59
|
'max',
|
|
15
60
|
'median',
|
|
16
61
|
]);
|
|
17
|
-
|
|
18
|
-
|
|
62
|
+
const QueryFieldSelectItemSchema = z.object({
|
|
63
|
+
field: z.string(),
|
|
64
|
+
as: z.string().optional(),
|
|
65
|
+
grain: z.enum(['hour', 'day', 'week', 'month', 'quarter', 'year']).optional(),
|
|
66
|
+
}).strict();
|
|
67
|
+
const QueryAggregateSelectItemSchema = z.object({
|
|
68
|
+
agg: QueryAggregateOperationSchema,
|
|
19
69
|
field: z.string().optional(),
|
|
20
|
-
|
|
21
|
-
|
|
70
|
+
as: z.string(),
|
|
71
|
+
filters: FilterExpressionSchema.optional(),
|
|
72
|
+
}).strict().superRefine((item, ctx) => {
|
|
73
|
+
if (!['count'].includes(item.agg) && !item.field) {
|
|
22
74
|
ctx.addIssue({
|
|
23
75
|
code: z.ZodIssueCode.custom,
|
|
24
76
|
path: ['field'],
|
|
25
|
-
message: `field is required for ${
|
|
77
|
+
message: `field is required for ${item.agg}`,
|
|
26
78
|
});
|
|
27
79
|
}
|
|
28
80
|
});
|
|
29
|
-
|
|
30
|
-
z.
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
81
|
+
const QueryCalcSelectItemSchema = z.object({
|
|
82
|
+
calc: z.string(),
|
|
83
|
+
as: z.string(),
|
|
84
|
+
}).strict();
|
|
85
|
+
const QuerySelectItemSchema = z.union([
|
|
86
|
+
QueryFieldSelectItemSchema,
|
|
87
|
+
QueryAggregateSelectItemSchema,
|
|
88
|
+
QueryCalcSelectItemSchema,
|
|
89
|
+
]);
|
|
90
|
+
const QueryGroupByItemSchema = z.union([
|
|
91
|
+
z.string(),
|
|
34
92
|
z.object({
|
|
35
|
-
type: z.literal('date_trunc'),
|
|
36
93
|
field: z.string(),
|
|
37
|
-
|
|
94
|
+
as: z.string().optional(),
|
|
95
|
+
grain: z.enum(['hour', 'day', 'week', 'month', 'quarter', 'year']).optional(),
|
|
38
96
|
timezone: z.string().optional(),
|
|
39
|
-
}),
|
|
97
|
+
}).strict(),
|
|
40
98
|
]);
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
aggregations: z.record(z.string(), AggregationRuleZodSchema),
|
|
45
|
-
groupBy: GroupByRuleZodSchema.optional(),
|
|
46
|
-
filters: z.unknown().optional(),
|
|
99
|
+
const QueryOrderByItemSchema = z.object({
|
|
100
|
+
field: z.string(),
|
|
101
|
+
direction: z.enum(['asc', 'desc']).optional(),
|
|
47
102
|
}).strict();
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
103
|
+
const TimeSeriesConfigSchema = z.object({
|
|
104
|
+
field: z.string(),
|
|
105
|
+
grain: z.enum(['hour', 'day', 'week', 'month', 'quarter', 'year']),
|
|
106
|
+
timezone: z.string().optional(),
|
|
107
|
+
}).strict();
|
|
108
|
+
const PeriodConfigSchema = z.object({
|
|
109
|
+
field: z.string(),
|
|
110
|
+
gte: z.unknown().optional(),
|
|
111
|
+
lt: z.unknown().optional(),
|
|
112
|
+
}).strict();
|
|
113
|
+
const BucketConfigSchema = z.object({
|
|
114
|
+
field: z.string(),
|
|
115
|
+
buckets: z.array(z.object({
|
|
116
|
+
label: z.string(),
|
|
117
|
+
min: z.number().optional(),
|
|
118
|
+
max: z.number().optional(),
|
|
119
|
+
}).strict()),
|
|
120
|
+
}).strict();
|
|
121
|
+
const QueryCalcItemSchema = z.object({
|
|
122
|
+
calc: z.string(),
|
|
123
|
+
as: z.string(),
|
|
124
|
+
}).strict();
|
|
125
|
+
const FormattingConfigSchema = z.record(z.string(), z.unknown());
|
|
126
|
+
export const QueryConfigSchema = z.object({
|
|
127
|
+
resource: z.string(),
|
|
128
|
+
select: z.array(QuerySelectItemSchema).optional(),
|
|
129
|
+
filters: FilterExpressionSchema.optional(),
|
|
130
|
+
groupBy: z.array(QueryGroupByItemSchema).optional(),
|
|
131
|
+
orderBy: z.array(QueryOrderByItemSchema).optional(),
|
|
132
|
+
limit: z.number().int().positive().optional(),
|
|
133
|
+
offset: z.number().int().nonnegative().optional(),
|
|
134
|
+
timeSeries: TimeSeriesConfigSchema.optional(),
|
|
135
|
+
period: PeriodConfigSchema.optional(),
|
|
136
|
+
bucket: BucketConfigSchema.optional(),
|
|
137
|
+
calcs: z.array(QueryCalcItemSchema).optional(),
|
|
138
|
+
formatting: FormattingConfigSchema.optional(),
|
|
139
|
+
}).strict();
|
|
140
|
+
const FunnelQueryStepSchema = z.object({
|
|
141
|
+
name: z.string(),
|
|
142
|
+
resource: z.string(),
|
|
143
|
+
metric: QueryAggregateSelectItemSchema,
|
|
144
|
+
filters: FilterExpressionSchema.optional(),
|
|
145
|
+
}).strict();
|
|
146
|
+
export const FunnelQueryConfigSchema = z.object({
|
|
147
|
+
steps: z.array(FunnelQueryStepSchema).min(1),
|
|
54
148
|
}).strict();
|
|
55
|
-
export const WidgetDataSourceZodSchema = z.discriminatedUnion('type', [
|
|
56
|
-
ResourceDataSourceZodSchema,
|
|
57
|
-
AggregateDataSourceZodSchema,
|
|
58
|
-
]);
|
|
59
149
|
const WidgetBaseSchema = z.object({
|
|
60
150
|
id: z.string().optional(),
|
|
61
151
|
group_id: z.string().optional(),
|
|
@@ -66,8 +156,12 @@ const WidgetBaseSchema = z.object({
|
|
|
66
156
|
minWidth: z.number().nonnegative('Min width must be a non-negative number').optional(),
|
|
67
157
|
maxWidth: z.number().nonnegative('Max width must be a non-negative number').nullable().optional(),
|
|
68
158
|
order: z.number().optional(),
|
|
69
|
-
dataSource: WidgetDataSourceZodSchema.optional(),
|
|
70
159
|
});
|
|
160
|
+
const TableViewConfigSchema = z.object({
|
|
161
|
+
columns: z.array(FieldRefSchema).optional(),
|
|
162
|
+
pagination: z.boolean().optional(),
|
|
163
|
+
pageSize: z.number().int().positive().optional(),
|
|
164
|
+
}).strict();
|
|
71
165
|
const ChartBaseSchema = z.object({
|
|
72
166
|
title: z.string().optional(),
|
|
73
167
|
});
|
|
@@ -75,51 +169,49 @@ const ChartBucketSchema = z.object({
|
|
|
75
169
|
label: z.string().min(1, 'Bucket label is required'),
|
|
76
170
|
min: z.number().optional(),
|
|
77
171
|
max: z.number().optional(),
|
|
78
|
-
});
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
});
|
|
172
|
+
}).strict();
|
|
173
|
+
const ChartSeriesRefSchema = z.object({
|
|
174
|
+
field: z.string(),
|
|
175
|
+
label: z.string().optional(),
|
|
176
|
+
}).strict();
|
|
84
177
|
const LineChartSchema = ChartBaseSchema.extend({
|
|
85
178
|
type: z.literal('line'),
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
179
|
+
x: ChartFieldRefSchema,
|
|
180
|
+
y: z.array(ChartFieldRefSchema).min(1),
|
|
181
|
+
series: ChartSeriesRefSchema.optional(),
|
|
89
182
|
color: z.string().optional(),
|
|
183
|
+
colors: z.array(z.string()).optional(),
|
|
90
184
|
});
|
|
91
185
|
const BarChartSchema = ChartBaseSchema.extend({
|
|
92
186
|
type: z.literal('bar'),
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
bucket_field: z.string().optional(),
|
|
96
|
-
buckets: z.array(ChartBucketSchema).optional(),
|
|
187
|
+
x: ChartFieldRefSchema,
|
|
188
|
+
y: ChartFieldRefSchema,
|
|
97
189
|
color: z.string().optional(),
|
|
98
190
|
});
|
|
99
191
|
const StackedBarChartSchema = ChartBaseSchema.extend({
|
|
100
192
|
type: z.literal('stacked_bar'),
|
|
101
|
-
|
|
102
|
-
|
|
193
|
+
x: ChartFieldRefSchema,
|
|
194
|
+
y: ChartFieldRefSchema,
|
|
195
|
+
series: ChartSeriesRefSchema,
|
|
103
196
|
colors: z.array(z.string()).optional(),
|
|
104
197
|
});
|
|
105
198
|
const PieChartSchema = ChartBaseSchema.extend({
|
|
106
199
|
type: z.literal('pie'),
|
|
107
|
-
|
|
108
|
-
|
|
200
|
+
label: ChartFieldRefSchema,
|
|
201
|
+
value: ChartFieldRefSchema,
|
|
109
202
|
colors: z.array(z.string()).optional(),
|
|
110
203
|
});
|
|
111
204
|
const HistogramChartSchema = ChartBaseSchema.extend({
|
|
112
205
|
type: z.literal('histogram'),
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
bucket_field: z.string().optional(),
|
|
206
|
+
x: ChartFieldRefSchema,
|
|
207
|
+
y: ChartFieldRefSchema,
|
|
116
208
|
buckets: z.array(ChartBucketSchema).optional(),
|
|
117
209
|
color: z.string().optional(),
|
|
118
210
|
});
|
|
119
211
|
const FunnelChartSchema = ChartBaseSchema.extend({
|
|
120
212
|
type: z.literal('funnel'),
|
|
121
|
-
|
|
122
|
-
|
|
213
|
+
label: ChartFieldRefSchema.optional(),
|
|
214
|
+
value: ChartFieldRefSchema.optional(),
|
|
123
215
|
colors: z.array(z.string()).optional(),
|
|
124
216
|
});
|
|
125
217
|
export const ChartConfigSchema = z.discriminatedUnion('type', [
|
|
@@ -130,115 +222,89 @@ export const ChartConfigSchema = z.discriminatedUnion('type', [
|
|
|
130
222
|
HistogramChartSchema,
|
|
131
223
|
FunnelChartSchema,
|
|
132
224
|
]);
|
|
225
|
+
const KpiCardViewConfigSchema = z.object({
|
|
226
|
+
title: z.string().optional(),
|
|
227
|
+
value: z.object({
|
|
228
|
+
field: z.string(),
|
|
229
|
+
format: ValueFormatSchema,
|
|
230
|
+
prefix: z.string().optional(),
|
|
231
|
+
suffix: z.string().optional(),
|
|
232
|
+
}).strict(),
|
|
233
|
+
subtitle: z.object({
|
|
234
|
+
text: z.string().optional(),
|
|
235
|
+
field: z.string().optional(),
|
|
236
|
+
}).strict().optional(),
|
|
237
|
+
comparison: z.unknown().optional(),
|
|
238
|
+
sparkline: z.unknown().optional(),
|
|
239
|
+
}).strict();
|
|
240
|
+
const GaugeCardViewConfigSchema = z.object({
|
|
241
|
+
title: z.string().optional(),
|
|
242
|
+
value: z.object({
|
|
243
|
+
field: z.string(),
|
|
244
|
+
format: ValueFormatSchema,
|
|
245
|
+
prefix: z.string().optional(),
|
|
246
|
+
suffix: z.string().optional(),
|
|
247
|
+
}).strict(),
|
|
248
|
+
target: z.object({
|
|
249
|
+
value: z.number().optional(),
|
|
250
|
+
field: z.string().optional(),
|
|
251
|
+
label: z.string().optional(),
|
|
252
|
+
}).strict().optional(),
|
|
253
|
+
progress: z.object({
|
|
254
|
+
valueField: z.string(),
|
|
255
|
+
targetValue: z.number().optional(),
|
|
256
|
+
targetField: z.string().optional(),
|
|
257
|
+
format: ValueFormatSchema,
|
|
258
|
+
}).strict().optional(),
|
|
259
|
+
color: z.string().optional(),
|
|
260
|
+
}).strict();
|
|
261
|
+
const PivotTableViewConfigSchema = z.object({
|
|
262
|
+
rows: z.array(FieldRefSchema).min(1),
|
|
263
|
+
columns: z.array(FieldRefSchema).optional(),
|
|
264
|
+
values: z.array(z.object({
|
|
265
|
+
field: z.string(),
|
|
266
|
+
label: z.string().optional(),
|
|
267
|
+
format: ValueFormatSchema,
|
|
268
|
+
aggregation: z.enum(['sum', 'count', 'avg', 'min', 'max']).optional(),
|
|
269
|
+
}).strict()).min(1),
|
|
270
|
+
}).strict();
|
|
133
271
|
export const EmptyWidgetConfigSchema = WidgetBaseSchema.extend({
|
|
134
272
|
target: z.literal('empty'),
|
|
135
273
|
});
|
|
136
274
|
const TableWidgetConfigSchema = WidgetBaseSchema.extend({
|
|
137
275
|
target: z.literal('table'),
|
|
138
|
-
table:
|
|
139
|
-
|
|
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
|
-
}
|
|
148
|
-
if (((_a = widget.dataSource) === null || _a === void 0 ? void 0 : _a.type) === 'aggregate') {
|
|
149
|
-
ctx.addIssue({
|
|
150
|
-
code: z.ZodIssueCode.custom,
|
|
151
|
-
path: ['dataSource'],
|
|
152
|
-
message: 'Table widget dataSource must use resource type',
|
|
153
|
-
});
|
|
154
|
-
}
|
|
276
|
+
table: TableViewConfigSchema.optional(),
|
|
277
|
+
query: QueryConfigSchema,
|
|
155
278
|
});
|
|
156
279
|
const ChartWidgetTargetConfigSchema = WidgetBaseSchema.extend({
|
|
157
280
|
target: z.literal('chart'),
|
|
158
281
|
chart: ChartConfigSchema,
|
|
282
|
+
query: z.union([QueryConfigSchema, FunnelQueryConfigSchema]),
|
|
159
283
|
}).superRefine((widget, ctx) => {
|
|
160
|
-
|
|
161
|
-
|
|
284
|
+
const isFunnelChart = widget.chart.type === 'funnel';
|
|
285
|
+
const isFunnelQuery = 'steps' in widget.query;
|
|
286
|
+
if (isFunnelChart !== isFunnelQuery) {
|
|
162
287
|
ctx.addIssue({
|
|
163
288
|
code: z.ZodIssueCode.custom,
|
|
164
|
-
path: ['
|
|
165
|
-
message: '
|
|
166
|
-
});
|
|
167
|
-
}
|
|
168
|
-
if (((_a = widget.dataSource) === null || _a === void 0 ? void 0 : _a.type) === 'resource') {
|
|
169
|
-
ctx.addIssue({
|
|
170
|
-
code: z.ZodIssueCode.custom,
|
|
171
|
-
path: ['dataSource'],
|
|
172
|
-
message: 'Chart widget dataSource must use aggregate type',
|
|
173
|
-
});
|
|
174
|
-
}
|
|
175
|
-
if (((_b = widget.dataSource) === null || _b === void 0 ? void 0 : _b.type) === 'aggregate' && !widget.dataSource.groupBy) {
|
|
176
|
-
ctx.addIssue({
|
|
177
|
-
code: z.ZodIssueCode.custom,
|
|
178
|
-
path: ['dataSource', 'groupBy'],
|
|
179
|
-
message: 'Chart widget aggregate dataSource must define groupBy',
|
|
289
|
+
path: ['query'],
|
|
290
|
+
message: 'Funnel charts must use steps query, other charts must use resource query',
|
|
180
291
|
});
|
|
181
292
|
}
|
|
182
293
|
});
|
|
183
294
|
const KpiCardWidgetConfigSchema = WidgetBaseSchema.extend({
|
|
184
295
|
target: z.literal('kpi_card'),
|
|
185
|
-
|
|
186
|
-
|
|
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
|
-
}
|
|
195
|
-
if (((_a = widget.dataSource) === null || _a === void 0 ? void 0 : _a.type) === 'aggregate' && widget.dataSource.groupBy) {
|
|
196
|
-
ctx.addIssue({
|
|
197
|
-
code: z.ZodIssueCode.custom,
|
|
198
|
-
path: ['dataSource', 'groupBy'],
|
|
199
|
-
message: 'KPI card aggregate dataSource must not define groupBy',
|
|
200
|
-
});
|
|
201
|
-
}
|
|
296
|
+
card: KpiCardViewConfigSchema,
|
|
297
|
+
query: QueryConfigSchema,
|
|
202
298
|
});
|
|
203
299
|
const GaugeCardWidgetConfigSchema = WidgetBaseSchema.extend({
|
|
204
300
|
target: z.literal('gauge_card'),
|
|
205
|
-
|
|
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
|
-
}
|
|
215
|
-
if (((_a = widget.dataSource) === null || _a === void 0 ? void 0 : _a.type) === 'aggregate' && widget.dataSource.groupBy) {
|
|
216
|
-
ctx.addIssue({
|
|
217
|
-
code: z.ZodIssueCode.custom,
|
|
218
|
-
path: ['dataSource', 'groupBy'],
|
|
219
|
-
message: 'Gauge card aggregate dataSource must not define groupBy',
|
|
220
|
-
});
|
|
221
|
-
}
|
|
301
|
+
card: GaugeCardViewConfigSchema,
|
|
302
|
+
query: QueryConfigSchema,
|
|
222
303
|
});
|
|
223
304
|
const PivotTableWidgetConfigSchema = WidgetBaseSchema.extend({
|
|
224
305
|
target: z.literal('pivot_table'),
|
|
225
|
-
|
|
226
|
-
|
|
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
|
-
}
|
|
235
|
-
if (((_a = widget.dataSource) === null || _a === void 0 ? void 0 : _a.type) === 'aggregate' && !widget.dataSource.groupBy) {
|
|
236
|
-
ctx.addIssue({
|
|
237
|
-
code: z.ZodIssueCode.custom,
|
|
238
|
-
path: ['dataSource', 'groupBy'],
|
|
239
|
-
message: 'Pivot table aggregate dataSource must define groupBy',
|
|
240
|
-
});
|
|
241
|
-
}
|
|
306
|
+
pivot: PivotTableViewConfigSchema,
|
|
307
|
+
query: QueryConfigSchema,
|
|
242
308
|
});
|
|
243
309
|
export const WidgetConfigSchema = z.discriminatedUnion('target', [
|
|
244
310
|
TableWidgetConfigSchema,
|
|
@@ -1,51 +1,27 @@
|
|
|
1
|
-
import { normalizeChartWidgetConfig } from '../custom/widgets/chart/chart.types.js';
|
|
2
1
|
export function validateDashboardWidgetApiConfig(adminforth, widget) {
|
|
3
|
-
if (
|
|
2
|
+
if (!('query' in widget)) {
|
|
4
3
|
return [];
|
|
5
4
|
}
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
if (!chart) {
|
|
9
|
-
errors.push({
|
|
10
|
-
field: 'chart',
|
|
11
|
-
message: 'Chart widget must have chart config',
|
|
12
|
-
});
|
|
13
|
-
return errors;
|
|
5
|
+
if ('steps' in widget.query) {
|
|
6
|
+
return widget.query.steps.flatMap((step, index) => validateResource(adminforth, step.resource, `query.steps.${index}.resource`));
|
|
14
7
|
}
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
if (!aggregateDataSource.groupBy) {
|
|
25
|
-
errors.push({
|
|
26
|
-
field: 'data_source.group_by',
|
|
27
|
-
message: 'Chart aggregate dataSource must define groupBy',
|
|
28
|
-
});
|
|
29
|
-
}
|
|
30
|
-
return errors;
|
|
8
|
+
return validateQueryConfig(adminforth, widget.query, 'query');
|
|
9
|
+
}
|
|
10
|
+
function validateQueryConfig(adminforth, query, fieldPrefix) {
|
|
11
|
+
return validateResource(adminforth, query.resource, `${fieldPrefix}.resource`);
|
|
12
|
+
}
|
|
13
|
+
function validateResource(adminforth, resourceId, field) {
|
|
14
|
+
const resource = adminforth.config.resources.find((item) => item.resourceId === resourceId);
|
|
15
|
+
if (resource) {
|
|
16
|
+
return [];
|
|
31
17
|
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
return errors;
|
|
18
|
+
return [{
|
|
19
|
+
field,
|
|
20
|
+
message: `Resource "${resourceId}" is not registered`,
|
|
21
|
+
}];
|
|
37
22
|
}
|
|
38
23
|
export function createWidgetConfigValidatorService(adminforth) {
|
|
39
24
|
return {
|
|
40
25
|
validateDashboardWidgetApiConfig: (widget) => validateDashboardWidgetApiConfig(adminforth, widget),
|
|
41
26
|
};
|
|
42
27
|
}
|
|
43
|
-
function getAggregateDataSource(dataSource) {
|
|
44
|
-
if (typeof dataSource !== 'object'
|
|
45
|
-
|| dataSource === null
|
|
46
|
-
|| dataSource.type !== 'aggregate'
|
|
47
|
-
|| typeof dataSource.resourceId !== 'string') {
|
|
48
|
-
return null;
|
|
49
|
-
}
|
|
50
|
-
return dataSource;
|
|
51
|
-
}
|