@adminforth/dashboard 1.4.2 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/custom/api/dashboardApi.ts +137 -5
  2. package/custom/model/dashboard.types.ts +32 -22
  3. package/custom/runtime/DashboardRuntime.vue +2 -3
  4. package/custom/skills/adminforth-dashboard/SKILL.md +165 -179
  5. package/custom/widgets/KpiCardWidget.vue +172 -9
  6. package/custom/widgets/chart/ChartWidget.vue +5 -5
  7. package/custom/widgets/registry.ts +4 -4
  8. package/dist/custom/api/dashboardApi.d.ts +46 -2
  9. package/dist/custom/api/dashboardApi.js +90 -5
  10. package/dist/custom/api/dashboardApi.ts +137 -5
  11. package/dist/custom/model/dashboard.types.d.ts +30 -14
  12. package/dist/custom/model/dashboard.types.js +2 -2
  13. package/dist/custom/model/dashboard.types.ts +32 -22
  14. package/dist/custom/queries/useDashboardConfig.d.ts +106 -104
  15. package/dist/custom/queries/useWidgetData.d.ts +106 -104
  16. package/dist/custom/runtime/DashboardRuntime.vue +2 -3
  17. package/dist/custom/skills/adminforth-dashboard/SKILL.md +165 -179
  18. package/dist/custom/widgets/KpiCardWidget.vue +172 -9
  19. package/dist/custom/widgets/chart/ChartWidget.vue +5 -5
  20. package/dist/custom/widgets/registry.js +4 -4
  21. package/dist/custom/widgets/registry.ts +4 -4
  22. package/dist/endpoint/dashboard.d.ts +2 -4
  23. package/dist/endpoint/dashboard.js +1 -21
  24. package/dist/endpoint/groups.d.ts +1 -0
  25. package/dist/endpoint/groups.js +61 -48
  26. package/dist/endpoint/widgets.d.ts +1 -0
  27. package/dist/endpoint/widgets.js +167 -64
  28. package/dist/schema/api.d.ts +11710 -2785
  29. package/dist/schema/api.js +118 -26
  30. package/dist/schema/widget.d.ts +425 -1980
  31. package/dist/schema/widget.js +13 -374
  32. package/dist/schema/widgets/charts.d.ts +1689 -0
  33. package/dist/schema/widgets/charts.js +92 -0
  34. package/dist/schema/widgets/common.d.ts +275 -0
  35. package/dist/schema/widgets/common.js +171 -0
  36. package/dist/schema/widgets/gauge-card.d.ts +172 -0
  37. package/dist/schema/widgets/gauge-card.js +28 -0
  38. package/dist/schema/widgets/kpi-card.d.ts +212 -0
  39. package/dist/schema/widgets/kpi-card.js +43 -0
  40. package/dist/schema/widgets/pivot-table.d.ts +196 -0
  41. package/dist/schema/widgets/pivot-table.js +17 -0
  42. package/dist/schema/widgets/table.d.ts +130 -0
  43. package/dist/schema/widgets/table.js +12 -0
  44. package/dist/services/dashboardConfigService.d.ts +4 -0
  45. package/dist/services/dashboardConfigService.js +46 -0
  46. package/dist/services/widgetDataService.js +96 -2
  47. package/endpoint/dashboard.ts +2 -33
  48. package/endpoint/groups.ts +91 -72
  49. package/endpoint/widgets.ts +260 -87
  50. package/package.json +1 -1
  51. package/schema/api.ts +148 -28
  52. package/schema/widget.ts +43 -425
  53. package/schema/widgets/charts.ts +113 -0
  54. package/schema/widgets/common.ts +194 -0
  55. package/schema/widgets/gauge-card.ts +34 -0
  56. package/schema/widgets/kpi-card.ts +49 -0
  57. package/schema/widgets/pivot-table.ts +24 -0
  58. package/schema/widgets/table.ts +18 -0
  59. package/services/dashboardConfigService.ts +73 -0
  60. package/services/widgetDataService.ts +129 -3
@@ -7,7 +7,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
7
7
  step((generator = generator.apply(thisArg, _arguments || [])).next());
8
8
  });
9
9
  };
