@firecms/user_management 3.0.0-canary.15 → 3.0.0-canary.150
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/useBuildUserManagement.d.ts +51 -0
- package/dist/index.es.js +1183 -818
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +1651 -1
- package/dist/index.umd.js.map +1 -1
- package/dist/types/user_management.d.ts +10 -3
- package/dist/useUserManagementPlugin.d.ts +8 -3
- package/dist/utils/permissions.d.ts +2 -2
- package/package.json +12 -28
- package/src/components/roles/RolesDetailsForm.tsx +70 -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 -6
- package/src/hooks/index.ts +1 -1
- package/src/hooks/useBuildUserManagement.tsx +360 -0
- package/src/types/user_management.tsx +13 -3
- package/src/useUserManagementPlugin.tsx +89 -3
- package/src/utils/permissions.ts +9 -8
- package/dist/hooks/useBuildFirestoreUserManagement.d.ts +0 -42
- 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.150",
|
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.150",
|
33
|
+
"@firecms/formex": "^3.0.0-canary.150",
|
34
|
+
"@firecms/ui": "^3.0.0-canary.150",
|
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.12",
|
44
|
+
"@types/react-dom": "^18.3.1",
|
45
|
+
"typescript": "^5.6.3",
|
46
|
+
"vite": "^5.4.10"
|
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": "d1871a35c8a7708b3e420c57ab3a05a178412fdf"
|
58
|
+
"gitHead": "4df9d54d1bd70b5b5823d797bc2ce6b6c34cf051"
|
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,16 @@ 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
|
+
console.log("loggedUserIsAdmin", loggedUser);
|
36
|
+
if (!loggedUserIsAdmin) {
|
37
|
+
throw new Error("Only admins can edit roles");
|
38
|
+
}
|
39
|
+
|
40
|
+
return true;
|
41
|
+
}
|
42
|
+
|
33
43
|
export function RolesDetailsForm({
|
34
44
|
open,
|
35
45
|
role,
|
@@ -46,27 +56,39 @@ export function RolesDetailsForm({
|
|
46
56
|
|
47
57
|
const { saveRole } = useUserManagement();
|
48
58
|
const isNewRole = !role;
|
59
|
+
const {
|
60
|
+
user: loggedInUser
|
61
|
+
} = useAuthController();
|
49
62
|
|
50
63
|
const [savingError, setSavingError] = useState<Error | undefined>();
|
51
64
|
|
52
65
|
const onRoleUpdated = useCallback((role: Role) => {
|
53
66
|
setSavingError(undefined);
|
67
|
+
if (!loggedInUser) throw new Error("User not found");
|
68
|
+
canRoleBeEdited(loggedInUser);
|
54
69
|
return saveRole(role);
|
55
|
-
}, [saveRole]);
|
70
|
+
}, [saveRole, loggedInUser]);
|
56
71
|
|
57
72
|
const formex = useCreateFormex({
|
58
73
|
initialValues: role ?? {
|
59
74
|
name: ""
|
60
75
|
} as Role,
|
61
76
|
onSubmit: (role: Role, formexController) => {
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
77
|
+
try {
|
78
|
+
return onRoleUpdated(role)
|
79
|
+
.then(() => {
|
80
|
+
formexController.resetForm({
|
81
|
+
values: role
|
82
|
+
});
|
83
|
+
handleClose();
|
84
|
+
})
|
85
|
+
.catch(e => {
|
86
|
+
setSavingError(e);
|
66
87
|
});
|
67
|
-
|
68
|
-
|
69
|
-
.
|
88
|
+
} catch (e: any) {
|
89
|
+
setSavingError(e);
|
90
|
+
return Promise.resolve();
|
91
|
+
}
|
70
92
|
},
|
71
93
|
validation: (values) => {
|
72
94
|
return RoleYupSchema.validate(values, { abortEarly: false })
|
@@ -168,11 +190,9 @@ export function RolesDetailsForm({
|
|
168
190
|
</div>
|
169
191
|
|
170
192
|
<div className={"col-span-12"}>
|
171
|
-
<Paper
|
172
|
-
|
173
|
-
|
174
|
-
<Table>
|
175
|
-
<TableHeader>
|
193
|
+
<Paper className="bg-inherit overflow-hidden">
|
194
|
+
<Table className={"w-full rounded-md"}>
|
195
|
+
<TableHeader className={"rounded-md"}>
|
176
196
|
<TableCell></TableCell>
|
177
197
|
<TableCell
|
178
198
|
align="center">Create
|
@@ -190,6 +210,9 @@ export function RolesDetailsForm({
|
|
190
210
|
align="center">Delete
|
191
211
|
entities
|
192
212
|
</TableCell>
|
213
|
+
<TableCell
|
214
|
+
align="center">
|
215
|
+
</TableCell>
|
193
216
|
</TableHeader>
|
194
217
|
|
195
218
|
<TableBody>
|
@@ -245,6 +268,9 @@ export function RolesDetailsForm({
|
|
245
268
|
|
246
269
|
</Tooltip>
|
247
270
|
</TableCell>
|
271
|
+
<TableCell
|
272
|
+
align="center">
|
273
|
+
</TableCell>
|
248
274
|
</TableRow>
|
249
275
|
{collections && collections.map((col) => (
|
250
276
|
<TableRow key={col.name}>
|
@@ -256,29 +282,49 @@ export function RolesDetailsForm({
|
|
256
282
|
align="center">
|
257
283
|
<Checkbox
|
258
284
|
disabled={isAdmin || defaultCreate || !editable}
|
259
|
-
checked={(isAdmin || defaultCreate || getIn(values, `collectionPermissions.${col.
|
260
|
-
onCheckedChange={(checked) => setFieldValue(`collectionPermissions.${col.
|
285
|
+
checked={(isAdmin || defaultCreate || getIn(values, `collectionPermissions.${col.id}.create`)) ?? false}
|
286
|
+
onCheckedChange={(checked) => setFieldValue(`collectionPermissions.${col.id}.create`, checked)}/>
|
261
287
|
</TableCell>
|
262
288
|
<TableCell
|
263
289
|
align="center">
|
264
290
|
<Checkbox
|
265
291
|
disabled={isAdmin || defaultRead || !editable}
|
266
|
-
checked={(isAdmin || defaultRead || getIn(values, `collectionPermissions.${col.
|
267
|
-
onCheckedChange={(checked) => setFieldValue(`collectionPermissions.${col.
|
292
|
+
checked={(isAdmin || defaultRead || getIn(values, `collectionPermissions.${col.id}.read`)) ?? false}
|
293
|
+
onCheckedChange={(checked) => setFieldValue(`collectionPermissions.${col.id}.read`, checked)}/>
|
268
294
|
</TableCell>
|
269
295
|
<TableCell
|
270
296
|
align="center">
|
271
297
|
<Checkbox
|
272
298
|
disabled={isAdmin || defaultEdit || !editable}
|
273
|
-
checked={(isAdmin || defaultEdit || getIn(values, `collectionPermissions.${col.
|
274
|
-
onCheckedChange={(checked) => setFieldValue(`collectionPermissions.${col.
|
299
|
+
checked={(isAdmin || defaultEdit || getIn(values, `collectionPermissions.${col.id}.edit`)) ?? false}
|
300
|
+
onCheckedChange={(checked) => setFieldValue(`collectionPermissions.${col.id}.edit`, checked)}/>
|
275
301
|
</TableCell>
|
276
302
|
<TableCell
|
277
303
|
align="center">
|
278
304
|
<Checkbox
|
279
305
|
disabled={isAdmin || defaultDelete || !editable}
|
280
|
-
checked={(isAdmin || defaultDelete || getIn(values, `collectionPermissions.${col.
|
281
|
-
onCheckedChange={(checked) => setFieldValue(`collectionPermissions.${col.
|
306
|
+
checked={(isAdmin || defaultDelete || getIn(values, `collectionPermissions.${col.id}.delete`)) ?? false}
|
307
|
+
onCheckedChange={(checked) => setFieldValue(`collectionPermissions.${col.id}.delete`, checked)}/>
|
308
|
+
</TableCell>
|
309
|
+
|
310
|
+
<TableCell
|
311
|
+
align="center">
|
312
|
+
<Tooltip
|
313
|
+
title="Allow all permissions in this collections">
|
314
|
+
<Button
|
315
|
+
className={"color-inherit"}
|
316
|
+
onClick={() => {
|
317
|
+
setFieldValue(`collectionPermissions.${col.id}.create`, true);
|
318
|
+
setFieldValue(`collectionPermissions.${col.id}.read`, true);
|
319
|
+
setFieldValue(`collectionPermissions.${col.id}.edit`, true);
|
320
|
+
setFieldValue(`collectionPermissions.${col.id}.delete`, true);
|
321
|
+
}}
|
322
|
+
disabled={isAdmin || !editable}
|
323
|
+
variant={"text"}>
|
324
|
+
All
|
325
|
+
</Button>
|
326
|
+
|
327
|
+
</Tooltip>
|
282
328
|
</TableCell>
|
283
329
|
</TableRow>
|
284
330
|
))}
|
@@ -373,8 +419,8 @@ export function RolesDetailsForm({
|
|
373
419
|
</DialogContent>
|
374
420
|
|
375
421
|
<DialogActions position={"sticky"}>
|
376
|
-
{savingError && <Typography className={"text-red-500"}>
|
377
|
-
There was an error saving this role
|
422
|
+
{savingError && <Typography className={"text-red-500 dark:text-red-500"}>
|
423
|
+
{savingError.message ?? "There was an error saving this role"}
|
378
424
|
</Typography>}
|
379
425
|
<Button variant={"text"}
|
380
426
|
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,11 +50,10 @@ 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>
|
57
|
-
<TableCell>ID</TableCell>
|
58
57
|
<TableCell>Email</TableCell>
|
59
58
|
<TableCell>Name</TableCell>
|
60
59
|
<TableCell>Roles</TableCell>
|
@@ -75,7 +74,9 @@ export function UsersTable({ onUserClicked }: {
|
|
75
74
|
}}
|
76
75
|
>
|
77
76
|
<TableCell className={"w-10"}>
|
78
|
-
<Tooltip
|
77
|
+
<Tooltip
|
78
|
+
asChild={true}
|
79
|
+
title={"Delete this user"}>
|
79
80
|
<IconButton
|
80
81
|
size={"small"}
|
81
82
|
onClick={(event) => {
|
@@ -86,7 +87,6 @@ export function UsersTable({ onUserClicked }: {
|
|
86
87
|
</IconButton>
|
87
88
|
</Tooltip>
|
88
89
|
</TableCell>
|
89
|
-
<TableCell>{user.uid}</TableCell>
|
90
90
|
<TableCell>{user.email}</TableCell>
|
91
91
|
<TableCell className={"font-medium align-left"}>{user.displayName}</TableCell>
|
92
92
|
<TableCell className="align-left">
|
@@ -147,7 +147,7 @@ export function UsersTable({ onUserClicked }: {
|
|
147
147
|
</TableBody>
|
148
148
|
</Table>
|
149
149
|
|
150
|
-
<
|
150
|
+
<ConfirmationDialog
|
151
151
|
open={Boolean(userToBeDeleted)}
|
152
152
|
loading={deleteInProgress}
|
153
153
|
onAccept={() => {
|
package/src/hooks/index.ts
CHANGED
@@ -1,2 +1,2 @@
|
|
1
|
-
export * from "./
|
1
|
+
export * from "./useBuildUserManagement";
|
2
2
|
export * from "./useUserManagement";
|