@firecms/user_management 3.0.0-canary.14 → 3.0.0-canary.140
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/LICENSE +114 -21
- package/README.md +2 -2
- package/dist/hooks/index.d.ts +1 -1
- package/dist/hooks/{useBuildFirestoreUserManagement.d.ts → useBuildUserManagement.d.ts} +14 -7
- package/dist/index.es.js +1167 -818
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +1635 -1
- package/dist/index.umd.js.map +1 -1
- package/dist/types/user_management.d.ts +14 -3
- package/dist/useUserManagementPlugin.d.ts +6 -1
- package/package.json +12 -28
- package/src/components/roles/RolesDetailsForm.tsx +69 -24
- package/src/components/roles/RolesTable.tsx +6 -4
- package/src/components/roles/RolesView.tsx +3 -1
- package/src/components/users/UserDetailsForm.tsx +11 -9
- package/src/components/users/UsersTable.tsx +6 -4
- package/src/hooks/index.ts +1 -1
- package/src/hooks/useBuildUserManagement.tsx +314 -0
- package/src/types/user_management.tsx +17 -3
- package/src/useUserManagementPlugin.tsx +88 -2
- package/src/utils/permissions.ts +7 -6
- package/src/hooks/useBuildFirestoreUserManagement.tsx +0 -240
package/package.json
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
{
|
2
2
|
"name": "@firecms/user_management",
|
3
3
|
"type": "module",
|
4
|
-
"version": "3.0.0-canary.
|
4
|
+
"version": "3.0.0-canary.140",
|
5
5
|
"publishConfig": {
|
6
6
|
"access": "public"
|
7
7
|
},
|
@@ -24,36 +24,26 @@
|
|
24
24
|
},
|
25
25
|
"./package.json": "./package.json"
|
26
26
|
},
|
27
|
-
"packageManager": "yarn@4.1.0",
|
28
27
|
"main": "./dist/index.umd.js",
|
29
28
|
"module": "./dist/index.es.js",
|
30
29
|
"types": "dist/index.d.ts",
|
31
30
|
"source": "src/index.ts",
|
32
31
|
"dependencies": {
|
33
|
-
"@firecms/core": "^3.0.0-canary.
|
34
|
-
"@firecms/formex": "^3.0.0-canary.
|
35
|
-
"@firecms/ui": "^3.0.0-canary.
|
32
|
+
"@firecms/core": "^3.0.0-canary.140",
|
33
|
+
"@firecms/formex": "^3.0.0-canary.140",
|
34
|
+
"@firecms/ui": "^3.0.0-canary.140",
|
36
35
|
"date-fns": "^3.6.0"
|
37
36
|
},
|
38
37
|
"peerDependencies": {
|
39
|
-
"
|
40
|
-
"react": "^18.
|
41
|
-
"react-dom": "^18.2.0"
|
38
|
+
"react": "^18.3.1",
|
39
|
+
"react-dom": "^18.3.1"
|
42
40
|
},
|
43
41
|
"devDependencies": {
|
44
|
-
"@types/node": "^20.11
|
45
|
-
"@types/react": "^18.
|
46
|
-
"@types/react-dom": "^18.
|
47
|
-
"
|
48
|
-
"
|
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.2.3"
|
42
|
+
"@types/node": "^20.16.11",
|
43
|
+
"@types/react": "^18.3.11",
|
44
|
+
"@types/react-dom": "^18.3.0",
|
45
|
+
"typescript": "^5.6.3",
|
46
|
+
"vite": "^5.4.8"
|
57
47
|
},
|
58
48
|
"scripts": {
|
59
49
|
"dev": "vite",
|
@@ -65,11 +55,5 @@
|
|
65
55
|
"src",
|
66
56
|
"bin"
|
67
57
|
],
|
68
|
-
"
|
69
|
-
"extends": [
|
70
|
-
"react-app",
|
71
|
-
"react-app/jest"
|
72
|
-
]
|
73
|
-
},
|
74
|
-
"gitHead": "42fe62aaa86f3330c79c7cbba551d9323299c141"
|
58
|
+
"gitHead": "112132e315d1ae63babf9f53500c00bc37a2d8e4"
|
75
59
|
}
|
@@ -1,7 +1,7 @@
|
|
1
1
|
import React, { useCallback, useState } from "react";
|
2
2
|
import * as Yup from "yup";
|
3
3
|
|
4
|
-
import { EntityCollection, FieldCaption, Role, toSnakeCase, } from "@firecms/core";
|
4
|
+
import { EntityCollection, FieldCaption, Role, toSnakeCase, useAuthController, User, } from "@firecms/core";
|
5
5
|
import {
|
6
6
|
Button,
|
7
7
|
Checkbox,
|
@@ -30,6 +30,15 @@ export const RoleYupSchema = Yup.object().shape({
|
|
30
30
|
name: Yup.string().required("Required")
|
31
31
|
});
|
32
32
|
|
33
|
+
function canRoleBeEdited(loggedUser: User) {
|
34
|
+
const loggedUserIsAdmin = loggedUser.roles?.map(r => r.id).includes("admin");
|
35
|
+
if (!loggedUserIsAdmin) {
|
36
|
+
throw new Error("Only admins can edit roles");
|
37
|
+
}
|
38
|
+
|
39
|
+
return true;
|
40
|
+
}
|
41
|
+
|
33
42
|
export function RolesDetailsForm({
|
34
43
|
open,
|
35
44
|
role,
|
@@ -46,27 +55,39 @@ export function RolesDetailsForm({
|
|
46
55
|
|
47
56
|
const { saveRole } = useUserManagement();
|
48
57
|
const isNewRole = !role;
|
58
|
+
const {
|
59
|
+
user: loggedInUser
|
60
|
+
} = useAuthController();
|
49
61
|
|
50
62
|
const [savingError, setSavingError] = useState<Error | undefined>();
|
51
63
|
|
52
64
|
const onRoleUpdated = useCallback((role: Role) => {
|
53
65
|
setSavingError(undefined);
|
66
|
+
if (!loggedInUser) throw new Error("User not found");
|
67
|
+
canRoleBeEdited(loggedInUser);
|
54
68
|
return saveRole(role);
|
55
|
-
}, [saveRole]);
|
69
|
+
}, [saveRole, loggedInUser]);
|
56
70
|
|
57
71
|
const formex = useCreateFormex({
|
58
72
|
initialValues: role ?? {
|
59
73
|
name: ""
|
60
74
|
} as Role,
|
61
75
|
onSubmit: (role: Role, formexController) => {
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
76
|
+
try {
|
77
|
+
return onRoleUpdated(role)
|
78
|
+
.then(() => {
|
79
|
+
formexController.resetForm({
|
80
|
+
values: role
|
81
|
+
});
|
82
|
+
handleClose();
|
83
|
+
})
|
84
|
+
.catch(e => {
|
85
|
+
setSavingError(e);
|
66
86
|
});
|
67
|
-
|
68
|
-
|
69
|
-
.
|
87
|
+
} catch (e: any) {
|
88
|
+
setSavingError(e);
|
89
|
+
return Promise.resolve();
|
90
|
+
}
|
70
91
|
},
|
71
92
|
validation: (values) => {
|
72
93
|
return RoleYupSchema.validate(values, { abortEarly: false })
|
@@ -168,11 +189,9 @@ export function RolesDetailsForm({
|
|
168
189
|
</div>
|
169
190
|
|
170
191
|
<div className={"col-span-12"}>
|
171
|
-
<Paper
|
172
|
-
|
173
|
-
|
174
|
-
<Table>
|
175
|
-
<TableHeader>
|
192
|
+
<Paper className="bg-inherit overflow-hidden">
|
193
|
+
<Table className={"w-full rounded-md"}>
|
194
|
+
<TableHeader className={"rounded-md"}>
|
176
195
|
<TableCell></TableCell>
|
177
196
|
<TableCell
|
178
197
|
align="center">Create
|
@@ -190,6 +209,9 @@ export function RolesDetailsForm({
|
|
190
209
|
align="center">Delete
|
191
210
|
entities
|
192
211
|
</TableCell>
|
212
|
+
<TableCell
|
213
|
+
align="center">
|
214
|
+
</TableCell>
|
193
215
|
</TableHeader>
|
194
216
|
|
195
217
|
<TableBody>
|
@@ -245,6 +267,9 @@ export function RolesDetailsForm({
|
|
245
267
|
|
246
268
|
</Tooltip>
|
247
269
|
</TableCell>
|
270
|
+
<TableCell
|
271
|
+
align="center">
|
272
|
+
</TableCell>
|
248
273
|
</TableRow>
|
249
274
|
{collections && collections.map((col) => (
|
250
275
|
<TableRow key={col.name}>
|
@@ -256,29 +281,49 @@ export function RolesDetailsForm({
|
|
256
281
|
align="center">
|
257
282
|
<Checkbox
|
258
283
|
disabled={isAdmin || defaultCreate || !editable}
|
259
|
-
checked={(isAdmin || defaultCreate || getIn(values, `collectionPermissions.${col.
|
260
|
-
onCheckedChange={(checked) => setFieldValue(`collectionPermissions.${col.
|
284
|
+
checked={(isAdmin || defaultCreate || getIn(values, `collectionPermissions.${col.id}.create`)) ?? false}
|
285
|
+
onCheckedChange={(checked) => setFieldValue(`collectionPermissions.${col.id}.create`, checked)}/>
|
261
286
|
</TableCell>
|
262
287
|
<TableCell
|
263
288
|
align="center">
|
264
289
|
<Checkbox
|
265
290
|
disabled={isAdmin || defaultRead || !editable}
|
266
|
-
checked={(isAdmin || defaultRead || getIn(values, `collectionPermissions.${col.
|
267
|
-
onCheckedChange={(checked) => setFieldValue(`collectionPermissions.${col.
|
291
|
+
checked={(isAdmin || defaultRead || getIn(values, `collectionPermissions.${col.id}.read`)) ?? false}
|
292
|
+
onCheckedChange={(checked) => setFieldValue(`collectionPermissions.${col.id}.read`, checked)}/>
|
268
293
|
</TableCell>
|
269
294
|
<TableCell
|
270
295
|
align="center">
|
271
296
|
<Checkbox
|
272
297
|
disabled={isAdmin || defaultEdit || !editable}
|
273
|
-
checked={(isAdmin || defaultEdit || getIn(values, `collectionPermissions.${col.
|
274
|
-
onCheckedChange={(checked) => setFieldValue(`collectionPermissions.${col.
|
298
|
+
checked={(isAdmin || defaultEdit || getIn(values, `collectionPermissions.${col.id}.edit`)) ?? false}
|
299
|
+
onCheckedChange={(checked) => setFieldValue(`collectionPermissions.${col.id}.edit`, checked)}/>
|
275
300
|
</TableCell>
|
276
301
|
<TableCell
|
277
302
|
align="center">
|
278
303
|
<Checkbox
|
279
304
|
disabled={isAdmin || defaultDelete || !editable}
|
280
|
-
checked={(isAdmin || defaultDelete || getIn(values, `collectionPermissions.${col.
|
281
|
-
onCheckedChange={(checked) => setFieldValue(`collectionPermissions.${col.
|
305
|
+
checked={(isAdmin || defaultDelete || getIn(values, `collectionPermissions.${col.id}.delete`)) ?? false}
|
306
|
+
onCheckedChange={(checked) => setFieldValue(`collectionPermissions.${col.id}.delete`, checked)}/>
|
307
|
+
</TableCell>
|
308
|
+
|
309
|
+
<TableCell
|
310
|
+
align="center">
|
311
|
+
<Tooltip
|
312
|
+
title="Allow all permissions in this collections">
|
313
|
+
<Button
|
314
|
+
className={"color-inherit"}
|
315
|
+
onClick={() => {
|
316
|
+
setFieldValue(`collectionPermissions.${col.id}.create`, true);
|
317
|
+
setFieldValue(`collectionPermissions.${col.id}.read`, true);
|
318
|
+
setFieldValue(`collectionPermissions.${col.id}.edit`, true);
|
319
|
+
setFieldValue(`collectionPermissions.${col.id}.delete`, true);
|
320
|
+
}}
|
321
|
+
disabled={isAdmin || !editable}
|
322
|
+
variant={"text"}>
|
323
|
+
All
|
324
|
+
</Button>
|
325
|
+
|
326
|
+
</Tooltip>
|
282
327
|
</TableCell>
|
283
328
|
</TableRow>
|
284
329
|
))}
|
@@ -373,8 +418,8 @@ export function RolesDetailsForm({
|
|
373
418
|
</DialogContent>
|
374
419
|
|
375
420
|
<DialogActions position={"sticky"}>
|
376
|
-
{savingError && <Typography className={"text-red-500"}>
|
377
|
-
There was an error saving this role
|
421
|
+
{savingError && <Typography className={"text-red-500 dark:text-red-500"}>
|
422
|
+
{savingError.message ?? "There was an error saving this role"}
|
378
423
|
</Typography>}
|
379
424
|
<Button variant={"text"}
|
380
425
|
onClick={() => {
|
@@ -13,7 +13,7 @@ import {
|
|
13
13
|
Tooltip,
|
14
14
|
Typography
|
15
15
|
} from "@firecms/ui";
|
16
|
-
import {
|
16
|
+
import { ConfirmationDialog, Role } from "@firecms/core";
|
17
17
|
import { useUserManagement } from "../../hooks";
|
18
18
|
import { RoleChip } from "./RoleChip";
|
19
19
|
import { DEFAULT_ROLES } from "./default_roles";
|
@@ -38,7 +38,7 @@ export function RolesTable({
|
|
38
38
|
|
39
39
|
return <div
|
40
40
|
className="w-full overflow-auto">
|
41
|
-
<Table>
|
41
|
+
<Table className={"w-full"}>
|
42
42
|
<TableHeader>
|
43
43
|
<TableCell header={true} className="w-16"></TableCell>
|
44
44
|
<TableCell header={true}>Role</TableCell>
|
@@ -61,7 +61,9 @@ export function RolesTable({
|
|
61
61
|
>
|
62
62
|
<TableCell style={{ width: "64px" }}>
|
63
63
|
{!role.isAdmin &&
|
64
|
-
<Tooltip
|
64
|
+
<Tooltip
|
65
|
+
asChild={true}
|
66
|
+
title={"Delete this role"}>
|
65
67
|
<IconButton
|
66
68
|
size={"small"}
|
67
69
|
disabled={!editable}
|
@@ -113,7 +115,7 @@ export function RolesTable({
|
|
113
115
|
|
114
116
|
</Table>
|
115
117
|
|
116
|
-
<
|
118
|
+
<ConfirmationDialog
|
117
119
|
open={Boolean(roleToBeDeleted)}
|
118
120
|
loading={deleteInProgress}
|
119
121
|
onAccept={() => {
|
@@ -36,7 +36,9 @@ export const RolesView = React.memo(
|
|
36
36
|
component="h4">
|
37
37
|
Roles
|
38
38
|
</Typography>
|
39
|
-
<Tooltip
|
39
|
+
<Tooltip
|
40
|
+
asChild={true}
|
41
|
+
title={!canEditRoles ? "Update plans to customise roles" : undefined}>
|
40
42
|
<Button
|
41
43
|
size={"large"}
|
42
44
|
disabled={!canEditRoles}
|
@@ -181,17 +181,19 @@ export function UserDetailsForm({
|
|
181
181
|
</div>
|
182
182
|
<div className={"col-span-12"}>
|
183
183
|
<MultiSelect
|
184
|
+
className={"w-full"}
|
184
185
|
label="Roles"
|
185
186
|
value={values.roles?.map(r => r.id) ?? []}
|
186
|
-
|
187
|
-
renderValue={(value: string) => {
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
}}
|
187
|
+
onValueChange={(value: string[]) => setFieldValue("roles", value.map(id => roles.find(r => r.id === id) as Role))}
|
188
|
+
// renderValue={(value: string) => {
|
189
|
+
// const userRole = roles
|
190
|
+
// .find((role) => role.id === value);
|
191
|
+
// if (!userRole) return null;
|
192
|
+
// return <div className="flex flex-wrap space-x-2 space-y-2">
|
193
|
+
// <RoleChip key={userRole?.id} role={userRole}/>
|
194
|
+
// </div>;
|
195
|
+
// }}
|
196
|
+
>
|
195
197
|
{roles.map(userRole => <MultiSelectItem key={userRole.id}
|
196
198
|
value={userRole.id}>
|
197
199
|
<RoleChip key={userRole?.id} role={userRole}/>
|
@@ -5,7 +5,7 @@ import * as locales from "date-fns/locale";
|
|
5
5
|
|
6
6
|
import {
|
7
7
|
defaultDateFormat,
|
8
|
-
|
8
|
+
ConfirmationDialog, Role,
|
9
9
|
useAuthController,
|
10
10
|
useCustomizationController, User,
|
11
11
|
useSnackbarController
|
@@ -50,7 +50,7 @@ export function UsersTable({ onUserClicked }: {
|
|
50
50
|
return (
|
51
51
|
<div className="overflow-auto">
|
52
52
|
|
53
|
-
<Table>
|
53
|
+
<Table className={"w-full"}>
|
54
54
|
|
55
55
|
<TableHeader>
|
56
56
|
<TableCell className="truncate w-16"></TableCell>
|
@@ -75,7 +75,9 @@ export function UsersTable({ onUserClicked }: {
|
|
75
75
|
}}
|
76
76
|
>
|
77
77
|
<TableCell className={"w-10"}>
|
78
|
-
<Tooltip
|
78
|
+
<Tooltip
|
79
|
+
asChild={true}
|
80
|
+
title={"Delete this user"}>
|
79
81
|
<IconButton
|
80
82
|
size={"small"}
|
81
83
|
onClick={(event) => {
|
@@ -147,7 +149,7 @@ export function UsersTable({ onUserClicked }: {
|
|
147
149
|
</TableBody>
|
148
150
|
</Table>
|
149
151
|
|
150
|
-
<
|
152
|
+
<ConfirmationDialog
|
151
153
|
open={Boolean(userToBeDeleted)}
|
152
154
|
loading={deleteInProgress}
|
153
155
|
onAccept={() => {
|
package/src/hooks/index.ts
CHANGED
@@ -1,2 +1,2 @@
|
|
1
|
-
export * from "./
|
1
|
+
export * from "./useBuildUserManagement";
|
2
2
|
export * from "./useUserManagement";
|