10
- import { DashboardApiResponseSchema, SetDashboardConfigRequestSchema, SlugRequestSchema, } from '../schema/api.js';
10
+ import { DashboardApiResponseSchema, SlugRequestSchema, } from '../schema/api.js';
11
11
  export function registerDashboardEndpoints(server, ctx) {
12
12
  server.endpoint({
13
13
  method: 'POST',
@@ -30,24 +30,4 @@ export function registerDashboardEndpoints(server, ctx) {
30
30
  };
31
31
  }),
32
32
  });
33
- server.endpoint({
34
- method: 'POST',
35
- path: '/dashboard/set_dashboard_config',
36
- description: 'Replaces one dashboard configuration, including groups and widgets. Superadmin only.',
37
- request_schema: SetDashboardConfigRequestSchema,
38
- response_schema: DashboardApiResponseSchema,
39
- handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body, adminUser, response }) {
40
- if (!ctx.canEditDashboard(adminUser)) {
41
- response.setStatus(403);
42
- return { error: 'Dashboard edit is not allowed' };
43
- }
44
- const dashboard = yield ctx.getDashboardRecord(body.slug);
45
- if (!dashboard) {
46
- response.setStatus(404);
47
- return { error: 'Dashboard not found' };
48
- }
49
- const config = body.config;
50
- return ctx.persistDashboardConfig(dashboard, config);
51
- }),
52
- });
53
33
  }
@@ -6,6 +6,7 @@ type GroupEndpointsContext = {
6
6
  getDashboardRecord: (slug: string) => Promise<DashboardRecord | null>;
7
7
  parseStoredDashboardConfig: (config: unknown) => DashboardConfig;
8
8
  persistDashboardConfig: (dashboard: DashboardRecord, config: DashboardConfig) => Promise<PersistedDashboardResponse>;
9
+ updateDashboardConfig: (slug: string, mutateConfig: (config: DashboardConfig, dashboard: DashboardRecord) => DashboardConfig | null | Promise<DashboardConfig | null>) => Promise<PersistedDashboardResponse | null>;
9
10
  };
10
11
  export declare function registerGroupEndpoints(server: IHttpServer, ctx: GroupEndpointsContext): void;
11
12
  export {};
@@ -21,19 +21,20 @@ export function registerGroupEndpoints(server, ctx) {
21
21
  response.setStatus(403);
22
22
  return { error: 'Dashboard edit is not allowed' };
23
23
  }
24
- const dashboard = yield ctx.getDashboardRecord(body.slug);
25
- if (!dashboard) {
24
+ const updatedDashboard = yield ctx.updateDashboardConfig(body.slug, (config) => {
25
+ const nextOrder = config.groups.length + 1;
26
+ const group = {
27
+ id: `group_${randomUUID()}`,
28
+ label: 'New group',
29
+ order: nextOrder,
30
+ };
31
+ return Object.assign(Object.assign({}, config), { groups: [...config.groups, group] });
32
+ });
33
+ if (!updatedDashboard) {
26
34
  response.setStatus(404);
27
35
  return { error: 'Dashboard not found' };
28
36
  }
29
- const config = ctx.parseStoredDashboardConfig(dashboard.config);
30
- const nextOrder = config.groups.length + 1;
31
- const group = {
32
- id: `group_${randomUUID()}`,
33
- label: 'New group',
34
- order: nextOrder,
35
- };
36
- return ctx.persistDashboardConfig(dashboard, Object.assign(Object.assign({}, config), { groups: [...config.groups, group] }));
37
+ return updatedDashboard;
37
38
  }),
38
39
  });
39
40
  server.endpoint({
@@ -48,21 +49,27 @@ export function registerGroupEndpoints(server, ctx) {
48
49
  return { error: 'Dashboard edit is not allowed' };
49
50
  }
50
51
  const groupId = body.groupId;
51
- const dashboard = yield ctx.getDashboardRecord(body.slug);
52
- if (!dashboard) {
52
+ let mutationError = null;
53
+ const updatedDashboard = yield ctx.updateDashboardConfig(body.slug, (config) => {
54
+ const group = config.groups.find((item) => item.id === groupId);
55
+ if (!group) {
56
+ mutationError = 'Dashboard group not found';
57
+ return null;
58
+ }
59
+ const nextGroup = Object.assign(Object.assign({}, body.config), { id: group.id, order: group.order });
60
+ return Object.assign(Object.assign({}, config), { groups: config.groups.map((item) => item.id === groupId
61
+ ? nextGroup
62
+ : item) });
63
+ });
64
+ if (!updatedDashboard) {
53
65
  response.setStatus(404);
54
66
  return { error: 'Dashboard not found' };
55
67
  }
56
- const config = ctx.parseStoredDashboardConfig(dashboard.config);
57
- const group = config.groups.find((item) => item.id === groupId);
58
- if (!group) {
68
+ if (mutationError) {
59
69
  response.setStatus(404);
60
- return { error: 'Dashboard group not found' };
70
+ return { error: mutationError };
61
71
  }
62
- const nextGroup = Object.assign(Object.assign({}, body.config), { id: group.id, order: group.order });
63
- return ctx.persistDashboardConfig(dashboard, Object.assign(Object.assign({}, config), { groups: config.groups.map((item) => item.id === groupId
64
- ? nextGroup
65
- : item) }));
72
+ return updatedDashboard;
66
73
  }),
67
74
  });
