2
0
Files
cal/calcom/packages/features/users/components/UserTable/EditSheet/EditUserForm.tsx

204 lines
5.9 KiB
TypeScript
Raw Normal View History

2024-08-09 00:39:27 +02:00
import { zodResolver } from "@hookform/resolvers/zod";
import { useSession } from "next-auth/react";
import type { Dispatch } from "react";
import { useMemo } from "react";
import { Controller, useForm } from "react-hook-form";
import { z } from "zod";
import { shallow } from "zustand/shallow";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { MembershipRole } from "@calcom/prisma/enums";
import { trpc, type RouterOutputs } from "@calcom/trpc/react";
import {
Form,
TextField,
ToggleGroup,
TextAreaField,
TimezoneSelect,
Label,
showToast,
Avatar,
ImageUploader,
SheetHeader,
SheetBody,
SheetFooter,
} from "@calcom/ui";
import type { Action } from "../UserListTable";
import { SheetFooterControls } from "./SheetFooterControls";
import { useEditMode } from "./store";
type MembershipOption = {
value: MembershipRole;
label: string;
};
const editSchema = z.object({
name: z.string(),
username: z.string(),
email: z.string().email(),
avatar: z.string(),
bio: z.string(),
role: z.enum([MembershipRole.MEMBER, MembershipRole.ADMIN, MembershipRole.OWNER]),
timeZone: z.string(),
// schedules: z.array(z.string()),
// teams: z.array(z.string()),
});
type EditSchema = z.infer<typeof editSchema>;
export function EditForm({
selectedUser,
avatarUrl,
domainUrl,
dispatch,
}: {
selectedUser: RouterOutputs["viewer"]["organizations"]["getUser"];
avatarUrl: string;
domainUrl: string;
dispatch: Dispatch<Action>;
}) {
const [setMutationLoading] = useEditMode((state) => [state.setMutationloading], shallow);
const { t } = useLocale();
const session = useSession();
const org = session?.data?.user?.org;
const utils = trpc.useUtils();
const form = useForm({
resolver: zodResolver(editSchema),
defaultValues: {
name: selectedUser?.name ?? "",
username: selectedUser?.username ?? "",
email: selectedUser?.email ?? "",
avatar: avatarUrl,
bio: selectedUser?.bio ?? "",
role: selectedUser?.role ?? "",
timeZone: selectedUser?.timeZone ?? "",
},
});
const isOwner = org?.role === MembershipRole.OWNER;
const membershipOptions = useMemo<MembershipOption[]>(() => {
const options: MembershipOption[] = [
{
value: MembershipRole.MEMBER,
label: t("member"),
},
{
value: MembershipRole.ADMIN,
label: t("admin"),
},
];
if (isOwner) {
options.push({
value: MembershipRole.OWNER,
label: t("owner"),
});
}
return options;
}, [t, isOwner]);
const mutation = trpc.viewer.organizations.updateUser.useMutation({
onSuccess: () => {
dispatch({ type: "CLOSE_MODAL" });
utils.viewer.organizations.listMembers.invalidate();
showToast(t("profile_updated_successfully"), "success");
},
onError: (error) => {
showToast(error.message, "error");
},
onSettled: () => {
/**
* /We need to do this as the submit button lives out side
* the form for some complicated reason so we can't relay on mutationState
*/
setMutationLoading(false);
},
});
const watchTimezone = form.watch("timeZone");
return (
<>
<Form
form={form}
id="edit-user-form"
className="flex h-full flex-col"
handleSubmit={(values) => {
setMutationLoading(true);
mutation.mutate({
userId: selectedUser?.id ?? "",
role: values.role,
username: values.username,
name: values.name,
email: values.email,
avatar: values.avatar,
bio: values.bio,
timeZone: values.timeZone,
});
}}>
<SheetHeader>
<div className="flex flex-col gap-2">
<Controller
control={form.control}
name="avatar"
render={({ field: { value } }) => (
<div className="flex items-center">
<Avatar alt={`${selectedUser?.name} avatar`} imageSrc={value} size="lg" />
<div className="ml-4">
<ImageUploader
target="avatar"
id="avatar-upload"
buttonMsg={t("change_avatar")}
handleAvatarChange={(newAvatar) => {
form.setValue("avatar", newAvatar, { shouldDirty: true });
}}
imageSrc={value || undefined}
/>
</div>
</div>
)}
/>
<div className="space-between flex flex-col leading-none">
<span className="text-emphasis text-lg font-semibold">
{selectedUser?.name ?? "Nameless User"}
</span>
<p className="subtle text-sm font-normal">
{domainUrl}/{selectedUser?.username}
</p>
</div>
</div>
</SheetHeader>
<SheetBody className="mt-6 flex h-full flex-col space-y-3">
<TextField label={t("username")} {...form.register("username")} />
<TextField label={t("name")} {...form.register("name")} />
<TextField label={t("email")} {...form.register("email")} />
<TextAreaField label={t("bio")} {...form.register("bio")} className="min-h-52" />
<div>
<Label>{t("role")}</Label>
<ToggleGroup
isFullWidth
defaultValue={selectedUser?.role ?? "MEMBER"}
value={form.watch("role")}
options={membershipOptions}
onValueChange={(value: EditSchema["role"]) => {
form.setValue("role", value);
}}
/>
</div>
<div>
<Label>{t("timezone")}</Label>
<TimezoneSelect value={watchTimezone ?? "America/Los_Angeles"} />
</div>
</SheetBody>
<SheetFooter>
<SheetFooterControls />
</SheetFooter>
</Form>
</>
);
}