@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/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.15",
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.15",
34
- "@firecms/formex": "^3.0.0-canary.15",
35
- "@firecms/ui": "^3.0.0-canary.15",
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
- "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.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
- "eslintConfig": {
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
- return onRoleUpdated(role)
63
- .then(() => {
64
- formexController.resetForm({
65
- values: role
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
- handleClose();
68
- })
69
- .catch(e => setSavingError(e));
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
- className="bg-inherit">
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.path}.create`)) ?? false}
260
- onCheckedChange={(checked) => setFieldValue(`collectionPermissions.${col.path}.create`, checked)}/>
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.path}.read`)) ?? false}
267
- onCheckedChange={(checked) => setFieldValue(`collectionPermissions.${col.path}.read`, checked)}/>
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.path}.edit`)) ?? false}
274
- onCheckedChange={(checked) => setFieldValue(`collectionPermissions.${col.path}.edit`, checked)}/>
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.path}.delete`)) ?? false}
281
- onCheckedChange={(checked) => setFieldValue(`collectionPermissions.${col.path}.delete`, checked)}/>
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 { 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,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 title={"Delete this user"}>
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
- <DeleteConfirmationDialog
150
+ <ConfirmationDialog
151
151
  open={Boolean(userToBeDeleted)}
152
152
  loading={deleteInProgress}
153
153
  onAccept={() => {
@@ -1,2 +1,2 @@
1
- export * from "./useBuildFirestoreUserManagement";
1
+ export * from "./useBuildUserManagement";
2
2
  export * from "./useUserManagement";