68
75
  server.endpoint({
@@ -76,32 +83,32 @@ export function registerGroupEndpoints(server, ctx) {
76
83
  response.setStatus(403);
77
84
  return { error: 'Dashboard edit is not allowed' };
78
85
  }
79
- const dashboard = yield ctx.getDashboardRecord(body.slug);
80
- if (!dashboard) {
86
+ let mutationError = null;
87
+ const updatedDashboard = yield ctx.updateDashboardConfig(body.slug, (config) => {
88
+ const sortedGroups = [...config.groups].sort((a, b) => a.order - b.order);
89
+ const currentIndex = sortedGroups.findIndex((group) => group.id === body.groupId);
90
+ if (currentIndex === -1) {
91
+ mutationError = 'Dashboard group not found';
92
+ return null;
93
+ }
94
+ const targetIndex = body.direction === 'up' ? currentIndex - 1 : currentIndex + 1;
95
+ if (targetIndex < 0 || targetIndex >= sortedGroups.length) {
96
+ return null;
97
+ }
98
+ const reorderedGroups = [...sortedGroups];
99
+ const [group] = reorderedGroups.splice(currentIndex, 1);
100
+ reorderedGroups.splice(targetIndex, 0, group);
101
+ return Object.assign(Object.assign({}, config), { groups: reorderedGroups });
102
+ });
103
+ if (!updatedDashboard) {
81
104
  response.setStatus(404);
82
105
  return { error: 'Dashboard not found' };
83
106
  }
84
- const config = ctx.parseStoredDashboardConfig(dashboard.config);
85
- const sortedGroups = [...config.groups].sort((a, b) => a.order - b.order);
86
- const currentIndex = sortedGroups.findIndex((group) => group.id === body.groupId);
87
- if (currentIndex === -1) {
107
+ if (mutationError) {
88
108
  response.setStatus(404);
89
- return { error: 'Dashboard group not found' };
90
- }
91
- const targetIndex = body.direction === 'up' ? currentIndex - 1 : currentIndex + 1;
92
- if (targetIndex < 0 || targetIndex >= sortedGroups.length) {
93
- return {
94
- id: dashboard.id,
95
- slug: dashboard.slug,
96
- label: dashboard.label,
97
- revision: dashboard.revision,
98
- config: ctx.parseStoredDashboardConfig(dashboard.config),
99
- };
109
+ return { error: mutationError };
100
110
  }
101
- const reorderedGroups = [...sortedGroups];
102
- const [group] = reorderedGroups.splice(currentIndex, 1);
103
- reorderedGroups.splice(targetIndex, 0, group);
104
- return ctx.persistDashboardConfig(dashboard, Object.assign(Object.assign({}, config), { groups: reorderedGroups }));
111
+ return updatedDashboard;
105
112
  }),
106
113
  });
