diff --git a/client/src/Hooks/useAddTeamMemberForm.ts b/client/src/Hooks/useAddTeamMemberForm.ts new file mode 100644 index 000000000..64cb0d353 --- /dev/null +++ b/client/src/Hooks/useAddTeamMemberForm.ts @@ -0,0 +1,20 @@ +import { + addTeamMemberSchema, + type AddTeamMemberFormData, +} from "@/Validation/addTeamMember"; + +export const useAddTeamMemberForm = () => { + const defaults: AddTeamMemberFormData = { + firstName: "", + lastName: "", + email: "", + password: "", + confirm: "", + role: ["user"], + }; + + return { + schema: addTeamMemberSchema, + defaults, + }; +}; diff --git a/client/src/Pages/Account/TabTeam.tsx b/client/src/Pages/Account/TabTeam.tsx index 731f4eabf..4eb2f8a97 100644 --- a/client/src/Pages/Account/TabTeam.tsx +++ b/client/src/Pages/Account/TabTeam.tsx @@ -14,7 +14,7 @@ export const TabTeam = () => { const [inviteDialogOpen, setInviteDialogOpen] = useState(false); const [addMemberDialogOpen, setAddMemberDialogOpen] = useState(false); - const { data: users } = useGet("/auth/users"); + const { data: users, refetch } = useGet("/auth/users"); const filteredUsers = useMemo(() => { if (!users) return []; @@ -29,6 +29,10 @@ export const TabTeam = () => { const handleOpenAddMemberDialog = () => setAddMemberDialogOpen(true); const handleCloseAddMemberDialog = () => setAddMemberDialogOpen(false); + const handleRefetch = () => { + refetch(); + }; + return ( { ); diff --git a/client/src/Pages/Account/components/AddTeamMemberDialog.tsx b/client/src/Pages/Account/components/AddTeamMemberDialog.tsx index 1ca086684..aed713b1f 100644 --- a/client/src/Pages/Account/components/AddTeamMemberDialog.tsx +++ b/client/src/Pages/Account/components/AddTeamMemberDialog.tsx @@ -2,17 +2,39 @@ import { Stack } from "@mui/material"; import MenuItem from "@mui/material/MenuItem"; import { useTheme } from "@mui/material"; import { useTranslation } from "react-i18next"; +import { useForm, Controller } from "react-hook-form"; +import { zodResolver } from "@hookform/resolvers/zod/dist/zod.js"; import { Dialog, TextField, Select } from "@/Components/v2/inputs"; -import type { UserRole } from "@/Types/User"; +import { useAddTeamMemberForm } from "@/Hooks/useAddTeamMemberForm"; +import type { AddTeamMemberFormData } from "@/Validation/addTeamMember"; +import type { UserRole, User } from "@/Types/User"; +import { usePost } from "@/Hooks/UseApi"; interface AddTeamMemberDialogProps { open: boolean; onClose: () => void; + onSuccess?: () => void; } -export const AddTeamMemberDialog = ({ open, onClose }: AddTeamMemberDialogProps) => { +interface RegisterPayload { + user: Omit; +} + +export const AddTeamMemberDialog = ({ + open, + onClose, + onSuccess, +}: AddTeamMemberDialogProps) => { const theme = useTheme(); const { t } = useTranslation(); + const { schema, defaults } = useAddTeamMemberForm(); + const { post, loading } = usePost(); + + const { control, handleSubmit, reset, setError } = useForm({ + resolver: zodResolver(schema), + defaultValues: defaults, + values: defaults, + }); const roleOptions: { value: UserRole; label: string }[] = [ { value: "admin", label: t("common.auth.roles.admin") }, @@ -20,17 +42,38 @@ export const AddTeamMemberDialog = ({ open, onClose }: AddTeamMemberDialogProps) ]; const handleClose = () => { + reset(); onClose(); }; + const onSubmit = async (data: AddTeamMemberFormData) => { + if (loading) return; + + const { confirm, ...userData } = data; + const payload: RegisterPayload = { user: userData }; + + const result = await post("/auth/register", payload); + + if (result?.success) { + reset(); + onClose(); + onSuccess?.(); + } else if (result?.msg) { + if (result.msg.toLowerCase().includes("email")) { + setError("email", { message: result.msg }); + } + } + }; + return ( {}} + onConfirm={handleSubmit(onSubmit)} confirmText={t("common.buttons.addMember")} + loading={loading} maxWidth="sm" fullWidth > @@ -38,48 +81,101 @@ export const AddTeamMemberDialog = ({ open, onClose }: AddTeamMemberDialogProps) gap={theme.spacing(4)} mt={theme.spacing(4)} > - ( + + )} /> - ( + + )} /> - ( + + )} /> - field.onChange([e.target.value])} + fieldLabel={t("pages.account.team.invite.role.label")} + placeholder={t("pages.account.team.invite.role.placeholder")} + fullWidth > - {option.label} - - ))} - - ( + + {option.label} + + ))} + + )} /> - ( + + )} + /> + ( + + )} /> diff --git a/client/src/Validation/addTeamMember.ts b/client/src/Validation/addTeamMember.ts new file mode 100644 index 000000000..bada2beb4 --- /dev/null +++ b/client/src/Validation/addTeamMember.ts @@ -0,0 +1,9 @@ +import { z } from "zod"; +import { registerSchema } from "@/Validation/register"; +import { UserRoles } from "@/Types/User"; + +export const addTeamMemberSchema = registerSchema.extend({ + role: z.array(z.enum(UserRoles)).min(1, "Please select a role"), +}); + +export type AddTeamMemberFormData = z.infer;