@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/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.14",
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.14",
34
- "@firecms/formex": "^3.0.0-canary.14",
35
- "@firecms/ui": "^3.0.0-canary.14",
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
- "firebase": "^10.7.1",
40
- "react": "^18.2.0",
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.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.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
- "eslintConfig": {
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
- return onRoleUpdated(role)
63
- .then(() => {
64
- formexController.resetForm({
65
- values: role
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
- handleClose();
68
- })
69
- .catch(e => setSavingError(e));
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
- className="bg-inherit">
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.path}.create`)) ?? false}
260
- onCheckedChange={(checked) => setFieldValue(`collectionPermissions.${col.path}.create`, checked)}/>
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.path}.read`)) ?? false}
267
- onCheckedChange={(checked) => setFieldValue(`collectionPermissions.${col.path}.read`, checked)}/>
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.path}.edit`)) ?? false}
274
- onCheckedChange={(checked) => setFieldValue(`collectionPermissions.${col.path}.edit`, checked)}/>
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.path}.delete`)) ?? false}
281
- onCheckedChange={(checked) => setFieldValue(`collectionPermissions.${col.path}.delete`, checked)}/>
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 { DeleteConfirmationDialog, Role } from "@firecms/core";
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 title={"Delete this role"}>
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
- <DeleteConfirmationDialog
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 title={!canEditRoles ? "Update plans to customise roles" : undefined}>
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
- onMultiValueChange={(value: string[]) => setFieldValue("roles", value.map(id => roles.find(r => r.id === id) as Role))}
187
- renderValue={(value: string) => {
188
- const userRole = roles
189
- .find((role) => role.id === value);
190
- if (!userRole) return null;
191
- return <div className="flex flex-wrap space-x-2 space-y-2">
192
- <RoleChip key={userRole?.id} role={userRole}/>
193
- </div>;
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
- DeleteConfirmationDialog, Role,
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 title={"Delete this user"}>
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
- <DeleteConfirmationDialog
152
+ <ConfirmationDialog
151
153
  open={Boolean(userToBeDeleted)}
152
154
  loading={deleteInProgress}
153
155
  onAccept={() => {
@@ -1,2 +1,2 @@
1
- export * from "./useBuildFirestoreUserManagement";
1
+ export * from "./useBuildUserManagement";
2
2
  export * from "./useUserManagement";