107
114
  server.endpoint({
@@ -116,18 +123,24 @@ export function registerGroupEndpoints(server, ctx) {
116
123
  return { error: 'Dashboard edit is not allowed' };
117
124
  }
118
125
  const groupId = body.groupId;
119
- const dashboard = yield ctx.getDashboardRecord(body.slug);
120
- if (!dashboard) {
126
+ let mutationError = null;
127
+ const updatedDashboard = yield ctx.updateDashboardConfig(body.slug, (config) => {
128
+ const nextGroups = config.groups.filter((group) => group.id !== groupId);
129
+ if (nextGroups.length === config.groups.length) {
130
+ mutationError = 'Dashboard group not found';
131
+ return null;
132
+ }
133
+ return Object.assign(Object.assign({}, config), { groups: nextGroups, widgets: config.widgets.filter((widget) => widget.group_id !== groupId) });
134
+ });
135
+ if (!updatedDashboard) {
121
136
  response.setStatus(404);
122
137
  return { error: 'Dashboard not found' };
123
138
  }
124
- const config = ctx.parseStoredDashboardConfig(dashboard.config);
125
- const nextGroups = config.groups.filter((group) => group.id !== groupId);
126
- if (nextGroups.length === config.groups.length) {
139
+ if (mutationError) {
127
140
  response.setStatus(404);
128
- return { error: 'Dashboard group not found' };
141
+ return { error: mutationError };
129
142
  }
130
- return ctx.persistDashboardConfig(dashboard, Object.assign(Object.assign({}, config), { groups: nextGroups, widgets: config.widgets.filter((widget) => widget.group_id !== groupId) }));
143
+ return updatedDashboard;
131
144
  }),
132
145
  });
133
146
  }
@@ -6,6 +6,7 @@ type WidgetEndpointsContext = {
6
6
  getDashboardRecord: (slug: string) => Promise<DashboardRecord | null>;
7
7
  parseStoredDashboardConfig: (config: unknown) => DashboardConfig;
8
8
  persistDashboardConfig: (dashboard: DashboardRecord, config: DashboardConfig) => Promise<PersistedDashboardResponse>;
9
+ updateDashboardConfig: (slug: string, mutateConfig: (config: DashboardConfig, dashboard: DashboardRecord) => DashboardConfig | null | Promise<DashboardConfig | null>) => Promise<PersistedDashboardResponse | null>;
9
10
  getWidgetData: (widget: DashboardWidgetConfig, options?: {
10
11
  pagination?: {
11
12
  page: number;
@@ -8,7 +8,53 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
8
8
  });
9
9
  };
10
10
  import { randomUUID } from 'crypto';
11
- import { DashboardApiResponseSchema, DashboardWidgetDataResponseSchema, GroupIdRequestSchema, MoveWidgetRequestSchema, SetWidgetConfigRequestSchema, WidgetDataRequestSchema, WidgetIdRequestSchema, } from '../schema/api.js';
11
+ import { ConfigureBarChartWidgetRequestSchema, ConfigureFunnelChartWidgetRequestSchema, ConfigureGaugeCardWidgetRequestSchema, ConfigureHistogramChartWidgetRequestSchema, ConfigureKpiCardWidgetRequestSchema, ConfigureLineChartWidgetRequestSchema, ConfigurePieChartWidgetRequestSchema, ConfigurePivotTableWidgetRequestSchema, ConfigureStackedBarChartWidgetRequestSchema, ConfigureTableWidgetRequestSchema, DashboardApiResponseSchema, DashboardWidgetDataResponseSchema, GroupIdRequestSchema, MoveWidgetRequestSchema, SetWidgetConfigRequestSchema, WidgetDataRequestSchema, WidgetIdRequestSchema, } from '../schema/api.js';
12
+ function replaceWidgetConfig(ctx, slug, widgetId, widgetConfig) {
13
+ return __awaiter(this, void 0, void 0, function* () {
14
+ let mutationError = null;
15
+ const updatedDashboard = yield ctx.updateDashboardConfig(slug, (config) => {
16
+ const widget = config.widgets.find((item) => item.id === widgetId);
17
+ if (!widget) {
18
+ mutationError = 'Dashboard widget not found';
19
+ return null;
20
+ }
21
+ const nextWidget = Object.assign(Object.assign({}, widgetConfig), { id: widget.id, group_id: widget.group_id, order: widget.order });
22
+ return Object.assign(Object.assign({}, config), { widgets: config.widgets.map((item) => item.id === widgetId
23
+ ? nextWidget
24
+ : item) });
25
+ });
26
+ return {
27
+ updatedDashboard,
28
+ mutationError,
29
+ };
30
+ });
31
+ }
32
+ function registerConfigureWidgetEndpoint(server, ctx, options) {
33
+ server.endpoint({
34
+ method: 'POST',
35
+ path: options.path,
36
+ description: options.description,
37
+ request_schema: options.requestSchema,
38
+ response_schema: DashboardApiResponseSchema,
39
+ handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body, adminUser, response }) {
40
+ if (!ctx.canEditDashboard(adminUser)) {
41
+ response.setStatus(403);
42
+ return { error: 'Dashboard edit is not allowed' };
43
+ }
44
+ const request = body;
45
+ const { updatedDashboard, mutationError } = yield replaceWidgetConfig(ctx, request.slug, request.widgetId, request.config);
46
+ if (!updatedDashboard) {
47
+ response.setStatus(404);
48
+ return { error: 'Dashboard not found' };
49
+ }
50
+ if (mutationError) {
51
+ response.setStatus(404);
52
+ return { error: mutationError };
53
+ }
54
+ return updatedDashboard;
55
+ }),
56
+ });
57
+ }
12
58
  export function registerWidgetEndpoints(server, ctx) {
13
59
  server.endpoint({
14
60
  method: 'POST',
@@ -21,27 +67,33 @@ export function registerWidgetEndpoints(server, ctx) {
21
67
  response.setStatus(403);
22
68
  return { error: 'Dashboard edit is not allowed' };
23
69
  }
24
- const dashboard = yield ctx.getDashboardRecord(body.slug);
25
- if (!dashboard) {
70
+ let mutationError = null;
71
+ const updatedDashboard = yield ctx.updateDashboardConfig(body.slug, (config) => {
72
+ const group = config.groups.find((item) => item.id === body.groupId);
73
+ if (!group) {
74
+ mutationError = 'Dashboard group not found';
75
+ return null;
76
+ }
77
+ const nextOrder = config.widgets.filter((item) => item.group_id === body.groupId).length + 1;
78
+ const widget = {
79
+ id: `widget_${randomUUID()}`,
80
+ group_id: body.groupId,
81
+ label: 'New widget',
82
+ size: 'small',
83
+ order: nextOrder,
84
+ target: 'empty',
85
+ };
86
+ return Object.assign(Object.assign({}, config), { widgets: [...config.widgets, widget] });
87
+ });
88
+ if (!updatedDashboard) {
26
89
  response.setStatus(404);
27
90
  return { error: 'Dashboard not found' };
28
91
  }
29
- const config = ctx.parseStoredDashboardConfig(dashboard.config);
30
- const group = config.groups.find((item) => item.id === body.groupId);
31
- if (!group) {
92
+ if (mutationError) {
32
93
  response.setStatus(404);
33
- return { error: 'Dashboard group not found' };
34
- }
35
- const nextOrder = config.widgets.filter((item) => item.group_id === body.groupId).length + 1;
36
- const widget = {
37
- id: `widget_${randomUUID()}`,
38
- group_id: body.groupId,
39
- label: 'New widget',
40
- size: 'small',
41
- order: nextOrder,
42
- target: 'empty',
43
- };
44
- return ctx.persistDashboardConfig(dashboard, Object.assign(Object.assign({}, config), { widgets: [...config.widgets, widget] }));
94
+ return { error: mutationError };
95
+ }
96
+ return updatedDashboard;
45
97
  }),
46
98
  });
