@adminforth/dashboard 1.4.0 → 1.4.2
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 +23 -4
- package/custom/api/dashboardApi.ts +6 -9
- package/custom/model/dashboard.types.ts +60 -275
- package/custom/model/dashboardTopics.ts +5 -0
- package/custom/package.json +1 -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 +2 -2
- 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 +4 -15
- 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 -8
- package/dist/custom/api/dashboardApi.js +2 -6
- package/dist/custom/api/dashboardApi.ts +6 -9
- package/dist/custom/composables/useElementSize.js +7 -10
- package/dist/custom/model/dashboard.types.d.ts +38 -32
- package/dist/custom/model/dashboard.types.js +4 -161
- package/dist/custom/model/dashboard.types.ts +60 -275
- package/dist/custom/model/dashboardTopics.d.ts +2 -0
- package/dist/custom/model/dashboardTopics.js +4 -0
- package/dist/custom/model/dashboardTopics.ts +5 -0
- package/dist/custom/package.json +1 -0
- package/dist/custom/queries/useDashboardConfig.d.ts +96 -96
- package/dist/custom/queries/useDashboardConfig.js +9 -12
- package/dist/custom/queries/useWidgetData.d.ts +96 -96
- package/dist/custom/queries/useWidgetData.js +9 -12
- 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 +2 -2
- 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 +4 -15
- 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 +1 -25
- package/dist/custom/widgets/chart/chart.types.ts +0 -28
- package/dist/custom/widgets/chart/chart.utils.js +6 -14
- package/dist/custom/widgets/registry.js +14 -22
- package/dist/endpoint/dashboard.d.ts +2 -3
- package/dist/endpoint/dashboard.js +12 -32
- package/dist/endpoint/groups.d.ts +2 -21
- package/dist/endpoint/groups.js +18 -16
- package/dist/endpoint/widgets.d.ts +0 -3
- package/dist/endpoint/widgets.js +27 -74
- package/dist/index.js +1 -3
- package/dist/schema/api.d.ts +2090 -511
- package/dist/schema/api.js +18 -15
- package/dist/schema/widget.d.ts +1003 -250
- package/dist/schema/widget.js +102 -46
- package/dist/services/dashboardConfigService.d.ts +0 -10
- package/dist/services/dashboardConfigService.js +6 -21
- package/dist/services/widgetDataService.js +226 -196
- package/endpoint/dashboard.ts +13 -46
- package/endpoint/groups.ts +25 -42
- package/endpoint/widgets.ts +36 -95
- package/index.ts +0 -3
- package/package.json +3 -3
- package/schema/api.ts +19 -15
- package/schema/widget.ts +113 -52
- package/services/dashboardConfigService.ts +6 -25
- package/services/widgetDataService.ts +304 -229
- 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, DashboardVariables, 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,10 +25,6 @@ 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
30
|
options?: {
|
|
@@ -38,39 +34,6 @@ type WidgetEndpointsContext = {
|
|
|
38
34
|
) => Promise<unknown>;
|
|
39
35
|
};
|
|
40
36
|
|
|
41
|
-
function formatWidgetConfigValidationErrors(error: { issues: { path: PropertyKey[], message: string }[] }) {
|
|
42
|
-
return error.issues.map((issue) => ({
|
|
43
|
-
field: issue.path.length ? formatWidgetConfigFieldPath(issue.path.map(String).join('.')) : 'config',
|
|
44
|
-
message: issue.message,
|
|
45
|
-
}));
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
function formatWidgetConfigApiValidationErrors(errors: DashboardWidgetConfigValidationError[]) {
|
|
49
|
-
return errors.map((error) => ({
|
|
50
|
-
...error,
|
|
51
|
-
field: formatWidgetConfigFieldPath(error.field),
|
|
52
|
-
}));
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
function formatWidgetConfigFieldPath(field: string) {
|
|
56
|
-
const fieldAliases = new Map([
|
|
57
|
-
['minWidth', 'min_width'],
|
|
58
|
-
['maxWidth', 'max_width'],
|
|
59
|
-
['groupBy', 'group_by'],
|
|
60
|
-
['orderBy', 'order_by'],
|
|
61
|
-
['pageSize', 'page_size'],
|
|
62
|
-
['timeSeries', 'time_series'],
|
|
63
|
-
['valueField', 'value_field'],
|
|
64
|
-
['targetValue', 'target_value'],
|
|
65
|
-
['targetField', 'target_field'],
|
|
66
|
-
]);
|
|
67
|
-
|
|
68
|
-
return field
|
|
69
|
-
.split('.')
|
|
70
|
-
.map((segment) => fieldAliases.get(segment) ?? segment)
|
|
71
|
-
.join('.');
|
|
72
|
-
}
|
|
73
|
-
|
|
74
37
|
export function registerWidgetEndpoints(
|
|
75
38
|
server: IHttpServer,
|
|
76
39
|
ctx: WidgetEndpointsContext,
|
|
@@ -87,9 +50,7 @@ export function registerWidgetEndpoints(
|
|
|
87
50
|
return { error: 'Dashboard edit is not allowed' };
|
|
88
51
|
}
|
|
89
52
|
|
|
90
|
-
const
|
|
91
|
-
const groupId = String(body?.groupId || '');
|
|
92
|
-
const dashboard = await ctx.getDashboardRecord(slug);
|
|
53
|
+
const dashboard = await ctx.getDashboardRecord(body.slug);
|
|
93
54
|
|
|
94
55
|
if (!dashboard) {
|
|
95
56
|
response.setStatus(404);
|
|
@@ -97,17 +58,17 @@ export function registerWidgetEndpoints(
|
|
|
97
58
|
}
|
|
98
59
|
|
|
99
60
|
const config = ctx.parseStoredDashboardConfig(dashboard.config);
|
|
100
|
-
const group = config.groups.find((item) => item.id === groupId);
|
|
61
|
+
const group = config.groups.find((item) => item.id === body.groupId);
|
|
101
62
|
|
|
102
63
|
if (!group) {
|
|
103
64
|
response.setStatus(404);
|
|
104
65
|
return { error: 'Dashboard group not found' };
|
|
105
66
|
}
|
|
106
67
|
|
|
107
|
-
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;
|
|
108
69
|
const widget: DashboardWidgetConfig = {
|
|
109
70
|
id: `widget_${randomUUID()}`,
|
|
110
|
-
group_id: groupId,
|
|
71
|
+
group_id: body.groupId,
|
|
111
72
|
label: 'New widget',
|
|
112
73
|
size: 'small',
|
|
113
74
|
order: nextOrder,
|
|
@@ -133,10 +94,7 @@ export function registerWidgetEndpoints(
|
|
|
133
94
|
return { error: 'Dashboard edit is not allowed' };
|
|
134
95
|
}
|
|
135
96
|
|
|
136
|
-
const
|
|
137
|
-
const widgetId = String(body?.widgetId || '');
|
|
138
|
-
const direction = body?.direction === 'down' ? 'down' : 'up';
|
|
139
|
-
const dashboard = await ctx.getDashboardRecord(slug);
|
|
97
|
+
const dashboard = await ctx.getDashboardRecord(body.slug);
|
|
140
98
|
|
|
141
99
|
if (!dashboard) {
|
|
142
100
|
response.setStatus(404);
|
|
@@ -144,7 +102,7 @@ export function registerWidgetEndpoints(
|
|
|
144
102
|
}
|
|
145
103
|
|
|
146
104
|
const config = ctx.parseStoredDashboardConfig(dashboard.config);
|
|
147
|
-
const widget = config.widgets.find((item) => item.id === widgetId);
|
|
105
|
+
const widget = config.widgets.find((item) => item.id === body.widgetId);
|
|
148
106
|
|
|
149
107
|
if (!widget) {
|
|
150
108
|
response.setStatus(404);
|
|
@@ -154,11 +112,17 @@ export function registerWidgetEndpoints(
|
|
|
154
112
|
const sortedWidgets = config.widgets
|
|
155
113
|
.filter((item) => item.group_id === widget.group_id)
|
|
156
114
|
.sort((a, b) => a.order - b.order);
|
|
157
|
-
const currentIndex = sortedWidgets.findIndex((item) => item.id === widgetId);
|
|
158
|
-
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;
|
|
159
117
|
|
|
160
118
|
if (targetIndex < 0 || targetIndex >= sortedWidgets.length) {
|
|
161
|
-
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
|
+
};
|
|
162
126
|
}
|
|
163
127
|
|
|
164
128
|
const reorderedWidgets = [...sortedWidgets];
|
|
@@ -188,9 +152,7 @@ export function registerWidgetEndpoints(
|
|
|
188
152
|
return { error: 'Dashboard edit is not allowed' };
|
|
189
153
|
}
|
|
190
154
|
|
|
191
|
-
const
|
|
192
|
-
const widgetId = String(body?.widgetId || '');
|
|
193
|
-
const dashboard = await ctx.getDashboardRecord(slug);
|
|
155
|
+
const dashboard = await ctx.getDashboardRecord(body.slug);
|
|
194
156
|
|
|
195
157
|
if (!dashboard) {
|
|
196
158
|
response.setStatus(404);
|
|
@@ -198,7 +160,7 @@ export function registerWidgetEndpoints(
|
|
|
198
160
|
}
|
|
199
161
|
|
|
200
162
|
const config = ctx.parseStoredDashboardConfig(dashboard.config);
|
|
201
|
-
const nextWidgets = config.widgets.filter((item) => item.id !== widgetId);
|
|
163
|
+
const nextWidgets = config.widgets.filter((item) => item.id !== body.widgetId);
|
|
202
164
|
|
|
203
165
|
if (nextWidgets.length === config.widgets.length) {
|
|
204
166
|
response.setStatus(404);
|
|
@@ -224,9 +186,7 @@ export function registerWidgetEndpoints(
|
|
|
224
186
|
return { error: 'Dashboard edit is not allowed' };
|
|
225
187
|
}
|
|
226
188
|
|
|
227
|
-
const
|
|
228
|
-
const widgetId = String(body?.widgetId || '');
|
|
229
|
-
const dashboard = await ctx.getDashboardRecord(slug);
|
|
189
|
+
const dashboard = await ctx.getDashboardRecord(body.slug);
|
|
230
190
|
|
|
231
191
|
if (!dashboard) {
|
|
232
192
|
response.setStatus(404);
|
|
@@ -234,43 +194,26 @@ export function registerWidgetEndpoints(
|
|
|
234
194
|
}
|
|
235
195
|
|
|
236
196
|
const config = ctx.parseStoredDashboardConfig(dashboard.config);
|
|
237
|
-
const widget = config.widgets.find((item) => item.id === widgetId);
|
|
197
|
+
const widget = config.widgets.find((item) => item.id === body.widgetId);
|
|
238
198
|
|
|
239
199
|
if (!widget) {
|
|
240
200
|
response.setStatus(404);
|
|
241
201
|
return { error: 'Dashboard widget not found' };
|
|
242
202
|
}
|
|
243
203
|
|
|
244
|
-
const
|
|
204
|
+
const typedWidgetConfig = body.config as EditableDashboardWidgetConfig;
|
|
245
205
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
const typedWidgetConfig = parsedWidgetConfig.data as DashboardWidgetConfig;
|
|
255
|
-
const apiValidationErrors = ctx.validateDashboardWidgetApiConfig(typedWidgetConfig);
|
|
256
|
-
|
|
257
|
-
if (apiValidationErrors.length) {
|
|
258
|
-
response.setStatus(422);
|
|
259
|
-
return {
|
|
260
|
-
error: 'Invalid widget config',
|
|
261
|
-
validationErrors: formatWidgetConfigApiValidationErrors(apiValidationErrors),
|
|
262
|
-
};
|
|
263
|
-
}
|
|
206
|
+
const nextWidget: DashboardWidgetConfig = {
|
|
207
|
+
...typedWidgetConfig,
|
|
208
|
+
id: widget.id,
|
|
209
|
+
group_id: widget.group_id,
|
|
210
|
+
order: widget.order,
|
|
211
|
+
};
|
|
264
212
|
|
|
265
213
|
return ctx.persistDashboardConfig(dashboard, {
|
|
266
214
|
...config,
|
|
267
|
-
widgets: config.widgets.map((item) => item.id === widgetId
|
|
268
|
-
?
|
|
269
|
-
...typedWidgetConfig,
|
|
270
|
-
id: widget.id,
|
|
271
|
-
group_id: widget.group_id,
|
|
272
|
-
order: widget.order,
|
|
273
|
-
}
|
|
215
|
+
widgets: config.widgets.map((item) => item.id === body.widgetId
|
|
216
|
+
? nextWidget
|
|
274
217
|
: item),
|
|
275
218
|
});
|
|
276
219
|
},
|
|
@@ -283,9 +226,7 @@ export function registerWidgetEndpoints(
|
|
|
283
226
|
request_schema: WidgetDataRequestSchema,
|
|
284
227
|
response_schema: DashboardWidgetDataResponseSchema,
|
|
285
228
|
handler: async ({ body, response }) => {
|
|
286
|
-
const
|
|
287
|
-
const widgetId = String(body?.widgetId || '');
|
|
288
|
-
const dashboard = await ctx.getDashboardRecord(slug);
|
|
229
|
+
const dashboard = await ctx.getDashboardRecord(body.slug);
|
|
289
230
|
|
|
290
231
|
if (!dashboard) {
|
|
291
232
|
response.setStatus(404);
|
|
@@ -293,7 +234,7 @@ export function registerWidgetEndpoints(
|
|
|
293
234
|
}
|
|
294
235
|
|
|
295
236
|
const config = ctx.parseStoredDashboardConfig(dashboard.config);
|
|
296
|
-
const widget = config.widgets.find((item) => item.id === widgetId);
|
|
237
|
+
const widget = config.widgets.find((item) => item.id === body.widgetId);
|
|
297
238
|
|
|
298
239
|
if (!widget) {
|
|
299
240
|
response.setStatus(404);
|
|
@@ -303,7 +244,7 @@ export function registerWidgetEndpoints(
|
|
|
303
244
|
return {
|
|
304
245
|
widget,
|
|
305
246
|
data: await ctx.getWidgetData(widget, {
|
|
306
|
-
pagination: body
|
|
247
|
+
pagination: body.pagination,
|
|
307
248
|
variables: widget.variables,
|
|
308
249
|
}),
|
|
309
250
|
};
|
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.4.
|
|
3
|
+
"version": "1.4.2",
|
|
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,38 +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
51
|
}).strict()
|
|
52
52
|
|
|
53
53
|
export const SetDashboardConfigRequestZodSchema = z.object({
|
|
54
|
-
slug: z.string()
|
|
55
|
-
config:
|
|
54
|
+
slug: z.string(),
|
|
55
|
+
config: DashboardConfigZodSchema,
|
|
56
56
|
}).strict()
|
|
57
57
|
|
|
58
58
|
export const GroupIdRequestZodSchema = z.object({
|
|
59
|
-
slug: z.string()
|
|
59
|
+
slug: z.string(),
|
|
60
60
|
groupId: z.string(),
|
|
61
61
|
}).strict()
|
|
62
62
|
|
|
63
63
|
export const MoveGroupRequestZodSchema = z.object({
|
|
64
|
-
slug: z.string()
|
|
64
|
+
slug: z.string(),
|
|
65
65
|
groupId: z.string(),
|
|
66
66
|
direction: z.enum(['up', 'down']),
|
|
67
67
|
}).strict()
|
|
68
68
|
|
|
69
|
+
export const EditableDashboardGroupConfigZodSchema = z.object({
|
|
70
|
+
label: z.string(),
|
|
71
|
+
}).strict()
|
|
72
|
+
|
|
69
73
|
export const SetGroupConfigRequestZodSchema = z.object({
|
|
70
|
-
slug: z.string()
|
|
74
|
+
slug: z.string(),
|
|
71
75
|
groupId: z.string(),
|
|
72
|
-
config:
|
|
76
|
+
config: EditableDashboardGroupConfigZodSchema,
|
|
73
77
|
}).strict()
|
|
74
78
|
|
|
75
79
|
export const WidgetIdRequestZodSchema = z.object({
|
|
76
|
-
slug: z.string()
|
|
80
|
+
slug: z.string(),
|
|
77
81
|
widgetId: z.string(),
|
|
78
82
|
}).strict()
|
|
79
83
|
|
|
80
84
|
export const WidgetDataRequestZodSchema = z.object({
|
|
81
|
-
slug: z.string()
|
|
85
|
+
slug: z.string(),
|
|
82
86
|
widgetId: z.string(),
|
|
83
87
|
pagination: z.object({
|
|
84
88
|
page: z.number().int().positive(),
|
|
@@ -87,15 +91,15 @@ export const WidgetDataRequestZodSchema = z.object({
|
|
|
87
91
|
}).strict()
|
|
88
92
|
|
|
89
93
|
export const MoveWidgetRequestZodSchema = z.object({
|
|
90
|
-
slug: z.string()
|
|
94
|
+
slug: z.string(),
|
|
91
95
|
widgetId: z.string(),
|
|
92
96
|
direction: z.enum(['up', 'down']),
|
|
93
97
|
}).strict()
|
|
94
98
|
|
|
95
99
|
export const SetWidgetConfigRequestZodSchema = z.object({
|
|
96
|
-
slug: z.string()
|
|
100
|
+
slug: z.string(),
|
|
97
101
|
widgetId: z.string(),
|
|
98
|
-
config:
|
|
102
|
+
config: EditableDashboardWidgetConfigSchema,
|
|
99
103
|
}).strict()
|
|
100
104
|
|
|
101
105
|
export const DashboardErrorResponseSchema = toAdminForthJsonSchema(DashboardErrorResponseZodSchema)
|