@firecms/user_management 3.0.0-canary.10 → 3.0.0-canary.101

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