@firecms/user_management 3.0.0-3.0.0-beta.4.pre.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +157 -0
  3. package/dist/UserManagementProvider.d.ts +7 -0
  4. package/dist/admin_views.d.ts +2 -0
  5. package/dist/components/index.d.ts +2 -0
  6. package/dist/components/roles/RoleChip.d.ts +5 -0
  7. package/dist/components/roles/RolesDetailsForm.d.ts +20 -0
  8. package/dist/components/roles/RolesTable.d.ts +5 -0
  9. package/dist/components/roles/RolesView.d.ts +4 -0
  10. package/dist/components/roles/default_roles.d.ts +2 -0
  11. package/dist/components/roles/index.d.ts +4 -0
  12. package/dist/components/users/UserDetailsForm.d.ts +20 -0
  13. package/dist/components/users/UsersTable.d.ts +4 -0
  14. package/dist/components/users/UsersView.d.ts +4 -0
  15. package/dist/components/users/index.d.ts +3 -0
  16. package/dist/hooks/index.d.ts +2 -0
  17. package/dist/hooks/useBuildFirestoreUserManagement.d.ts +44 -0
  18. package/dist/hooks/useUserManagement.d.ts +2 -0
  19. package/dist/index.d.ts +7 -0
  20. package/dist/index.es.js +1263 -0
  21. package/dist/index.es.js.map +1 -0
  22. package/dist/index.umd.js +2 -0
  23. package/dist/index.umd.js.map +1 -0
  24. package/dist/types/firecms_user.d.ts +7 -0
  25. package/dist/types/index.d.ts +3 -0
  26. package/dist/types/roles.d.ts +31 -0
  27. package/dist/types/user_management.d.ts +39 -0
  28. package/dist/useUserManagementPlugin.d.ts +5 -0
  29. package/dist/utils/colors.d.ts +2 -0
  30. package/dist/utils/index.d.ts +3 -0
  31. package/dist/utils/local_storage.d.ts +3 -0
  32. package/dist/utils/permissions.d.ts +9 -0
  33. package/dist/utils/useTraceUpdate.d.ts +1 -0
  34. package/package.json +76 -0
  35. package/src/UserManagementProvider.tsx +19 -0
  36. package/src/admin_views.tsx +19 -0
  37. package/src/components/index.ts +2 -0
  38. package/src/components/roles/RoleChip.tsx +28 -0
  39. package/src/components/roles/RolesDetailsForm.tsx +402 -0
  40. package/src/components/roles/RolesTable.tsx +139 -0
  41. package/src/components/roles/RolesView.tsx +63 -0
  42. package/src/components/roles/default_roles.tsx +36 -0
  43. package/src/components/roles/index.ts +4 -0
  44. package/src/components/users/UserDetailsForm.tsx +230 -0
  45. package/src/components/users/UsersTable.tsx +178 -0
  46. package/src/components/users/UsersView.tsx +59 -0
  47. package/src/components/users/index.ts +3 -0
  48. package/src/hooks/index.ts +2 -0
  49. package/src/hooks/useBuildFirestoreUserManagement.tsx +241 -0
  50. package/src/hooks/useUserManagement.tsx +5 -0
  51. package/src/index.ts +7 -0
  52. package/src/types/firecms_user.ts +8 -0
  53. package/src/types/index.ts +3 -0
  54. package/src/types/roles.ts +41 -0
  55. package/src/types/user_management.tsx +50 -0
  56. package/src/useUserManagementPlugin.tsx +18 -0
  57. package/src/utils/colors.ts +52 -0
  58. package/src/utils/index.ts +3 -0
  59. package/src/utils/local_storage.ts +53 -0
  60. package/src/utils/permissions.ts +83 -0
  61. package/src/utils/useTraceUpdate.tsx +23 -0
  62. package/tailwind.config.js +68 -0
