@adminforth/dashboard 1.3.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.
- package/README.md +103 -15
- package/custom/api/dashboardApi.ts +9 -8
- package/custom/model/dashboard.types.ts +63 -270
- package/custom/model/dashboardTopics.ts +5 -0
- package/custom/runtime/DashboardGroup.vue +2 -2
- package/custom/runtime/DashboardPage.vue +17 -7
- package/custom/runtime/DashboardRuntime.vue +20 -8
- package/custom/runtime/WidgetRenderer.vue +1 -2
- package/custom/runtime/WidgetShell.vue +3 -3
- package/custom/skills/adminforth-dashboard/SKILL.md +110 -3
- package/custom/widgets/{gauge-card/GaugeCardWidget.vue → GaugeCardWidget.vue} +63 -61
- package/custom/widgets/{kpi-card/KpiCardWidget.vue → KpiCardWidget.vue} +35 -33
- package/custom/widgets/{pivot-table/PivotTableWidget.vue → PivotTableWidget.vue} +71 -68
- package/custom/widgets/{table/TableWidget.vue → TableWidget.vue} +5 -5
- package/custom/widgets/chart/{bar/BarChart.vue → BarChart.vue} +2 -2
- package/custom/widgets/chart/ChartWidget.vue +24 -18
- package/{dist/custom/widgets/chart/funnel → custom/widgets/chart}/FunnelChart.vue +80 -78
- package/{dist/custom/widgets/chart/line → custom/widgets/chart}/LineChart.vue +2 -2
- package/custom/widgets/chart/{pie/PieChart.vue → PieChart.vue} +2 -2
- package/{dist/custom/widgets/chart/stacked-bar → custom/widgets/chart}/StackedBarChart.vue +97 -95
- package/custom/widgets/chart/chart.types.ts +0 -28
- package/dist/custom/api/dashboardApi.d.ts +4 -7
- package/dist/custom/api/dashboardApi.js +5 -0
- package/dist/custom/api/dashboardApi.ts +9 -8
- package/dist/custom/model/dashboard.types.d.ts +40 -31
- package/dist/custom/model/dashboard.types.js +13 -152
- package/dist/custom/model/dashboard.types.ts +63 -270
- package/dist/custom/model/dashboardTopics.d.ts +2 -0
- package/dist/custom/model/dashboardTopics.js +8 -0
- package/dist/custom/model/dashboardTopics.ts +5 -0
- package/dist/custom/queries/useDashboardConfig.d.ts +116 -96
- package/dist/custom/queries/useWidgetData.d.ts +116 -96
- package/dist/custom/runtime/DashboardGroup.vue +2 -2
- package/dist/custom/runtime/DashboardPage.vue +17 -7
- package/dist/custom/runtime/DashboardRuntime.vue +20 -8
- package/dist/custom/runtime/WidgetRenderer.vue +1 -2
- package/dist/custom/runtime/WidgetShell.vue +3 -3
- package/dist/custom/skills/adminforth-dashboard/SKILL.md +110 -3
- package/dist/custom/widgets/{gauge-card/GaugeCardWidget.vue → GaugeCardWidget.vue} +63 -61
- package/dist/custom/widgets/{kpi-card/KpiCardWidget.vue → KpiCardWidget.vue} +35 -33
- package/dist/custom/widgets/{pivot-table/PivotTableWidget.vue → PivotTableWidget.vue} +71 -68
- package/dist/custom/widgets/{table/TableWidget.vue → TableWidget.vue} +5 -5
- package/dist/custom/widgets/chart/{bar/BarChart.vue → BarChart.vue} +2 -2
- package/dist/custom/widgets/chart/ChartWidget.vue +24 -18
- package/{custom/widgets/chart/funnel → dist/custom/widgets/chart}/FunnelChart.vue +80 -78
- package/{custom/widgets/chart/line → dist/custom/widgets/chart}/LineChart.vue +2 -2
- package/dist/custom/widgets/chart/{pie/PieChart.vue → PieChart.vue} +2 -2
- package/{custom/widgets/chart/stacked-bar → dist/custom/widgets/chart}/StackedBarChart.vue +97 -95
- package/dist/custom/widgets/chart/chart.types.d.ts +0 -2
- package/dist/custom/widgets/chart/chart.types.js +0 -23
- package/dist/custom/widgets/chart/chart.types.ts +0 -28
- package/dist/endpoint/dashboard.d.ts +6 -2
- package/dist/endpoint/dashboard.js +29 -5
- package/dist/endpoint/groups.d.ts +2 -21
- package/dist/endpoint/groups.js +18 -16
- package/dist/endpoint/widgets.d.ts +2 -4
- package/dist/endpoint/widgets.js +28 -74
- package/dist/index.js +1 -3
- package/dist/schema/api.d.ts +2172 -500
- package/dist/schema/api.js +21 -13
- package/dist/schema/widget.d.ts +1076 -263
- package/dist/schema/widget.js +108 -49
- package/dist/services/dashboardConfigService.d.ts +0 -10
- package/dist/services/dashboardConfigService.js +6 -21
- package/dist/services/widgetDataService.d.ts +2 -1
- package/dist/services/widgetDataService.js +266 -206
- package/endpoint/dashboard.ts +47 -7
- package/endpoint/groups.ts +25 -42
- package/endpoint/widgets.ts +41 -96
- package/index.ts +0 -3
- package/package.json +3 -3
- package/schema/api.ts +23 -13
- package/schema/widget.ts +119 -55
- package/services/dashboardConfigService.ts +6 -25
- package/services/widgetDataService.ts +350 -237
- package/custom/widgets/chart/histogram/HistogramChart.vue +0 -21
- package/dist/custom/widgets/chart/histogram/HistogramChart.vue +0 -21
- package/dist/services/widgetConfigValidator.d.ts +0 -8
- package/dist/services/widgetConfigValidator.js +0 -27
- package/services/widgetConfigValidator.ts +0 -61
package/endpoint/groups.ts
CHANGED
|
@@ -2,6 +2,7 @@ import type { AdminUser, IHttpServer } from 'adminforth';
|
|
|
2
2
|
import { randomUUID } from 'crypto';
|
|
3
3
|
import type {
|
|
4
4
|
DashboardConfig,
|
|
5
|
+
EditableDashboardGroupConfig,
|
|
5
6
|
DashboardGroupConfig,
|
|
6
7
|
} from '../custom/model/dashboard.types.js';
|
|
7
8
|
import {
|
|
@@ -11,14 +12,7 @@ import {
|
|
|
11
12
|
SetGroupConfigRequestSchema,
|
|
12
13
|
SlugRequestSchema,
|
|
13
14
|
} from '../schema/api.js';
|
|
14
|
-
|
|
15
|
-
type DashboardRecord = {
|
|
16
|
-
id: string;
|
|
17
|
-
slug: string;
|
|
18
|
-
label: string;
|
|
19
|
-
revision: number;
|
|
20
|
-
config: unknown;
|
|
21
|
-
};
|
|
15
|
+
import type { DashboardRecord, PersistedDashboardResponse } from '../services/dashboardConfigService.js';
|
|
22
16
|
|
|
23
17
|
type GroupEndpointsContext = {
|
|
24
18
|
canEditDashboard: (adminUser: AdminUser) => boolean;
|
|
@@ -27,20 +21,7 @@ type GroupEndpointsContext = {
|
|
|
27
21
|
persistDashboardConfig: (
|
|
28
22
|
dashboard: DashboardRecord,
|
|
29
23
|
config: DashboardConfig,
|
|
30
|
-
) => Promise<
|
|
31
|
-
id: string;
|
|
32
|
-
slug: string;
|
|
33
|
-
label: string;
|
|
34
|
-
revision: number;
|
|
35
|
-
config: DashboardConfig;
|
|
36
|
-
}>;
|
|
37
|
-
buildDashboardResponse: (dashboard: DashboardRecord) => {
|
|
38
|
-
id: string;
|
|
39
|
-
slug: string;
|
|
40
|
-
label: string;
|
|
41
|
-
revision: number;
|
|
42
|
-
config: DashboardConfig;
|
|
43
|
-
};
|
|
24
|
+
) => Promise<PersistedDashboardResponse>;
|
|
44
25
|
};
|
|
45
26
|
|
|
46
27
|
export function registerGroupEndpoints(
|
|
@@ -59,8 +40,7 @@ export function registerGroupEndpoints(
|
|
|
59
40
|
return { error: 'Dashboard edit is not allowed' };
|
|
60
41
|
}
|
|
61
42
|
|
|
62
|
-
const
|
|
63
|
-
const dashboard = await ctx.getDashboardRecord(slug);
|
|
43
|
+
const dashboard = await ctx.getDashboardRecord(body.slug);
|
|
64
44
|
|
|
65
45
|
if (!dashboard) {
|
|
66
46
|
response.setStatus(404);
|
|
@@ -95,9 +75,8 @@ export function registerGroupEndpoints(
|
|
|
95
75
|
return { error: 'Dashboard edit is not allowed' };
|
|
96
76
|
}
|
|
97
77
|
|
|
98
|
-
const
|
|
99
|
-
const
|
|
100
|
-
const dashboard = await ctx.getDashboardRecord(slug);
|
|
78
|
+
const groupId = body.groupId;
|
|
79
|
+
const dashboard = await ctx.getDashboardRecord(body.slug);
|
|
101
80
|
|
|
102
81
|
if (!dashboard) {
|
|
103
82
|
response.setStatus(404);
|
|
@@ -112,14 +91,16 @@ export function registerGroupEndpoints(
|
|
|
112
91
|
return { error: 'Dashboard group not found' };
|
|
113
92
|
}
|
|
114
93
|
|
|
94
|
+
const nextGroup: DashboardGroupConfig = {
|
|
95
|
+
...(body.config as EditableDashboardGroupConfig),
|
|
96
|
+
id: group.id,
|
|
97
|
+
order: group.order,
|
|
98
|
+
};
|
|
99
|
+
|
|
115
100
|
return ctx.persistDashboardConfig(dashboard, {
|
|
116
101
|
...config,
|
|
117
102
|
groups: config.groups.map((item) => item.id === groupId
|
|
118
|
-
?
|
|
119
|
-
...(body.config as DashboardGroupConfig),
|
|
120
|
-
id: group.id,
|
|
121
|
-
order: group.order,
|
|
122
|
-
}
|
|
103
|
+
? nextGroup
|
|
123
104
|
: item),
|
|
124
105
|
});
|
|
125
106
|
},
|
|
@@ -137,10 +118,7 @@ export function registerGroupEndpoints(
|
|
|
137
118
|
return { error: 'Dashboard edit is not allowed' };
|
|
138
119
|
}
|
|
139
120
|
|
|
140
|
-
const
|
|
141
|
-
const groupId = String(body?.groupId || '');
|
|
142
|
-
const direction = body?.direction === 'down' ? 'down' : 'up';
|
|
143
|
-
const dashboard = await ctx.getDashboardRecord(slug);
|
|
121
|
+
const dashboard = await ctx.getDashboardRecord(body.slug);
|
|
144
122
|
|
|
145
123
|
if (!dashboard) {
|
|
146
124
|
response.setStatus(404);
|
|
@@ -149,17 +127,23 @@ export function registerGroupEndpoints(
|
|
|
149
127
|
|
|
150
128
|
const config = ctx.parseStoredDashboardConfig(dashboard.config);
|
|
151
129
|
const sortedGroups = [...config.groups].sort((a, b) => a.order - b.order);
|
|
152
|
-
const currentIndex = sortedGroups.findIndex((group) => group.id === groupId);
|
|
130
|
+
const currentIndex = sortedGroups.findIndex((group) => group.id === body.groupId);
|
|
153
131
|
|
|
154
132
|
if (currentIndex === -1) {
|
|
155
133
|
response.setStatus(404);
|
|
156
134
|
return { error: 'Dashboard group not found' };
|
|
157
135
|
}
|
|
158
136
|
|
|
159
|
-
const targetIndex = direction === 'up' ? currentIndex - 1 : currentIndex + 1;
|
|
137
|
+
const targetIndex = body.direction === 'up' ? currentIndex - 1 : currentIndex + 1;
|
|
160
138
|
|
|
161
139
|
if (targetIndex < 0 || targetIndex >= sortedGroups.length) {
|
|
162
|
-
return
|
|
140
|
+
return {
|
|
141
|
+
id: dashboard.id,
|
|
142
|
+
slug: dashboard.slug,
|
|
143
|
+
label: dashboard.label,
|
|
144
|
+
revision: dashboard.revision,
|
|
145
|
+
config: ctx.parseStoredDashboardConfig(dashboard.config),
|
|
146
|
+
};
|
|
163
147
|
}
|
|
164
148
|
|
|
165
149
|
const reorderedGroups = [...sortedGroups];
|
|
@@ -185,9 +169,8 @@ export function registerGroupEndpoints(
|
|
|
185
169
|
return { error: 'Dashboard edit is not allowed' };
|
|
186
170
|
}
|
|
187
171
|
|
|
188
|
-
const
|
|
189
|
-
const
|
|
190
|
-
const dashboard = await ctx.getDashboardRecord(slug);
|
|
172
|
+
const groupId = body.groupId;
|
|
173
|
+
const dashboard = await ctx.getDashboardRecord(body.slug);
|
|
191
174
|
|
|
192
175
|
if (!dashboard) {
|
|
193
176
|
response.setStatus(404);
|
package/endpoint/widgets.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import type { AdminUser, IHttpServer } from 'adminforth';
|
|
2
2
|
import { randomUUID } from 'crypto';
|
|
3
|
-
import {
|
|
4
|
-
|
|
3
|
+
import type {
|
|
4
|
+
DashboardConfig,
|
|
5
|
+
DashboardVariables,
|
|
6
|
+
DashboardWidgetConfig,
|
|
7
|
+
EditableDashboardWidgetConfig,
|
|
5
8
|
} from '../custom/model/dashboard.types.js';
|
|
6
|
-
import type { DashboardConfig, DashboardWidgetConfig } from '../custom/model/dashboard.types.js';
|
|
7
9
|
import {
|
|
8
10
|
DashboardApiResponseSchema,
|
|
9
11
|
DashboardWidgetDataResponseSchema,
|
|
@@ -13,8 +15,6 @@ import {
|
|
|
13
15
|
WidgetDataRequestSchema,
|
|
14
16
|
WidgetIdRequestSchema,
|
|
15
17
|
} from '../schema/api.js';
|
|
16
|
-
import { StoredWidgetConfigSchema } from '../schema/widget.js';
|
|
17
|
-
import type { DashboardWidgetConfigValidationError } from '../schema/widget.js';
|
|
18
18
|
import type { DashboardRecord, PersistedDashboardResponse } from '../services/dashboardConfigService.js';
|
|
19
19
|
|
|
20
20
|
type WidgetEndpointsContext = {
|
|
@@ -25,49 +25,15 @@ type WidgetEndpointsContext = {
|
|
|
25
25
|
dashboard: DashboardRecord,
|
|
26
26
|
config: DashboardConfig,
|
|
27
27
|
) => Promise<PersistedDashboardResponse>;
|
|
28
|
-
buildDashboardResponse: (dashboard: DashboardRecord) => PersistedDashboardResponse;
|
|
29
|
-
validateDashboardWidgetApiConfig: (
|
|
30
|
-
widget: DashboardWidgetConfig,
|
|
31
|
-
) => DashboardWidgetConfigValidationError[];
|
|
32
28
|
getWidgetData: (
|
|
33
29
|
widget: DashboardWidgetConfig,
|
|
34
|
-
options?: {
|
|
30
|
+
options?: {
|
|
31
|
+
pagination?: { page: number, pageSize: number },
|
|
32
|
+
variables?: DashboardVariables,
|
|
33
|
+
},
|
|
35
34
|
) => Promise<unknown>;
|
|
36
35
|
};
|
|
37
36
|
|
|
38
|
-
function formatWidgetConfigValidationErrors(error: { issues: { path: PropertyKey[], message: string }[] }) {
|
|
39
|
-
return error.issues.map((issue) => ({
|
|
40
|
-
field: issue.path.length ? formatWidgetConfigFieldPath(issue.path.map(String).join('.')) : 'config',
|
|
41
|
-
message: issue.message,
|
|
42
|
-
}));
|
|
43
|
-
}
|
|
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
|
-
['groupBy', 'group_by'],
|
|
57
|
-
['orderBy', 'order_by'],
|
|
58
|
-
['pageSize', 'page_size'],
|
|
59
|
-
['timeSeries', 'time_series'],
|
|
60
|
-
['valueField', 'value_field'],
|
|
61
|
-
['targetValue', 'target_value'],
|
|
62
|
-
['targetField', 'target_field'],
|
|
63
|
-
]);
|
|
64
|
-
|
|
65
|
-
return field
|
|
66
|
-
.split('.')
|
|
67
|
-
.map((segment) => fieldAliases.get(segment) ?? segment)
|
|
68
|
-
.join('.');
|
|
69
|
-
}
|
|
70
|
-
|
|
71
37
|
export function registerWidgetEndpoints(
|
|
72
38
|
server: IHttpServer,
|
|
73
39
|
ctx: WidgetEndpointsContext,
|
|
@@ -84,9 +50,7 @@ export function registerWidgetEndpoints(
|
|
|
84
50
|
return { error: 'Dashboard edit is not allowed' };
|
|
85
51
|
}
|
|
86
52
|
|
|
87
|
-
const
|
|
88
|
-
const groupId = String(body?.groupId || '');
|
|
89
|
-
const dashboard = await ctx.getDashboardRecord(slug);
|
|
53
|
+
const dashboard = await ctx.getDashboardRecord(body.slug);
|
|
90
54
|
|
|
91
55
|
if (!dashboard) {
|
|
92
56
|
response.setStatus(404);
|
|
@@ -94,17 +58,17 @@ export function registerWidgetEndpoints(
|
|
|
94
58
|
}
|
|
95
59
|
|
|
96
60
|
const config = ctx.parseStoredDashboardConfig(dashboard.config);
|
|
97
|
-
const group = config.groups.find((item) => item.id === groupId);
|
|
61
|
+
const group = config.groups.find((item) => item.id === body.groupId);
|
|
98
62
|
|
|
99
63
|
if (!group) {
|
|
100
64
|
response.setStatus(404);
|
|
101
65
|
return { error: 'Dashboard group not found' };
|
|
102
66
|
}
|
|
103
67
|
|
|
104
|
-
const nextOrder = config.widgets.filter((item) => item.group_id === groupId).length + 1;
|
|
68
|
+
const nextOrder = config.widgets.filter((item) => item.group_id === body.groupId).length + 1;
|
|
105
69
|
const widget: DashboardWidgetConfig = {
|
|
106
70
|
id: `widget_${randomUUID()}`,
|
|
107
|
-
group_id: groupId,
|
|
71
|
+
group_id: body.groupId,
|
|
108
72
|
label: 'New widget',
|
|
109
73
|
size: 'small',
|
|
110
74
|
order: nextOrder,
|
|
@@ -130,10 +94,7 @@ export function registerWidgetEndpoints(
|
|
|
130
94
|
return { error: 'Dashboard edit is not allowed' };
|
|
131
95
|
}
|
|
132
96
|
|
|
133
|
-
const
|
|
134
|
-
const widgetId = String(body?.widgetId || '');
|
|
135
|
-
const direction = body?.direction === 'down' ? 'down' : 'up';
|
|
136
|
-
const dashboard = await ctx.getDashboardRecord(slug);
|
|
97
|
+
const dashboard = await ctx.getDashboardRecord(body.slug);
|
|
137
98
|
|
|
138
99
|
if (!dashboard) {
|
|
139
100
|
response.setStatus(404);
|
|
@@ -141,7 +102,7 @@ export function registerWidgetEndpoints(
|
|
|
141
102
|
}
|
|
142
103
|
|
|
143
104
|
const config = ctx.parseStoredDashboardConfig(dashboard.config);
|
|
144
|
-
const widget = config.widgets.find((item) => item.id === widgetId);
|
|
105
|
+
const widget = config.widgets.find((item) => item.id === body.widgetId);
|
|
145
106
|
|
|
146
107
|
if (!widget) {
|
|
147
108
|
response.setStatus(404);
|
|
@@ -151,11 +112,17 @@ export function registerWidgetEndpoints(
|
|
|
151
112
|
const sortedWidgets = config.widgets
|
|
152
113
|
.filter((item) => item.group_id === widget.group_id)
|
|
153
114
|
.sort((a, b) => a.order - b.order);
|
|
154
|
-
const currentIndex = sortedWidgets.findIndex((item) => item.id === widgetId);
|
|
155
|
-
const targetIndex = direction === 'up' ? currentIndex - 1 : currentIndex + 1;
|
|
115
|
+
const currentIndex = sortedWidgets.findIndex((item) => item.id === body.widgetId);
|
|
116
|
+
const targetIndex = body.direction === 'up' ? currentIndex - 1 : currentIndex + 1;
|
|
156
117
|
|
|
157
118
|
if (targetIndex < 0 || targetIndex >= sortedWidgets.length) {
|
|
158
|
-
return
|
|
119
|
+
return {
|
|
120
|
+
id: dashboard.id,
|
|
121
|
+
slug: dashboard.slug,
|
|
122
|
+
label: dashboard.label,
|
|
123
|
+
revision: dashboard.revision,
|
|
124
|
+
config: ctx.parseStoredDashboardConfig(dashboard.config),
|
|
125
|
+
};
|
|
159
126
|
}
|
|
160
127
|
|
|
161
128
|
const reorderedWidgets = [...sortedWidgets];
|
|
@@ -185,9 +152,7 @@ export function registerWidgetEndpoints(
|
|
|
185
152
|
return { error: 'Dashboard edit is not allowed' };
|
|
186
153
|
}
|
|
187
154
|
|
|
188
|
-
const
|
|
189
|
-
const widgetId = String(body?.widgetId || '');
|
|
190
|
-
const dashboard = await ctx.getDashboardRecord(slug);
|
|
155
|
+
const dashboard = await ctx.getDashboardRecord(body.slug);
|
|
191
156
|
|
|
192
157
|
if (!dashboard) {
|
|
193
158
|
response.setStatus(404);
|
|
@@ -195,7 +160,7 @@ export function registerWidgetEndpoints(
|
|
|
195
160
|
}
|
|
196
161
|
|
|
197
162
|
const config = ctx.parseStoredDashboardConfig(dashboard.config);
|
|
198
|
-
const nextWidgets = config.widgets.filter((item) => item.id !== widgetId);
|
|
163
|
+
const nextWidgets = config.widgets.filter((item) => item.id !== body.widgetId);
|
|
199
164
|
|
|
200
165
|
if (nextWidgets.length === config.widgets.length) {
|
|
201
166
|
response.setStatus(404);
|
|
@@ -221,9 +186,7 @@ export function registerWidgetEndpoints(
|
|
|
221
186
|
return { error: 'Dashboard edit is not allowed' };
|
|
222
187
|
}
|
|
223
188
|
|
|
224
|
-
const
|
|
225
|
-
const widgetId = String(body?.widgetId || '');
|
|
226
|
-
const dashboard = await ctx.getDashboardRecord(slug);
|
|
189
|
+
const dashboard = await ctx.getDashboardRecord(body.slug);
|
|
227
190
|
|
|
228
191
|
if (!dashboard) {
|
|
229
192
|
response.setStatus(404);
|
|
@@ -231,43 +194,26 @@ export function registerWidgetEndpoints(
|
|
|
231
194
|
}
|
|
232
195
|
|
|
233
196
|
const config = ctx.parseStoredDashboardConfig(dashboard.config);
|
|
234
|
-
const widget = config.widgets.find((item) => item.id === widgetId);
|
|
197
|
+
const widget = config.widgets.find((item) => item.id === body.widgetId);
|
|
235
198
|
|
|
236
199
|
if (!widget) {
|
|
237
200
|
response.setStatus(404);
|
|
238
201
|
return { error: 'Dashboard widget not found' };
|
|
239
202
|
}
|
|
240
203
|
|
|
241
|
-
const
|
|
242
|
-
|
|
243
|
-
if (!parsedWidgetConfig.success) {
|
|
244
|
-
response.setStatus(422);
|
|
245
|
-
return {
|
|
246
|
-
error: 'Invalid widget config',
|
|
247
|
-
validationErrors: formatWidgetConfigValidationErrors(parsedWidgetConfig.error),
|
|
248
|
-
};
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
const typedWidgetConfig = parsedWidgetConfig.data as DashboardWidgetConfig;
|
|
252
|
-
const apiValidationErrors = ctx.validateDashboardWidgetApiConfig(typedWidgetConfig);
|
|
204
|
+
const typedWidgetConfig = body.config as EditableDashboardWidgetConfig;
|
|
253
205
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
}
|
|
206
|
+
const nextWidget: DashboardWidgetConfig = {
|
|
207
|
+
...typedWidgetConfig,
|
|
208
|
+
id: widget.id,
|
|
209
|
+
group_id: widget.group_id,
|
|
210
|
+
order: widget.order,
|
|
211
|
+
};
|
|
261
212
|
|
|
262
213
|
return ctx.persistDashboardConfig(dashboard, {
|
|
263
214
|
...config,
|
|
264
|
-
widgets: config.widgets.map((item) => item.id === widgetId
|
|
265
|
-
?
|
|
266
|
-
...typedWidgetConfig,
|
|
267
|
-
id: widget.id,
|
|
268
|
-
group_id: widget.group_id,
|
|
269
|
-
order: widget.order,
|
|
270
|
-
}
|
|
215
|
+
widgets: config.widgets.map((item) => item.id === body.widgetId
|
|
216
|
+
? nextWidget
|
|
271
217
|
: item),
|
|
272
218
|
});
|
|
273
219
|
},
|
|
@@ -280,9 +226,7 @@ export function registerWidgetEndpoints(
|
|
|
280
226
|
request_schema: WidgetDataRequestSchema,
|
|
281
227
|
response_schema: DashboardWidgetDataResponseSchema,
|
|
282
228
|
handler: async ({ body, response }) => {
|
|
283
|
-
const
|
|
284
|
-
const widgetId = String(body?.widgetId || '');
|
|
285
|
-
const dashboard = await ctx.getDashboardRecord(slug);
|
|
229
|
+
const dashboard = await ctx.getDashboardRecord(body.slug);
|
|
286
230
|
|
|
287
231
|
if (!dashboard) {
|
|
288
232
|
response.setStatus(404);
|
|
@@ -290,7 +234,7 @@ export function registerWidgetEndpoints(
|
|
|
290
234
|
}
|
|
291
235
|
|
|
292
236
|
const config = ctx.parseStoredDashboardConfig(dashboard.config);
|
|
293
|
-
const widget = config.widgets.find((item) => item.id === widgetId);
|
|
237
|
+
const widget = config.widgets.find((item) => item.id === body.widgetId);
|
|
294
238
|
|
|
295
239
|
if (!widget) {
|
|
296
240
|
response.setStatus(404);
|
|
@@ -300,7 +244,8 @@ export function registerWidgetEndpoints(
|
|
|
300
244
|
return {
|
|
301
245
|
widget,
|
|
302
246
|
data: await ctx.getWidgetData(widget, {
|
|
303
|
-
pagination: body
|
|
247
|
+
pagination: body.pagination,
|
|
248
|
+
variables: widget.variables,
|
|
304
249
|
}),
|
|
305
250
|
};
|
|
306
251
|
},
|
package/index.ts
CHANGED
|
@@ -8,7 +8,6 @@ import { registerGroupEndpoints } from "./endpoint/groups.js";
|
|
|
8
8
|
import { registerWidgetEndpoints } from './endpoint/widgets.js';
|
|
9
9
|
import { createDashboardConfigService } from "./services/dashboardConfigService.js";
|
|
10
10
|
import { createWidgetDataService } from "./services/widgetDataService.js";
|
|
11
|
-
import { createWidgetConfigValidatorService } from "./services/widgetConfigValidator.js";
|
|
12
11
|
|
|
13
12
|
const DEFAULT_DASHBOARD_CONFIG = {
|
|
14
13
|
version: 1,
|
|
@@ -119,7 +118,6 @@ export default class DashboardPlugin extends AdminForthPlugin {
|
|
|
119
118
|
this.options.dashboardConfigsResourceId,
|
|
120
119
|
);
|
|
121
120
|
const widgetDataService = createWidgetDataService(this.adminforth);
|
|
122
|
-
const widgetConfigValidatorService = createWidgetConfigValidatorService(this.adminforth);
|
|
123
121
|
|
|
124
122
|
const ctx = {
|
|
125
123
|
adminforth: this.adminforth,
|
|
@@ -127,7 +125,6 @@ export default class DashboardPlugin extends AdminForthPlugin {
|
|
|
127
125
|
canEditDashboard,
|
|
128
126
|
...dashboardConfigService,
|
|
129
127
|
...widgetDataService,
|
|
130
|
-
...widgetConfigValidatorService,
|
|
131
128
|
};
|
|
132
129
|
|
|
133
130
|
registerDashboardEndpoints(server, ctx);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adminforth/dashboard",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.1",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"type": "module",
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"access": "public"
|
|
10
10
|
},
|
|
11
11
|
"scripts": {
|
|
12
|
-
"build": "tsc && rsync -av --exclude 'node_modules' custom dist/",
|
|
12
|
+
"build": "rm -rf dist && tsc && rsync -av --exclude 'node_modules' custom dist/",
|
|
13
13
|
"typecheck": "tsc --noEmit"
|
|
14
14
|
},
|
|
15
15
|
"keywords": [
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"description": "Dashboard plugin for AdminForth",
|
|
22
22
|
"devDependencies": {
|
|
23
23
|
"@types/node": "latest",
|
|
24
|
-
"adminforth": "
|
|
24
|
+
"adminforth": "2.61.0-next.9",
|
|
25
25
|
"semantic-release": "^24.2.1",
|
|
26
26
|
"semantic-release-slack-bot": "^4.0.2",
|
|
27
27
|
"typescript": "^5.7.3",
|
package/schema/api.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { toJSONSchema, z } from 'zod'
|
|
2
|
-
import {
|
|
2
|
+
import { EditableDashboardWidgetConfigSchema, StoredWidgetConfigSchema } from './widget.js'
|
|
3
3
|
|
|
4
4
|
function toAdminForthJsonSchema(schema: z.ZodType) {
|
|
5
5
|
return toJSONSchema(schema, { target: 'draft-7' })
|
|
@@ -17,13 +17,13 @@ export const DashboardGroupZodSchema = z.object({
|
|
|
17
17
|
id: z.string(),
|
|
18
18
|
label: z.string(),
|
|
19
19
|
order: z.number(),
|
|
20
|
-
})
|
|
20
|
+
}).strict()
|
|
21
21
|
|
|
22
22
|
export const DashboardConfigZodSchema = z.object({
|
|
23
23
|
version: z.number(),
|
|
24
24
|
groups: z.array(DashboardGroupZodSchema),
|
|
25
25
|
widgets: z.array(StoredWidgetConfigSchema),
|
|
26
|
-
})
|
|
26
|
+
}).strict()
|
|
27
27
|
|
|
28
28
|
export const DashboardResponseZodSchema = z.object({
|
|
29
29
|
id: z.string(),
|
|
@@ -47,33 +47,42 @@ export const DashboardWidgetDataResponseZodSchema = z.union([
|
|
|
47
47
|
])
|
|
48
48
|
|
|
49
49
|
export const SlugRequestZodSchema = z.object({
|
|
50
|
-
slug: z.string()
|
|
50
|
+
slug: z.string(),
|
|
51
|
+
}).strict()
|
|
52
|
+
|
|
53
|
+
export const SetDashboardConfigRequestZodSchema = z.object({
|
|
54
|
+
slug: z.string(),
|
|
55
|
+
config: DashboardConfigZodSchema,
|
|
51
56
|
}).strict()
|
|
52
57
|
|
|
53
58
|
export const GroupIdRequestZodSchema = z.object({
|
|
54
|
-
slug: z.string()
|
|
59
|
+
slug: z.string(),
|
|
55
60
|
groupId: z.string(),
|
|
56
61
|
}).strict()
|
|
57
62
|
|
|
58
63
|
export const MoveGroupRequestZodSchema = z.object({
|
|
59
|
-
slug: z.string()
|
|
64
|
+
slug: z.string(),
|
|
60
65
|
groupId: z.string(),
|
|
61
66
|
direction: z.enum(['up', 'down']),
|
|
62
67
|
}).strict()
|
|
63
68
|
|
|
69
|
+
export const EditableDashboardGroupConfigZodSchema = z.object({
|
|
70
|
+
label: z.string(),
|
|
71
|
+
}).strict()
|
|
72
|
+
|
|
64
73
|
export const SetGroupConfigRequestZodSchema = z.object({
|
|
65
|
-
slug: z.string()
|
|
74
|
+
slug: z.string(),
|
|
66
75
|
groupId: z.string(),
|
|
67
|
-
config:
|
|
76
|
+
config: EditableDashboardGroupConfigZodSchema,
|
|
68
77
|
}).strict()
|
|
69
78
|
|
|
70
79
|
export const WidgetIdRequestZodSchema = z.object({
|
|
71
|
-
slug: z.string()
|
|
80
|
+
slug: z.string(),
|
|
72
81
|
widgetId: z.string(),
|
|
73
82
|
}).strict()
|
|
74
83
|
|
|
75
84
|
export const WidgetDataRequestZodSchema = z.object({
|
|
76
|
-
slug: z.string()
|
|
85
|
+
slug: z.string(),
|
|
77
86
|
widgetId: z.string(),
|
|
78
87
|
pagination: z.object({
|
|
79
88
|
page: z.number().int().positive(),
|
|
@@ -82,15 +91,15 @@ export const WidgetDataRequestZodSchema = z.object({
|
|
|
82
91
|
}).strict()
|
|
83
92
|
|
|
84
93
|
export const MoveWidgetRequestZodSchema = z.object({
|
|
85
|
-
slug: z.string()
|
|
94
|
+
slug: z.string(),
|
|
86
95
|
widgetId: z.string(),
|
|
87
96
|
direction: z.enum(['up', 'down']),
|
|
88
97
|
}).strict()
|
|
89
98
|
|
|
90
99
|
export const SetWidgetConfigRequestZodSchema = z.object({
|
|
91
|
-
slug: z.string()
|
|
100
|
+
slug: z.string(),
|
|
92
101
|
widgetId: z.string(),
|
|
93
|
-
config:
|
|
102
|
+
config: EditableDashboardWidgetConfigSchema,
|
|
94
103
|
}).strict()
|
|
95
104
|
|
|
96
105
|
export const DashboardErrorResponseSchema = toAdminForthJsonSchema(DashboardErrorResponseZodSchema)
|
|
@@ -100,6 +109,7 @@ export const DashboardResponseSchema = toAdminForthJsonSchema(DashboardResponseZ
|
|
|
100
109
|
export const DashboardApiResponseSchema = toAdminForthJsonSchema(DashboardApiResponseZodSchema)
|
|
101
110
|
export const DashboardWidgetDataResponseSchema = toAdminForthJsonSchema(DashboardWidgetDataResponseZodSchema)
|
|
102
111
|
export const SlugRequestSchema = toAdminForthJsonSchema(SlugRequestZodSchema)
|
|
112
|
+
export const SetDashboardConfigRequestSchema = toAdminForthJsonSchema(SetDashboardConfigRequestZodSchema)
|
|
103
113
|
export const GroupIdRequestSchema = toAdminForthJsonSchema(GroupIdRequestZodSchema)
|
|
104
114
|
export const MoveGroupRequestSchema = toAdminForthJsonSchema(MoveGroupRequestZodSchema)
|
|
105
115
|
export const SetGroupConfigRequestSchema = toAdminForthJsonSchema(SetGroupConfigRequestZodSchema)
|