@firecms/user_management 3.1.0 → 3.2.0-canary.44dc65b

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.
@@ -3,35 +3,40 @@ import * as Yup from "yup";
3
3
  import {
4
4
  Button,
5
5
  CheckIcon,
6
+ Chip,
7
+ CopyIcon,
6
8
  Dialog,
7
9
  DialogActions,
8
10
  DialogContent,
9
11
  DialogTitle,
12
+ IconButton,
10
13
  LoadingButton,
11
14
  MultiSelect,
12
15
  MultiSelectItem,
13
16
  TextField,
17
+ Tooltip,
14
18
  } from "@firecms/ui";
15
- import { FieldCaption, Role, useAuthController, User, useSnackbarController } from "@firecms/core";
19
+ import { FieldCaption, Role, useAuthController, User, useSnackbarController, useTranslation
20
+ } from "@firecms/core";
16
21
  import { Formex, useCreateFormex } from "@firecms/formex";
17
22
 
18
23
  import { areRolesEqual } from "../../utils";
19
24
  import { useUserManagement } from "../../hooks";
20
25
  import { RoleChip } from "../roles";
21
26
 
22
- export const UserYupSchema = Yup.object().shape({
23
- displayName: Yup.string().required("Required"),
24
- email: Yup.string().email().required("Required"),
27
+ export const getUserYupSchema = (t: any) => Yup.object().shape({
28
+ displayName: Yup.string().required(t("required")),
29
+ email: Yup.string().email().required(t("required")),
25
30
  roles: Yup.array().min(1)
26
31
  });
27
32
 
28
- function canUserBeEdited(loggedUser: User, user: User, users: User[], roles: Role[], prevUser?: User) {
33
+ function canUserBeEdited(loggedUser: User, user: User, users: User[], roles: Role[], t: any, prevUser?: User) {
29
34
  const admins = users.filter(u => u.roles?.map(r => r.id).includes("admin"));
30
35
  const loggedUserIsAdmin = loggedUser.roles?.map(r => r.id).includes("admin");
31
36
  const didRolesChange = !prevUser || !areRolesEqual(prevUser.roles ?? [], user.roles ?? []);
32
37
 
33
38
  if (didRolesChange && !loggedUserIsAdmin) {
34
- throw new Error("Only admins can change roles");
39
+ throw new Error(t("only_admins_change_roles"));
35
40
  }
36
41
 
37
42
  // was the admin role removed
@@ -39,7 +44,7 @@ function canUserBeEdited(loggedUser: User, user: User, users: User[], roles: Rol
39
44
 
40
45
  // avoid removing the last admin
41
46
  if (adminRoleRemoved && admins.length === 1) {
42
- throw new Error("There must be at least one admin");
47
+ throw new Error(t("must_be_at_least_one_admin"));
43
48
  }
44
49
  return true;
45
50
  }
@@ -53,7 +58,7 @@ export function UserDetailsForm({
53
58
  user?: User,
54
59
  handleClose: () => void
55
60
  }) {
56
-
61
+ const { t } = useTranslation();
57
62
  const snackbarController = useSnackbarController();
58
63
  const {
59
64
  user: loggedInUser
@@ -67,15 +72,15 @@ export function UserDetailsForm({
67
72
 
68
73
  const onUserUpdated = useCallback((savedUser: User): Promise<User> => {
69
74
  if (!loggedInUser) {
70
- throw new Error("Logged user not found");
75
+ throw new Error(t("logged_user_not_found"));
71
76
  }
72
77
  try {
73
- canUserBeEdited(loggedInUser, savedUser, users, roles, userProp);
78
+ canUserBeEdited(loggedInUser, savedUser, users, roles, t, userProp);
74
79
  return saveUser(savedUser);
75
80
  } catch (e: any) {
76
81
  return Promise.reject(e);
77
82
  }
78
- }, [roles, saveUser, userProp, users, loggedInUser]);
83
+ }, [roles, saveUser, userProp, users, loggedInUser, t]);
79
84
 
80
85
  const formex = useCreateFormex({
81
86
  initialValues: userProp ?? {
@@ -84,7 +89,7 @@ export function UserDetailsForm({
84
89
  roles: roles.filter(r => r.id === "editor")
85
90
  } as User,
86
91
  validation: (values) => {
87
- return UserYupSchema.validate(values, { abortEarly: false })
92
+ return getUserYupSchema(t).validate(values, { abortEarly: false })
88
93
  .then(() => {
89
94
  return {};
90
95
  }).catch((e) => {
@@ -141,13 +146,35 @@ export function UserDetailsForm({
141
146
  height: "100%"
142
147
  }}>
143
148
 
144
- <DialogTitle variant={"h4"} gutterBottom={false}>
145
- User
146
- </DialogTitle>
149
+ <DialogTitle variant={"h4"} gutterBottom={false}>{t("user")}</DialogTitle>
147
150
  <DialogContent className="h-full flex-grow">
148
151
 
149
152
  <div className={"grid grid-cols-12 gap-4"}>
150
153
 
154
+ {!isNewUser && userProp?.uid && (
155
+ <div className={"col-span-12"}>
156
+ <div className={"flex items-center gap-2"}>
157
+ <span className={"text-sm text-surface-accent-600 dark:text-surface-accent-400"}>{t("user_id")}</span>
158
+ <Chip size={"small"}>
159
+ {userProp.uid}
160
+ </Chip>
161
+ <Tooltip title={t("copy")}>
162
+ <IconButton
163
+ size={"small"}
164
+ onClick={() => {
165
+ navigator.clipboard.writeText(userProp.uid);
166
+ snackbarController.open({
167
+ type: "success",
168
+ message: t("copied")
169
+ });
170
+ }}>
171
+ <CopyIcon size={"small"}/>
172
+ </IconButton>
173
+ </Tooltip>
174
+ </div>
175
+ </div>
176
+ )}
177
+
151
178
  <div className={"col-span-12"}>
152
179
  <TextField
153
180
  name="displayName"
@@ -156,10 +183,10 @@ export function UserDetailsForm({
156
183
  value={values.displayName ?? ""}
157
184
  onChange={handleChange}
158
185
  aria-describedby="name-helper-text"
159
- label="Name"
186
+ label={t("name")}
160
187
  />
161
188
  <FieldCaption>
162
- {submitCount > 0 && Boolean(errors.displayName) ? errors.displayName : "Name of this user"}
189
+ {submitCount > 0 && Boolean(errors.displayName) ? errors.displayName : t("name_of_this_user")}
163
190
  </FieldCaption>
164
191
  </div>
165
192
  <div className={"col-span-12"}>
@@ -170,16 +197,16 @@ export function UserDetailsForm({
170
197
  value={values.email ?? ""}
171
198
  onChange={handleChange}
172
199
  aria-describedby="email-helper-text"
173
- label="Email"
200
+ label={t("email")}
174
201
  />
175
202
  <FieldCaption>
176
- {submitCount > 0 && Boolean(errors.email) ? errors.email : "Email of this user"}
203
+ {submitCount > 0 && Boolean(errors.email) ? errors.email : t("email_of_this_user")}
177
204
  </FieldCaption>
178
205
  </div>
179
206
  <div className={"col-span-12"}>
180
207
  <MultiSelect
181
208
  className={"w-full"}
182
- label="Roles"
209
+ label={t("roles")}
183
210
  value={values.roles?.map(r => r.id) ?? []}
184
211
  onValueChange={(value: string[]) => setFieldValue("roles", value.map(id => roles.find(r => r.id === id) as Role))}
185
212
  // renderValue={(value: string) => {
@@ -207,9 +234,7 @@ export function UserDetailsForm({
207
234
  <Button variant={"text"}
208
235
  onClick={() => {
209
236
  handleClose();
210
- }}>
211
- Cancel
212
- </Button>
237
+ }}>{t("cancel")}</Button>
213
238
 
214
239
  <LoadingButton
215
240
  variant="filled"
@@ -217,7 +242,7 @@ export function UserDetailsForm({
217
242
  disabled={!dirty}
218
243
  loading={isSubmitting}
219
244
  >
220
- {isNewUser ? "Create user" : "Update"}
245
+ {isNewUser ? t("create_user") : t("update")}
221
246
  </LoadingButton>
222
247
  </DialogActions>
223
248
  </form>
@@ -8,7 +8,8 @@ import {
8
8
  ConfirmationDialog, Role,
9
9
  useAuthController,
10
10
  useCustomizationController, User,
11
- useSnackbarController
11
+ useSnackbarController,
12
+ useTranslation
12
13
  } from "@firecms/core";
13
14
  import {
14
15
  Avatar,
@@ -31,6 +32,7 @@ import { PersistedUser } from "../../types";
31
32
  export function UsersTable({ onUserClicked }: {
32
33
  onUserClicked: (user: User) => void;
33
34
  }) {
35
+ const { t } = useTranslation();
34
36
 
35
37
  const {
36
38
  users,
@@ -55,10 +57,10 @@ export function UsersTable({ onUserClicked }: {
55
57
 
56
58
  <TableHeader>
57
59
  <TableCell className="w-12"></TableCell>
58
- <TableCell>Email</TableCell>
59
- <TableCell>Name</TableCell>
60
- <TableCell>Roles</TableCell>
61
- <TableCell>Created on</TableCell>
60
+ <TableCell>{t("email")}</TableCell>
61
+ <TableCell>{t("name")}</TableCell>
62
+ <TableCell>{t("roles")}</TableCell>
63
+ <TableCell>{t("created_on")}</TableCell>
62
64
  <TableCell className="w-12"></TableCell>
63
65
  </TableHeader>
64
66
  <TableBody>
@@ -102,7 +104,7 @@ export function UsersTable({ onUserClicked }: {
102
104
  <TableCell className={"w-12"}>
103
105
  <Tooltip
104
106
  asChild={true}
105
- title={"Delete this user"}>
107
+ title={t("delete_this_user")}>
106
108
  <IconButton
107
109
  size={"smallest"}
108
110
  onClick={(event) => {
@@ -121,7 +123,7 @@ export function UsersTable({ onUserClicked }: {
121
123
  <TableCell colspan={6}>
122
124
  <CenteredView className={"flex flex-col gap-4 my-8 items-center"}>
123
125
  <Typography variant={"label"}>
124
- There are no users yet
126
+ {t("no_users_yet")}
125
127
  </Typography>
126
128
  <Button
127
129
  onClick={() => {
@@ -151,8 +153,7 @@ export function UsersTable({ onUserClicked }: {
151
153
  })
152
154
  });
153
155
  }}>
154
-
155
- Add the logged user as an admin
156
+ {t("add_logged_user_as_admin")}
156
157
  </Button>
157
158
  </CenteredView>
158
159
  </TableCell>
@@ -185,7 +186,7 @@ export function UsersTable({ onUserClicked }: {
185
186
  onCancel={() => {
186
187
  setUserToBeDeleted(undefined);
187
188
  }}
188
- title={<>Delete?</>}
189
- body={<>Are you sure you want to delete this user?</>} />
189
+ title={<>{t("delete_confirmation_title")}</>}
190
+ body={<>{t("delete_user_confirmation")}</>} />
190
191
  </div>);
191
192
  }
@@ -4,9 +4,12 @@ import { UsersTable } from "./UsersTable";
4
4
  import { UserDetailsForm } from "./UserDetailsForm";
5
5
  import React, { useCallback, useState } from "react";
6
6
  import { useUserManagement } from "../../hooks/useUserManagement";
7
- import { User } from "@firecms/core";
7
+ import { User, useTranslation
8
+ } from "@firecms/core";
8
9
 
9
10
  export const UsersView = function UsersView({ children }: { children?: React.ReactNode }) {
11
+ const { t } = useTranslation();
12
+
10
13
 
11
14
  const [dialogOpen, setDialogOpen] = useState<boolean>(false);
12
15
  const [selectedUser, setSelectedUser] = useState<User | undefined>();
@@ -39,15 +42,11 @@ export const UsersView = function UsersView({ children }: { children?: React.Rea
39
42
  className="flex items-center mt-12">
40
43
  <Typography gutterBottom variant="h4"
41
44
  className="flex-grow"
42
- component="h4">
43
- Users
44
- </Typography>
45
+ component="h4">{t("users")}</Typography>
45
46
  <Button
46
47
  size={"large"}
47
48
  startIcon={<AddIcon/>}
48
- onClick={handleAddUser}>
49
- Add user
50
- </Button>
49
+ onClick={handleAddUser}>{t("add_user")}</Button>
51
50
  </div>
52
51
 
53
52
  <UsersTable onUserClicked={onUserClicked}/>
@@ -1,4 +1,4 @@
1
- import { FireCMSPlugin, useAuthController, User, useSnackbarController } from "@firecms/core";
1
+ import { FireCMSPlugin, useAuthController, User, useSnackbarController, useTranslation } from "@firecms/core";
2
2
  import { UserManagementProvider } from "./UserManagementProvider";
3
3
  import { UserManagement } from "./types";
4
4
  import { AddIcon, Button, Paper, Typography } from "@firecms/ui";
@@ -46,19 +46,20 @@ export function IntroWidget({
46
46
 
47
47
  const authController = useAuthController();
48
48
  const snackbarController = useSnackbarController();
49
+ const { t } = useTranslation();
49
50
 
50
51
  const buttonLabel = noUsers && noRoles
51
- ? "Create default roles and add current user as admin"
52
+ ? t("create_default_roles_and_add_admin")
52
53
  : noUsers
53
- ? "Add current user as admin"
54
- : noRoles ? "Create default roles" : undefined;
54
+ ? t("add_current_user_as_admin")
55
+ : noRoles ? t("create_default_roles") : undefined;
55
56
 
56
57
  return (
57
58
  <Paper
58
59
  className={"my-4 flex flex-col px-4 py-6 bg-white dark:bg-surface-accent-800 gap-2"}>
59
- <Typography variant={"subtitle2"} className={"uppercase"}>Create your users and roles</Typography>
60
+ <Typography variant={"subtitle2"} className={"uppercase"}>{t("create_your_users_and_roles")}</Typography>
60
61
  <Typography>
61
- You have no users or roles defined. You can create default roles and add the current user as admin.
62
+ {t("no_users_or_roles_defined")}
62
63
  </Typography>
63
64
  <Button
64
65