@adminforth/dashboard 1.4.2 → 1.5.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.
@@ -41,10 +41,6 @@ export const DashboardWidgetDataResponseZodSchema = z.union([
41
41
  export const SlugRequestZodSchema = z.object({
42
42
  slug: z.string(),
43
43
  }).strict();
44
- export const SetDashboardConfigRequestZodSchema = z.object({
45
- slug: z.string(),
46
- config: DashboardConfigZodSchema,
47
- }).strict();
48
44
  export const GroupIdRequestZodSchema = z.object({
49
45
  slug: z.string(),
50
46
  groupId: z.string(),
@@ -91,7 +87,6 @@ export const DashboardResponseSchema = toAdminForthJsonSchema(DashboardResponseZ
91
87
  export const DashboardApiResponseSchema = toAdminForthJsonSchema(DashboardApiResponseZodSchema);
92
88
  export const DashboardWidgetDataResponseSchema = toAdminForthJsonSchema(DashboardWidgetDataResponseZodSchema);
93
89
  export const SlugRequestSchema = toAdminForthJsonSchema(SlugRequestZodSchema);
94
- export const SetDashboardConfigRequestSchema = toAdminForthJsonSchema(SetDashboardConfigRequestZodSchema);
95
90
  export const GroupIdRequestSchema = toAdminForthJsonSchema(GroupIdRequestZodSchema);
96
91
  export const MoveGroupRequestSchema = toAdminForthJsonSchema(MoveGroupRequestZodSchema);
97
92
  export const SetGroupConfigRequestSchema = toAdminForthJsonSchema(SetGroupConfigRequestZodSchema);
@@ -15,11 +15,15 @@ export type PersistedDashboardResponse = {
15
15
  revision: number;
16
16
  config: DashboardConfig;
17
17
  };
18
+ type DashboardConfigMutator = (config: DashboardConfig, dashboard: DashboardRecord) => DashboardConfig | null | Promise<DashboardConfig | null>;
18
19
  export declare function getDashboardRecord(adminforth: IAdminForth, dashboardConfigsResourceId: string, slug: string): Promise<DashboardRecord | null>;
19
20
  export declare function persistDashboardConfig(adminforth: IAdminForth, dashboardConfigsResourceId: string, dashboard: DashboardRecord, config: DashboardConfig): Promise<PersistedDashboardResponse>;
21
+ export declare function updateDashboardConfig(adminforth: IAdminForth, dashboardConfigsResourceId: string, slug: string, mutateConfig: DashboardConfigMutator): Promise<PersistedDashboardResponse | null>;
20
22
  export type DashboardConfigService = {
21
23
  getDashboardRecord: (slug: string) => Promise<DashboardRecord | null>;
22
24
  parseStoredDashboardConfig: typeof parseStoredDashboardConfig;
23
25
  persistDashboardConfig: (dashboard: DashboardRecord, config: DashboardConfig) => Promise<PersistedDashboardResponse>;
26
+ updateDashboardConfig: (slug: string, mutateConfig: DashboardConfigMutator) => Promise<PersistedDashboardResponse | null>;
24
27
  };
25
28
  export declare function createDashboardConfigService(adminforth: IAdminForth, dashboardConfigsResourceId: string): DashboardConfigService;
29
+ export {};
@@ -14,6 +14,29 @@ export function parseStoredDashboardConfig(config) {
14
14
  const parsedConfig = typeof config === 'string' ? JSON.parse(config) : config;
15
15
  return DashboardConfigZodSchema.parse(parsedConfig);
16
16
  }
17
+ const dashboardConfigUpdateQueues = new Map();
18
+ function runDashboardConfigUpdateQueued(dashboardSlug, callback) {
19
+ return __awaiter(this, void 0, void 0, function* () {
20
+ var _a;
21
+ const previousUpdate = (_a = dashboardConfigUpdateQueues.get(dashboardSlug)) !== null && _a !== void 0 ? _a : Promise.resolve();
22
+ let releaseCurrentUpdate;
23
+ const currentUpdate = new Promise((resolve) => {
24
+ releaseCurrentUpdate = resolve;
25
+ });
26
+ const queuedUpdate = previousUpdate.then(() => currentUpdate, () => currentUpdate);
27
+ dashboardConfigUpdateQueues.set(dashboardSlug, queuedUpdate);
28
+ yield previousUpdate.catch(() => undefined);
29
+ try {
30
+ return yield callback();
31
+ }
32
+ finally {
33
+ releaseCurrentUpdate();
34
+ if (dashboardConfigUpdateQueues.get(dashboardSlug) === queuedUpdate) {
35
+ dashboardConfigUpdateQueues.delete(dashboardSlug);
36
+ }
37
+ }
38
+ });
39
+ }
17
40
  function normalizeDashboardOrder(config) {
18
41
  var _a;
19
42
  const widgetsByGroupId = new Map();
@@ -55,10 +78,33 @@ export function persistDashboardConfig(adminforth, dashboardConfigsResourceId, d
55
78
  };
56
79
  });
57
80
  }
81
+ export function updateDashboardConfig(adminforth, dashboardConfigsResourceId, slug, mutateConfig) {
82
+ return __awaiter(this, void 0, void 0, function* () {
83
+ return runDashboardConfigUpdateQueued(slug, () => __awaiter(this, void 0, void 0, function* () {
84
+ const dashboard = yield getDashboardRecord(adminforth, dashboardConfigsResourceId, slug);
85
+ if (!dashboard) {
86
+ return null;
87
+ }
88
+ const config = parseStoredDashboardConfig(dashboard.config);
89
+ const nextConfig = yield mutateConfig(config, dashboard);
90
+ if (nextConfig === null) {
91
+ return {
92
+ id: dashboard.id,
93
+ slug: dashboard.slug,
94
+ label: dashboard.label,
95
+ revision: dashboard.revision,
96
+ config,
97
+ };
98
+ }
99
+ return persistDashboardConfig(adminforth, dashboardConfigsResourceId, dashboard, nextConfig);
100
+ }));
101
+ });
102
+ }
58
103
  export function createDashboardConfigService(adminforth, dashboardConfigsResourceId) {
59
104
  return {
60
105
  getDashboardRecord: (slug) => getDashboardRecord(adminforth, dashboardConfigsResourceId, slug),
61
106
  parseStoredDashboardConfig,
62
107
  persistDashboardConfig: (dashboard, config) => persistDashboardConfig(adminforth, dashboardConfigsResourceId, dashboard, config),
108
+ updateDashboardConfig: (slug, mutateConfig) => updateDashboardConfig(adminforth, dashboardConfigsResourceId, slug, mutateConfig),
63
109
  };
64
110
  }
@@ -1,20 +1,14 @@
1
- import type { AdminUser, IHttpServer } from 'adminforth';
1
+ import type { IHttpServer } from 'adminforth';
2
2
  import type { DashboardConfig } from '../custom/model/dashboard.types.js';
3
3
  import {
4
4
  DashboardApiResponseSchema,
5
- SetDashboardConfigRequestSchema,
6
5
  SlugRequestSchema,
7
6
  } from '../schema/api.js';
8
- import type { DashboardRecord, PersistedDashboardResponse } from '../services/dashboardConfigService.js';
7
+ import type { DashboardRecord } from '../services/dashboardConfigService.js';
9
8
 
10
9
  type DashboardEndpointsContext = {
11
- canEditDashboard: (adminUser: AdminUser) => boolean;
12
10
  getDashboardRecord: (slug: string) => Promise<DashboardRecord | null>;
13
11
  parseStoredDashboardConfig: (config: unknown) => DashboardConfig;
14
- persistDashboardConfig: (
15
- dashboard: DashboardRecord,
16
- config: DashboardConfig,
17
- ) => Promise<PersistedDashboardResponse>;
18
12
  };
19
13
 
20
14
  export function registerDashboardEndpoints(
@@ -44,29 +38,4 @@ export function registerDashboardEndpoints(
44
38
  };
45
39
  },
46
40
  });
47
-
48
- server.endpoint({
49
- method: 'POST',
50
- path: '/dashboard/set_dashboard_config',
51
- description: 'Replaces one dashboard configuration, including groups and widgets. Superadmin only.',
52
- request_schema: SetDashboardConfigRequestSchema,
53
- response_schema: DashboardApiResponseSchema,
54
- handler: async ({ body, adminUser, response }) => {
55
- if (!ctx.canEditDashboard(adminUser)) {
56
- response.setStatus(403);
57
- return { error: 'Dashboard edit is not allowed' };
58
- }
59
-
60
- const dashboard = await ctx.getDashboardRecord(body.slug);
61
-
62
- if (!dashboard) {
63
- response.setStatus(404);
64
- return { error: 'Dashboard not found' };
65
- }
66
-
67
- const config = body.config as DashboardConfig;
68
-
69
- return ctx.persistDashboardConfig(dashboard, config);
70
- },
71
- });
72
41
  }