47
99
  server.endpoint({
@@ -55,39 +107,39 @@ export function registerWidgetEndpoints(server, ctx) {
55
107
  response.setStatus(403);
56
108
  return { error: 'Dashboard edit is not allowed' };
57
109
  }
58
- const dashboard = yield ctx.getDashboardRecord(body.slug);
59
- if (!dashboard) {
110
+ let mutationError = null;
111
+ const updatedDashboard = yield ctx.updateDashboardConfig(body.slug, (config) => {
112
+ const widget = config.widgets.find((item) => item.id === body.widgetId);
113
+ if (!widget) {
114
+ mutationError = 'Dashboard widget not found';
115
+ return null;
116
+ }
117
+ const sortedWidgets = config.widgets
118
+ .filter((item) => item.group_id === widget.group_id)
119
+ .sort((a, b) => a.order - b.order);
120
+ const currentIndex = sortedWidgets.findIndex((item) => item.id === body.widgetId);
121
+ const targetIndex = body.direction === 'up' ? currentIndex - 1 : currentIndex + 1;
122
+ if (targetIndex < 0 || targetIndex >= sortedWidgets.length) {
123
+ return null;
124
+ }
125
+ const reorderedWidgets = [...sortedWidgets];
126
+ const [movedWidget] = reorderedWidgets.splice(currentIndex, 1);
127
+ reorderedWidgets.splice(targetIndex, 0, movedWidget);
128
+ const reorderedWidgetIds = new Map(reorderedWidgets.map((item, index) => [item.id, index + 1]));
129
+ return Object.assign(Object.assign({}, config), { widgets: config.widgets.map((item) => {
130
+ var _a;
131
+ return (Object.assign(Object.assign({}, item), { order: (_a = reorderedWidgetIds.get(item.id)) !== null && _a !== void 0 ? _a : item.order }));
132
+ }) });
133
+ });
134
+ if (!updatedDashboard) {
60
135
  response.setStatus(404);
61
136
  return { error: 'Dashboard not found' };
62
137
  }
63
- const config = ctx.parseStoredDashboardConfig(dashboard.config);
64
- const widget = config.widgets.find((item) => item.id === body.widgetId);
65
- if (!widget) {
138
+ if (mutationError) {
66
139
  response.setStatus(404);
67
- return { error: 'Dashboard widget not found' };
68
- }
69
- const sortedWidgets = config.widgets
70
- .filter((item) => item.group_id === widget.group_id)
71
- .sort((a, b) => a.order - b.order);
72
- const currentIndex = sortedWidgets.findIndex((item) => item.id === body.widgetId);
73
- const targetIndex = body.direction === 'up' ? currentIndex - 1 : currentIndex + 1;
74
- if (targetIndex < 0 || targetIndex >= sortedWidgets.length) {
75
- return {
76
- id: dashboard.id,
77
- slug: dashboard.slug,
78
- label: dashboard.label,
79
- revision: dashboard.revision,
80
- config: ctx.parseStoredDashboardConfig(dashboard.config),
81
- };
140
+ return { error: mutationError };
82
141
  }
83
- const reorderedWidgets = [...sortedWidgets];
84
- const [movedWidget] = reorderedWidgets.splice(currentIndex, 1);
85
- reorderedWidgets.splice(targetIndex, 0, movedWidget);
86
- const reorderedWidgetIds = new Map(reorderedWidgets.map((item, index) => [item.id, index + 1]));
87
- return ctx.persistDashboardConfig(dashboard, Object.assign(Object.assign({}, config), { widgets: config.widgets.map((item) => {
88
- var _a;
89
- return (Object.assign(Object.assign({}, item), { order: (_a = reorderedWidgetIds.get(item.id)) !== null && _a !== void 0 ? _a : item.order }));
90
- }) }));
142
+ return updatedDashboard;
91
143
  }),
92
144
  });
