@firecms/user_management 3.0.0-3.0.0-canary.27.0 → 3.0.0-3.0.0-canary.44.0

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-3.0.0-canary.27.0",
4
+ "version": "3.0.0-3.0.0-canary.44.0",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -24,15 +24,14 @@
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-3.0.0-canary.27.0",
34
- "@firecms/formex": "^3.0.0-3.0.0-canary.27.0",
35
- "@firecms/ui": "^3.0.0-3.0.0-canary.27.0",
32
+ "@firecms/core": "^3.0.0-3.0.0-canary.44.0",
33
+ "@firecms/formex": "^3.0.0-3.0.0-canary.44.0",
34
+ "@firecms/ui": "^3.0.0-3.0.0-canary.44.0",
36
35
  "date-fns": "^3.6.0"
37
36
  },
38
37
  "peerDependencies": {
@@ -41,10 +40,10 @@
41
40
  "react-dom": "^18.2.0"
42
41
  },
43
42
  "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",
43
+ "@types/node": "^20.12.7",
44
+ "@types/react": "^18.2.79",
45
+ "@types/react-dom": "^18.2.25",
46
+ "@typescript-eslint/parser": "^7.7.0",
48
47
  "eslint": "^8.57.0",
49
48
  "eslint-config-standard": "^17.1.0",
50
49
  "eslint-plugin-import": "^2.29.1",
@@ -52,8 +51,8 @@
52
51
  "eslint-plugin-promise": "^6.1.1",
53
52
  "eslint-plugin-react": "^7.34.1",
54
53
  "eslint-plugin-react-hooks": "^4.6.0",
55
- "typescript": "^5.4.2",
56
- "vite": "^5.2.3"
54
+ "typescript": "^5.4.5",
55
+ "vite": "^5.2.9"
57
56
  },
58
57
  "scripts": {
59
58
  "dev": "vite",
@@ -71,5 +70,5 @@
71
70
  "react-app/jest"
72
71
  ]
73
72
  },
74
- "gitHead": "17a60c250932174649cf62f3794247ff5b2e07e2"
73
+ "gitHead": "ed16b40251cd10faff8af7fe7b6817aa5deee02b"
75
74
  }
@@ -1,2 +1,2 @@
1
- export * from "./useBuildFirestoreUserManagement";
1
+ export * from "./useFirestoreUserManagement";
2
2
  export * from "./useUserManagement";