@@ -22,6 +22,10 @@ type GroupEndpointsContext = {
22
22
  dashboard: DashboardRecord,
23
23
  config: DashboardConfig,
24
24
  ) => Promise<PersistedDashboardResponse>;
25
+ updateDashboardConfig: (
26
+ slug: string,
27
+ mutateConfig: (config: DashboardConfig, dashboard: DashboardRecord) => DashboardConfig | null | Promise<DashboardConfig | null>,
28
+ ) => Promise<PersistedDashboardResponse | null>;
25
29
  };
26
30
 
27
31
  export function registerGroupEndpoints(
@@ -40,26 +44,26 @@ export function registerGroupEndpoints(
40
44
  return { error: 'Dashboard edit is not allowed' };
41
45
  }
42
46
 
43
- const dashboard = await ctx.getDashboardRecord(body.slug);
47
+ const updatedDashboard = await ctx.updateDashboardConfig(body.slug, (config) => {
48
+ const nextOrder = config.groups.length + 1;
49
+ const group: DashboardGroupConfig = {
50
+ id: `group_${randomUUID()}`,
51
+ label: 'New group',
52
+ order: nextOrder,
53
+ };
54
+
55
+ return {
56
+ ...config,
57
+ groups: [...config.groups, group],
58
+ };
59
+ });
44
60
 
45
- if (!dashboard) {
61
+ if (!updatedDashboard) {
46
62
  response.setStatus(404);
47
63
  return { error: 'Dashboard not found' };
48
64
  }
49
65
 
50
- const config = ctx.parseStoredDashboardConfig(dashboard.config);
51
- const nextOrder = config.groups.length + 1;
52
-
53
- const group: DashboardGroupConfig = {
54
- id: `group_${randomUUID()}`,
55
- label: 'New group',
56
- order: nextOrder,
57
- };
58
-
59
- return ctx.persistDashboardConfig(dashboard, {
60
- ...config,
61
- groups: [...config.groups, group],
62
- });
66
+ return updatedDashboard;
63
67
  },
64
68
  });