93
145
  server.endpoint({
@@ -101,18 +153,24 @@ export function registerWidgetEndpoints(server, ctx) {
101
153
  response.setStatus(403);
102
154
  return { error: 'Dashboard edit is not allowed' };
103
155
  }
104
- const dashboard = yield ctx.getDashboardRecord(body.slug);
105
- if (!dashboard) {
156
+ let mutationError = null;
157
+ const updatedDashboard = yield ctx.updateDashboardConfig(body.slug, (config) => {
158
+ const nextWidgets = config.widgets.filter((item) => item.id !== body.widgetId);
159
+ if (nextWidgets.length === config.widgets.length) {
160
+ mutationError = 'Dashboard widget not found';
161
+ return null;
162
+ }
163
+ return Object.assign(Object.assign({}, config), { widgets: nextWidgets });
164
+ });
165
+ if (!updatedDashboard) {
106
166
  response.setStatus(404);
107
167
  return { error: 'Dashboard not found' };
108
168
  }
109
- const config = ctx.parseStoredDashboardConfig(dashboard.config);
110
- const nextWidgets = config.widgets.filter((item) => item.id !== body.widgetId);
111
- if (nextWidgets.length === config.widgets.length) {
169
+ if (mutationError) {
112
170
  response.setStatus(404);
113
- return { error: 'Dashboard widget not found' };
171
+ return { error: mutationError };
114
172
  }
115
- return ctx.persistDashboardConfig(dashboard, Object.assign(Object.assign({}, config), { widgets: nextWidgets }));
173
+ return updatedDashboard;
116
174
  }),
117
175
  });
