@firecms/user_management 3.0.0-canary.9 → 3.0.0-canary.90

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/dist/index.es.js CHANGED
@@ -1,308 +1,448 @@
1
- import A, { useRef as Se, useEffect as Z, useCallback as E, useContext as Ie, useState as F } from "react";
2
- import { getFirestore as ee, onSnapshot as le, collection as se, setDoc as ae, doc as G, deleteDoc as ce } from "firebase/firestore";
3
- import { jsx as e, jsxs as s, Fragment as H } from "react/jsx-runtime";
4
- import { getColorSchemeForSeed as ke, Chip as Ee, Dialog as de, DialogContent as ue, Typography as _, TextField as K, Paper as Pe, Table as ie, TableHeader as re, TableCell as a, TableBody as oe, TableRow as M, Tooltip as L, Checkbox as P, Select as ne, SelectItem as T, DialogActions as me, Button as q, LoadingButton as he, DoneIcon as fe, IconButton as ge, DeleteIcon as pe, CenteredView as Ce, Container as ve, AddIcon as we, MultiSelect as Ae, MultiSelectItem as Fe } from "@firecms/ui";
5
- import * as V from "yup";
6
- import { toSnakeCase as Te, FieldCaption as B, DeleteConfirmationDialog as ye, useNavigationController as Be, useSnackbarController as be, useAuthController as Le, useCustomizationController as Ve, defaultDateFormat as _e } from "@firecms/core";
7
- import { useCreateFormex as Ne, getIn as j, Formex as xe } from "@firecms/formex";
8
- import { format as Oe } from "date-fns";
9
- import * as $e from "date-fns/locale";
10
- const hn = ["Admin"], Me = {
11
- read: !1,
12
- edit: !1,
13
- create: !1,
14
- delete: !1
1
+ import React, { useEffect, useCallback, useContext, useState } from "react";
2
+ import equal from "react-fast-compare";
3
+ import { jsx, jsxs, Fragment } from "react/jsx-runtime";
4
+ import { getColorSchemeForSeed, Chip, Dialog, DialogContent, Typography, TextField, Paper, Table, TableHeader, TableCell, TableBody, TableRow, Tooltip, Checkbox, Select, SelectItem, DialogActions, Button, LoadingButton, DoneIcon, IconButton, DeleteIcon, CenteredView, Container, AddIcon, MultiSelect, MultiSelectItem } from "@firecms/ui";
5
+ import * as Yup from "yup";
6
+ import { toSnakeCase, FieldCaption, DeleteConfirmationDialog, useNavigationController, useSnackbarController, useAuthController, useCustomizationController, defaultDateFormat } from "@firecms/core";
7
+ import { useCreateFormex, getIn, Formex } from "@firecms/formex";
8
+ import { format } from "date-fns";
9
+ import * as locales from "date-fns/locale";
10
+ const RESERVED_GROUPS = ["Admin"];
11
+ const DEFAULT_PERMISSIONS = {
12
+ read: false,
13
+ edit: false,
14
+ create: false,
15
+ delete: false
15
16
  };
16
- function qe({ collection: t, user: i }) {
17
- const n = i?.roles;
18
- if (n) {
19
- if (t.ownerId === i?.uid)
20
- return {
21
- read: !0,
22
- create: !0,
23
- edit: !0,
24
- delete: !0
25
- };
26
- {
27
- const d = {
28
- read: !1,
29
- create: !1,
30
- edit: !1,
31
- delete: !1
32
- };
33
- return n.map((r) => Ye(r, t.id)).reduce(te, d);
34
- }
35
- } else
36
- return Me;
17
+ function resolveUserRolePermissions({
18
+ collection,
19
+ user
20
+ }) {
21
+ const roles = user?.roles;
22
+ if (!roles) {
23
+ return DEFAULT_PERMISSIONS;
24
+ } else if (collection.ownerId === user?.uid) {
25
+ return {
26
+ read: true,
27
+ create: true,
28
+ edit: true,
29
+ delete: true
30
+ };
31
+ } else {
32
+ const basePermissions = {
33
+ read: false,
34
+ create: false,
35
+ edit: false,
36
+ delete: false
37
+ };
38
+ return roles.map((role) => resolveCollectionRole(role, collection.id)).reduce(mergePermissions, basePermissions);
39
+ }
37
40
  }
38
- function Ye(t, i) {
39
- const n = {
40
- read: t.isAdmin || t.defaultPermissions?.read,
41
- create: t.isAdmin || t.defaultPermissions?.create,
42
- edit: t.isAdmin || t.defaultPermissions?.edit,
43
- delete: t.isAdmin || t.defaultPermissions?.delete
41
+ function resolveCollectionRole(role, id) {
42
+ const basePermissions = {
43
+ read: role.isAdmin || role.defaultPermissions?.read,
44
+ create: role.isAdmin || role.defaultPermissions?.create,
45
+ edit: role.isAdmin || role.defaultPermissions?.edit,
46
+ delete: role.isAdmin || role.defaultPermissions?.delete
44
47
  };
45
- return t.collectionPermissions && t.collectionPermissions[i] ? te(t.collectionPermissions[i], n) : t.defaultPermissions ? te(t.defaultPermissions, n) : n;
48
+ if (role.collectionPermissions && role.collectionPermissions[id]) {
49
+ return mergePermissions(role.collectionPermissions[id], basePermissions);
50
+ } else if (role.defaultPermissions) {
51
+ return mergePermissions(role.defaultPermissions, basePermissions);
52
+ } else {
53
+ return basePermissions;
54
+ }
46
55
  }
47
- const te = (t, i) => ({
48
- read: t.read || i.read,
49
- create: t.create || i.create,
50
- edit: t.edit || i.edit,
51
- delete: t.delete || i.delete
52
- });
53
- function fn(t, i) {
54
- return t ? i.roles ? i.roles.map((n) => t.find((d) => d.id === n.id)).filter(Boolean) : [] : void 0;
56
+ const mergePermissions = (permA, permB) => {
57
+ return {
58
+ read: permA.read || permB.read,
59
+ create: permA.create || permB.create,
60
+ edit: permA.edit || permB.edit,
61
+ delete: permA.delete || permB.delete
62
+ };
63
+ };
64
+ function getUserRoles(roles, fireCMSUser) {
65
+ return !roles ? void 0 : fireCMSUser.roles ? fireCMSUser.roles.map((role) => roles.find((r) => r.id === role.id)).filter(Boolean) : [];
55
66
  }
56
- const We = (t, i) => {
57
- const n = t.map((r) => r.id), d = i.map((r) => r.id);
58
- return n.length === i.length && n.every((r) => d.includes(r));
67
+ const areRolesEqual = (rolesA, rolesB) => {
68
+ const rolesAIds = rolesA.map((r) => r.id);
69
+ const rolesBIds = rolesB.map((r) => r.id);
70
+ return rolesAIds.length === rolesB.length && rolesAIds.every((role) => rolesBIds.includes(role));
59
71
  };
60
- function gn(t, i) {
61
- if (!i)
72
+ function cacheDelegatedLoginToken(projectId, delegatedToken) {
73
+ if (!delegatedToken) {
62
74
  return;
63
- const n = je(i), d = new Date(n.exp * 1e3);
64
- localStorage.setItem(`auth_token::${t}`, JSON.stringify({
65
- token: i,
66
- expiry: d
75
+ }
76
+ const data = parseJwt(delegatedToken);
77
+ const expiry = new Date(data.exp * 1e3);
78
+ localStorage.setItem(`auth_token::${projectId}`, JSON.stringify({
79
+ token: delegatedToken,
80
+ expiry
67
81
  }));
68
82
  }
69
- function pn(t) {
70
- const i = localStorage.getItem(`auth_token::${t}`);
71
- if (i) {
72
- const n = JSON.parse(i);
73
- if (n.expiry = new Date(n.expiry), n.expiry > /* @__PURE__ */ new Date())
74
- return n.token;
83
+ function getDelegatedLoginTokenFromCache(projectId) {
84
+ const entry = localStorage.getItem(`auth_token::${projectId}`);
85
+ if (entry) {
86
+ const data = JSON.parse(entry);
87
+ data.expiry = new Date(data.expiry);
88
+ if (data.expiry > /* @__PURE__ */ new Date()) {
89
+ return data.token;
90
+ }
75
91
  }
92
+ return void 0;
76
93
  }
77
- function Cn() {
78
- for (let t = 0; t < localStorage.length; t++) {
79
- const i = localStorage.key(t);
80
- i?.startsWith("auth_token::") && localStorage.removeItem(i);
94
+ function clearDelegatedLoginTokensCache() {
95
+ for (let i = 0; i < localStorage.length; i++) {
96
+ const key = localStorage.key(i);
97
+ if (key?.startsWith("auth_token::")) {
98
+ localStorage.removeItem(key);
99
+ }
81
100
  }
82
101
  }
83
- function je(t) {
84
- if (!t)
102
+ function parseJwt(token) {
103
+ if (!token) {
85
104
  throw new Error("No JWT token");
86
- const n = t.split(".")[1].replace(/-/g, "+").replace(/_/g, "/"), d = decodeURIComponent(window.atob(n).split("").map(function(r) {
87
- return "%" + ("00" + r.charCodeAt(0).toString(16)).slice(-2);
105
+ }
106
+ const base64Url = token.split(".")[1];
107
+ const base64 = base64Url.replace(/-/g, "+").replace(/_/g, "/");
108
+ const jsonPayload = decodeURIComponent(window.atob(base64).split("").map(function(c) {
109
+ return "%" + ("00" + c.charCodeAt(0).toString(16)).slice(-2);
88
110
  }).join(""));
89
- return JSON.parse(d);
111
+ return JSON.parse(jsonPayload);
90
112
  }
91
- function vn(t, i = 10) {
92
- if (!/^#([0-9A-Fa-f]{3}){1,2}$/.test(t))
113
+ function darkenColor(hexColor, darkenBy = 10) {
114
+ if (!/^#([0-9A-Fa-f]{3}){1,2}$/.test(hexColor)) {
93
115
  throw new Error("Invalid color format");
94
- let n = t.substring(1).split("");
95
- n.length === 3 && (n = [n[0], n[0], n[1], n[1], n[2], n[2]]);
96
- let d = parseInt(n[0] + n[1], 16), r = parseInt(n[2] + n[3], 16), m = parseInt(n[4] + n[5], 16);
97
- return d = Math.floor(d * (1 - i / 100)), r = Math.floor(r * (1 - i / 100)), m = Math.floor(m * (1 - i / 100)), "#" + (d < 16 ? "0" : "") + d.toString(16) + (r < 16 ? "0" : "") + r.toString(16) + (m < 16 ? "0" : "") + m.toString(16);
116
+ }
117
+ let color = hexColor.substring(1).split("");
118
+ if (color.length === 3) {
119
+ color = [color[0], color[0], color[1], color[1], color[2], color[2]];
120
+ }
121
+ let r = parseInt(color[0] + color[1], 16);
122
+ let g = parseInt(color[2] + color[3], 16);
123
+ let b = parseInt(color[4] + color[5], 16);
124
+ r = Math.floor(r * (1 - darkenBy / 100));
125
+ g = Math.floor(g * (1 - darkenBy / 100));
126
+ b = Math.floor(b * (1 - darkenBy / 100));
127
+ return "#" + (r < 16 ? "0" : "") + r.toString(16) + (g < 16 ? "0" : "") + g.toString(16) + (b < 16 ? "0" : "") + b.toString(16);
98
128
  }
99
- function wn(t, i = 10) {
100
- if (!/^#([0-9A-Fa-f]{3}){1,2}$/.test(t))
129
+ function hexToRgbaWithOpacity(hexColor, opacity = 10) {
130
+ if (!/^#([0-9A-Fa-f]{3}){1,2}$/.test(hexColor)) {
101
131
  throw new Error("Invalid color format");
102
- let n = t.substring(1).split("");
103
- n.length === 3 && (n = [n[0], n[0], n[1], n[1], n[2], n[2]]);
104
- const d = parseInt(n[0] + n[1], 16), r = parseInt(n[2] + n[3], 16), m = parseInt(n[4] + n[5], 16), f = i / 100;
105
- return `rgba(${d}, ${r}, ${m}, ${f})`;
132
+ }
133
+ let color = hexColor.substring(1).split("");
134
+ if (color.length === 3) {
135
+ color = [color[0], color[0], color[1], color[1], color[2], color[2]];
136
+ }
137
+ const r = parseInt(color[0] + color[1], 16);
138
+ const g = parseInt(color[2] + color[3], 16);
139
+ const b = parseInt(color[4] + color[5], 16);
140
+ const alpha = opacity / 100;
141
+ return `rgba(${r}, ${g}, ${b}, ${alpha})`;
106
142
  }
107
- function yn({
108
- firebaseApp: t,
109
- usersPath: i = "__FIRECMS/config/users",
110
- rolesPath: n = "__FIRECMS/config/roles",
111
- usersLimit: d,
112
- canEditRoles: r = !0,
113
- authController: m,
114
- allowDefaultRolesCreation: f,
115
- includeCollectionConfigPermissions: p
143
+ function useBuildUserManagement({
144
+ dataSourceDelegate,
145
+ usersPath = "__FIRECMS/config/users",
146
+ rolesPath = "__FIRECMS/config/roles",
147
+ usersLimit,
148
+ canEditRoles = true,
149
+ allowDefaultRolesCreation,
150
+ includeCollectionConfigPermissions
116
151
  }) {
117
- const N = Se(), [v, c] = A.useState(!0), [I, w] = A.useState(!0), [l, g] = A.useState([]), [R, y] = A.useState([]), O = R.map((C) => ({
118
- ...C,
119
- roles: l.filter((b) => C.roles?.includes(b.id))
120
- })), [z, u] = A.useState(), [h, x] = A.useState(), U = v || I, k = O.find((C) => C.email?.toLowerCase() === m.user?.email?.toLowerCase());
121
- Z(() => {
122
- t && (N.current = ee(t));
123
- }, [t]), Z(() => {
124
- if (!t || !n)
125
- return;
126
- const C = ee(t);
127
- return le(
128
- se(C, n),
129
- {
130
- next: (b) => {
131
- u(void 0);
132
- try {
133
- const D = Je(b.docs);
134
- g(D);
135
- } catch (D) {
136
- u(D);
137
- }
138
- c(!1);
139
- },
140
- error: (b) => {
141
- u(b), c(!1);
152
+ const [rolesLoading, setRolesLoading] = React.useState(true);
153
+ const [usersLoading, setUsersLoading] = React.useState(true);
154
+ const [roles, setRoles] = React.useState([]);
155
+ const [usersWithRoleIds, setUsersWithRoleIds] = React.useState([]);
156
+ const users = usersWithRoleIds.map((u) => ({
157
+ ...u,
158
+ roles: roles.filter((r) => u.roles?.includes(r.id))
159
+ }));
160
+ const [rolesError, setRolesError] = React.useState();
161
+ const [usersError, setUsersError] = React.useState();
162
+ const loading = rolesLoading || usersLoading;
163
+ useEffect(() => {
164
+ if (!dataSourceDelegate || !rolesPath) return;
165
+ if (dataSourceDelegate.initialised !== void 0 && !dataSourceDelegate.initialised) return;
166
+ return dataSourceDelegate.listenCollection?.({
167
+ path: rolesPath,
168
+ onUpdate(entities) {
169
+ setRolesError(void 0);
170
+ try {
171
+ const newRoles = entityToRoles(entities);
172
+ if (!equal(newRoles, roles))
173
+ setRoles(newRoles);
174
+ } catch (e) {
175
+ console.error("Error loading roles", e);
176
+ setRolesError(e);
142
177
  }
178
+ setRolesLoading(false);
179
+ },
180
+ onError(e) {
181
+ console.error("Error loading roles", e);
182
+ setRolesError(e);
183
+ setRolesLoading(false);
143
184
  }
144
- );
145
- }, [t, n]), Z(() => {
146
- if (!t || !i)
147
- return;
148
- const C = ee(t);
149
- return le(
150
- se(C, i),
151
- {
152
- next: (b) => {
153
- x(void 0);
154
- try {
155
- const D = ze(b.docs);
156
- y(D);
157
- } catch (D) {
158
- x(D);
159
- }
160
- w(!1);
161
- },
162
- error: (b) => {
163
- x(b), w(!1);
185
+ });
186
+ }, [dataSourceDelegate, rolesPath]);
187
+ useEffect(() => {
188
+ if (!dataSourceDelegate || !usersPath) return;
189
+ if (dataSourceDelegate.initialised !== void 0 && !dataSourceDelegate.initialised) return;
190
+ return dataSourceDelegate.listenCollection?.({
191
+ path: usersPath,
192
+ onUpdate(entities) {
193
+ setUsersError(void 0);
194
+ try {
195
+ const newUsers = entitiesToUsers(entities);
196
+ if (!equal(newUsers, usersWithRoleIds))
197
+ setUsersWithRoleIds(newUsers);
198
+ } catch (e) {
199
+ console.error("Error loading users", e);
200
+ setUsersError(e);
164
201
  }
202
+ setUsersLoading(false);
203
+ },
204
+ onError(e) {
205
+ console.error("Error loading users", e);
206
+ setUsersError(e);
207
+ setUsersLoading(false);
165
208
  }
166
- );
167
- }, [t, i]);
168
- const o = E(async (C) => {
169
- const b = N.current;
170
- if (!b || !i)
171
- throw Error("useFirestoreConfigurationPersistence Firestore not initialised");
172
- console.debug("Persisting user", C);
173
- const D = C.roles?.map((Ue) => Ue.id), {
174
- uid: W,
175
- ...X
176
- } = C;
177
- return ae(G(b, i, W), {
178
- ...X,
179
- roles: D
180
- }, { merge: !0 }).then(() => C);
181
- }, [i]), S = E((C) => {
182
- const b = N.current;
183
- if (!b || !n)
184
- throw Error("useFirestoreConfigurationPersistence Firestore not initialised");
185
- console.debug("Persisting role", C);
209
+ });
210
+ }, [dataSourceDelegate, usersPath]);
211
+ const saveUser = useCallback(async (user) => {
212
+ if (!dataSourceDelegate) throw Error("useBuildUserManagement Firebase not initialised");
213
+ if (!usersPath) throw Error("useBuildUserManagement Firestore not initialised");
214
+ console.debug("Persisting user", user);
215
+ const roleIds = user.roles?.map((r) => r.id);
186
216
  const {
187
- id: D,
188
- ...W
189
- } = C, X = G(b, n, D);
190
- return ae(X, W, { merge: !0 });
191
- }, [n]), $ = E(async (C) => {
192
- const b = N.current;
193
- if (!b || !i)
194
- throw Error("useFirestoreConfigurationPersistence Firestore not initialised");
195
- console.debug("Deleting", C);
196
- const { uid: D } = C;
197
- return ce(G(b, i, D));
198
- }, [i]), J = E((C) => {
199
- const b = N.current;
200
- if (!b || !n)
201
- throw Error("useFirestoreConfigurationPersistence Firestore not initialised");
202
- console.debug("Deleting", C);
203
- const { id: D } = C, W = G(b, n, D);
204
- return ce(W);
205
- }, [n]), De = E(({
206
- collection: C
207
- }) => qe({
208
- collection: C,
209
- user: k ?? null
210
- }), [k?.uid]);
217
+ uid,
218
+ ...userData
219
+ } = user;
220
+ const data = {
221
+ ...userData,
222
+ roles: roleIds
223
+ };
224
+ if (uid) {
225
+ return dataSourceDelegate.saveEntity({
226
+ status: "existing",
227
+ path: usersPath,
228
+ entityId: uid,
229
+ values: data
230
+ }).then(() => user);
231
+ } else {
232
+ return dataSourceDelegate.saveEntity({
233
+ status: "new",
234
+ path: usersPath,
235
+ values: data
236
+ }).then(() => user);
237
+ }
238
+ }, [usersPath, dataSourceDelegate]);
239
+ const saveRole = useCallback((role) => {
240
+ if (!dataSourceDelegate) throw Error("useBuildUserManagement Firebase not initialised");
241
+ if (!rolesPath) throw Error("useBuildUserManagement Firestore not initialised");
242
+ console.debug("Persisting role", role);
243
+ const {
244
+ id,
245
+ ...roleData
246
+ } = role;
247
+ return dataSourceDelegate.saveEntity({
248
+ status: "existing",
249
+ path: rolesPath,
250
+ entityId: id,
251
+ values: roleData
252
+ }).then(() => {
253
+ return;
254
+ });
255
+ }, [rolesPath, dataSourceDelegate]);
256
+ const deleteUser = useCallback(async (user) => {
257
+ if (!dataSourceDelegate) throw Error("useBuildUserManagement Firebase not initialised");
258
+ if (!usersPath) throw Error("useBuildUserManagement Firestore not initialised");
259
+ console.debug("Deleting", user);
260
+ const { uid } = user;
261
+ const entity = {
262
+ path: usersPath,
263
+ id: uid,
264
+ values: {}
265
+ };
266
+ await dataSourceDelegate.deleteEntity({ entity });
267
+ }, [usersPath, dataSourceDelegate]);
268
+ const deleteRole = useCallback(async (role) => {
269
+ if (!dataSourceDelegate) throw Error("useBuildUserManagement Firebase not initialised");
270
+ if (!rolesPath) throw Error("useBuildUserManagement Firestore not initialised");
271
+ console.debug("Deleting", role);
272
+ const { id } = role;
273
+ const entity = {
274
+ path: usersPath,
275
+ id,
276
+ values: {}
277
+ };
278
+ await dataSourceDelegate.deleteEntity({ entity });
279
+ }, [rolesPath, dataSourceDelegate]);
280
+ const collectionPermissions = useCallback(({
281
+ collection,
282
+ user
283
+ }) => resolveUserRolePermissions({
284
+ collection,
285
+ user
286
+ }), []);
287
+ const defineRolesFor = useCallback((user) => {
288
+ if (!users) throw Error("Users not loaded");
289
+ const mgmtUser = users.find((u) => u.email?.toLowerCase() === user?.email?.toLowerCase());
290
+ return mgmtUser?.roles;
291
+ }, [users]);
292
+ const authenticator = useCallback(({ user }) => {
293
+ console.debug("Authenticating user", user);
294
+ if (loading) {
295
+ console.warn("User management is still loading");
296
+ return false;
297
+ }
298
+ if (users.length === 0) {
299
+ return true;
300
+ }
301
+ const mgmtUser = users.find((u) => u.email?.toLowerCase() === user?.email?.toLowerCase());
302
+ if (mgmtUser) {
303
+ return true;
304
+ }
305
+ throw Error("Could not find a user with the provided email in the user management system.");
306
+ }, [loading, users]);
307
+ const isAdmin = roles.some((r) => r.id === "admin");
211
308
  return {
212
- loading: U,
213
- loggedInUser: k,
214
- roles: l,
215
- users: O,
216
- saveUser: o,
217
- saveRole: S,
218
- deleteUser: $,
219
- deleteRole: J,
220
- usersLimit: d,
221
- canEditRoles: r === void 0 ? !0 : r,
222
- allowDefaultRolesCreation: f === void 0 ? !0 : f,
223
- includeCollectionConfigPermissions: !!p,
224
- collectionPermissions: De
309
+ loading,
310
+ roles,
311
+ users,
312
+ saveUser,
313
+ saveRole,
314
+ rolesError,
315
+ deleteUser,
316
+ deleteRole,
317
+ usersLimit,
318
+ usersError,
319
+ isAdmin,
320
+ canEditRoles: canEditRoles === void 0 ? true : canEditRoles,
321
+ allowDefaultRolesCreation: allowDefaultRolesCreation === void 0 ? true : allowDefaultRolesCreation,
322
+ includeCollectionConfigPermissions: Boolean(includeCollectionConfigPermissions),
323
+ collectionPermissions,
324
+ defineRolesFor,
325
+ authenticator
225
326
  };
226
327
  }
227
- const ze = (t) => t.map((i) => {
228
- const n = i.data();
229
- return {
230
- uid: i.id,
231
- ...n,
232
- created_on: n?.created_on?.toDate(),
233
- updated_on: n?.updated_on?.toDate()
234
- };
235
- }), Je = (t) => t.map((i) => ({
236
- id: i.id,
237
- ...i.data()
238
- })), Re = A.createContext({});
239
- function Ge({
240
- children: t,
241
- userManagement: i
328
+ const entitiesToUsers = (docs) => {
329
+ return docs.map((doc) => {
330
+ const data = doc.values;
331
+ const newVar = {
332
+ uid: doc.id,
333
+ ...data,
334
+ created_on: data?.created_on,
335
+ updated_on: data?.updated_on
336
+ };
337
+ return newVar;
338
+ });
339
+ };
340
+ const entityToRoles = (entities) => {
341
+ return entities.map((doc) => ({
342
+ id: doc.id,
343
+ ...doc.values
344
+ }));
345
+ };
346
+ const UserManagementContext = React.createContext({});
347
+ function UserManagementProvider({
348
+ children,
349
+ userManagement
242
350
  }) {
243
- return /* @__PURE__ */ e(Re.Provider, { value: i, children: t });
351
+ return /* @__PURE__ */ jsx(UserManagementContext.Provider, { value: userManagement, children });
244
352
  }
245
- const Y = () => Ie(Re);
246
- function Q({ role: t }) {
247
- let i;
248
- return t.isAdmin ? i = "blueDarker" : t.id === "editor" ? i = "yellowLight" : t.id === "viewer" ? i = "grayLight" : i = ke(t.id), /* @__PURE__ */ e(
249
- Ee,
353
+ const useUserManagement = () => useContext(UserManagementContext);
354
+ function RoleChip({ role }) {
355
+ let colorScheme;
356
+ if (role.isAdmin) {
357
+ colorScheme = "blueDarker";
358
+ } else if (role.id === "editor") {
359
+ colorScheme = "yellowLight";
360
+ } else if (role.id === "viewer") {
361
+ colorScheme = "grayLight";
362
+ } else {
363
+ colorScheme = getColorSchemeForSeed(role.id);
364
+ }
365
+ return /* @__PURE__ */ jsx(
366
+ Chip,
250
367
  {
251
- colorScheme: i,
252
- children: t.name
368
+ colorScheme,
369
+ children: role.name
253
370
  },
254
- t.id
371
+ role.id
255
372
  );
256
373
  }
257
- const He = V.object().shape({
258
- id: V.string().required("Required"),
259
- name: V.string().required("Required")
374
+ const RoleYupSchema = Yup.object().shape({
375
+ id: Yup.string().required("Required"),
376
+ name: Yup.string().required("Required")
260
377
  });
261
- function Ke({
262
- open: t,
263
- role: i,
264
- editable: n,
265
- handleClose: d,
266
- collections: r
378
+ function RolesDetailsForm({
379
+ open,
380
+ role,
381
+ editable,
382
+ handleClose,
383
+ collections
267
384
  }) {
268
- const { saveRole: m } = Y(), f = !i, [p, N] = F(), v = E((o) => (N(void 0), m(o)), [m]), c = Ne({
269
- initialValues: i ?? {
385
+ const { saveRole } = useUserManagement();
386
+ const isNewRole = !role;
387
+ const [savingError, setSavingError] = useState();
388
+ const onRoleUpdated = useCallback((role2) => {
389
+ setSavingError(void 0);
390
+ return saveRole(role2);
391
+ }, [saveRole]);
392
+ const formex = useCreateFormex({
393
+ initialValues: role ?? {
270
394
  name: ""
271
395
  },
272
- onSubmit: (o, S) => v(o).then(() => {
273
- S.resetForm({
274
- values: o
275
- }), d();
276
- }).catch(($) => N($)),
277
- validation: (o) => He.validate(o, { abortEarly: !1 }).then(() => ({})).catch((S) => {
278
- const $ = {};
279
- return S.inner.forEach((J) => {
280
- $[J.path] = J.message;
281
- }), $;
282
- })
283
- }), {
284
- isSubmitting: I,
285
- touched: w,
286
- values: l,
287
- errors: g,
288
- handleChange: R,
289
- setFieldValue: y,
290
- dirty: O,
291
- setFieldTouched: z
292
- } = c, u = l.isAdmin ?? !1, h = l.defaultPermissions?.create ?? !1, x = l.defaultPermissions?.read ?? !1, U = l.defaultPermissions?.edit ?? !1, k = l.defaultPermissions?.delete ?? !1;
293
- return A.useEffect(() => {
294
- !j(w, "id") && l.name && y("id", Te(l.name));
295
- }, [w, l.name]), /* @__PURE__ */ e(
296
- de,
396
+ onSubmit: (role2, formexController) => {
397
+ return onRoleUpdated(role2).then(() => {
398
+ formexController.resetForm({
399
+ values: role2
400
+ });
401
+ handleClose();
402
+ }).catch((e) => setSavingError(e));
403
+ },
404
+ validation: (values2) => {
405
+ return RoleYupSchema.validate(values2, { abortEarly: false }).then(() => ({})).catch((e) => {
406
+ const errors2 = {};
407
+ e.inner.forEach((error) => {
408
+ errors2[error.path] = error.message;
409
+ });
410
+ return errors2;
411
+ });
412
+ }
413
+ });
414
+ const {
415
+ isSubmitting,
416
+ touched,
417
+ values,
418
+ errors,
419
+ handleChange,
420
+ setFieldValue,
421
+ dirty,
422
+ setFieldTouched
423
+ } = formex;
424
+ const isAdmin = values.isAdmin ?? false;
425
+ const defaultCreate = values.defaultPermissions?.create ?? false;
426
+ const defaultRead = values.defaultPermissions?.read ?? false;
427
+ const defaultEdit = values.defaultPermissions?.edit ?? false;
428
+ const defaultDelete = values.defaultPermissions?.delete ?? false;
429
+ React.useEffect(() => {
430
+ const idTouched = getIn(touched, "id");
431
+ if (!idTouched && values.name) {
432
+ setFieldValue("id", toSnakeCase(values.name));
433
+ }
434
+ }, [touched, values.name]);
435
+ return /* @__PURE__ */ jsx(
436
+ Dialog,
297
437
  {
298
- open: t,
438
+ open,
299
439
  maxWidth: "4xl",
300
- children: /* @__PURE__ */ e(xe, { value: c, children: /* @__PURE__ */ s(
440
+ children: /* @__PURE__ */ jsx(Formex, { value: formex, children: /* @__PURE__ */ jsxs(
301
441
  "form",
302
442
  {
303
- noValidate: !0,
443
+ noValidate: true,
304
444
  autoComplete: "off",
305
- onSubmit: c.handleSubmit,
445
+ onSubmit: formex.handleSubmit,
306
446
  style: {
307
447
  display: "flex",
308
448
  flexDirection: "column",
@@ -310,13 +450,13 @@ function Ke({
310
450
  height: "100%"
311
451
  },
312
452
  children: [
313
- /* @__PURE__ */ s(ue, { className: "flex-grow", children: [
314
- /* @__PURE__ */ e(
453
+ /* @__PURE__ */ jsxs(DialogContent, { className: "flex-grow", children: [
454
+ /* @__PURE__ */ jsx(
315
455
  "div",
316
456
  {
317
457
  className: "flex flex-row pt-12 pb-8",
318
- children: /* @__PURE__ */ e(
319
- _,
458
+ children: /* @__PURE__ */ jsx(
459
+ Typography,
320
460
  {
321
461
  variant: "h4",
322
462
  className: "flex-grow",
@@ -325,161 +465,162 @@ function Ke({
325
465
  )
326
466
  }
327
467
  ),
328
- /* @__PURE__ */ s("div", { className: "grid grid-cols-12 gap-8", children: [
329
- /* @__PURE__ */ s("div", { className: "col-span-12 md:col-span-8", children: [
330
- /* @__PURE__ */ e(
331
- K,
468
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-12 gap-8", children: [
469
+ /* @__PURE__ */ jsxs("div", { className: "col-span-12 md:col-span-8", children: [
470
+ /* @__PURE__ */ jsx(
471
+ TextField,
332
472
  {
333
473
  name: "name",
334
- required: !0,
335
- error: w.name && !!g.name,
336
- value: l.name,
337
- disabled: u || !n,
338
- onChange: R,
474
+ required: true,
475
+ error: touched.name && Boolean(errors.name),
476
+ value: values.name,
477
+ disabled: isAdmin || !editable,
478
+ onChange: handleChange,
339
479
  "aria-describedby": "name-helper-text",
340
480
  label: "Name"
341
481
  }
342
482
  ),
343
- /* @__PURE__ */ e(B, { children: w.name && g.name ? g.name : "Name of this role" })
483
+ /* @__PURE__ */ jsx(FieldCaption, { children: touched.name && Boolean(errors.name) ? errors.name : "Name of this role" })
344
484
  ] }),
345
- /* @__PURE__ */ s("div", { className: "col-span-12 md:col-span-4", children: [
346
- /* @__PURE__ */ e(
347
- K,
485
+ /* @__PURE__ */ jsxs("div", { className: "col-span-12 md:col-span-4", children: [
486
+ /* @__PURE__ */ jsx(
487
+ TextField,
348
488
  {
349
489
  name: "id",
350
- required: !0,
351
- error: w.id && !!g.id,
352
- value: l.id,
353
- disabled: !f || !n,
354
- onChange: (o) => {
355
- R(o), z("id", !0);
490
+ required: true,
491
+ error: touched.id && Boolean(errors.id),
492
+ value: values.id,
493
+ disabled: !isNewRole || !editable,
494
+ onChange: (e) => {
495
+ handleChange(e);
496
+ setFieldTouched("id", true);
356
497
  },
357
498
  "aria-describedby": "id-helper-text",
358
499
  label: "ID"
359
500
  }
360
501
  ),
361
- /* @__PURE__ */ e(B, { children: w.id && g.id ? g.id : "ID of this role" })
502
+ /* @__PURE__ */ jsx(FieldCaption, { children: touched.id && Boolean(errors.id) ? errors.id : "ID of this role" })
362
503
  ] }),
363
- /* @__PURE__ */ s("div", { className: "col-span-12", children: [
364
- /* @__PURE__ */ e(
365
- Pe,
504
+ /* @__PURE__ */ jsxs("div", { className: "col-span-12", children: [
505
+ /* @__PURE__ */ jsx(
506
+ Paper,
366
507
  {
367
508
  className: "bg-inherit",
368
- children: /* @__PURE__ */ s(ie, { children: [
369
- /* @__PURE__ */ s(re, { children: [
370
- /* @__PURE__ */ e(a, {}),
371
- /* @__PURE__ */ e(
372
- a,
509
+ children: /* @__PURE__ */ jsxs(Table, { children: [
510
+ /* @__PURE__ */ jsxs(TableHeader, { children: [
511
+ /* @__PURE__ */ jsx(TableCell, {}),
512
+ /* @__PURE__ */ jsx(
513
+ TableCell,
373
514
  {
374
515
  align: "center",
375
516
  children: "Create entities"
376
517
  }
377
518
  ),
378
- /* @__PURE__ */ e(
379
- a,
519
+ /* @__PURE__ */ jsx(
520
+ TableCell,
380
521
  {
381
522
  align: "center",
382
523
  children: "Read entities"
383
524
  }
384
525
  ),
385
- /* @__PURE__ */ e(
386
- a,
526
+ /* @__PURE__ */ jsx(
527
+ TableCell,
387
528
  {
388
529
  align: "center",
389
530
  children: "Update entities"
390
531
  }
391
532
  ),
392
- /* @__PURE__ */ e(
393
- a,
533
+ /* @__PURE__ */ jsx(
534
+ TableCell,
394
535
  {
395
536
  align: "center",
396
537
  children: "Delete entities"
397
538
  }
398
539
  )
399
540
  ] }),
400
- /* @__PURE__ */ s(oe, { children: [
401
- /* @__PURE__ */ s(M, { children: [
402
- /* @__PURE__ */ e(
403
- a,
541
+ /* @__PURE__ */ jsxs(TableBody, { children: [
542
+ /* @__PURE__ */ jsxs(TableRow, { children: [
543
+ /* @__PURE__ */ jsx(
544
+ TableCell,
404
545
  {
405
546
  scope: "row",
406
- children: /* @__PURE__ */ e("strong", { children: "All collections" })
547
+ children: /* @__PURE__ */ jsx("strong", { children: "All collections" })
407
548
  }
408
549
  ),
409
- /* @__PURE__ */ e(
410
- a,
550
+ /* @__PURE__ */ jsx(
551
+ TableCell,
411
552
  {
412
553
  align: "center",
413
- children: /* @__PURE__ */ e(
414
- L,
554
+ children: /* @__PURE__ */ jsx(
555
+ Tooltip,
415
556
  {
416
557
  title: "Create entities in collections",
417
- children: /* @__PURE__ */ e(
418
- P,
558
+ children: /* @__PURE__ */ jsx(
559
+ Checkbox,
419
560
  {
420
- disabled: u || !n,
421
- checked: (u || h) ?? !1,
422
- onCheckedChange: (o) => y("defaultPermissions.create", o)
561
+ disabled: isAdmin || !editable,
562
+ checked: (isAdmin || defaultCreate) ?? false,
563
+ onCheckedChange: (checked) => setFieldValue("defaultPermissions.create", checked)
423
564
  }
424
565
  )
425
566
  }
426
567
  )
427
568
  }
428
569
  ),
429
- /* @__PURE__ */ e(
430
- a,
570
+ /* @__PURE__ */ jsx(
571
+ TableCell,
431
572
  {
432
573
  align: "center",
433
- children: /* @__PURE__ */ e(
434
- L,
574
+ children: /* @__PURE__ */ jsx(
575
+ Tooltip,
435
576
  {
436
577
  title: "Access all data in every collection",
437
- children: /* @__PURE__ */ e(
438
- P,
578
+ children: /* @__PURE__ */ jsx(
579
+ Checkbox,
439
580
  {
440
- disabled: u || !n,
441
- checked: (u || x) ?? !1,
442
- onCheckedChange: (o) => y("defaultPermissions.read", o)
581
+ disabled: isAdmin || !editable,
582
+ checked: (isAdmin || defaultRead) ?? false,
583
+ onCheckedChange: (checked) => setFieldValue("defaultPermissions.read", checked)
443
584
  }
444
585
  )
445
586
  }
446
587
  )
447
588
  }
448
589
  ),
449
- /* @__PURE__ */ e(
450
- a,
590
+ /* @__PURE__ */ jsx(
591
+ TableCell,
451
592
  {
452
593
  align: "center",
453
- children: /* @__PURE__ */ e(
454
- L,
594
+ children: /* @__PURE__ */ jsx(
595
+ Tooltip,
455
596
  {
456
597
  title: "Update data in any collection",
457
- children: /* @__PURE__ */ e(
458
- P,
598
+ children: /* @__PURE__ */ jsx(
599
+ Checkbox,
459
600
  {
460
- disabled: u || !n,
461
- checked: (u || U) ?? !1,
462
- onCheckedChange: (o) => y("defaultPermissions.edit", o)
601
+ disabled: isAdmin || !editable,
602
+ checked: (isAdmin || defaultEdit) ?? false,
603
+ onCheckedChange: (checked) => setFieldValue("defaultPermissions.edit", checked)
463
604
  }
464
605
  )
465
606
  }
466
607
  )
467
608
  }
468
609
  ),
469
- /* @__PURE__ */ e(
470
- a,
610
+ /* @__PURE__ */ jsx(
611
+ TableCell,
471
612
  {
472
613
  align: "center",
473
- children: /* @__PURE__ */ e(
474
- L,
614
+ children: /* @__PURE__ */ jsx(
615
+ Tooltip,
475
616
  {
476
617
  title: "Delete data in any collection",
477
- children: /* @__PURE__ */ e(
478
- P,
618
+ children: /* @__PURE__ */ jsx(
619
+ Checkbox,
479
620
  {
480
- disabled: u || !n,
481
- checked: (u || k) ?? !1,
482
- onCheckedChange: (o) => y("defaultPermissions.delete", o)
621
+ disabled: isAdmin || !editable,
622
+ checked: (isAdmin || defaultDelete) ?? false,
623
+ onCheckedChange: (checked) => setFieldValue("defaultPermissions.delete", checked)
483
624
  }
484
625
  )
485
626
  }
@@ -487,100 +628,100 @@ function Ke({
487
628
  }
488
629
  )
489
630
  ] }),
490
- r && r.map((o) => /* @__PURE__ */ s(M, { children: [
491
- /* @__PURE__ */ e(
492
- a,
631
+ collections && collections.map((col) => /* @__PURE__ */ jsxs(TableRow, { children: [
632
+ /* @__PURE__ */ jsx(
633
+ TableCell,
493
634
  {
494
635
  scope: "row",
495
- children: o.name
636
+ children: col.name
496
637
  }
497
638
  ),
498
- /* @__PURE__ */ e(
499
- a,
639
+ /* @__PURE__ */ jsx(
640
+ TableCell,
500
641
  {
501
642
  align: "center",
502
- children: /* @__PURE__ */ e(
503
- P,
643
+ children: /* @__PURE__ */ jsx(
644
+ Checkbox,
504
645
  {
505
- disabled: u || h || !n,
506
- checked: (u || h || j(l, `collectionPermissions.${o.path}.create`)) ?? !1,
507
- onCheckedChange: (S) => y(`collectionPermissions.${o.path}.create`, S)
646
+ disabled: isAdmin || defaultCreate || !editable,
647
+ checked: (isAdmin || defaultCreate || getIn(values, `collectionPermissions.${col.path}.create`)) ?? false,
648
+ onCheckedChange: (checked) => setFieldValue(`collectionPermissions.${col.path}.create`, checked)
508
649
  }
509
650
  )
510
651
  }
511
652
  ),
512
- /* @__PURE__ */ e(
513
- a,
653
+ /* @__PURE__ */ jsx(
654
+ TableCell,
514
655
  {
515
656
  align: "center",
516
- children: /* @__PURE__ */ e(
517
- P,
657
+ children: /* @__PURE__ */ jsx(
658
+ Checkbox,
518
659
  {
519
- disabled: u || x || !n,
520
- checked: (u || x || j(l, `collectionPermissions.${o.path}.read`)) ?? !1,
521
- onCheckedChange: (S) => y(`collectionPermissions.${o.path}.read`, S)
660
+ disabled: isAdmin || defaultRead || !editable,
661
+ checked: (isAdmin || defaultRead || getIn(values, `collectionPermissions.${col.path}.read`)) ?? false,
662
+ onCheckedChange: (checked) => setFieldValue(`collectionPermissions.${col.path}.read`, checked)
522
663
  }
523
664
  )
524
665
  }
525
666
  ),
526
- /* @__PURE__ */ e(
527
- a,
667
+ /* @__PURE__ */ jsx(
668
+ TableCell,
528
669
  {
529
670
  align: "center",
530
- children: /* @__PURE__ */ e(
531
- P,
671
+ children: /* @__PURE__ */ jsx(
672
+ Checkbox,
532
673
  {
533
- disabled: u || U || !n,
534
- checked: (u || U || j(l, `collectionPermissions.${o.path}.edit`)) ?? !1,
535
- onCheckedChange: (S) => y(`collectionPermissions.${o.path}.edit`, S)
674
+ disabled: isAdmin || defaultEdit || !editable,
675
+ checked: (isAdmin || defaultEdit || getIn(values, `collectionPermissions.${col.path}.edit`)) ?? false,
676
+ onCheckedChange: (checked) => setFieldValue(`collectionPermissions.${col.path}.edit`, checked)
536
677
  }
537
678
  )
538
679
  }
539
680
  ),
540
- /* @__PURE__ */ e(
541
- a,
681
+ /* @__PURE__ */ jsx(
682
+ TableCell,
542
683
  {
543
684
  align: "center",
544
- children: /* @__PURE__ */ e(
545
- P,
685
+ children: /* @__PURE__ */ jsx(
686
+ Checkbox,
546
687
  {
547
- disabled: u || k || !n,
548
- checked: (u || k || j(l, `collectionPermissions.${o.path}.delete`)) ?? !1,
549
- onCheckedChange: (S) => y(`collectionPermissions.${o.path}.delete`, S)
688
+ disabled: isAdmin || defaultDelete || !editable,
689
+ checked: (isAdmin || defaultDelete || getIn(values, `collectionPermissions.${col.path}.delete`)) ?? false,
690
+ onCheckedChange: (checked) => setFieldValue(`collectionPermissions.${col.path}.delete`, checked)
550
691
  }
551
692
  )
552
693
  }
553
694
  )
554
- ] }, o.name))
695
+ ] }, col.name))
555
696
  ] })
556
697
  ] })
557
698
  }
558
699
  ),
559
- /* @__PURE__ */ e(B, { children: "You can customise the permissions that the users related to this role can perform in the entities of each collection" })
700
+ /* @__PURE__ */ jsx(FieldCaption, { children: "You can customise the permissions that the users related to this role can perform in the entities of each collection" })
560
701
  ] }),
561
- /* @__PURE__ */ s("div", { className: "col-span-12 md:col-span-4", children: [
562
- /* @__PURE__ */ s(
563
- ne,
702
+ /* @__PURE__ */ jsxs("div", { className: "col-span-12 md:col-span-4", children: [
703
+ /* @__PURE__ */ jsxs(
704
+ Select,
564
705
  {
565
- error: w.config && !!g.config,
706
+ error: touched.config && Boolean(errors.config),
566
707
  id: "createCollections",
567
708
  name: "createCollections",
568
709
  label: "Create collections",
569
710
  position: "item-aligned",
570
- disabled: u || !n,
571
- onChange: (o) => y("config.createCollections", o.target.value === "true"),
572
- value: u || l.config?.createCollections ? "true" : "false",
573
- renderValue: (o) => o === "true" ? "Yes" : "No",
711
+ disabled: isAdmin || !editable,
712
+ onChange: (event) => setFieldValue("config.createCollections", event.target.value === "true"),
713
+ value: isAdmin || values.config?.createCollections ? "true" : "false",
714
+ renderValue: (value) => value === "true" ? "Yes" : "No",
574
715
  children: [
575
- /* @__PURE__ */ e(
576
- T,
716
+ /* @__PURE__ */ jsx(
717
+ SelectItem,
577
718
  {
578
719
  value: "true",
579
720
  children: " Yes "
580
721
  }
581
722
  ),
582
- /* @__PURE__ */ e(
583
- T,
723
+ /* @__PURE__ */ jsx(
724
+ SelectItem,
584
725
  {
585
726
  value: "false",
586
727
  children: " No "
@@ -589,38 +730,38 @@ function Ke({
589
730
  ]
590
731
  }
591
732
  ),
592
- /* @__PURE__ */ e(B, { children: w.config && g.config ? g.config : "Can the user create collections" })
733
+ /* @__PURE__ */ jsx(FieldCaption, { children: touched.config && Boolean(errors.config) ? errors.config : "Can the user create collections" })
593
734
  ] }),
594
- /* @__PURE__ */ s("div", { className: "col-span-12 md:col-span-4", children: [
595
- /* @__PURE__ */ s(
596
- ne,
735
+ /* @__PURE__ */ jsxs("div", { className: "col-span-12 md:col-span-4", children: [
736
+ /* @__PURE__ */ jsxs(
737
+ Select,
597
738
  {
598
- error: w.config && !!g.config,
739
+ error: touched.config && Boolean(errors.config),
599
740
  id: "editCollections",
600
741
  name: "editCollections",
601
742
  label: "Edit collections",
602
- disabled: u || !n,
743
+ disabled: isAdmin || !editable,
603
744
  position: "item-aligned",
604
- onChange: (o) => y("config.editCollections", o.target.value === "own" ? "own" : o.target.value === "true"),
605
- value: u ? "true" : l.config?.editCollections === "own" ? "own" : l.config?.editCollections ? "true" : "false",
606
- renderValue: (o) => o === "own" ? "Own" : o === "true" ? "Yes" : "No",
745
+ onChange: (event) => setFieldValue("config.editCollections", event.target.value === "own" ? "own" : event.target.value === "true"),
746
+ value: isAdmin ? "true" : values.config?.editCollections === "own" ? "own" : values.config?.editCollections ? "true" : "false",
747
+ renderValue: (value) => value === "own" ? "Own" : value === "true" ? "Yes" : "No",
607
748
  children: [
608
- /* @__PURE__ */ e(
609
- T,
749
+ /* @__PURE__ */ jsx(
750
+ SelectItem,
610
751
  {
611
752
  value: "true",
612
753
  children: " Yes "
613
754
  }
614
755
  ),
615
- /* @__PURE__ */ e(
616
- T,
756
+ /* @__PURE__ */ jsx(
757
+ SelectItem,
617
758
  {
618
759
  value: "false",
619
760
  children: " No "
620
761
  }
621
762
  ),
622
- /* @__PURE__ */ e(
623
- T,
763
+ /* @__PURE__ */ jsx(
764
+ SelectItem,
624
765
  {
625
766
  value: "own",
626
767
  children: " Only his/her own "
@@ -629,38 +770,38 @@ function Ke({
629
770
  ]
630
771
  }
631
772
  ),
632
- /* @__PURE__ */ e(B, { children: w.config && g.config ? g.config : "Can the user edit collections" })
773
+ /* @__PURE__ */ jsx(FieldCaption, { children: touched.config && Boolean(errors.config) ? errors.config : "Can the user edit collections" })
633
774
  ] }),
634
- /* @__PURE__ */ s("div", { className: "col-span-12 md:col-span-4", children: [
635
- /* @__PURE__ */ s(
636
- ne,
775
+ /* @__PURE__ */ jsxs("div", { className: "col-span-12 md:col-span-4", children: [
776
+ /* @__PURE__ */ jsxs(
777
+ Select,
637
778
  {
638
- error: w.config && !!g.config,
779
+ error: touched.config && Boolean(errors.config),
639
780
  id: "deleteCollections",
640
781
  name: "deleteCollections",
641
782
  label: "Delete collections",
642
- disabled: u || !n,
783
+ disabled: isAdmin || !editable,
643
784
  position: "item-aligned",
644
- onChange: (o) => y("config.deleteCollections", o.target.value === "own" ? "own" : o.target.value === "true"),
645
- value: u ? "true" : l.config?.deleteCollections === "own" ? "own" : l.config?.deleteCollections ? "true" : "false",
646
- renderValue: (o) => o === "own" ? "Own" : o === "true" ? "Yes" : "No",
785
+ onChange: (event) => setFieldValue("config.deleteCollections", event.target.value === "own" ? "own" : event.target.value === "true"),
786
+ value: isAdmin ? "true" : values.config?.deleteCollections === "own" ? "own" : values.config?.deleteCollections ? "true" : "false",
787
+ renderValue: (value) => value === "own" ? "Own" : value === "true" ? "Yes" : "No",
647
788
  children: [
648
- /* @__PURE__ */ e(
649
- T,
789
+ /* @__PURE__ */ jsx(
790
+ SelectItem,
650
791
  {
651
792
  value: "true",
652
793
  children: " Yes "
653
794
  }
654
795
  ),
655
- /* @__PURE__ */ e(
656
- T,
796
+ /* @__PURE__ */ jsx(
797
+ SelectItem,
657
798
  {
658
799
  value: "false",
659
800
  children: " No "
660
801
  }
661
802
  ),
662
- /* @__PURE__ */ e(
663
- T,
803
+ /* @__PURE__ */ jsx(
804
+ SelectItem,
664
805
  {
665
806
  value: "own",
666
807
  children: " Only his/her own "
@@ -669,32 +810,32 @@ function Ke({
669
810
  ]
670
811
  }
671
812
  ),
672
- /* @__PURE__ */ e(B, { children: w.config && g.config ? g.config : "Can the user delete collections" })
813
+ /* @__PURE__ */ jsx(FieldCaption, { children: touched.config && Boolean(errors.config) ? errors.config : "Can the user delete collections" })
673
814
  ] })
674
815
  ] })
675
816
  ] }),
676
- /* @__PURE__ */ s(me, { position: "sticky", children: [
677
- p && /* @__PURE__ */ e(_, { className: "text-red-500", children: "There was an error saving this role" }),
678
- /* @__PURE__ */ e(
679
- q,
817
+ /* @__PURE__ */ jsxs(DialogActions, { position: "sticky", children: [
818
+ savingError && /* @__PURE__ */ jsx(Typography, { className: "text-red-500", children: "There was an error saving this role" }),
819
+ /* @__PURE__ */ jsx(
820
+ Button,
680
821
  {
681
822
  variant: "text",
682
823
  onClick: () => {
683
- d();
824
+ handleClose();
684
825
  },
685
826
  children: "Cancel"
686
827
  }
687
828
  ),
688
- /* @__PURE__ */ e(
689
- he,
829
+ /* @__PURE__ */ jsx(
830
+ LoadingButton,
690
831
  {
691
832
  variant: "filled",
692
833
  color: "primary",
693
834
  type: "submit",
694
- disabled: !O,
695
- loading: I,
696
- startIcon: /* @__PURE__ */ e(fe, {}),
697
- children: f ? "Create role" : "Update"
835
+ disabled: !dirty,
836
+ loading: isSubmitting,
837
+ startIcon: /* @__PURE__ */ jsx(DoneIcon, {}),
838
+ children: isNewRole ? "Create role" : "Update"
698
839
  }
699
840
  )
700
841
  ] })
@@ -704,24 +845,24 @@ function Ke({
704
845
  }
705
846
  );
706
847
  }
707
- const Qe = [
848
+ const DEFAULT_ROLES = [
708
849
  {
709
850
  id: "admin",
710
851
  name: "Admin",
711
- isAdmin: !0
852
+ isAdmin: true
712
853
  },
713
854
  {
714
855
  id: "editor",
715
856
  name: "Editor",
716
- isAdmin: !1,
857
+ isAdmin: false,
717
858
  defaultPermissions: {
718
- read: !0,
719
- create: !0,
720
- edit: !0,
721
- delete: !0
859
+ read: true,
860
+ create: true,
861
+ edit: true,
862
+ delete: true
722
863
  },
723
864
  config: {
724
- createCollections: !0,
865
+ createCollections: true,
725
866
  editCollections: "own",
726
867
  deleteCollections: "own"
727
868
  }
@@ -729,78 +870,86 @@ const Qe = [
729
870
  {
730
871
  id: "viewer",
731
872
  name: "Viewer",
732
- isAdmin: !1,
873
+ isAdmin: false,
733
874
  defaultPermissions: {
734
- read: !0,
735
- create: !1,
736
- edit: !1,
737
- delete: !1
875
+ read: true,
876
+ create: false,
877
+ edit: false,
878
+ delete: false
738
879
  }
739
880
  }
740
881
  ];
741
- function Xe({
742
- onRoleClicked: t,
743
- editable: i
882
+ function RolesTable({
883
+ onRoleClicked,
884
+ editable
744
885
  }) {
745
886
  const {
746
- roles: n,
747
- saveRole: d,
748
- deleteRole: r,
749
- allowDefaultRolesCreation: m
750
- } = Y(), [f, p] = F(void 0), [N, v] = F(!1);
751
- return /* @__PURE__ */ s(
887
+ roles,
888
+ saveRole,
889
+ deleteRole,
890
+ allowDefaultRolesCreation
891
+ } = useUserManagement();
892
+ const [roleToBeDeleted, setRoleToBeDeleted] = useState(void 0);
893
+ const [deleteInProgress, setDeleteInProgress] = useState(false);
894
+ return /* @__PURE__ */ jsxs(
752
895
  "div",
753
896
  {
754
897
  className: "w-full overflow-auto",
755
898
  children: [
756
- /* @__PURE__ */ s(ie, { children: [
757
- /* @__PURE__ */ s(re, { children: [
758
- /* @__PURE__ */ e(a, { header: !0, className: "w-16" }),
759
- /* @__PURE__ */ e(a, { header: !0, children: "Role" }),
760
- /* @__PURE__ */ e(a, { header: !0, className: "items-center", children: "Is Admin" }),
761
- /* @__PURE__ */ e(a, { header: !0, children: "Default permissions" })
899
+ /* @__PURE__ */ jsxs(Table, { children: [
900
+ /* @__PURE__ */ jsxs(TableHeader, { children: [
901
+ /* @__PURE__ */ jsx(TableCell, { header: true, className: "w-16" }),
902
+ /* @__PURE__ */ jsx(TableCell, { header: true, children: "Role" }),
903
+ /* @__PURE__ */ jsx(TableCell, { header: true, className: "items-center", children: "Is Admin" }),
904
+ /* @__PURE__ */ jsx(TableCell, { header: true, children: "Default permissions" })
762
905
  ] }),
763
- /* @__PURE__ */ s(oe, { children: [
764
- n && n.map((c) => {
765
- const I = c.isAdmin || c.defaultPermissions?.create, w = c.isAdmin || c.defaultPermissions?.read, l = c.isAdmin || c.defaultPermissions?.edit, g = c.isAdmin || c.defaultPermissions?.delete;
766
- return /* @__PURE__ */ s(
767
- M,
906
+ /* @__PURE__ */ jsxs(TableBody, { children: [
907
+ roles && roles.map((role) => {
908
+ const canCreateAll = role.isAdmin || role.defaultPermissions?.create;
909
+ const canReadAll = role.isAdmin || role.defaultPermissions?.read;
910
+ const canUpdateAll = role.isAdmin || role.defaultPermissions?.edit;
911
+ const canDeleteAll = role.isAdmin || role.defaultPermissions?.delete;
912
+ return /* @__PURE__ */ jsxs(
913
+ TableRow,
768
914
  {
769
915
  onClick: () => {
770
- t(c);
916
+ onRoleClicked(role);
771
917
  },
772
918
  children: [
773
- /* @__PURE__ */ e(a, { style: { width: "64px" }, children: !c.isAdmin && /* @__PURE__ */ e(L, { title: "Delete this role", children: /* @__PURE__ */ e(
774
- ge,
919
+ /* @__PURE__ */ jsx(TableCell, { style: { width: "64px" }, children: !role.isAdmin && /* @__PURE__ */ jsx(Tooltip, { title: "Delete this role", children: /* @__PURE__ */ jsx(
920
+ IconButton,
775
921
  {
776
922
  size: "small",
777
- disabled: !i,
778
- onClick: (R) => (R.stopPropagation(), p(c)),
779
- children: /* @__PURE__ */ e(pe, {})
923
+ disabled: !editable,
924
+ onClick: (event) => {
925
+ event.stopPropagation();
926
+ return setRoleToBeDeleted(role);
927
+ },
928
+ children: /* @__PURE__ */ jsx(DeleteIcon, {})
780
929
  }
781
930
  ) }) }),
782
- /* @__PURE__ */ e(a, { children: /* @__PURE__ */ e(Q, { role: c }) }),
783
- /* @__PURE__ */ e(a, { className: "items-center", children: /* @__PURE__ */ e(P, { checked: c.isAdmin ?? !1 }) }),
784
- /* @__PURE__ */ e(a, { children: /* @__PURE__ */ s("ul", { children: [
785
- I && /* @__PURE__ */ e("li", { children: "Create" }),
786
- w && /* @__PURE__ */ e("li", { children: "Read" }),
787
- l && /* @__PURE__ */ e("li", { children: "Update" }),
788
- g && /* @__PURE__ */ e("li", { children: "Delete" })
931
+ /* @__PURE__ */ jsx(TableCell, { children: /* @__PURE__ */ jsx(RoleChip, { role }) }),
932
+ /* @__PURE__ */ jsx(TableCell, { className: "items-center", children: /* @__PURE__ */ jsx(Checkbox, { checked: role.isAdmin ?? false }) }),
933
+ /* @__PURE__ */ jsx(TableCell, { children: /* @__PURE__ */ jsxs("ul", { children: [
934
+ canCreateAll && /* @__PURE__ */ jsx("li", { children: "Create" }),
935
+ canReadAll && /* @__PURE__ */ jsx("li", { children: "Read" }),
936
+ canUpdateAll && /* @__PURE__ */ jsx("li", { children: "Update" }),
937
+ canDeleteAll && /* @__PURE__ */ jsx("li", { children: "Delete" })
789
938
  ] }) })
790
939
  ]
791
940
  },
792
- c.name
941
+ role.name
793
942
  );
794
943
  }),
795
- (!n || n.length === 0) && /* @__PURE__ */ e(M, { children: /* @__PURE__ */ e(a, { colspan: 4, children: /* @__PURE__ */ s(Ce, { className: "flex flex-col gap-4 my-8 items-center", children: [
796
- /* @__PURE__ */ e(_, { variant: "label", children: "You don't have any roles yet." }),
797
- m && /* @__PURE__ */ e(
798
- q,
944
+ (!roles || roles.length === 0) && /* @__PURE__ */ jsx(TableRow, { children: /* @__PURE__ */ jsx(TableCell, { colspan: 4, children: /* @__PURE__ */ jsxs(CenteredView, { className: "flex flex-col gap-4 my-8 items-center", children: [
945
+ /* @__PURE__ */ jsx(Typography, { variant: "label", children: "You don't have any roles yet." }),
946
+ allowDefaultRolesCreation && /* @__PURE__ */ jsx(
947
+ Button,
799
948
  {
800
949
  variant: "outlined",
801
950
  onClick: () => {
802
- Qe.forEach((c) => {
803
- d(c);
951
+ DEFAULT_ROLES.forEach((role) => {
952
+ saveRole(role);
804
953
  });
805
954
  },
806
955
  children: "Create default roles"
@@ -809,145 +958,182 @@ function Xe({
809
958
  ] }) }) })
810
959
  ] })
811
960
  ] }),
812
- /* @__PURE__ */ e(
813
- ye,
961
+ /* @__PURE__ */ jsx(
962
+ DeleteConfirmationDialog,
814
963
  {
815
- open: !!f,
816
- loading: N,
964
+ open: Boolean(roleToBeDeleted),
965
+ loading: deleteInProgress,
817
966
  onAccept: () => {
818
- f && (v(!0), r(f).then(() => {
819
- p(void 0);
820
- }).finally(() => {
821
- v(!1);
822
- }));
967
+ if (roleToBeDeleted) {
968
+ setDeleteInProgress(true);
969
+ deleteRole(roleToBeDeleted).then(() => {
970
+ setRoleToBeDeleted(void 0);
971
+ }).finally(() => {
972
+ setDeleteInProgress(false);
973
+ });
974
+ }
823
975
  },
824
976
  onCancel: () => {
825
- p(void 0);
977
+ setRoleToBeDeleted(void 0);
826
978
  },
827
- title: /* @__PURE__ */ e(H, { children: "Delete?" }),
828
- body: /* @__PURE__ */ e(H, { children: "Are you sure you want to delete this role?" })
979
+ title: /* @__PURE__ */ jsx(Fragment, { children: "Delete?" }),
980
+ body: /* @__PURE__ */ jsx(Fragment, { children: "Are you sure you want to delete this role?" })
829
981
  }
830
982
  )
831
983
  ]
832
984
  }
833
985
  );
834
986
  }
835
- const Ze = A.memo(
836
- function({ children: i }) {
837
- const { collections: n } = Be(), [d, r] = F(!1), [m, f] = F(), { canEditRoles: p } = Y(), N = E((c) => {
838
- r(!0), f(c);
987
+ const RolesView = React.memo(
988
+ function RolesView2({ children }) {
989
+ const { collections } = useNavigationController();
990
+ const [dialogOpen, setDialogOpen] = useState(false);
991
+ const [selectedRole, setSelectedRole] = useState();
992
+ const { canEditRoles } = useUserManagement();
993
+ const onRoleClicked = useCallback((user) => {
994
+ setDialogOpen(true);
995
+ setSelectedRole(user);
839
996
  }, []);
840
- return /* @__PURE__ */ s(ve, { className: "w-full flex flex-col py-4 gap-4", maxWidth: "6xl", children: [
841
- i,
842
- /* @__PURE__ */ s("div", { className: "flex items-center mt-12", children: [
843
- /* @__PURE__ */ e(
844
- _,
997
+ const handleClose = () => {
998
+ setSelectedRole(void 0);
999
+ setDialogOpen(false);
1000
+ };
1001
+ return /* @__PURE__ */ jsxs(Container, { className: "w-full flex flex-col py-4 gap-4", maxWidth: "6xl", children: [
1002
+ children,
1003
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center mt-12", children: [
1004
+ /* @__PURE__ */ jsx(
1005
+ Typography,
845
1006
  {
846
- gutterBottom: !0,
1007
+ gutterBottom: true,
847
1008
  variant: "h4",
848
1009
  className: "flex-grow",
849
1010
  component: "h4",
850
1011
  children: "Roles"
851
1012
  }
852
1013
  ),
853
- /* @__PURE__ */ e(L, { title: p ? void 0 : "Update plans to customise roles", children: /* @__PURE__ */ e(
854
- q,
1014
+ /* @__PURE__ */ jsx(Tooltip, { title: !canEditRoles ? "Update plans to customise roles" : void 0, children: /* @__PURE__ */ jsx(
1015
+ Button,
855
1016
  {
856
1017
  size: "large",
857
- disabled: !p,
858
- startIcon: /* @__PURE__ */ e(we, {}),
859
- onClick: () => r(!0),
1018
+ disabled: !canEditRoles,
1019
+ startIcon: /* @__PURE__ */ jsx(AddIcon, {}),
1020
+ onClick: () => setDialogOpen(true),
860
1021
  children: "Add role"
861
1022
  }
862
1023
  ) })
863
1024
  ] }),
864
- /* @__PURE__ */ e(Xe, { onRoleClicked: N, editable: !!p }),
865
- /* @__PURE__ */ e(
866
- Ke,
1025
+ /* @__PURE__ */ jsx(RolesTable, { onRoleClicked, editable: Boolean(canEditRoles) }),
1026
+ /* @__PURE__ */ jsx(
1027
+ RolesDetailsForm,
867
1028
  {
868
- open: d,
869
- role: m,
870
- editable: p,
871
- collections: n,
872
- handleClose: () => {
873
- f(void 0), r(!1);
874
- }
1029
+ open: dialogOpen,
1030
+ role: selectedRole,
1031
+ editable: canEditRoles,
1032
+ collections,
1033
+ handleClose
875
1034
  },
876
- m?.id ?? "new"
1035
+ selectedRole?.id ?? "new"
877
1036
  )
878
1037
  ] });
879
1038
  }
880
- ), en = V.object().shape({
881
- displayName: V.string().required("Required"),
882
- email: V.string().email().required("Required"),
883
- roles: V.array().min(1)
1039
+ );
1040
+ const UserYupSchema = Yup.object().shape({
1041
+ displayName: Yup.string().required("Required"),
1042
+ email: Yup.string().email().required("Required"),
1043
+ roles: Yup.array().min(1)
884
1044
  });
885
- function nn(t, i, n, d, r) {
886
- const m = n.filter((v) => v.roles?.map((c) => c.id).includes("admin")), f = t.roles?.map((v) => v.id).includes("admin");
887
- if ((!r || !We(r.roles ?? [], i.roles ?? [])) && !f)
1045
+ function canUserBeEdited(loggedUser, user, users, roles, prevUser) {
1046
+ const admins = users.filter((u) => u.roles?.map((r) => r.id).includes("admin"));
1047
+ const loggedUserIsAdmin = loggedUser.roles?.map((r) => r.id).includes("admin");
1048
+ const didRolesChange = !prevUser || !areRolesEqual(prevUser.roles ?? [], user.roles ?? []);
1049
+ if (didRolesChange && !loggedUserIsAdmin) {
888
1050
  throw new Error("Only admins can change roles");
889
- if (r && r.roles?.map((v) => v.id).includes("admin") && !i.roles?.map((v) => v.id).includes("admin") && m.length === 1)
1051
+ }
1052
+ const adminRoleRemoved = prevUser && prevUser.roles?.map((r) => r.id).includes("admin") && !user.roles?.map((r) => r.id).includes("admin");
1053
+ if (adminRoleRemoved && admins.length === 1) {
890
1054
  throw new Error("There must be at least one admin");
891
- return !0;
1055
+ }
1056
+ return true;
892
1057
  }
893
- function tn({
894
- open: t,
895
- user: i,
896
- handleClose: n
1058
+ function UserDetailsForm({
1059
+ open,
1060
+ user: userProp,
1061
+ handleClose
897
1062
  }) {
898
- const d = be(), {
899
- loggedInUser: r,
900
- saveUser: m,
901
- users: f,
902
- roles: p
903
- } = Y(), N = !i, v = E((h) => {
904
- if (!r)
1063
+ const snackbarController = useSnackbarController();
1064
+ const {
1065
+ user: loggedInUser
1066
+ } = useAuthController();
1067
+ const {
1068
+ saveUser,
1069
+ users,
1070
+ roles
1071
+ } = useUserManagement();
1072
+ const isNewUser = !userProp;
1073
+ const onUserUpdated = useCallback((savedUser) => {
1074
+ if (!loggedInUser) {
905
1075
  throw new Error("Logged user not found");
1076
+ }
906
1077
  try {
907
- return nn(r, h, f, p, i), m(h);
908
- } catch (x) {
909
- return Promise.reject(x);
1078
+ canUserBeEdited(loggedInUser, savedUser, users, roles, userProp);
1079
+ return saveUser(savedUser);
1080
+ } catch (e) {
1081
+ return Promise.reject(e);
910
1082
  }
911
- }, [p, m, i, f, r]), c = Ne({
912
- initialValues: i ?? {
1083
+ }, [roles, saveUser, userProp, users, loggedInUser]);
1084
+ const formex = useCreateFormex({
1085
+ initialValues: userProp ?? {
913
1086
  displayName: "",
914
1087
  email: "",
915
- roles: p.filter((h) => h.id === "editor")
1088
+ roles: roles.filter((r) => r.id === "editor")
916
1089
  },
917
- validation: (h) => en.validate(h, { abortEarly: !1 }).then(() => ({})).catch((x) => x.inner.reduce((U, k) => (U[k.path] = k.message, U), {})),
918
- onSubmit: (h, x) => v(h).then(() => {
919
- n(), x.resetForm({
920
- values: h
1090
+ validation: (values2) => {
1091
+ return UserYupSchema.validate(values2, { abortEarly: false }).then(() => {
1092
+ return {};
1093
+ }).catch((e) => {
1094
+ return e.inner.reduce((acc, error) => {
1095
+ acc[error.path] = error.message;
1096
+ return acc;
1097
+ }, {});
921
1098
  });
922
- }).catch((U) => {
923
- d.open({
924
- type: "error",
925
- message: U.message
1099
+ },
1100
+ onSubmit: (user, formexController) => {
1101
+ return onUserUpdated(user).then(() => {
1102
+ handleClose();
1103
+ formexController.resetForm({
1104
+ values: user
1105
+ });
1106
+ }).catch((e) => {
1107
+ snackbarController.open({
1108
+ type: "error",
1109
+ message: e.message
1110
+ });
926
1111
  });
927
- })
928
- }), {
929
- isSubmitting: I,
930
- touched: w,
931
- handleChange: l,
932
- values: g,
933
- errors: R,
934
- setFieldValue: y,
935
- dirty: O,
936
- handleSubmit: z,
937
- submitCount: u
938
- } = c;
939
- return /* @__PURE__ */ e(
940
- de,
1112
+ }
1113
+ });
1114
+ const {
1115
+ isSubmitting,
1116
+ touched,
1117
+ handleChange,
1118
+ values,
1119
+ errors,
1120
+ setFieldValue,
1121
+ dirty,
1122
+ handleSubmit,
1123
+ submitCount
1124
+ } = formex;
1125
+ return /* @__PURE__ */ jsx(
1126
+ Dialog,
941
1127
  {
942
- open: t,
943
- onOpenChange: (h) => h ? void 0 : n(),
1128
+ open,
1129
+ onOpenChange: (open2) => !open2 ? handleClose() : void 0,
944
1130
  maxWidth: "4xl",
945
- children: /* @__PURE__ */ e(xe, { value: c, children: /* @__PURE__ */ s(
1131
+ children: /* @__PURE__ */ jsx(Formex, { value: formex, children: /* @__PURE__ */ jsxs(
946
1132
  "form",
947
1133
  {
948
- onSubmit: z,
1134
+ onSubmit: handleSubmit,
949
1135
  autoComplete: "off",
950
- noValidate: !0,
1136
+ noValidate: true,
951
1137
  style: {
952
1138
  display: "flex",
953
1139
  flexDirection: "column",
@@ -955,13 +1141,13 @@ function tn({
955
1141
  height: "100%"
956
1142
  },
957
1143
  children: [
958
- /* @__PURE__ */ s(ue, { className: "h-full flex-grow", children: [
959
- /* @__PURE__ */ e(
1144
+ /* @__PURE__ */ jsxs(DialogContent, { className: "h-full flex-grow", children: [
1145
+ /* @__PURE__ */ jsx(
960
1146
  "div",
961
1147
  {
962
1148
  className: "flex flex-row pt-4 pb-4",
963
- children: /* @__PURE__ */ e(
964
- _,
1149
+ children: /* @__PURE__ */ jsx(
1150
+ Typography,
965
1151
  {
966
1152
  variant: "h4",
967
1153
  className: "flex-grow",
@@ -970,80 +1156,81 @@ function tn({
970
1156
  )
971
1157
  }
972
1158
  ),
973
- /* @__PURE__ */ s("div", { className: "grid grid-cols-12 gap-8", children: [
974
- /* @__PURE__ */ s("div", { className: "col-span-12", children: [
975
- /* @__PURE__ */ e(
976
- K,
1159
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-12 gap-8", children: [
1160
+ /* @__PURE__ */ jsxs("div", { className: "col-span-12", children: [
1161
+ /* @__PURE__ */ jsx(
1162
+ TextField,
977
1163
  {
978
1164
  name: "displayName",
979
- required: !0,
980
- error: u > 0 && !!R.displayName,
981
- value: g.displayName ?? "",
982
- onChange: l,
1165
+ required: true,
1166
+ error: submitCount > 0 && Boolean(errors.displayName),
1167
+ value: values.displayName ?? "",
1168
+ onChange: handleChange,
983
1169
  "aria-describedby": "name-helper-text",
984
1170
  label: "Name"
985
1171
  }
986
1172
  ),
987
- /* @__PURE__ */ e(B, { children: u > 0 && R.displayName ? R.displayName : "Name of this user" })
1173
+ /* @__PURE__ */ jsx(FieldCaption, { children: submitCount > 0 && Boolean(errors.displayName) ? errors.displayName : "Name of this user" })
988
1174
  ] }),
989
- /* @__PURE__ */ s("div", { className: "col-span-12", children: [
990
- /* @__PURE__ */ e(
991
- K,
1175
+ /* @__PURE__ */ jsxs("div", { className: "col-span-12", children: [
1176
+ /* @__PURE__ */ jsx(
1177
+ TextField,
992
1178
  {
993
- required: !0,
994
- error: u > 0 && !!R.email,
1179
+ required: true,
1180
+ error: submitCount > 0 && Boolean(errors.email),
995
1181
  name: "email",
996
- value: g.email ?? "",
997
- onChange: l,
1182
+ value: values.email ?? "",
1183
+ onChange: handleChange,
998
1184
  "aria-describedby": "email-helper-text",
999
1185
  label: "Email"
1000
1186
  }
1001
1187
  ),
1002
- /* @__PURE__ */ e(B, { children: u > 0 && R.email ? R.email : "Email of this user" })
1188
+ /* @__PURE__ */ jsx(FieldCaption, { children: submitCount > 0 && Boolean(errors.email) ? errors.email : "Email of this user" })
1003
1189
  ] }),
1004
- /* @__PURE__ */ e("div", { className: "col-span-12", children: /* @__PURE__ */ e(
1005
- Ae,
1190
+ /* @__PURE__ */ jsx("div", { className: "col-span-12", children: /* @__PURE__ */ jsx(
1191
+ MultiSelect,
1006
1192
  {
1007
1193
  label: "Roles",
1008
- value: g.roles?.map((h) => h.id) ?? [],
1009
- onMultiValueChange: (h) => y("roles", h.map((x) => p.find((U) => U.id === x))),
1010
- renderValue: (h) => {
1011
- const x = p.find((U) => U.id === h);
1012
- return x ? /* @__PURE__ */ e("div", { className: "flex flex-wrap space-x-2 space-y-2", children: /* @__PURE__ */ e(Q, { role: x }, x?.id) }) : null;
1194
+ value: values.roles?.map((r) => r.id) ?? [],
1195
+ onMultiValueChange: (value) => setFieldValue("roles", value.map((id) => roles.find((r) => r.id === id))),
1196
+ renderValue: (value) => {
1197
+ const userRole = roles.find((role) => role.id === value);
1198
+ if (!userRole) return null;
1199
+ return /* @__PURE__ */ jsx("div", { className: "flex flex-wrap space-x-2 space-y-2", children: /* @__PURE__ */ jsx(RoleChip, { role: userRole }, userRole?.id) });
1013
1200
  },
1014
- children: p.map((h) => /* @__PURE__ */ e(
1015
- Fe,
1201
+ children: roles.map((userRole) => /* @__PURE__ */ jsx(
1202
+ MultiSelectItem,
1016
1203
  {
1017
- value: h.id,
1018
- children: /* @__PURE__ */ e(Q, { role: h }, h?.id)
1204
+ value: userRole.id,
1205
+ children: /* @__PURE__ */ jsx(RoleChip, { role: userRole }, userRole?.id)
1019
1206
  },
1020
- h.id
1207
+ userRole.id
1021
1208
  ))
1022
1209
  }
1023
1210
  ) })
1024
1211
  ] })
1025
1212
  ] }),
1026
- /* @__PURE__ */ s(me, { children: [
1027
- /* @__PURE__ */ e(
1028
- q,
1213
+ /* @__PURE__ */ jsxs(DialogActions, { children: [
1214
+ /* @__PURE__ */ jsx(
1215
+ Button,
1029
1216
  {
1030
1217
  variant: "text",
1031
1218
  onClick: () => {
1032
- n();
1219
+ handleClose();
1033
1220
  },
1034
1221
  children: "Cancel"
1035
1222
  }
1036
1223
  ),
1037
- /* @__PURE__ */ e(
1038
- he,
1224
+ /* @__PURE__ */ jsx(
1225
+ LoadingButton,
1039
1226
  {
1040
1227
  variant: "filled",
1041
1228
  color: "primary",
1042
1229
  type: "submit",
1043
- disabled: !O,
1044
- loading: I,
1045
- startIcon: /* @__PURE__ */ e(fe, {}),
1046
- children: N ? "Create user" : "Update"
1230
+ disabled: !dirty,
1231
+ loading: isSubmitting,
1232
+ startIcon: /* @__PURE__ */ jsx(DoneIcon, {}),
1233
+ children: isNewUser ? "Create user" : "Update"
1047
1234
  }
1048
1235
  )
1049
1236
  ] })
@@ -1053,79 +1240,91 @@ function tn({
1053
1240
  }
1054
1241
  );
1055
1242
  }
1056
- function rn({ onUserClicked: t }) {
1243
+ function UsersTable({ onUserClicked }) {
1057
1244
  const {
1058
- users: i,
1059
- saveUser: n,
1060
- deleteUser: d
1061
- } = Y(), r = Le(), m = be(), f = Ve(), p = f?.locale ? $e[f?.locale] : void 0, N = f?.dateTimeFormat ?? _e, [v, c] = F(void 0), [I, w] = F(!1);
1062
- return /* @__PURE__ */ s("div", { className: "overflow-auto", children: [
1063
- /* @__PURE__ */ s(ie, { children: [
1064
- /* @__PURE__ */ s(re, { children: [
1065
- /* @__PURE__ */ e(a, { className: "truncate w-16" }),
1066
- /* @__PURE__ */ e(a, { children: "ID" }),
1067
- /* @__PURE__ */ e(a, { children: "Email" }),
1068
- /* @__PURE__ */ e(a, { children: "Name" }),
1069
- /* @__PURE__ */ e(a, { children: "Roles" }),
1070
- /* @__PURE__ */ e(a, { children: "Created on" })
1245
+ users,
1246
+ saveUser,
1247
+ deleteUser
1248
+ } = useUserManagement();
1249
+ const authController = useAuthController();
1250
+ const snackbarController = useSnackbarController();
1251
+ const customizationController = useCustomizationController();
1252
+ const dateUtilsLocale = customizationController?.locale ? locales[customizationController?.locale] : void 0;
1253
+ const dateFormat = customizationController?.dateTimeFormat ?? defaultDateFormat;
1254
+ const [userToBeDeleted, setUserToBeDeleted] = useState(void 0);
1255
+ const [deleteInProgress, setDeleteInProgress] = useState(false);
1256
+ return /* @__PURE__ */ jsxs("div", { className: "overflow-auto", children: [
1257
+ /* @__PURE__ */ jsxs(Table, { children: [
1258
+ /* @__PURE__ */ jsxs(TableHeader, { children: [
1259
+ /* @__PURE__ */ jsx(TableCell, { className: "truncate w-16" }),
1260
+ /* @__PURE__ */ jsx(TableCell, { children: "ID" }),
1261
+ /* @__PURE__ */ jsx(TableCell, { children: "Email" }),
1262
+ /* @__PURE__ */ jsx(TableCell, { children: "Name" }),
1263
+ /* @__PURE__ */ jsx(TableCell, { children: "Roles" }),
1264
+ /* @__PURE__ */ jsx(TableCell, { children: "Created on" })
1071
1265
  ] }),
1072
- /* @__PURE__ */ s(oe, { children: [
1073
- i && i.map((l) => {
1074
- const g = l.roles, R = l.created_on ? Oe(l.created_on, N, { locale: p }) : "";
1075
- return /* @__PURE__ */ s(
1076
- M,
1266
+ /* @__PURE__ */ jsxs(TableBody, { children: [
1267
+ users && users.map((user) => {
1268
+ const userRoles = user.roles;
1269
+ const formattedDate = user.created_on ? format(user.created_on, dateFormat, { locale: dateUtilsLocale }) : "";
1270
+ return /* @__PURE__ */ jsxs(
1271
+ TableRow,
1077
1272
  {
1078
1273
  onClick: () => {
1079
- t(l);
1274
+ onUserClicked(user);
1080
1275
  },
1081
1276
  children: [
1082
- /* @__PURE__ */ e(a, { className: "w-10", children: /* @__PURE__ */ e(L, { title: "Delete this user", children: /* @__PURE__ */ e(
1083
- ge,
1277
+ /* @__PURE__ */ jsx(TableCell, { className: "w-10", children: /* @__PURE__ */ jsx(Tooltip, { title: "Delete this user", children: /* @__PURE__ */ jsx(
1278
+ IconButton,
1084
1279
  {
1085
1280
  size: "small",
1086
- onClick: (y) => (y.stopPropagation(), c(l)),
1087
- children: /* @__PURE__ */ e(pe, {})
1281
+ onClick: (event) => {
1282
+ event.stopPropagation();
1283
+ return setUserToBeDeleted(user);
1284
+ },
1285
+ children: /* @__PURE__ */ jsx(DeleteIcon, {})
1088
1286
  }
1089
1287
  ) }) }),
1090
- /* @__PURE__ */ e(a, { children: l.uid }),
1091
- /* @__PURE__ */ e(a, { children: l.email }),
1092
- /* @__PURE__ */ e(a, { className: "font-medium align-left", children: l.displayName }),
1093
- /* @__PURE__ */ e(a, { className: "align-left", children: g ? /* @__PURE__ */ e("div", { className: "flex flex-wrap gap-2", children: g.map(
1094
- (y) => /* @__PURE__ */ e(Q, { role: y }, y?.id)
1288
+ /* @__PURE__ */ jsx(TableCell, { children: user.uid }),
1289
+ /* @__PURE__ */ jsx(TableCell, { children: user.email }),
1290
+ /* @__PURE__ */ jsx(TableCell, { className: "font-medium align-left", children: user.displayName }),
1291
+ /* @__PURE__ */ jsx(TableCell, { className: "align-left", children: userRoles ? /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-2", children: userRoles.map(
1292
+ (userRole) => /* @__PURE__ */ jsx(RoleChip, { role: userRole }, userRole?.id)
1095
1293
  ) }) : null }),
1096
- /* @__PURE__ */ e(a, { children: R })
1294
+ /* @__PURE__ */ jsx(TableCell, { children: formattedDate })
1097
1295
  ]
1098
1296
  },
1099
- "row_" + l.uid
1297
+ "row_" + user.uid
1100
1298
  );
1101
1299
  }),
1102
- (!i || i.length === 0) && /* @__PURE__ */ e(M, { children: /* @__PURE__ */ e(a, { colspan: 6, children: /* @__PURE__ */ s(Ce, { className: "flex flex-col gap-4 my-8 items-center", children: [
1103
- /* @__PURE__ */ e(_, { variant: "label", children: "There are no users yet" }),
1104
- /* @__PURE__ */ e(
1105
- q,
1300
+ (!users || users.length === 0) && /* @__PURE__ */ jsx(TableRow, { children: /* @__PURE__ */ jsx(TableCell, { colspan: 6, children: /* @__PURE__ */ jsxs(CenteredView, { className: "flex flex-col gap-4 my-8 items-center", children: [
1301
+ /* @__PURE__ */ jsx(Typography, { variant: "label", children: "There are no users yet" }),
1302
+ /* @__PURE__ */ jsx(
1303
+ Button,
1106
1304
  {
1107
1305
  variant: "outlined",
1108
1306
  onClick: () => {
1109
- if (!r.user?.uid)
1307
+ if (!authController.user?.uid) {
1110
1308
  throw Error("UsersTable, authController misconfiguration");
1111
- n({
1112
- uid: r.user?.uid,
1113
- email: r.user?.email,
1114
- displayName: r.user?.displayName,
1115
- photoURL: r.user?.photoURL,
1116
- providerId: r.user?.providerId,
1117
- isAnonymous: r.user?.isAnonymous,
1309
+ }
1310
+ saveUser({
1311
+ uid: authController.user?.uid,
1312
+ email: authController.user?.email,
1313
+ displayName: authController.user?.displayName,
1314
+ photoURL: authController.user?.photoURL,
1315
+ providerId: authController.user?.providerId,
1316
+ isAnonymous: authController.user?.isAnonymous,
1118
1317
  roles: [{ id: "admin", name: "Admin" }],
1119
1318
  created_on: /* @__PURE__ */ new Date()
1120
1319
  }).then(() => {
1121
- m.open({
1320
+ snackbarController.open({
1122
1321
  type: "success",
1123
1322
  message: "User added successfully"
1124
1323
  });
1125
- }).catch((l) => {
1126
- m.open({
1324
+ }).catch((error) => {
1325
+ snackbarController.open({
1127
1326
  type: "error",
1128
- message: "Error adding user: " + l.message
1327
+ message: "Error adding user: " + error.message
1129
1328
  });
1130
1329
  });
1131
1330
  },
@@ -1135,132 +1334,212 @@ function rn({ onUserClicked: t }) {
1135
1334
  ] }) }) })
1136
1335
  ] })
1137
1336
  ] }),
1138
- /* @__PURE__ */ e(
1139
- ye,
1337
+ /* @__PURE__ */ jsx(
1338
+ DeleteConfirmationDialog,
1140
1339
  {
1141
- open: !!v,
1142
- loading: I,
1340
+ open: Boolean(userToBeDeleted),
1341
+ loading: deleteInProgress,
1143
1342
  onAccept: () => {
1144
- v && (w(!0), d(v).then(() => {
1145
- c(void 0);
1146
- }).catch((l) => {
1147
- m.open({
1148
- type: "error",
1149
- message: "Error deleting user: " + l.message
1343
+ if (userToBeDeleted) {
1344
+ setDeleteInProgress(true);
1345
+ deleteUser(userToBeDeleted).then(() => {
1346
+ setUserToBeDeleted(void 0);
1347
+ }).catch((error) => {
1348
+ snackbarController.open({
1349
+ type: "error",
1350
+ message: "Error deleting user: " + error.message
1351
+ });
1352
+ }).finally(() => {
1353
+ setDeleteInProgress(false);
1150
1354
  });
1151
- }).finally(() => {
1152
- w(!1);
1153
- }));
1355
+ }
1154
1356
  },
1155
1357
  onCancel: () => {
1156
- c(void 0);
1358
+ setUserToBeDeleted(void 0);
1157
1359
  },
1158
- title: /* @__PURE__ */ e(H, { children: "Delete?" }),
1159
- body: /* @__PURE__ */ e(H, { children: "Are you sure you want to delete this user?" })
1360
+ title: /* @__PURE__ */ jsx(Fragment, { children: "Delete?" }),
1361
+ body: /* @__PURE__ */ jsx(Fragment, { children: "Are you sure you want to delete this user?" })
1160
1362
  }
1161
1363
  )
1162
1364
  ] });
1163
1365
  }
1164
- const on = function({ children: i }) {
1165
- const [n, d] = F(), [r, m] = F(), { users: f, usersLimit: p } = Y(), N = p !== void 0 && f && f.length >= p, v = E((I) => {
1166
- m(I), d(!0);
1167
- }, []), c = E(() => {
1168
- d(!1), m(void 0);
1366
+ const UsersView = function UsersView2({ children }) {
1367
+ const [dialogOpen, setDialogOpen] = useState();
1368
+ const [selectedUser, setSelectedUser] = useState();
1369
+ const { users, usersLimit } = useUserManagement();
1370
+ const reachedUsersLimit = usersLimit !== void 0 && (users && users.length >= usersLimit);
1371
+ const onUserClicked = useCallback((user) => {
1372
+ setSelectedUser(user);
1373
+ setDialogOpen(true);
1374
+ }, []);
1375
+ const handleClose = useCallback(() => {
1376
+ setDialogOpen(false);
1377
+ setSelectedUser(void 0);
1169
1378
  }, []);
1170
- return /* @__PURE__ */ s(ve, { className: "w-full flex flex-col py-4 gap-4", maxWidth: "6xl", children: [
1171
- i,
1172
- /* @__PURE__ */ s(
1379
+ return /* @__PURE__ */ jsxs(Container, { className: "w-full flex flex-col py-4 gap-4", maxWidth: "6xl", children: [
1380
+ children,
1381
+ /* @__PURE__ */ jsxs(
1173
1382
  "div",
1174
1383
  {
1175
1384
  className: "flex items-center mt-12",
1176
1385
  children: [
1177
- /* @__PURE__ */ e(
1178
- _,
1386
+ /* @__PURE__ */ jsx(
1387
+ Typography,
1179
1388
  {
1180
- gutterBottom: !0,
1389
+ gutterBottom: true,
1181
1390
  variant: "h4",
1182
1391
  className: "flex-grow",
1183
1392
  component: "h4",
1184
1393
  children: "Users"
1185
1394
  }
1186
1395
  ),
1187
- /* @__PURE__ */ e(
1188
- q,
1396
+ /* @__PURE__ */ jsx(
1397
+ Button,
1189
1398
  {
1190
1399
  size: "large",
1191
- disabled: N,
1192
- startIcon: /* @__PURE__ */ e(we, {}),
1193
- onClick: () => d(!0),
1400
+ disabled: reachedUsersLimit,
1401
+ startIcon: /* @__PURE__ */ jsx(AddIcon, {}),
1402
+ onClick: () => setDialogOpen(true),
1194
1403
  children: "Add user"
1195
1404
  }
1196
1405
  )
1197
1406
  ]
1198
1407
  }
1199
1408
  ),
1200
- /* @__PURE__ */ e(rn, { onUserClicked: v }),
1201
- /* @__PURE__ */ e(
1202
- tn,
1409
+ /* @__PURE__ */ jsx(UsersTable, { onUserClicked }),
1410
+ /* @__PURE__ */ jsx(
1411
+ UserDetailsForm,
1203
1412
  {
1204
- open: n ?? !1,
1205
- user: r,
1206
- handleClose: c
1413
+ open: dialogOpen ?? false,
1414
+ user: selectedUser,
1415
+ handleClose
1207
1416
  },
1208
- r?.uid ?? "new"
1417
+ selectedUser?.uid ?? "new"
1209
1418
  )
1210
1419
  ] });
1211
1420
  };
1212
- function bn({ userManagement: t }) {
1421
+ function useUserManagementPlugin({ userManagement }) {
1422
+ const noUsers = userManagement.users.length === 0;
1423
+ const noRoles = userManagement.roles.length === 0;
1213
1424
  return {
1214
- name: "User management plugin",
1215
- loading: t.loading,
1425
+ key: "user_management",
1426
+ loading: userManagement.loading,
1427
+ homePage: {
1428
+ additionalChildrenStart: noUsers || noRoles ? /* @__PURE__ */ jsx(
1429
+ IntroWidget,
1430
+ {
1431
+ noUsers,
1432
+ noRoles,
1433
+ userManagement
1434
+ }
1435
+ ) : void 0
1436
+ },
1216
1437
  provider: {
1217
- Component: Ge,
1438
+ Component: UserManagementProvider,
1218
1439
  props: {
1219
- userManagement: t
1440
+ userManagement
1220
1441
  }
1221
1442
  }
1222
1443
  };
1223
1444
  }
1224
- const Nn = [
1445
+ function IntroWidget({
1446
+ noUsers,
1447
+ noRoles,
1448
+ userManagement
1449
+ }) {
1450
+ const authController = useAuthController();
1451
+ const snackbarController = useSnackbarController();
1452
+ const buttonLabel = noUsers && noRoles ? "Create default roles and add current user as admin" : noUsers ? "Add current user as admin" : noRoles ? "Create default roles" : void 0;
1453
+ return /* @__PURE__ */ jsxs(
1454
+ Paper,
1455
+ {
1456
+ className: "my-4 flex flex-col px-4 py-6 bg-white dark:bg-slate-800 gap-2",
1457
+ children: [
1458
+ /* @__PURE__ */ jsx(Typography, { variant: "subtitle2", className: "uppercase", children: "Create your users and roles" }),
1459
+ /* @__PURE__ */ jsx(Typography, { children: "You have no users or roles defined. You can create default roles and add the current user as admin." }),
1460
+ /* @__PURE__ */ jsxs(Button, { onClick: () => {
1461
+ if (!authController.user?.uid) {
1462
+ throw Error("UsersTable, authController misconfiguration");
1463
+ }
1464
+ if (noUsers) {
1465
+ userManagement.saveUser({
1466
+ uid: authController.user?.uid,
1467
+ email: authController.user?.email,
1468
+ displayName: authController.user?.displayName,
1469
+ photoURL: authController.user?.photoURL,
1470
+ providerId: authController.user?.providerId,
1471
+ isAnonymous: authController.user?.isAnonymous,
1472
+ roles: [{
1473
+ id: "admin",
1474
+ name: "Admin"
1475
+ }],
1476
+ created_on: /* @__PURE__ */ new Date()
1477
+ }).then(() => {
1478
+ snackbarController.open({
1479
+ type: "success",
1480
+ message: "User added successfully"
1481
+ });
1482
+ }).catch((error) => {
1483
+ snackbarController.open({
1484
+ type: "error",
1485
+ message: "Error adding user: " + error.message
1486
+ });
1487
+ });
1488
+ }
1489
+ if (noRoles) {
1490
+ DEFAULT_ROLES.forEach((role) => {
1491
+ userManagement.saveRole(role);
1492
+ });
1493
+ }
1494
+ }, children: [
1495
+ /* @__PURE__ */ jsx(AddIcon, {}),
1496
+ buttonLabel
1497
+ ] })
1498
+ ]
1499
+ }
1500
+ );
1501
+ }
1502
+ const userManagementAdminViews = [
1225
1503
  {
1226
1504
  path: "users",
1227
1505
  name: "CMS Users",
1228
1506
  group: "Admin",
1229
1507
  icon: "face",
1230
- view: /* @__PURE__ */ e(on, {})
1508
+ view: /* @__PURE__ */ jsx(UsersView, {})
1231
1509
  },
1232
1510
  {
1233
1511
  path: "roles",
1234
1512
  name: "Roles",
1235
1513
  group: "Admin",
1236
1514
  icon: "gpp_good",
1237
- view: /* @__PURE__ */ e(Ze, {})
1515
+ view: /* @__PURE__ */ jsx(RolesView, {})
1238
1516
  }
1239
1517
  ];
1240
1518
  export {
1241
- hn as RESERVED_GROUPS,
1242
- Q as RoleChip,
1243
- He as RoleYupSchema,
1244
- Ke as RolesDetailsForm,
1245
- Xe as RolesTable,
1246
- Ze as RolesView,
1247
- tn as UserDetailsForm,
1248
- Re as UserManagementContext,
1249
- Ge as UserManagementProvider,
1250
- en as UserYupSchema,
1251
- rn as UsersTable,
1252
- on as UsersView,
1253
- We as areRolesEqual,
1254
- gn as cacheDelegatedLoginToken,
1255
- Cn as clearDelegatedLoginTokensCache,
1256
- vn as darkenColor,
1257
- pn as getDelegatedLoginTokenFromCache,
1258
- fn as getUserRoles,
1259
- wn as hexToRgbaWithOpacity,
1260
- qe as resolveUserRolePermissions,
1261
- yn as useBuildFirestoreUserManagement,
1262
- Y as useUserManagement,
1263
- bn as useUserManagementPlugin,
1264
- Nn as userManagementAdminViews
1519
+ IntroWidget,
1520
+ RESERVED_GROUPS,
1521
+ RoleChip,
1522
+ RoleYupSchema,
1523
+ RolesDetailsForm,
1524
+ RolesTable,
1525
+ RolesView,
1526
+ UserDetailsForm,
1527
+ UserManagementContext,
1528
+ UserManagementProvider,
1529
+ UserYupSchema,
1530
+ UsersTable,
1531
+ UsersView,
1532
+ areRolesEqual,
1533
+ cacheDelegatedLoginToken,
1534
+ clearDelegatedLoginTokensCache,
1535
+ darkenColor,
1536
+ getDelegatedLoginTokenFromCache,
1537
+ getUserRoles,
1538
+ hexToRgbaWithOpacity,
1539
+ resolveUserRolePermissions,
1540
+ useBuildUserManagement,
1541
+ useUserManagement,
1542
+ useUserManagementPlugin,
1543
+ userManagementAdminViews
1265
1544
  };
1266
1545
  //# sourceMappingURL=index.es.js.map