65
69
 
@@ -76,33 +80,40 @@ export function registerGroupEndpoints(
76
80
  }
77
81
 
78
82
  const groupId = body.groupId;
79
- const dashboard = await ctx.getDashboardRecord(body.slug);
83
+ let mutationError: string | null = null;
84
+ const updatedDashboard = await ctx.updateDashboardConfig(body.slug, (config) => {
85
+ const group = config.groups.find((item) => item.id === groupId);
86
+
87
+ if (!group) {
88
+ mutationError = 'Dashboard group not found';
89
+ return null;
90
+ }
91
+
92
+ const nextGroup: DashboardGroupConfig = {
93
+ ...(body.config as EditableDashboardGroupConfig),
94
+ id: group.id,
95
+ order: group.order,
96
+ };
80
97
 
81
- if (!dashboard) {
98
+ return {
99
+ ...config,
100
+ groups: config.groups.map((item) => item.id === groupId
101
+ ? nextGroup
102
+ : item),
103
+ };
104
+ });
105
+
106
+ if (!updatedDashboard) {
82
107
  response.setStatus(404);
83
108
  return { error: 'Dashboard not found' };
84
109
  }
85
110
 
86
- const config = ctx.parseStoredDashboardConfig(dashboard.config);
87
- const group = config.groups.find((item) => item.id === groupId);
88
-
89
- if (!group) {
111
+ if (mutationError) {
90
112
  response.setStatus(404);
91
- return { error: 'Dashboard group not found' };
113
+ return { error: mutationError };
92
114
  }
93
115
 
94
- const nextGroup: DashboardGroupConfig = {
95
- ...(body.config as EditableDashboardGroupConfig),
96
- id: group.id,
97
- order: group.order,
98
- };
99
-
100
- return ctx.persistDashboardConfig(dashboard, {
101
- ...config,
102
- groups: config.groups.map((item) => item.id === groupId
103
- ? nextGroup
104
- : item),
105
- });
116
+ return updatedDashboard;
106
117
  },
107
118
  });
