import { useFieldArray, useForm } from "react-hook-form"; import dayjs from "@calcom/dayjs"; import { DateOverrideInputDialog, DateOverrideList } from "@calcom/features/schedules"; import Schedule from "@calcom/features/schedules/components/Schedule"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import { HttpError } from "@calcom/lib/http-error"; import type { RouterOutputs } from "@calcom/trpc/react"; import { trpc } from "@calcom/trpc/react"; import useMeQuery from "@calcom/trpc/react/hooks/useMeQuery"; import type { Schedule as ScheduleType, TimeRange, WorkingHours } from "@calcom/types/schedule"; import { Alert, Button, Form, Label, Sheet, SheetBody, SheetClose, SheetContent, SheetFooter, SheetHeader, SheetTitle, showToast, TimezoneSelect, } from "@calcom/ui"; import type { SliderUser } from "./AvailabilitySliderTable"; interface Props { open: boolean; onOpenChange: (open: boolean) => void; selectedUser?: SliderUser | null; } type AvailabilityFormValues = { name: string; schedule: ScheduleType; dateOverrides: { ranges: TimeRange[] }[]; timeZone: string; isDefault: boolean; }; const useSettings = () => { const { data } = useMeQuery(); return { userTimeFormat: data?.timeFormat ?? 12, }; }; const DateOverride = ({ workingHours, disabled }: { workingHours: WorkingHours[]; disabled?: boolean }) => { const { userTimeFormat } = useSettings(); const { append, replace, fields } = useFieldArray({ name: "dateOverrides", }); const excludedDates = fields.map((field) => dayjs(field.ranges[0].start).utc().format("YYYY-MM-DD")); const { t } = useLocale(); return (
ranges.forEach((range) => append({ ranges: [range] }))} Trigger={ } />
); }; export function AvailabilityEditSheet(props: Props) { // This sheet will not be rendered without a selected user const userId = props.selectedUser?.id as number; const { data, isPending } = trpc.viewer.availability.schedule.getScheduleByUserId.useQuery({ userId: userId, }); // TODO: reimplement Skeletons for this page in here if (isPending) return null; if (!data) return null; // We wait for the schedule to be loaded before rendering the form since `defaultValues` // cannot be redeclared after first render. And using `values` will trigger a form reset // when revalidating. return ; } type Data = RouterOutputs["viewer"]["availability"]["schedule"]["getScheduleByUserId"]; export function AvailabilityEditSheetForm(props: Props & { data: Data; isPending: boolean }) { // This sheet will not be rendered without a selected user const userId = props.selectedUser?.id as number; const { t } = useLocale(); const utils = trpc.useUtils(); const { data: hasEditPermission, isPending: loadingPermissions } = trpc.viewer.teams.hasEditPermissionForUser.useQuery({ memberId: userId, }); const { data, isPending } = props; const updateMutation = trpc.viewer.availability.schedule.update.useMutation({ onSuccess: async () => { await utils.viewer.availability.listTeam.invalidate(); showToast(t("success"), "success"); props.onOpenChange(false); }, onError: (err) => { if (err instanceof HttpError) { const message = `${err.statusCode}: ${err.message}`; showToast(message, "error"); } }, }); const form = useForm({ defaultValues: { ...data, timeZone: data.timeZone || Intl.DateTimeFormat().resolvedOptions().timeZone, schedule: data.availability || [], }, }); const watchTimezone = form.watch("timeZone"); return (
{ // Just blocking on a UI side -> Backend will also do the validation if (!hasEditPermission) return; updateMutation.mutate({ scheduleId: data.id, dateOverrides: dateOverrides.flatMap((override) => override.ranges), ...values, }); }}> {t("edit_users_availability", { username: props.selectedUser?.username ?? "Nameless user", })} {!data.hasDefaultSchedule && !isPending && hasEditPermission && (
)} {!hasEditPermission && !loadingPermissions && (
)}
{ if (event) form.setValue("timeZone", event.value, { shouldDirty: true }); }} />
{/* Remove padding from schedule without touching the component */}
{data.workingHours && ( )}
); }