@@ -1,11 +1,10 @@
1
- import React, { useCallback, useEffect, useRef } from "react";
1
+ import React, { useCallback, useEffect } from "react";
2
2
  import {
3
3
  addDoc,
4
4
  collection,
5
5
  deleteDoc,
6
6
  doc,
7
7
  DocumentSnapshot,
8
- Firestore,
9
8
  getFirestore,
10
9
  onSnapshot,
11
10
  setDoc
@@ -38,8 +37,14 @@ export interface UserManagementParams {
38
37
  */
39
38
  rolesPath?: string;
40
39
 
40
+ /**
41
+ * Maximum number of users that can be created.
42
+ */
41
43
  usersLimit?: number;
42
44
 
45
+ /**
46
+ * Can the logged user edit roles
47
+ */
43
48
  canEditRoles?: boolean;
44
49
 
45
50
  /**
@@ -63,17 +68,15 @@ export interface UserManagementParams {
63
68
  * @param usersLimit
64
69
  * @param canEditRoles
65
70
  */
66
- export function useBuildFirestoreUserManagement({
67
- firebaseApp,
68
- usersPath = "__FIRECMS/config/users",
69
- rolesPath = "__FIRECMS/config/roles",
70
- usersLimit,
71
- canEditRoles = true,
72
- allowDefaultRolesCreation,
73
- includeCollectionConfigPermissions
74
- }: UserManagementParams): UserManagement {
75
-
76
- const firestoreRef = useRef<Firestore>();
71
+ export function useFirestoreUserManagement({
72
+ firebaseApp,
73
+ usersPath = "__FIRECMS/config/users",
74
+ rolesPath = "__FIRECMS/config/roles",
75
+ usersLimit,
76
+ canEditRoles = true,
77
+ allowDefaultRolesCreation,
78
+ includeCollectionConfigPermissions
79
+ }: UserManagementParams): UserManagement {
77
80
 
78
81
  const [rolesLoading, setRolesLoading] = React.useState<boolean>(true);
79
82
  const [usersLoading, setUsersLoading] = React.useState<boolean>(true);
@@ -90,11 +93,6 @@ export function useBuildFirestoreUserManagement({
90
93
 
91
94
  const loading = rolesLoading || usersLoading;
92
95
 
93
- useEffect(() => {
94
- if (!firebaseApp) return;
95
- firestoreRef.current = getFirestore(firebaseApp);
96
- }, [firebaseApp]);
97
-
98
96
  useEffect(() => {
99
97
  if (!firebaseApp || !rolesPath) return;
100
98
  const firestore = getFirestore(firebaseApp);
@@ -107,12 +105,13 @@ export function useBuildFirestoreUserManagement({
107
105
  const newRoles = docsToRoles(snapshot.docs);
108
106
  setRoles(newRoles);
109
107
  } catch (e) {
110
- // console.error(e);
108
+ console.error("Error loading roles", e);
111
109
  setRolesError(e as Error);
112
110
  }
113
111
  setRolesLoading(false);
114
112
  },
115
113
  error: (e) => {
114
+ console.error("Error loading roles", e);
116
115
  setRolesError(e);
117
116
  setRolesLoading(false);
118
117
  }
@@ -132,11 +131,13 @@ export function useBuildFirestoreUserManagement({
132
131
  const newUsers = docsToUsers(snapshot.docs);
133
132
  setUsersWithRoleIds(newUsers);
134
133
  } catch (e) {
134
+ console.error("Error loading users", e);
135
135
  setUsersError(e as Error);
136
136
  }
137
137
  setUsersLoading(false);
138
138
  },
139
139
  error: (e) => {
140
+ console.error("Error loading users", e);
140
141
  setUsersError(e);
141
142
  setUsersLoading(false);
142
143
  }
@@ -145,8 +146,9 @@ export function useBuildFirestoreUserManagement({
145
146
  }, [firebaseApp, usersPath]);
146
147
 
147
148
  const saveUser = useCallback(async (user: User): Promise<User> => {
148
- const firestore = firestoreRef.current;
149
- if (!firestore || !usersPath) throw Error("useFirestoreConfigurationPersistence Firestore not initialised");
149
+ if (!firebaseApp) throw Error("useFirestoreUserManagement Firebase not initialised");
150
+ const firestore = getFirestore(firebaseApp);
151
+ if (!firestore || !usersPath) throw Error("useFirestoreUserManagement Firestore not initialised");
150
152
  console.debug("Persisting user", user);
151
153
  const roleIds = user.roles?.map(r => r.id);
152
154
  const {
@@ -162,11 +164,12 @@ export function useBuildFirestoreUserManagement({
162
164
  } else {
163
165
  return addDoc(collection(firestore, usersPath), data).then(() => user);
164
166
  }
165
- }, [usersPath]);
167
+ }, [usersPath, firebaseApp]);
166
168
 
167
169
  const saveRole = useCallback((role: Role): Promise<void> => {
168
- const firestore = firestoreRef.current;
169
- if (!firestore || !rolesPath) throw Error("useFirestoreConfigurationPersistence Firestore not initialised");
170
+ if (!firebaseApp) throw Error("useFirestoreUserManagement Firebase not initialised");
171
+ const firestore = getFirestore(firebaseApp);
172
+ if (!firestore || !rolesPath) throw Error("useFirestoreUserManagement Firestore not initialised");
170
173
  console.debug("Persisting role", role);
171
174
  const {
172
175
  id,
@@ -174,24 +177,26 @@ export function useBuildFirestoreUserManagement({
174
177
  } = role;
175
178
  const ref = doc(firestore, rolesPath, id);
176
179
  return setDoc(ref, roleData, { merge: true });
177
- }, [rolesPath]);
180
+ }, [rolesPath, firebaseApp]);
178
181
 
179
182
  const deleteUser = useCallback(async (user: User): Promise<void> => {
180
- const firestore = firestoreRef.current;
181
- if (!firestore || !usersPath) throw Error("useFirestoreConfigurationPersistence Firestore not initialised");
183
+ if (!firebaseApp) throw Error("useFirestoreUserManagement Firebase not initialised");
184
+ const firestore = getFirestore(firebaseApp);
185
+ if (!firestore || !usersPath) throw Error("useFirestoreUserManagement Firestore not initialised");
182
186
  console.debug("Deleting", user);
183
187
  const { uid } = user;
184
188
  return deleteDoc(doc(firestore, usersPath, uid));
185
- }, [usersPath]);
189
+ }, [usersPath, firebaseApp]);
186
190
 
187
191
  const deleteRole = useCallback((role: Role): Promise<void> => {
188
- const firestore = firestoreRef.current;
189
- if (!firestore || !rolesPath) throw Error("useFirestoreConfigurationPersistence Firestore not initialised");
192
+ if (!firebaseApp) throw Error("useFirestoreUserManagement Firebase not initialised");
193
+ const firestore = getFirestore(firebaseApp);
194
+ if (!firestore || !rolesPath) throw Error("useFirestoreUserManagement Firestore not initialised");
190
195
  console.debug("Deleting", role);
191
196
  const { id } = role;
192
197
  const ref = doc(firestore, rolesPath, id);
193
198
  return deleteDoc(ref);
194
- }, [rolesPath]);
199
+ }, [rolesPath, firebaseApp]);
195
200
 
196
201
  const collectionPermissions: PermissionsBuilder = useCallback(({
197
202
  collection,
@@ -201,20 +206,17 @@ export function useBuildFirestoreUserManagement({
201
206
  user
202
207
  }), []);
203
208
 
204
- const userIds = users.map(u => u.uid);
205
209
  const defineRolesFor: ((user: User) => Role[] | undefined) = useCallback((user) => {
206
210
  if (!users) throw Error("Users not loaded");
207
211
  const mgmtUser = users.find(u => u.email?.toLowerCase() === user?.email?.toLowerCase());
208
212
  return mgmtUser?.roles;
209
- }, [userIds]);
213
+ }, [users]);
210
214
 
211
215
  const authenticator: Authenticator = useCallback(({ user }) => {
212
- console.log("Authenticating user", user);
213
- // return true;"
216
+ console.debug("Authenticating user", user);
214
217
 
215
- // console.log("authentication", user, userManagement);
216
218
  if (loading) {
217
- console.log("User management is still loading");
219
+ console.warn("User management is still loading");
218
220
  return false;
219
221
  }
220
222
 
@@ -225,11 +227,10 @@ export function useBuildFirestoreUserManagement({
225
227
 
226
228
  const mgmtUser = users.find(u => u.email?.toLowerCase() === user?.email?.toLowerCase());
227
229
  if (mgmtUser) {
228
- // authController.setRoles(mgmtUser.roles ?? [])
229
230
  return true;
230
231
  }
231
232
 
232
- throw Error("Could not find a user with the provided email");
233
+ throw Error("Could not find a user with the provided email in the user management system.");
233
234
  }, [loading, users])
234
235
 
235
236
  return {
@@ -238,9 +239,11 @@ export function useBuildFirestoreUserManagement({
238
239
  users,
239
240
  saveUser,
240
241
  saveRole,
242
+ rolesError,
241
243
  deleteUser,
242
244
  deleteRole,
243
245
  usersLimit,
246
+ usersError,
244
247
  canEditRoles: canEditRoles === undefined ? true : canEditRoles,
245
248
  allowDefaultRolesCreation: allowDefaultRolesCreation === undefined ? true : allowDefaultRolesCreation,
246
249
  includeCollectionConfigPermissions: Boolean(includeCollectionConfigPermissions),
@@ -42,7 +42,7 @@ export type UserManagement<USER extends User = User> = {
42
42
  * Define the roles for a given user. You will typically want to plug this into your auth controller.
43
43
  * @param user
44
44
  */
45
- defineRolesFor: (user: User) => Promise<Role[]> | Role[] | undefined;
45
+ defineRolesFor: (user: User) => Promise<Role[] | undefined> | Role[] | undefined;
46
46
 
47
47
  /**
48
48
  * You can build an authenticator callback from the current configuration of the user management.
@@ -50,4 +50,7 @@ export type UserManagement<USER extends User = User> = {
50
50
  */
51
51
  authenticator?: Authenticator;
52
52
 
53
+ rolesError?: Error;
54
+ usersError?: Error;
55
+
53
56
  };
@@ -1,13 +1,28 @@
1
- import { FireCMSPlugin } from "@firecms/core";
1
+ import { FireCMSPlugin, useAuthController, useSnackbarController } from "@firecms/core";
2
2
  import { UserManagementProvider } from "./UserManagementProvider";
3
- import { UserManagement } from "./types";
3
+ import { PersistedUser, UserManagement } from "./types";
4
+ import { AddIcon, Button, Paper, Typography } from "@firecms/ui";
5
+ import { DEFAULT_ROLES } from "./components/roles/default_roles";
4
6
 
5
7
  export function useUserManagementPlugin({ userManagement }: {
6
8
  userManagement: UserManagement,
7
9
  }): FireCMSPlugin {
10
+
11
+ const noUsers = userManagement.users.length === 0;
12
+ const noRoles = userManagement.roles.length === 0;
13
+
8
14
  return {
9
15
  key: "user_management",
10
16
  loading: userManagement.loading,
17
+
18
+ homePage: {
19
+ additionalChildrenStart: noUsers || noRoles
20
+ ? <IntroWidget
21
+ noUsers={noUsers}
22
+ noRoles={noRoles}
23
+ userManagement={userManagement}/>
24
+ : undefined
25
+ },
11
26
  provider: {
12
27
  Component: UserManagementProvider,
13
28
  props: {
@@ -16,3 +31,74 @@ export function useUserManagementPlugin({ userManagement }: {
16
31
  }
17
32
  }
18
33
  }
34
+
35
+ export function IntroWidget({
36
+ noUsers,
37
+ noRoles,
38
+ userManagement
39
+ }: {
40
+ noUsers: boolean;
41
+ noRoles: boolean;
42
+ userManagement: UserManagement<PersistedUser>;
43
+ }) {
44
+
45
+ const authController = useAuthController();
46
+ const snackbarController = useSnackbarController();
47
+
48
+ const buttonLabel = noUsers && noRoles
49
+ ? "Create default roles and add current user as admin"
50
+ : noUsers
51
+ ? "Add current user as admin"
52
+ : noRoles ? "Create default roles" : undefined;
53
+
54
+ return (
55
+ <Paper
56
+ className={"my-4 flex flex-col px-4 py-6 bg-white dark:bg-slate-800 gap-2"}>
57
+ <Typography variant={"subtitle2"} className={"uppercase"}>Create your users and roles</Typography>
58
+ <Typography>
59
+ You have no users or roles defined. You can create default roles and add the current user as admin.
60
+ </Typography>
61
+ <Button onClick={() => {
62
+ if (!authController.user?.uid) {
63
+ throw Error("UsersTable, authController misconfiguration");
64
+ }
65
+ if (noUsers) {
66
+ userManagement.saveUser({
67
+ uid: authController.user?.uid,
68
+ email: authController.user?.email,
69
+ displayName: authController.user?.displayName,
70
+ photoURL: authController.user?.photoURL,
71
+ providerId: authController.user?.providerId,
72
+ isAnonymous: authController.user?.isAnonymous,
73
+ roles: [{
74
+ id: "admin",
75
+ name: "Admin"
76
+ }],
77
+ created_on: new Date()
78
+ })
79
+ .then(() => {
80
+ snackbarController.open({
81
+ type: "success",
82
+ message: "User added successfully"
83
+ })
84
+ })
85
+ .catch((error) => {
86
+ snackbarController.open({
87
+ type: "error",
88
+ message: "Error adding user: " + error.message
89
+ })
90
+ });
91
+ }
92
+ if (noRoles) {
93
+ DEFAULT_ROLES.forEach((role) => {
94
+ userManagement.saveRole(role);
95
+ });
96
+ }
97
+ }}>
98
+ <AddIcon/>
99
+ {buttonLabel}
100
+ </Button>
101
+ </Paper>
102
+ );
103
+
104
+ }