@firecms/user_management 3.0.0-canary.8 → 3.0.0-canary.9
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/dist/UserManagementProvider.d.ts +4 -3
- package/dist/components/roles/RoleChip.d.ts +1 -1
- package/dist/components/roles/RolesDetailsForm.d.ts +1 -2
- package/dist/components/roles/RolesTable.d.ts +1 -1
- package/dist/components/roles/default_roles.d.ts +1 -1
- package/dist/components/users/UserDetailsForm.d.ts +2 -2
- package/dist/components/users/UsersTable.d.ts +2 -2
- package/dist/hooks/useUserManagement.d.ts +3 -2
- package/dist/index.es.js +9 -6
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +1 -1
- package/dist/index.umd.js.map +1 -1
- package/dist/types/index.d.ts +1 -2
- package/dist/types/persisted_user.d.ts +5 -0
- package/dist/types/user_management.d.ts +2 -4
- package/dist/utils/permissions.d.ts +3 -4
- package/package.json +6 -8
- package/src/UserManagementProvider.tsx +4 -3
- package/src/components/roles/RoleChip.tsx +1 -1
- package/src/components/roles/RolesDetailsForm.tsx +1 -2
- package/src/components/roles/RolesTable.tsx +1 -2
- package/src/components/roles/RolesView.tsx +1 -2
- package/src/components/roles/default_roles.tsx +1 -1
- package/src/components/users/UserDetailsForm.tsx +11 -12
- package/src/components/users/UsersTable.tsx +6 -6
- package/src/components/users/UsersView.tsx +3 -3
- package/src/hooks/useBuildFirestoreUserManagement.tsx +11 -8
- package/src/hooks/useUserManagement.tsx +3 -3
- package/src/types/index.ts +1 -2
- package/src/types/persisted_user.ts +6 -0
- package/src/types/user_management.tsx +2 -4
- package/src/useUserManagementPlugin.tsx +1 -1
- package/src/utils/permissions.ts +3 -4
- package/dist/types/firecms_user.d.ts +0 -7
- package/dist/types/roles.d.ts +0 -31
- package/src/types/firecms_user.ts +0 -8
- package/src/types/roles.ts +0 -41
- package/tailwind.config.js +0 -68
@@ -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, toSnakeCase, } from "@firecms/core";
|
4
|
+
import { EntityCollection, FieldCaption, Role, toSnakeCase, } from "@firecms/core";
|
5
5
|
import {
|
6
6
|
Button,
|
7
7
|
Checkbox,
|
@@ -24,7 +24,6 @@ import {
|
|
24
24
|
} from "@firecms/ui";
|
25
25
|
import { useUserManagement } from "../../hooks";
|
26
26
|
import { Formex, getIn, useCreateFormex } from "@firecms/formex";
|
27
|
-
import { Role } from "../../types";
|
28
27
|
|
29
28
|
export const RoleYupSchema = Yup.object().shape({
|
30
29
|
id: Yup.string().required("Required"),
|
@@ -13,9 +13,8 @@ import {
|
|
13
13
|
Tooltip,
|
14
14
|
Typography
|
15
15
|
} from "@firecms/ui";
|
16
|
-
import { DeleteConfirmationDialog } from "@firecms/core";
|
16
|
+
import { DeleteConfirmationDialog, Role } from "@firecms/core";
|
17
17
|
import { useUserManagement } from "../../hooks";
|
18
|
-
import { Role } from "../../types";
|
19
18
|
import { RoleChip } from "./RoleChip";
|
20
19
|
import { DEFAULT_ROLES } from "./default_roles";
|
21
20
|
|
@@ -1,11 +1,10 @@
|
|
1
1
|
import React, { useCallback, useState } from "react";
|
2
2
|
|
3
|
-
import { useNavigationController } from "@firecms/core";
|
3
|
+
import { Role, useNavigationController } from "@firecms/core";
|
4
4
|
import { AddIcon, Button, Container, Tooltip, Typography } from "@firecms/ui";
|
5
5
|
import { RolesTable } from "./RolesTable";
|
6
6
|
import { RolesDetailsForm } from "./RolesDetailsForm";
|
7
7
|
import { useUserManagement } from "../../hooks";
|
8
|
-
import { Role } from "../../types";
|
9
8
|
|
10
9
|
export const RolesView = React.memo(
|
11
10
|
function RolesView({ children }: { children?: React.ReactNode }) {
|
@@ -12,10 +12,9 @@ import {
|
|
12
12
|
TextField,
|
13
13
|
Typography,
|
14
14
|
} from "@firecms/ui";
|
15
|
-
import { FieldCaption, useSnackbarController } from "@firecms/core";
|
15
|
+
import { FieldCaption, Role, User, useSnackbarController } from "@firecms/core";
|
16
16
|
import { Formex, useCreateFormex } from "@firecms/formex";
|
17
17
|
|
18
|
-
import { Role, UserWithRoles } from "../../types";
|
19
18
|
import { areRolesEqual } from "../../utils";
|
20
19
|
import { useUserManagement } from "../../hooks";
|
21
20
|
import { RoleChip } from "../roles";
|
@@ -26,17 +25,17 @@ export const UserYupSchema = Yup.object().shape({
|
|
26
25
|
roles: Yup.array().min(1)
|
27
26
|
});
|
28
27
|
|
29
|
-
function canUserBeEdited(loggedUser:
|
30
|
-
const admins = users.filter(u => u.roles
|
31
|
-
const loggedUserIsAdmin = loggedUser.roles
|
32
|
-
const didRolesChange = !prevUser || !areRolesEqual(prevUser.roles, user.roles);
|
28
|
+
function canUserBeEdited(loggedUser: User, user: User, users: User[], roles: Role[], prevUser?: User) {
|
29
|
+
const admins = users.filter(u => u.roles?.map(r => r.id).includes("admin"));
|
30
|
+
const loggedUserIsAdmin = loggedUser.roles?.map(r => r.id).includes("admin");
|
31
|
+
const didRolesChange = !prevUser || !areRolesEqual(prevUser.roles ?? [], user.roles ?? []);
|
33
32
|
|
34
33
|
if (didRolesChange && !loggedUserIsAdmin) {
|
35
34
|
throw new Error("Only admins can change roles");
|
36
35
|
}
|
37
36
|
|
38
37
|
// was the admin role removed
|
39
|
-
const adminRoleRemoved = prevUser && prevUser.roles
|
38
|
+
const adminRoleRemoved = prevUser && prevUser.roles?.map(r => r.id).includes("admin") && !user.roles?.map(r => r.id).includes("admin");
|
40
39
|
|
41
40
|
// avoid removing the last admin
|
42
41
|
if (adminRoleRemoved && admins.length === 1) {
|
@@ -51,7 +50,7 @@ export function UserDetailsForm({
|
|
51
50
|
handleClose
|
52
51
|
}: {
|
53
52
|
open: boolean,
|
54
|
-
user?:
|
53
|
+
user?: User,
|
55
54
|
handleClose: () => void
|
56
55
|
}) {
|
57
56
|
|
@@ -64,7 +63,7 @@ export function UserDetailsForm({
|
|
64
63
|
} = useUserManagement();
|
65
64
|
const isNewUser = !userProp;
|
66
65
|
|
67
|
-
const onUserUpdated = useCallback((savedUser:
|
66
|
+
const onUserUpdated = useCallback((savedUser: User): Promise<User> => {
|
68
67
|
if (!loggedInUser) {
|
69
68
|
throw new Error("Logged user not found");
|
70
69
|
}
|
@@ -81,7 +80,7 @@ export function UserDetailsForm({
|
|
81
80
|
displayName: "",
|
82
81
|
email: "",
|
83
82
|
roles: roles.filter(r => r.id === "editor")
|
84
|
-
} as
|
83
|
+
} as User,
|
85
84
|
validation: (values) => {
|
86
85
|
return UserYupSchema.validate(values, { abortEarly: false })
|
87
86
|
.then(() => {
|
@@ -93,7 +92,7 @@ export function UserDetailsForm({
|
|
93
92
|
}, {});
|
94
93
|
});
|
95
94
|
},
|
96
|
-
onSubmit: (user:
|
95
|
+
onSubmit: (user: User, formexController) => {
|
97
96
|
|
98
97
|
return onUserUpdated(user)
|
99
98
|
.then(() => {
|
@@ -181,7 +180,7 @@ export function UserDetailsForm({
|
|
181
180
|
<div className={"col-span-12"}>
|
182
181
|
<MultiSelect
|
183
182
|
label="Roles"
|
184
|
-
value={values.roles
|
183
|
+
value={values.roles?.map(r => r.id) ?? []}
|
185
184
|
onMultiValueChange={(value: string[]) => setFieldValue("roles", value.map(id => roles.find(r => r.id === id) as Role))}
|
186
185
|
renderValue={(value: string) => {
|
187
186
|
const userRole = roles
|
@@ -5,9 +5,9 @@ import * as locales from "date-fns/locale";
|
|
5
5
|
|
6
6
|
import {
|
7
7
|
defaultDateFormat,
|
8
|
-
DeleteConfirmationDialog,
|
8
|
+
DeleteConfirmationDialog, Role,
|
9
9
|
useAuthController,
|
10
|
-
useCustomizationController,
|
10
|
+
useCustomizationController, User,
|
11
11
|
useSnackbarController
|
12
12
|
} from "@firecms/core";
|
13
13
|
import {
|
@@ -23,19 +23,19 @@ import {
|
|
23
23
|
Tooltip,
|
24
24
|
Typography,
|
25
25
|
} from "@firecms/ui";
|
26
|
-
import { Role, UserWithRoles } from "../../types";
|
27
26
|
import { useUserManagement } from "../../hooks";
|
28
27
|
import { RoleChip } from "../roles";
|
28
|
+
import { PersistedUser } from "../../types";
|
29
29
|
|
30
30
|
export function UsersTable({ onUserClicked }: {
|
31
|
-
onUserClicked: (user:
|
31
|
+
onUserClicked: (user: User) => void;
|
32
32
|
}) {
|
33
33
|
|
34
34
|
const {
|
35
35
|
users,
|
36
36
|
saveUser,
|
37
37
|
deleteUser
|
38
|
-
} = useUserManagement();
|
38
|
+
} = useUserManagement<PersistedUser>();
|
39
39
|
|
40
40
|
const authController = useAuthController();
|
41
41
|
const snackbarController = useSnackbarController();
|
@@ -44,7 +44,7 @@ export function UsersTable({ onUserClicked }: {
|
|
44
44
|
const dateUtilsLocale = customizationController?.locale ? locales[customizationController?.locale as keyof typeof locales] : undefined;
|
45
45
|
const dateFormat: string = customizationController?.dateTimeFormat ?? defaultDateFormat;
|
46
46
|
|
47
|
-
const [userToBeDeleted, setUserToBeDeleted] = useState<
|
47
|
+
const [userToBeDeleted, setUserToBeDeleted] = useState<User | undefined>(undefined);
|
48
48
|
const [deleteInProgress, setDeleteInProgress] = useState<boolean>(false);
|
49
49
|
|
50
50
|
return (
|
@@ -3,19 +3,19 @@ import { AddIcon, Button, Container, Typography } from "@firecms/ui";
|
|
3
3
|
import { UsersTable } from "./UsersTable";
|
4
4
|
import { UserDetailsForm } from "./UserDetailsForm";
|
5
5
|
import React, { useCallback, useState } from "react";
|
6
|
-
import { UserWithRoles } from "../../types";
|
7
6
|
import { useUserManagement } from "../../hooks/useUserManagement";
|
7
|
+
import { User } from "@firecms/core";
|
8
8
|
|
9
9
|
export const UsersView = function UsersView({ children }: { children?: React.ReactNode }) {
|
10
10
|
|
11
11
|
const [dialogOpen, setDialogOpen] = useState<boolean>();
|
12
|
-
const [selectedUser, setSelectedUser] = useState<
|
12
|
+
const [selectedUser, setSelectedUser] = useState<User | undefined>();
|
13
13
|
|
14
14
|
const { users, usersLimit } = useUserManagement();
|
15
15
|
|
16
16
|
const reachedUsersLimit = usersLimit !== undefined && (users && users.length >= usersLimit);
|
17
17
|
|
18
|
-
const onUserClicked = useCallback((user:
|
18
|
+
const onUserClicked = useCallback((user: User) => {
|
19
19
|
setSelectedUser(user);
|
20
20
|
setDialogOpen(true);
|
21
21
|
}, []);
|
@@ -10,8 +10,8 @@ import {
|
|
10
10
|
setDoc
|
11
11
|
} from "firebase/firestore";
|
12
12
|
import { FirebaseApp } from "firebase/app";
|
13
|
-
import {
|
14
|
-
import { AuthController, PermissionsBuilder, User } from "@firecms/core";
|
13
|
+
import { UserManagement } from "../types";
|
14
|
+
import { AuthController, PermissionsBuilder, Role, User } from "@firecms/core";
|
15
15
|
import { resolveUserRolePermissions } from "../utils";
|
16
16
|
|
17
17
|
type UserWithRoleIds = User & { roles: string[] };
|
@@ -85,14 +85,14 @@ export function useBuildFirestoreUserManagement({
|
|
85
85
|
const users = usersWithRoleIds.map(u => ({
|
86
86
|
...u,
|
87
87
|
roles: roles.filter(r => u.roles?.includes(r.id))
|
88
|
-
}) as
|
88
|
+
}) as User);
|
89
89
|
|
90
90
|
const [rolesError, setRolesError] = React.useState<Error | undefined>();
|
91
91
|
const [usersError, setUsersError] = React.useState<Error | undefined>();
|
92
92
|
|
93
93
|
const loading = rolesLoading || usersLoading;
|
94
94
|
|
95
|
-
const loggedInUser:
|
95
|
+
const loggedInUser: User | undefined = users.find(u => u.email?.toLowerCase() === authController.user?.email?.toLowerCase());
|
96
96
|
// console.log("authController", authController);
|
97
97
|
// if (!loading && !authController.authLoading) {
|
98
98
|
// const user = authController.user;
|
@@ -155,16 +155,19 @@ export function useBuildFirestoreUserManagement({
|
|
155
155
|
);
|
156
156
|
}, [firebaseApp, usersPath]);
|
157
157
|
|
158
|
-
const saveUser = useCallback(async (user:
|
158
|
+
const saveUser = useCallback(async (user: User): Promise<User> => {
|
159
159
|
const firestore = firestoreRef.current;
|
160
160
|
if (!firestore || !usersPath) throw Error("useFirestoreConfigurationPersistence Firestore not initialised");
|
161
161
|
console.debug("Persisting user", user);
|
162
|
-
const roleIds = user.roles
|
162
|
+
const roleIds = user.roles?.map(r => r.id);
|
163
163
|
const {
|
164
164
|
uid,
|
165
165
|
...userData
|
166
166
|
} = user;
|
167
|
-
return setDoc(doc(firestore, usersPath, uid), {
|
167
|
+
return setDoc(doc(firestore, usersPath, uid), {
|
168
|
+
...userData,
|
169
|
+
roles: roleIds
|
170
|
+
}, { merge: true }).then(() => user);
|
168
171
|
}, [usersPath]);
|
169
172
|
|
170
173
|
const saveRole = useCallback((role: Role): Promise<void> => {
|
@@ -179,7 +182,7 @@ export function useBuildFirestoreUserManagement({
|
|
179
182
|
return setDoc(ref, roleData, { merge: true });
|
180
183
|
}, [rolesPath]);
|
181
184
|
|
182
|
-
const deleteUser = useCallback(async (user:
|
185
|
+
const deleteUser = useCallback(async (user: User): Promise<void> => {
|
183
186
|
const firestore = firestoreRef.current;
|
184
187
|
if (!firestore || !usersPath) throw Error("useFirestoreConfigurationPersistence Firestore not initialised");
|
185
188
|
console.debug("Deleting", user);
|
@@ -1,5 +1,5 @@
|
|
1
1
|
import { useContext } from "react";
|
2
|
-
import { UserManagement } from "../types
|
2
|
+
import { UserManagement } from "../types";
|
3
3
|
import { UserManagementContext } from "../UserManagementProvider";
|
4
|
-
|
5
|
-
export const useUserManagement = () => useContext<UserManagement
|
4
|
+
import { User } from "@firecms/core";
|
5
|
+
export const useUserManagement = <USER extends User>() => useContext<UserManagement<USER>>(UserManagementContext);
|
package/src/types/index.ts
CHANGED
@@ -1,8 +1,6 @@
|
|
1
|
-
import {
|
2
|
-
import { Role } from "./roles";
|
3
|
-
import { PermissionsBuilder } from "@firecms/core";
|
1
|
+
import { PermissionsBuilder, Role, User } from "@firecms/core";
|
4
2
|
|
5
|
-
export type UserManagement<USER extends
|
3
|
+
export type UserManagement<USER extends User = User> = {
|
6
4
|
|
7
5
|
loading: boolean;
|
8
6
|
|
package/src/utils/permissions.ts
CHANGED
@@ -1,5 +1,4 @@
|
|
1
|
-
import {
|
2
|
-
import { Role, UserWithRoles } from "../types";
|
1
|
+
import { EntityCollection, Permissions, Role, User } from "@firecms/core";
|
3
2
|
|
4
3
|
export const RESERVED_GROUPS = ["Admin"];
|
5
4
|
|
@@ -10,7 +9,7 @@ const DEFAULT_PERMISSIONS = {
|
|
10
9
|
delete: false
|
11
10
|
};
|
12
11
|
|
13
|
-
export function resolveUserRolePermissions<UserType extends
|
12
|
+
export function resolveUserRolePermissions<UserType extends User>
|
14
13
|
({ collection, user }: {
|
15
14
|
collection: EntityCollection<any>,
|
16
15
|
user: UserType | null
|
@@ -66,7 +65,7 @@ const mergePermissions = (permA: Permissions, permB: Permissions) => {
|
|
66
65
|
};
|
67
66
|
}
|
68
67
|
|
69
|
-
export function getUserRoles(roles: Role[], fireCMSUser:
|
68
|
+
export function getUserRoles(roles: Role[], fireCMSUser: User): Role[] | undefined {
|
70
69
|
return !roles
|
71
70
|
? undefined
|
72
71
|
: (fireCMSUser.roles
|
package/dist/types/roles.d.ts
DELETED
@@ -1,31 +0,0 @@
|
|
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
|
-
};
|
package/src/types/roles.ts
DELETED
@@ -1,41 +0,0 @@
|
|
1
|
-
import { Permissions } from "@firecms/core";
|
2
|
-
|
3
|
-
export type Role = {
|
4
|
-
|
5
|
-
/**
|
6
|
-
* ID of the role
|
7
|
-
*/
|
8
|
-
id: string;
|
9
|
-
|
10
|
-
/**
|
11
|
-
* Name of the role
|
12
|
-
*/
|
13
|
-
name: string;
|
14
|
-
|
15
|
-
/**
|
16
|
-
* If this flag is true, the user can perform any action
|
17
|
-
*/
|
18
|
-
isAdmin?: boolean;
|
19
|
-
|
20
|
-
/**
|
21
|
-
* Default permissions for all collections for this role.
|
22
|
-
* You can override this values at the collection level using
|
23
|
-
* {@link collectionPermissions}
|
24
|
-
*/
|
25
|
-
defaultPermissions?: Permissions;
|
26
|
-
|
27
|
-
/**
|
28
|
-
* Record of stripped collection ids to their permissions.
|
29
|
-
* @see stripCollectionPath
|
30
|
-
*/
|
31
|
-
collectionPermissions?: Record<string, Permissions>;
|
32
|
-
|
33
|
-
config?: {
|
34
|
-
|
35
|
-
createCollections?: boolean;
|
36
|
-
|
37
|
-
editCollections?: boolean | "own";
|
38
|
-
|
39
|
-
deleteCollections?: boolean | "own";
|
40
|
-
}
|
41
|
-
}
|
package/tailwind.config.js
DELETED
@@ -1,68 +0,0 @@
|
|
1
|
-
export default {
|
2
|
-
darkMode: ["selector", "[data-theme=\"dark\"]"],
|
3
|
-
mode: "jit",
|
4
|
-
content: [
|
5
|
-
"./index.html",
|
6
|
-
"./src/**/*.{js,ts,jsx,tsx}",
|
7
|
-
"./node_modules/firecms/src/**/*.{js,ts,jsx,tsx}",
|
8
|
-
"./node_modules/@firecms/**/src/**/*.{js,ts,jsx,tsx}"
|
9
|
-
],
|
10
|
-
theme: {
|
11
|
-
extend: {
|
12
|
-
fontFamily: {
|
13
|
-
sans: [
|
14
|
-
"Rubik",
|
15
|
-
"Roboto",
|
16
|
-
"Helvetica",
|
17
|
-
"Arial",
|
18
|
-
"sans-serif"
|
19
|
-
],
|
20
|
-
headers: [
|
21
|
-
"Rubik",
|
22
|
-
"Roboto",
|
23
|
-
"Helvetica",
|
24
|
-
"Arial",
|
25
|
-
"sans-serif"
|
26
|
-
],
|
27
|
-
mono: [
|
28
|
-
"IBM Plex Mono",
|
29
|
-
"Space Mono",
|
30
|
-
"Lucida Console",
|
31
|
-
"monospace"
|
32
|
-
]
|
33
|
-
},
|
34
|
-
colors: {
|
35
|
-
primary: "var(--fcms-primary)",
|
36
|
-
"primary-dark": "var(--fcms-primary-dark)",
|
37
|
-
"primary-bg": "var(--fcms-primary-bg)",
|
38
|
-
secondary: "var(--fcms-secondary)",
|
39
|
-
field: {
|
40
|
-
disabled: "rgb(224 224 226)",
|
41
|
-
"disabled-dark": "rgb(35 35 37)"
|
42
|
-
},
|
43
|
-
text: {
|
44
|
-
primary: "rgba(0, 0, 0, 0.87)",
|
45
|
-
"primary-dark": "#ffffff",
|
46
|
-
secondary: "rgba(0, 0, 0, 0.6)",
|
47
|
-
"secondary-dark": "rgba(255, 255, 255, 0.7)",
|
48
|
-
disabled: "rgba(0, 0, 0, 0.38)",
|
49
|
-
"disabled-dark": "rgba(255, 255, 255, 0.5)",
|
50
|
-
label: "rgb(131, 131, 131)"
|
51
|
-
},
|
52
|
-
gray: {
|
53
|
-
50: "#f8f8fc",
|
54
|
-
100: "#E7E7EB",
|
55
|
-
200: "#CFCFD6",
|
56
|
-
300: "#B7B7BF",
|
57
|
-
400: "#A0A0A9",
|
58
|
-
500: "#87878F",
|
59
|
-
600: "#6C6C75",
|
60
|
-
700: "#505058",
|
61
|
-
800: "#35353A",
|
62
|
-
900: "#18181C",
|
63
|
-
950: "#101013"
|
64
|
-
}
|
65
|
-
}
|
66
|
-
}
|
67
|
-
}
|
68
|
-
};
|