import React, { useCallback } from "react"; import * as Yup from "yup"; import { Button, CheckIcon, Chip, CopyIcon, Dialog, DialogActions, DialogContent, DialogTitle, IconButton, LoadingButton, MultiSelect, MultiSelectItem, TextField, Tooltip, } from "@firecms/ui"; import { FieldCaption, Role, useAuthController, User, useSnackbarController, useTranslation } from "@firecms/core"; import { Formex, useCreateFormex } from "@firecms/formex"; import { areRolesEqual } from "../../utils"; import { useUserManagement } from "../../hooks"; import { RoleChip } from "../roles"; export const getUserYupSchema = (t: any) => Yup.object().shape({ displayName: Yup.string().required(t("required")), email: Yup.string().email().required(t("required")), roles: Yup.array().min(1) }); function canUserBeEdited(loggedUser: User, user: User, users: User[], roles: Role[], t: any, prevUser?: User) { const admins = users.filter(u => u.roles?.map(r => r.id).includes("admin")); const loggedUserIsAdmin = loggedUser.roles?.map(r => r.id).includes("admin"); const didRolesChange = !prevUser || !areRolesEqual(prevUser.roles ?? [], user.roles ?? []); if (didRolesChange && !loggedUserIsAdmin) { throw new Error(t("only_admins_change_roles")); } // was the admin role removed const adminRoleRemoved = prevUser && prevUser.roles?.map(r => r.id).includes("admin") && !user.roles?.map(r => r.id).includes("admin"); // avoid removing the last admin if (adminRoleRemoved && admins.length === 1) { throw new Error(t("must_be_at_least_one_admin")); } return true; } export function UserDetailsForm({ open, user: userProp, handleClose }: { open: boolean, user?: User, handleClose: () => void }) { const { t } = useTranslation(); const snackbarController = useSnackbarController(); const { user: loggedInUser } = useAuthController(); const { saveUser, users, roles, } = useUserManagement(); const isNewUser = !userProp; const onUserUpdated = useCallback((savedUser: User): Promise => { if (!loggedInUser) { throw new Error(t("logged_user_not_found")); } try { canUserBeEdited(loggedInUser, savedUser, users, roles, t, userProp); return saveUser(savedUser); } catch (e: any) { return Promise.reject(e); } }, [roles, saveUser, userProp, users, loggedInUser, t]); const formex = useCreateFormex({ initialValues: userProp ?? { displayName: "", email: "", roles: roles.filter(r => r.id === "editor") } as User, validation: (values) => { return getUserYupSchema(t).validate(values, { abortEarly: false }) .then(() => { return {}; }).catch((e) => { return e.inner.reduce((acc: any, error: any) => { acc[error.path] = error.message; return acc; }, {}); }); }, onSubmit: (user: User, formexController) => { return onUserUpdated(user) .then(() => { handleClose(); formexController.resetForm({ values: user }); }).catch((e) => { snackbarController.open({ type: "error", message: e.message }); }); } }); const { isSubmitting, touched, handleChange, values, errors, setFieldValue, dirty, handleSubmit, submitCount } = formex; return ( !open ? handleClose() : undefined} maxWidth={"4xl"} >
{t("user")}
{!isNewUser && userProp?.uid && (
{userProp.uid} { navigator.clipboard.writeText(userProp.uid!); snackbarController.open({ type: "success", message: t("copied") }); }}>
)}
0 && Boolean(errors.displayName)} value={values.displayName ?? ""} onChange={handleChange} aria-describedby="name-helper-text" label={t("name")} /> {submitCount > 0 && Boolean(errors.displayName) ? errors.displayName : t("name_of_this_user")}
0 && Boolean(errors.email)} name="email" value={values.email ?? ""} onChange={handleChange} aria-describedby="email-helper-text" label={t("email")} /> {submitCount > 0 && Boolean(errors.email) ? errors.email : t("email_of_this_user")}
r.id) ?? []} onValueChange={(value: string[]) => setFieldValue("roles", value.map(id => roles.find(r => r.id === id) as Role))} // renderValue={(value: string) => { // const userRole = roles // .find((role) => role.id === value); // if (!userRole) return null; // return
// //
; // }} > {roles.map(userRole => )}
{isNewUser ? t("create_user") : t("update")}
); }