118
176
  server.endpoint({
@@ -126,24 +184,69 @@ export function registerWidgetEndpoints(server, ctx) {
126
184
  response.setStatus(403);
127
185
  return { error: 'Dashboard edit is not allowed' };
128
186
  }
129
- const dashboard = yield ctx.getDashboardRecord(body.slug);
130
- if (!dashboard) {
187
+ const request = body;
188
+ const { updatedDashboard, mutationError } = yield replaceWidgetConfig(ctx, request.slug, request.widgetId, request.config);
189
+ if (!updatedDashboard) {
131
190
  response.setStatus(404);
132
191
  return { error: 'Dashboard not found' };
133
192
  }
134
- const config = ctx.parseStoredDashboardConfig(dashboard.config);
135
- const widget = config.widgets.find((item) => item.id === body.widgetId);
136
- if (!widget) {
193
+ if (mutationError) {
137
194
  response.setStatus(404);
138
- return { error: 'Dashboard widget not found' };
195
+ return { error: mutationError };
139
196
  }
140
- const typedWidgetConfig = body.config;
141
- const nextWidget = Object.assign(Object.assign({}, typedWidgetConfig), { id: widget.id, group_id: widget.group_id, order: widget.order });
142
- return ctx.persistDashboardConfig(dashboard, Object.assign(Object.assign({}, config), { widgets: config.widgets.map((item) => item.id === body.widgetId
143
- ? nextWidget
144
- : item) }));
197
+ return updatedDashboard;
145
198
  }),
146
199
  });
200
+ registerConfigureWidgetEndpoint(server, ctx, {
201
+ path: '/dashboard/configure_table_widget',
202
+ description: 'Configures an existing dashboard widget as a table. Superadmin only.',
203
+ requestSchema: ConfigureTableWidgetRequestSchema,
204
+ });
205
+ registerConfigureWidgetEndpoint(server, ctx, {
206
+ path: '/dashboard/configure_kpi_card_widget',
207
+ description: 'Configures an existing dashboard widget as a KPI card. Superadmin only.',
208
+ requestSchema: ConfigureKpiCardWidgetRequestSchema,
209
+ });
210
+ registerConfigureWidgetEndpoint(server, ctx, {
211
+ path: '/dashboard/configure_gauge_card_widget',
212
+ description: 'Configures an existing dashboard widget as a gauge card. Superadmin only.',
213
+ requestSchema: ConfigureGaugeCardWidgetRequestSchema,
214
+ });
215
+ registerConfigureWidgetEndpoint(server, ctx, {
216
+ path: '/dashboard/configure_pivot_table_widget',
217
+ description: 'Configures an existing dashboard widget as a pivot table. Superadmin only.',
218
+ requestSchema: ConfigurePivotTableWidgetRequestSchema,
219
+ });
220
+ registerConfigureWidgetEndpoint(server, ctx, {
221
+ path: '/dashboard/configure_line_chart_widget',
222
+ description: 'Configures an existing dashboard widget as a line chart. Superadmin only.',
223
+ requestSchema: ConfigureLineChartWidgetRequestSchema,
224
+ });
225
+ registerConfigureWidgetEndpoint(server, ctx, {
226
+ path: '/dashboard/configure_bar_chart_widget',
227
+ description: 'Configures an existing dashboard widget as a bar chart. Superadmin only.',
228
+ requestSchema: ConfigureBarChartWidgetRequestSchema,
229
+ });
230
+ registerConfigureWidgetEndpoint(server, ctx, {
231
+ path: '/dashboard/configure_stacked_bar_chart_widget',
232
+ description: 'Configures an existing dashboard widget as a stacked bar chart. Superadmin only.',
233
+ requestSchema: ConfigureStackedBarChartWidgetRequestSchema,
234
+ });
235
+ registerConfigureWidgetEndpoint(server, ctx, {
236
+ path: '/dashboard/configure_pie_chart_widget',
237
+ description: 'Configures an existing dashboard widget as a pie chart. Superadmin only.',
238
+ requestSchema: ConfigurePieChartWidgetRequestSchema,
239
+ });
240
+ registerConfigureWidgetEndpoint(server, ctx, {
241
+ path: '/dashboard/configure_histogram_chart_widget',
242
+ description: 'Configures an existing dashboard widget as a histogram chart. Superadmin only.',
243
+ requestSchema: ConfigureHistogramChartWidgetRequestSchema,
244
+ });
245
+ registerConfigureWidgetEndpoint(server, ctx, {
246
+ path: '/dashboard/configure_funnel_chart_widget',
247
+ description: 'Configures an existing dashboard widget as a funnel chart. Superadmin only.',
248
+ requestSchema: ConfigureFunnelChartWidgetRequestSchema,
249
+ });
147
250
  server.endpoint({
148
251
  method: 'POST',
149
252
  path: '/dashboard/get_dashboard_widget_data',