108
119
 
@@ -118,42 +129,43 @@ export function registerGroupEndpoints(
118
129
  return { error: 'Dashboard edit is not allowed' };
119
130
  }
120
131
 
121
- const dashboard = await ctx.getDashboardRecord(body.slug);
132
+ let mutationError: string | null = null;
133
+ const updatedDashboard = await ctx.updateDashboardConfig(body.slug, (config) => {
134
+ const sortedGroups = [...config.groups].sort((a, b) => a.order - b.order);
135
+ const currentIndex = sortedGroups.findIndex((group) => group.id === body.groupId);
122
136
 
123
- if (!dashboard) {
124
- response.setStatus(404);
125
- return { error: 'Dashboard not found' };
126
- }
137
+ if (currentIndex === -1) {
138
+ mutationError = 'Dashboard group not found';
139
+ return null;
140
+ }
127
141
 
128
- const config = ctx.parseStoredDashboardConfig(dashboard.config);
129
- const sortedGroups = [...config.groups].sort((a, b) => a.order - b.order);
130
- const currentIndex = sortedGroups.findIndex((group) => group.id === body.groupId);
142
+ const targetIndex = body.direction === 'up' ? currentIndex - 1 : currentIndex + 1;
131
143
 
132
- if (currentIndex === -1) {
133
- response.setStatus(404);
134
- return { error: 'Dashboard group not found' };
135
- }
144
+ if (targetIndex < 0 || targetIndex >= sortedGroups.length) {
145
+ return null;
146
+ }
136
147
 
137
- const targetIndex = body.direction === 'up' ? currentIndex - 1 : currentIndex + 1;
148
+ const reorderedGroups = [...sortedGroups];
149
+ const [group] = reorderedGroups.splice(currentIndex, 1);
150
+ reorderedGroups.splice(targetIndex, 0, group);
138
151
 
139
- if (targetIndex < 0 || targetIndex >= sortedGroups.length) {
140
152
  return {
141
- id: dashboard.id,
142
- slug: dashboard.slug,
143
- label: dashboard.label,
144
- revision: dashboard.revision,
145
- config: ctx.parseStoredDashboardConfig(dashboard.config),
153
+ ...config,
154
+ groups: reorderedGroups,
146
155
  };
156
+ });
157
+
158
+ if (!updatedDashboard) {
159
+ response.setStatus(404);
160
+ return { error: 'Dashboard not found' };
147
161
  }
148
162
 
149
- const reorderedGroups = [...sortedGroups];
150
- const [group] = reorderedGroups.splice(currentIndex, 1);
151
- reorderedGroups.splice(targetIndex, 0, group);
163
+ if (mutationError) {
164
+ response.setStatus(404);
165
+ return { error: mutationError };
166
+ }
152
167
 
153
- return ctx.persistDashboardConfig(dashboard, {
154
- ...config,
155
- groups: reorderedGroups,
156
- });
168
+ return updatedDashboard;
157
169
  },
158
170
  });
159
171
 
@@ -170,26 +182,33 @@ export function registerGroupEndpoints(
170
182
  }
171
183
 
172
184
  const groupId = body.groupId;
173
- const dashboard = await ctx.getDashboardRecord(body.slug);
185
+ let mutationError: string | null = null;
186
+ const updatedDashboard = await ctx.updateDashboardConfig(body.slug, (config) => {
187
+ const nextGroups = config.groups.filter((group) => group.id !== groupId);
188
+
189
+ if (nextGroups.length === config.groups.length) {
190
+ mutationError = 'Dashboard group not found';
191
+ return null;
192
+ }
193
+
194
+ return {
195
+ ...config,
196
+ groups: nextGroups,
197
+ widgets: config.widgets.filter((widget) => widget.group_id !== groupId),
198
+ };
199
+ });
174
200
 