@@ -0,0 +1,31 @@
1
+ import { Permissions } from "@firecms/core";
2
+ export type Role = {
3
+ /**
4
+ * ID of the role
5
+ */
6
+ id: string;
7
+ /**
8
+ * Name of the role
9
+ */
10
+ name: string;
11
+ /**
12
+ * If this flag is true, the user can perform any action
13
+ */
14
+ isAdmin?: boolean;
15
+ /**
16
+ * Default permissions for all collections for this role.
17
+ * You can override this values at the collection level using
18
+ * {@link collectionPermissions}
19
+ */
20
+ defaultPermissions?: Permissions;
21
+ /**
22
+ * Record of stripped collection ids to their permissions.
23
+ * @see stripCollectionPath
24
+ */
25
+ collectionPermissions?: Record<string, Permissions>;
26
+ config?: {
27
+ createCollections?: boolean;
28
+ editCollections?: boolean | "own";
29
+ deleteCollections?: boolean | "own";
30
+ };
31
+ };
@@ -0,0 +1,39 @@
1
+ import { UserWithRoles } from "./firecms_user";
2
+ import { Role } from "./roles";
3
+ import { PermissionsBuilder } from "@firecms/core";
4
+ export type UserManagement<USER extends UserWithRoles = UserWithRoles> = {
5
+ loading: boolean;
6
+ /**
7
+ * The user currently logged in, in the user management system.
8
+ * This is the same user that is logged in the Authenticator, but with the roles
9
+ * and permissions loaded.
10
+ */
11
+ loggedInUser: USER | undefined;
12
+ users: USER[];
13
+ saveUser: (user: USER) => Promise<USER>;
14
+ deleteUser: (user: USER) => Promise<void>;
15
+ roles: Role[];
16
+ saveRole: (role: Role) => Promise<void>;
17
+ deleteRole: (role: Role) => Promise<void>;
18
+ /**
19
+ * Maximum number of users that can be created.
20
+ */
21
+ usersLimit?: number;
22
+ /**
23
+ * Can the logged user edit roles?
24
+ */
25
+ canEditRoles?: boolean;
26
+ /**
27
+ * Include a button to create default roles, in case there are no roles in the system.
28
+ */
29
+ allowDefaultRolesCreation?: boolean;
30
+ /**
31
+ * Include the collection config permissions in the user management system.
32
+ */
33
+ includeCollectionConfigPermissions?: boolean;
34
+ /**
35
+ * Get a permissions builder that defines which operations can be performed by a user in a collection.
36
+ * The permission builder generated should be based in the user roles and the collection config.
37
+ */
38
+ collectionPermissions: PermissionsBuilder;
39
+ };
@@ -0,0 +1,5 @@
1
+ import { FireCMSPlugin } from "@firecms/core";
2
+ import { UserManagement } from "./types";
3
+ export declare function useUserManagementPlugin({ userManagement }: {
4
+ userManagement: UserManagement;
5
+ }): FireCMSPlugin;
@@ -0,0 +1,2 @@
1
+ export declare function darkenColor(hexColor: string, darkenBy?: number): string;
2
+ export declare function hexToRgbaWithOpacity(hexColor: string, opacity?: number): string;
@@ -0,0 +1,3 @@
1
+ export * from "./permissions";
2
+ export * from "./local_storage";
3
+ export * from "./colors";
@@ -0,0 +1,3 @@
1
+ export declare function cacheDelegatedLoginToken(projectId: string, delegatedToken?: string): void;
2
+ export declare function getDelegatedLoginTokenFromCache(projectId: string): any;
3
+ export declare function clearDelegatedLoginTokensCache(): void;
@@ -0,0 +1,9 @@
1
+ import { EntityCollection, Permissions } from "@firecms/core";
2
+ import { Role, UserWithRoles } from "../types";
3
+ export declare const RESERVED_GROUPS: string[];
4
+ export declare function resolveUserRolePermissions<UserType extends UserWithRoles>({ collection, user }: {
5
+ collection: EntityCollection<any>;
6
+ user: UserType | null;
7
+ }): Permissions;
8
+ export declare function getUserRoles(roles: Role[], fireCMSUser: UserWithRoles): Role[] | undefined;
9
+ export declare const areRolesEqual: (rolesA: Role[], rolesB: Role[]) => boolean;
@@ -0,0 +1 @@
1
+ export declare function useTraceUpdate(props: any): void;
package/package.json ADDED
@@ -0,0 +1,76 @@
1
+ {
2
+ "name": "@firecms/user_management",
3
+ "type": "module",
4
+ "version": "3.0.0-3.0.0-beta.4.pre.1.0",
5
+ "publishConfig": {
6
+ "access": "public"
7
+ },
8
+ "keywords": [
9
+ "firebase",
10
+ "cms",
11
+ "admin",
12
+ "admin panel",
13
+ "firebase panel",
14
+ "firestore",
15
+ "headless",
16
+ "headless cms",
17
+ "content manager"
18
+ ],
19
+ "exports": {
20
+ ".": {
21
+ "import": "./dist/index.es.js",
22
+ "require": "./dist/index.umd.js",
23
+ "types": "./dist/index.d.ts"
24
+ },
25
+ "./package.json": "./package.json",
26
+ "./tailwind.config.js": "./tailwind.config.js"
27
+ },
28
+ "packageManager": "yarn@4.1.0",
29
+ "main": "./dist/index.umd.js",
30
+ "module": "./dist/index.es.js",
31
+ "types": "dist/index.d.ts",
32
+ "source": "src/index.ts",
33
+ "dependencies": {
34
+ "@firecms/core": "^3.0.0-3.0.0-beta.4.pre.1.0",
35
+ "@firecms/formex": "^3.0.0-3.0.0-beta.4.pre.1.0",
36
+ "@firecms/ui": "^3.0.0-3.0.0-beta.4.pre.1.0"
37
+ },
38
+ "peerDependencies": {
39
+ "firebase": "^10.7.1",
40
+ "react": "^18.2.0",
41
+ "react-dom": "^18.2.0"
42
+ },
43
+ "devDependencies": {
44
+ "@types/node": "^20.11.30",
45
+ "@types/react": "^18.2.67",
46
+ "@types/react-dom": "^18.2.22",
47
+ "@typescript-eslint/parser": "^7.3.1",
48
+ "eslint": "^8.57.0",
49
+ "eslint-config-standard": "^17.1.0",
50
+ "eslint-plugin-import": "^2.29.1",
51
+ "eslint-plugin-n": "^16.6.2",
52
+ "eslint-plugin-promise": "^6.1.1",
53
+ "eslint-plugin-react": "^7.34.1",
54
+ "eslint-plugin-react-hooks": "^4.6.0",
55
+ "typescript": "^5.4.2",
56
+ "vite": "^5.1.6"
57
+ },
58
+ "scripts": {
59
+ "dev": "vite",
60
+ "build": "vite build && tsc --emitDeclarationOnly -p tsconfig.prod.json",
61
+ "clean": "rm -rf dist && find ./src -name '*.js' -type f | xargs rm -f"
62
+ },
63
+ "files": [
64
+ "dist",
65
+ "src",
66
+ "tailwind.config.js",
67
+ "bin"
68
+ ],
69
+ "eslintConfig": {
70
+ "extends": [
71
+ "react-app",
72
+ "react-app/jest"
73
+ ]
74
+ },
75
+ "gitHead": "bfe875c02132b5763ac0c9d8c8a885c791bb5a24"
76
+ }
@@ -0,0 +1,19 @@
1
+ import React, { PropsWithChildren } from "react";
2
+ import { UserManagement, UserWithRoles } from "./types";
3
+
4
+ export const UserManagementContext = React.createContext<UserManagement<any>>({} as any);
5
+
6
+ export interface UserManagementProviderProps<U extends UserWithRoles = UserWithRoles> {
7
+ userManagement: UserManagement<U>
8
+ }
9
+
10
+ export function UserManagementProvider<U extends UserWithRoles = UserWithRoles>({
11
+ children,
12
+ userManagement
13
+ }: PropsWithChildren<UserManagementProviderProps<U>>) {
14
+ return (
15
+ <UserManagementContext.Provider value={userManagement}>
16
+ {children}
17
+ </UserManagementContext.Provider>
18
+ );
19
+ };
@@ -0,0 +1,19 @@
1
+ import { CMSView } from "@firecms/core";
2
+ import { RolesView, UsersView } from "./components";
3
+
4
+ export const userManagementAdminViews: CMSView[] = [
5
+ {
6
+ path: "users",
7
+ name: "CMS Users",
8
+ group: "Admin",
9
+ icon: "face",
10
+ view: <UsersView/>
11
+ },
12
+ {
13
+ path: "roles",
14
+ name: "Roles",
15
+ group: "Admin",
16
+ icon: "gpp_good",
17
+ view: <RolesView/>
18
+ }
19
+ ]
@@ -0,0 +1,2 @@
1
+ export * from "./roles";
2
+ export * from "./users";
@@ -0,0 +1,28 @@
1
+ import { Chip, getColorSchemeForSeed } from "@firecms/ui";
2
+ import { Role } from "../../types";
3
+
4
+ export type RoleChipProps = {
5
+ role: Role;
6
+ }
7
+
8
+ export function RoleChip({ role }: RoleChipProps) {
9
+ let colorScheme;
10
+ if (role.isAdmin) {
11
+ colorScheme = "blueDarker";
12
+ } else if (role.id === "editor") {
13
+ colorScheme = "yellowLight";
14
+ } else if (role.id === "viewer") {
15
+ colorScheme = "grayLight";
16
+ } else {
17
+ colorScheme = getColorSchemeForSeed(role.id);
18
+ }
19
+
20
+ return (
21
+ <Chip
22
+ colorScheme={colorScheme}
23
+ key={role.id}>
24
+ {role.name}
25
+ </Chip>
26
+ );
27
+
28
+ }
@@ -0,0 +1,402 @@
1
+ import React, { useCallback, useState } from "react";
2
+ import * as Yup from "yup";
3
+
4
+ import { EntityCollection, FieldCaption, toSnakeCase, } from "@firecms/core";
5
+ import {
6
+ Button,
7
+ Checkbox,
8
+ Dialog,
9
+ DialogActions,
10
+ DialogContent,
11
+ DoneIcon,
12
+ LoadingButton,
13
+ Paper,
14
+ Select,
15
+ SelectItem,
16
+ Table,
17
+ TableBody,
18
+ TableCell,
19
+ TableHeader,
20
+ TableRow,
21
+ TextField,
22
+ Tooltip,
23
+ Typography
24
+ } from "@firecms/ui";
25
+ import { useUserManagement } from "../../hooks";
26
+ import { Formex, getIn, useCreateFormex } from "@firecms/formex";
27
+ import { Role } from "../../types";
28
+
29
+ export const RoleYupSchema = Yup.object().shape({
30
+ id: Yup.string().required("Required"),
31
+ name: Yup.string().required("Required")
32
+ });
33
+
34
+ export function RolesDetailsForm({
35
+ open,
36
+ role,
37
+ editable,
38
+ handleClose,
39
+ collections
40
+ }: {
41
+ open: boolean,
42
+ editable?: boolean,
43
+ role?: Role,
44
+ handleClose: () => void,
45
+ collections?: EntityCollection[]
46
+ }) {
47
+
48
+ const { saveRole } = useUserManagement();
49
+ const isNewRole = !role;
50
+
51
+ const [savingError, setSavingError] = useState<Error | undefined>();
52
+
53
+ const onRoleUpdated = useCallback((role: Role) => {
54
+ setSavingError(undefined);
55
+ return saveRole(role);
56
+ }, [saveRole]);
57
+
58
+ const formex = useCreateFormex({
59
+ initialValues: role ?? {
60
+ name: ""
61
+ } as Role,
62
+ onSubmit: (role: Role, formexController) => {
63
+ return onRoleUpdated(role)
64
+ .then(() => {
65
+ formexController.resetForm({
66
+ values: role
67
+ });
68
+ handleClose();
69
+ })
70
+ .catch(e => setSavingError(e));
71
+ },
72
+ validation: (values) => {
73
+ return RoleYupSchema.validate(values, { abortEarly: false })
74
+ .then(() => ({}))
75
+ .catch((e) => {
76
+ const errors: Record<string, string> = {};
77
+ e.inner.forEach((error: any) => {
78
+ errors[error.path] = error.message;
79
+ });
80
+ return errors;
81
+ });
82
+ }
83
+
84
+ });
85
+
86
+ const {
87
+ isSubmitting,
88
+ touched,
89
+ values,
90
+ errors,
91
+ handleChange,
92
+ setFieldValue,
93
+ dirty,
94
+ setFieldTouched
95
+ } = formex;
96
+
97
+ const isAdmin = values.isAdmin ?? false;
98
+ const defaultCreate = values.defaultPermissions?.create ?? false;
99
+ const defaultRead = values.defaultPermissions?.read ?? false;
100
+ const defaultEdit = values.defaultPermissions?.edit ?? false;
101
+ const defaultDelete = values.defaultPermissions?.delete ?? false;
102
+
103
+ React.useEffect(() => {
104
+ const idTouched = getIn(touched, "id");
105
+ if (!idTouched && values.name) {
106
+ setFieldValue("id", toSnakeCase(values.name))
107
+ }
108
+ }, [touched, values.name]);
109
+
110
+ return (
111
+ <Dialog
112
+ open={open}
113
+ maxWidth={"4xl"}
114
+ >
115
+ <Formex value={formex}>
116
+ <form noValidate
117
+ autoComplete={"off"}
118
+ onSubmit={formex.handleSubmit}
119
+ style={{
120
+ display: "flex",
121
+ flexDirection: "column",
122
+ position: "relative",
123
+ height: "100%"
124
+ }}>
125
+ <DialogContent className="flex-grow">
126
+ <div
127
+ className="flex flex-row pt-12 pb-8">
128
+ <Typography variant={"h4"}
129
+ className="flex-grow">
130
+ Role
131
+ </Typography>
132
+ </div>
133
+
134
+ <div className={"grid grid-cols-12 gap-8"}>
135
+
136
+ <div className={"col-span-12 md:col-span-8"}>
137
+ <TextField
138
+ name="name"
139
+ required
140
+ error={touched.name && Boolean(errors.name)}
141
+ value={values.name}
142
+ disabled={isAdmin || !editable}
143
+ onChange={handleChange}
144
+ aria-describedby="name-helper-text"
145
+ label="Name"
146
+ />
147
+ <FieldCaption>
148
+ {touched.name && Boolean(errors.name) ? errors.name : "Name of this role"}
149
+ </FieldCaption>
150
+ </div>
151
+
152
+ <div className={"col-span-12 md:col-span-4"}>
153
+ <TextField
154
+ name="id"
155
+ required
156
+ error={touched.id && Boolean(errors.id)}
157
+ value={values.id}
158
+ disabled={!isNewRole || !editable}
159
+ onChange={(e) => {
160
+ handleChange(e);
161
+ setFieldTouched("id", true)
162
+ }}
163
+ aria-describedby="id-helper-text"
164
+ label="ID"
165
+ />
166
+ <FieldCaption>
167
+ {touched.id && Boolean(errors.id) ? errors.id : "ID of this role"}
168
+ </FieldCaption>
169
+ </div>
170
+
171
+ <div className={"col-span-12"}>
172
+ <Paper
173
+
174
+ className="bg-inherit">
175
+ <Table>
176
+ <TableHeader>
177
+ <TableCell></TableCell>
178
+ <TableCell
179
+ align="center">Create
180
+ entities
181
+ </TableCell>
182
+ <TableCell
183
+ align="center">Read
184
+ entities
185
+ </TableCell>
186
+ <TableCell
187
+ align="center">Update
188
+ entities
189
+ </TableCell>
190
+ <TableCell
191
+ align="center">Delete
192
+ entities
193
+ </TableCell>
194
+ </TableHeader>
195
+
196
+ <TableBody>
197
+ <TableRow>
198
+ <TableCell
199
+ scope="row">
200
+ <strong>All
201
+ collections</strong>
202
+ </TableCell>
203
+ <TableCell
204
+ align="center">
205
+ <Tooltip
206
+ title="Create entities in collections">
207
+ <Checkbox
208
+ disabled={isAdmin || !editable}
209
+ checked={(isAdmin || defaultCreate) ?? false}
210
+ onCheckedChange={(checked) => setFieldValue("defaultPermissions.create", checked)}
211
+ />
212
+ </Tooltip>
213
+ </TableCell>
214
+
215
+ <TableCell
216
+ align="center">
217
+ <Tooltip
218
+ title="Access all data in every collection">
219
+ <Checkbox
220
+ disabled={isAdmin || !editable}
221
+ checked={(isAdmin || defaultRead) ?? false}
222
+ onCheckedChange={(checked) => setFieldValue("defaultPermissions.read", checked)}
223
+ />
224
+ </Tooltip>
225
+ </TableCell>
226
+ <TableCell
227
+ align="center">
228
+ <Tooltip
229
+ title="Update data in any collection">
230
+ <Checkbox
231
+ disabled={isAdmin || !editable}
232
+ checked={(isAdmin || defaultEdit) ?? false}
233
+ onCheckedChange={(checked) => setFieldValue("defaultPermissions.edit", checked)}
234
+ />
235
+ </Tooltip>
236
+ </TableCell>
237
+ <TableCell
238
+ align="center">
239
+ <Tooltip
240
+ title="Delete data in any collection">
241
+ <Checkbox
242
+ disabled={isAdmin || !editable}
243
+ checked={(isAdmin || defaultDelete) ?? false}
244
+ onCheckedChange={(checked) => setFieldValue("defaultPermissions.delete", checked)}
245
+ />
246
+
247
+ </Tooltip>
248
+ </TableCell>
249
+ </TableRow>
250
+ {collections && collections.map((col) => (
251
+ <TableRow key={col.name}>
252
+ <TableCell
253
+ scope="row">
254
+ {col.name}
255
+ </TableCell>
256
+ <TableCell
257
+ align="center">
258
+ <Checkbox
259
+ disabled={isAdmin || defaultCreate || !editable}
260
+ checked={(isAdmin || defaultCreate || getIn(values, `collectionPermissions.${col.path}.create`)) ?? false}
261
+ onCheckedChange={(checked) => setFieldValue(`collectionPermissions.${col.path}.create`, checked)}/>
262
+ </TableCell>
263
+ <TableCell
264
+ align="center">
265
+ <Checkbox
266
+ disabled={isAdmin || defaultRead || !editable}
267
+ checked={(isAdmin || defaultRead || getIn(values, `collectionPermissions.${col.path}.read`)) ?? false}
268
+ onCheckedChange={(checked) => setFieldValue(`collectionPermissions.${col.path}.read`, checked)}/>
269
+ </TableCell>
270
+ <TableCell
271
+ align="center">
272
+ <Checkbox
273
+ disabled={isAdmin || defaultEdit || !editable}
274
+ checked={(isAdmin || defaultEdit || getIn(values, `collectionPermissions.${col.path}.edit`)) ?? false}
275
+ onCheckedChange={(checked) => setFieldValue(`collectionPermissions.${col.path}.edit`, checked)}/>
276
+ </TableCell>
277
+ <TableCell
278
+ align="center">
279
+ <Checkbox
280
+ disabled={isAdmin || defaultDelete || !editable}
281
+ checked={(isAdmin || defaultDelete || getIn(values, `collectionPermissions.${col.path}.delete`)) ?? false}
282
+ onCheckedChange={(checked) => setFieldValue(`collectionPermissions.${col.path}.delete`, checked)}/>
283
+ </TableCell>
284
+ </TableRow>
285
+ ))}
286
+ </TableBody>
287
+ </Table>
288
+ </Paper>
289
+ <FieldCaption>
290
+ You can customise the permissions
291
+ that the users related to this
292
+ role can perform in the entities
293
+ of each collection
294
+ </FieldCaption>
295
+ </div>
296
+
297
+ <div className={"col-span-12 md:col-span-4"}>
298
+ <Select
299
+ error={touched.config && Boolean(errors.config)}
300
+ id="createCollections"
301
+ name="createCollections"
302
+ label="Create collections"
303
+ position={"item-aligned"}
304
+ disabled={isAdmin || !editable}
305
+ onChange={(event) => setFieldValue("config.createCollections", event.target.value === "true")}
306
+ value={isAdmin || values.config?.createCollections ? "true" : "false"}
307
+ renderValue={(value: any) => value === "true" ? "Yes" : "No"}
308
+ >
309
+ <SelectItem
310
+ value={"true"}> Yes </SelectItem>
311
+ <SelectItem
312
+ value={"false"}> No </SelectItem>
313
+ </Select>
314
+
315
+ <FieldCaption>
316
+ {touched.config && Boolean(errors.config) ? errors.config : "Can the user create collections"}
317
+ </FieldCaption>
318
+ </div>
319
+
320
+ <div className={"col-span-12 md:col-span-4"}>
321
+ <Select
322
+ error={touched.config && Boolean(errors.config)}
323
+ id="editCollections"
324
+ name="editCollections"
325
+ label="Edit collections"
326
+ disabled={isAdmin || !editable}
327
+ position={"item-aligned"}
328
+ onChange={(event) => setFieldValue("config.editCollections", event.target.value === "own" ? "own" : event.target.value === "true")}
329
+ value={isAdmin ? "true" : (values.config?.editCollections === "own" ? "own" : (values.config?.editCollections ? "true" : "false"))}
330
+ renderValue={(value: any) => value === "own" ? "Own" : (value === "true" ? "Yes" : "No")}
331
+ >
332
+ <SelectItem
333
+ value={"true"}> Yes </SelectItem>
334
+ <SelectItem
335
+ value={"false"}> No </SelectItem>
336
+ <SelectItem
337
+ value={"own"}> Only
338
+ his/her own </SelectItem>
339
+ </Select>
340
+
341
+ <FieldCaption>
342
+ {touched.config && Boolean(errors.config) ? errors.config : "Can the user edit collections"}
343
+ </FieldCaption>
344
+ </div>
345
+
346
+ <div className={"col-span-12 md:col-span-4"}>
347
+ <Select
348
+ error={touched.config && Boolean(errors.config)}
349
+ id="deleteCollections"
350
+ name="deleteCollections"
351
+ label="Delete collections"
352
+ disabled={isAdmin || !editable}
353
+ position={"item-aligned"}
354
+ onChange={(event) => setFieldValue("config.deleteCollections", event.target.value === "own" ? "own" : event.target.value === "true")}
355
+ value={isAdmin ? "true" : (values.config?.deleteCollections === "own" ? "own" : (values.config?.deleteCollections ? "true" : "false"))}
356
+ renderValue={(value: any) => value === "own" ? "Own" : (value === "true" ? "Yes" : "No")}
357
+ >
358
+ <SelectItem
359
+ value={"true"}> Yes </SelectItem>
360
+ <SelectItem
361
+ value={"false"}> No </SelectItem>
362
+ <SelectItem
363
+ value={"own"}> Only
364
+ his/her own </SelectItem>
365
+ </Select>
366
+
367
+ <FieldCaption>
368
+ {touched.config && Boolean(errors.config) ? errors.config : "Can the user delete collections"}
369
+ </FieldCaption>
370
+
371
+ </div>
372
+
373
+ </div>
374
+ </DialogContent>
375
+
376
+ <DialogActions position={"sticky"}>
377
+ {savingError && <Typography className={"text-red-500"}>
378
+ There was an error saving this role
379
+ </Typography>}
380
+ <Button variant={"text"}
381
+ onClick={() => {
382
+ handleClose();
383
+ }}>
384
+ Cancel
385
+ </Button>
386
+ <LoadingButton
387
+ variant="filled"
388
+ color="primary"
389
+ type="submit"
390
+ disabled={!dirty}
391
+ loading={isSubmitting}
392
+ startIcon={<DoneIcon/>}
393
+ >
394
+ {isNewRole ? "Create role" : "Update"}
395
+ </LoadingButton>
396
+ </DialogActions>
397
+ </form>
398
+
399
+ </Formex>
400
+ </Dialog>
401
+ );
402
+ }