175
- if (!dashboard) {
201
+ if (!updatedDashboard) {
176
202
  response.setStatus(404);
177
203
  return { error: 'Dashboard not found' };
178
204
  }
179
205
 
180
- const config = ctx.parseStoredDashboardConfig(dashboard.config);
181
- const nextGroups = config.groups.filter((group) => group.id !== groupId);
182
-
183
- if (nextGroups.length === config.groups.length) {
206
+ if (mutationError) {
184
207
  response.setStatus(404);
185
- return { error: 'Dashboard group not found' };
208
+ return { error: mutationError };
186
209
  }
187
210
 
188
- return ctx.persistDashboardConfig(dashboard, {
189
- ...config,
190
- groups: nextGroups,
191
- widgets: config.widgets.filter((widget) => widget.group_id !== groupId),
192
- });
211
+ return updatedDashboard;
193
212
  },
194
213
  });
195
214
 
@@ -25,6 +25,10 @@ type WidgetEndpointsContext = {
25
25
  dashboard: DashboardRecord,
26
26
  config: DashboardConfig,
27
27
  ) => Promise<PersistedDashboardResponse>;
28
+ updateDashboardConfig: (
29
+ slug: string,
30
+ mutateConfig: (config: DashboardConfig, dashboard: DashboardRecord) => DashboardConfig | null | Promise<DashboardConfig | null>,
31
+ ) => Promise<PersistedDashboardResponse | null>;
28
32
  getWidgetData: (
29
33
  widget: DashboardWidgetConfig,
30
34
  options?: {
@@ -50,35 +54,42 @@ export function registerWidgetEndpoints(
50
54
  return { error: 'Dashboard edit is not allowed' };
51
55
  }
52
56
 
53
- const dashboard = await ctx.getDashboardRecord(body.slug);
57
+ let mutationError: string | null = null;
58
+ const updatedDashboard = await ctx.updateDashboardConfig(body.slug, (config) => {
59
+ const group = config.groups.find((item) => item.id === body.groupId);
60
+
61
+ if (!group) {
62
+ mutationError = 'Dashboard group not found';
63
+ return null;
64
+ }
65
+
66
+ const nextOrder = config.widgets.filter((item) => item.group_id === body.groupId).length + 1;
67
+ const widget: DashboardWidgetConfig = {
68
+ id: `widget_${randomUUID()}`,
69
+ group_id: body.groupId,
70
+ label: 'New widget',
71
+ size: 'small',
72
+ order: nextOrder,
73
+ target: 'empty',
74
+ };
54
75
 
55
- if (!dashboard) {
76
+ return {
77
+ ...config,
78
+ widgets: [...config.widgets, widget],
79
+ };
80
+ });
81
+
82
+ if (!updatedDashboard) {
56
83
  response.setStatus(404);
57
84
  return { error: 'Dashboard not found' };
58
85
  }
59
86
 
60
- const config = ctx.parseStoredDashboardConfig(dashboard.config);
61
- const group = config.groups.find((item) => item.id === body.groupId);
62
-
63
- if (!group) {
87
+ if (mutationError) {
64
88
  response.setStatus(404);
65
- return { error: 'Dashboard group not found' };
89
+ return { error: mutationError };
66
90
  }
67
91
 
68
- const nextOrder = config.widgets.filter((item) => item.group_id === body.groupId).length + 1;
69
- const widget: DashboardWidgetConfig = {
70
- id: `widget_${randomUUID()}`,
71
- group_id: body.groupId,
72
- label: 'New widget',
73
- size: 'small',
74
- order: nextOrder,
75
- target: 'empty',
76
- };
77
-
78
- return ctx.persistDashboardConfig(dashboard, {
79
- ...config,
80
- widgets: [...config.widgets, widget],
81
- });
92
+ return updatedDashboard;
82
93
  },
83
94
  });
84
95
 
@@ -94,49 +105,50 @@ export function registerWidgetEndpoints(
94
105
  return { error: 'Dashboard edit is not allowed' };
95
106
  }
96
107
 
97
- const dashboard = await ctx.getDashboardRecord(body.slug);
108
+ let mutationError: string | null = null;
109
+ const updatedDashboard = await ctx.updateDashboardConfig(body.slug, (config) => {
110
+ const widget = config.widgets.find((item) => item.id === body.widgetId);
98
111
 
99
- if (!dashboard) {
100
- response.setStatus(404);
101
- return { error: 'Dashboard not found' };
102
- }
112
+ if (!widget) {
113
+ mutationError = 'Dashboard widget not found';
114
+ return null;
115
+ }
103
116
 
104
- const config = ctx.parseStoredDashboardConfig(dashboard.config);
105
- const widget = config.widgets.find((item) => item.id === body.widgetId);
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;
106
122
 
107
- if (!widget) {
108
- response.setStatus(404);
109
- return { error: 'Dashboard widget not found' };
110
- }
123
+ if (targetIndex < 0 || targetIndex >= sortedWidgets.length) {
124
+ return null;
125
+ }
111
126
 
112
- const sortedWidgets = config.widgets
113
- .filter((item) => item.group_id === widget.group_id)
114
- .sort((a, b) => a.order - b.order);
115
- const currentIndex = sortedWidgets.findIndex((item) => item.id === body.widgetId);
116
- const targetIndex = body.direction === 'up' ? currentIndex - 1 : currentIndex + 1;
127
+ const reorderedWidgets = [...sortedWidgets];
128
+ const [movedWidget] = reorderedWidgets.splice(currentIndex, 1);
129
+ reorderedWidgets.splice(targetIndex, 0, movedWidget);
130
+ const reorderedWidgetIds = new Map(reorderedWidgets.map((item, index) => [item.id, index + 1]));
117
131
 
118
- if (targetIndex < 0 || targetIndex >= sortedWidgets.length) {
119
132
  return {
120
- id: dashboard.id,
121
- slug: dashboard.slug,
122
- label: dashboard.label,
123
- revision: dashboard.revision,
124
- config: ctx.parseStoredDashboardConfig(dashboard.config),
133
+ ...config,
134
+ widgets: config.widgets.map((item) => ({
135
+ ...item,
136
+ order: reorderedWidgetIds.get(item.id) ?? item.order,
137
+ })),
125
138
  };
139
+ });
140
+
141
+ if (!updatedDashboard) {
142
+ response.setStatus(404);
143
+ return { error: 'Dashboard not found' };
126
144
  }
127
145
 
128
- const reorderedWidgets = [...sortedWidgets];
129
- const [movedWidget] = reorderedWidgets.splice(currentIndex, 1);
130
- reorderedWidgets.splice(targetIndex, 0, movedWidget);
131
- const reorderedWidgetIds = new Map(reorderedWidgets.map((item, index) => [item.id, index + 1]));
132
-
133
- return ctx.persistDashboardConfig(dashboard, {
134
- ...config,
135
- widgets: config.widgets.map((item) => ({
136
- ...item,
137
- order: reorderedWidgetIds.get(item.id) ?? item.order,
138
- })),
139
- });
146
+ if (mutationError) {
147
+ response.setStatus(404);
148
+ return { error: mutationError };
149
+ }
150
+
151
+ return updatedDashboard;
140
152
  },
141
153
  });
142
154
 
@@ -152,25 +164,32 @@ export function registerWidgetEndpoints(
152
164
  return { error: 'Dashboard edit is not allowed' };
153
165
  }
154
166
 
155
- const dashboard = await ctx.getDashboardRecord(body.slug);
167
+ let mutationError: string | null = null;
168
+ const updatedDashboard = await ctx.updateDashboardConfig(body.slug, (config) => {
169
+ const nextWidgets = config.widgets.filter((item) => item.id !== body.widgetId);
156
170
 
157
- if (!dashboard) {
171
+ if (nextWidgets.length === config.widgets.length) {
172
+ mutationError = 'Dashboard widget not found';
173
+ return null;
174
+ }
175
+
176
+ return {
177
+ ...config,
178
+ widgets: nextWidgets,
179
+ };
180
+ });
181
+
182
+ if (!updatedDashboard) {
158
183
  response.setStatus(404);
159
184
  return { error: 'Dashboard not found' };
160
185
  }
161
186
 
162
- const config = ctx.parseStoredDashboardConfig(dashboard.config);
163
- const nextWidgets = config.widgets.filter((item) => item.id !== body.widgetId);
164
-
165
- if (nextWidgets.length === config.widgets.length) {
187
+ if (mutationError) {
166
188
  response.setStatus(404);
167
- return { error: 'Dashboard widget not found' };
189
+ return { error: mutationError };
168
190
  }
169
191
 
170
- return ctx.persistDashboardConfig(dashboard, {
171
- ...config,
172
- widgets: nextWidgets,
173
- });
192
+ return updatedDashboard;
174
193
  },
175
194
  });
176
195
 
@@ -186,36 +205,43 @@ export function registerWidgetEndpoints(
186
205
  return { error: 'Dashboard edit is not allowed' };
187
206
  }
188
207
 
189
- const dashboard = await ctx.getDashboardRecord(body.slug);
208
+ let mutationError: string | null = null;
209
+ const updatedDashboard = await ctx.updateDashboardConfig(body.slug, (config) => {
210
+ const widget = config.widgets.find((item) => item.id === body.widgetId);
190
211
 
191
- if (!dashboard) {
212
+ if (!widget) {
213
+ mutationError = 'Dashboard widget not found';
214
+ return null;
215
+ }
216
+
217
+ const typedWidgetConfig = body.config as EditableDashboardWidgetConfig;
218
+
219
+ const nextWidget: DashboardWidgetConfig = {
220
+ ...typedWidgetConfig,
221
+ id: widget.id,
222
+ group_id: widget.group_id,
223
+ order: widget.order,
224
+ };
225
+
226
+ return {
227
+ ...config,
228
+ widgets: config.widgets.map((item) => item.id === body.widgetId
229
+ ? nextWidget
230
+ : item),
231
+ };
232
+ });
233
+
234
+ if (!updatedDashboard) {
192
235
  response.setStatus(404);
193
236
  return { error: 'Dashboard not found' };
194
237
  }
195
238
 
196
- const config = ctx.parseStoredDashboardConfig(dashboard.config);
197
- const widget = config.widgets.find((item) => item.id === body.widgetId);
198
-
199
- if (!widget) {
239
+ if (mutationError) {
200
240
  response.setStatus(404);
201
- return { error: 'Dashboard widget not found' };
241
+ return { error: mutationError };
202
242
  }
203
243
 
204
- const typedWidgetConfig = body.config as EditableDashboardWidgetConfig;
205
-
206
- const nextWidget: DashboardWidgetConfig = {
207
- ...typedWidgetConfig,
208
- id: widget.id,
209
- group_id: widget.group_id,
210
- order: widget.order,
211
- };
212
-
213
- return ctx.persistDashboardConfig(dashboard, {
214
- ...config,
215
- widgets: config.widgets.map((item) => item.id === body.widgetId
216
- ? nextWidget
217
- : item),
218
- });
244
+ return updatedDashboard;
219
245
  },
220
246
  });
221
247
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adminforth/dashboard",
3
- "version": "1.4.2",
3
+ "version": "1.5.0",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "type": "module",
package/schema/api.ts CHANGED
@@ -50,11 +50,6 @@ export const SlugRequestZodSchema = z.object({
50
50
  slug: z.string(),
51
51
  }).strict()
52
52
 
53
- export const SetDashboardConfigRequestZodSchema = z.object({
54
- slug: z.string(),
55
- config: DashboardConfigZodSchema,
56
- }).strict()
57
-
58
53
  export const GroupIdRequestZodSchema = z.object({
59
54
  slug: z.string(),
60
55
  groupId: z.string(),
@@ -109,7 +104,6 @@ export const DashboardResponseSchema = toAdminForthJsonSchema(DashboardResponseZ
109
104
  export const DashboardApiResponseSchema = toAdminForthJsonSchema(DashboardApiResponseZodSchema)
110
105
  export const DashboardWidgetDataResponseSchema = toAdminForthJsonSchema(DashboardWidgetDataResponseZodSchema)
111
106
  export const SlugRequestSchema = toAdminForthJsonSchema(SlugRequestZodSchema)
112
- export const SetDashboardConfigRequestSchema = toAdminForthJsonSchema(SetDashboardConfigRequestZodSchema)
113
107
  export const GroupIdRequestSchema = toAdminForthJsonSchema(GroupIdRequestZodSchema)
114
108
  export const MoveGroupRequestSchema = toAdminForthJsonSchema(MoveGroupRequestZodSchema)
115
109
  export const SetGroupConfigRequestSchema = toAdminForthJsonSchema(SetGroupConfigRequestZodSchema)