|
|
|
@@ -1,8 +1,8 @@
|
|
|
|
|
import { useAutoAnimate } from "@formkit/auto-animate/react";
|
|
|
|
|
import * as RadioGroup from "@radix-ui/react-radio-group";
|
|
|
|
|
import { EventTypeSetupProps, FormValues } from "pages/event-types/[type]";
|
|
|
|
|
import { useMemo, useRef, useState } from "react";
|
|
|
|
|
import { Controller, useFormContext, useWatch } from "react-hook-form";
|
|
|
|
|
import React, { useEffect, useState } from "react";
|
|
|
|
|
import { Controller, useFormContext, UseFormRegisterReturn, useWatch } from "react-hook-form";
|
|
|
|
|
|
|
|
|
|
import { classNames } from "@calcom/lib";
|
|
|
|
|
import convertToNewDurationType, { DurationType } from "@calcom/lib/convertToNewDurationType";
|
|
|
|
@@ -12,13 +12,95 @@ import { PeriodType } from "@calcom/prisma/client";
|
|
|
|
|
import type { BookingLimit } from "@calcom/types/Calendar";
|
|
|
|
|
import { Button, DateRangePicker, Icon, Input, InputField, Label, Select, SettingsToggle } from "@calcom/ui";
|
|
|
|
|
|
|
|
|
|
const MinimumBookingNoticeInput = React.forwardRef<
|
|
|
|
|
HTMLInputElement,
|
|
|
|
|
Omit<UseFormRegisterReturn<"minimumBookingNotice">, "ref">
|
|
|
|
|
>(function MinimumBookingNoticeInput({ ...passThroughProps }, ref) {
|
|
|
|
|
const { t } = useLocale();
|
|
|
|
|
const { setValue, getValues } = useFormContext<FormValues>();
|
|
|
|
|
const durationTypeOptions: {
|
|
|
|
|
value: DurationType;
|
|
|
|
|
label: string;
|
|
|
|
|
}[] = [
|
|
|
|
|
{
|
|
|
|
|
label: t("minutes"),
|
|
|
|
|
value: "minutes",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
label: t("hours"),
|
|
|
|
|
value: "hours",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
label: t("days"),
|
|
|
|
|
value: "days",
|
|
|
|
|
},
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
const [minimumBookingNoticeDisplayValues, setMinimumBookingNoticeDisplayValues] = useState<{
|
|
|
|
|
type: DurationType;
|
|
|
|
|
value: number;
|
|
|
|
|
}>({
|
|
|
|
|
type: findDurationType(getValues(passThroughProps.name)),
|
|
|
|
|
value: convertToNewDurationType(
|
|
|
|
|
"minutes",
|
|
|
|
|
findDurationType(getValues(passThroughProps.name)),
|
|
|
|
|
getValues(passThroughProps.name)
|
|
|
|
|
),
|
|
|
|
|
});
|
|
|
|
|
// keep hidden field in sync with minimumBookingNoticeDisplayValues
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
setValue(
|
|
|
|
|
passThroughProps.name,
|
|
|
|
|
convertToNewDurationType(
|
|
|
|
|
minimumBookingNoticeDisplayValues.type,
|
|
|
|
|
"minutes",
|
|
|
|
|
minimumBookingNoticeDisplayValues.value
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
}, [minimumBookingNoticeDisplayValues, setValue, passThroughProps.name]);
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div className="flex items-end justify-end">
|
|
|
|
|
<div className="w-1/2 md:w-full">
|
|
|
|
|
<InputField
|
|
|
|
|
required
|
|
|
|
|
defaultValue={minimumBookingNoticeDisplayValues.value}
|
|
|
|
|
onChange={(e) =>
|
|
|
|
|
setMinimumBookingNoticeDisplayValues({
|
|
|
|
|
...minimumBookingNoticeDisplayValues,
|
|
|
|
|
value: parseInt(e.target.value || "0", 10),
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
label={t("minimum_booking_notice")}
|
|
|
|
|
type="number"
|
|
|
|
|
placeholder="0"
|
|
|
|
|
className="mb-0 h-[38px] rounded-[4px] ltr:mr-2 rtl:ml-2"
|
|
|
|
|
/>
|
|
|
|
|
<input type="hidden" ref={ref} {...passThroughProps} />
|
|
|
|
|
</div>
|
|
|
|
|
<Select
|
|
|
|
|
isSearchable={false}
|
|
|
|
|
className="mb-0 ml-2 h-[38px] w-full capitalize md:min-w-[150px] md:max-w-[200px]"
|
|
|
|
|
defaultValue={durationTypeOptions.find(
|
|
|
|
|
(option) => option.value === minimumBookingNoticeDisplayValues.type
|
|
|
|
|
)}
|
|
|
|
|
onChange={(input) => {
|
|
|
|
|
if (input) {
|
|
|
|
|
setMinimumBookingNoticeDisplayValues({
|
|
|
|
|
...minimumBookingNoticeDisplayValues,
|
|
|
|
|
type: input.value,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}}
|
|
|
|
|
options={durationTypeOptions}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
export const EventLimitsTab = ({ eventType }: Pick<EventTypeSetupProps, "eventType">) => {
|
|
|
|
|
const { t } = useLocale();
|
|
|
|
|
const formMethods = useFormContext<FormValues>();
|
|
|
|
|
const minimumBookingNoticeType = useRef<DurationType>(findDurationType(eventType.minimumBookingNotice));
|
|
|
|
|
const prevBookingNoticeType = useRef<DurationType>(minimumBookingNoticeType.current);
|
|
|
|
|
|
|
|
|
|
const minimumBookingNoticeInDurationTypeFormValue = formMethods.watch("minimumBookingNoticeInDurationType");
|
|
|
|
|
|
|
|
|
|
const PERIOD_TYPES = [
|
|
|
|
|
{
|
|
|
|
@@ -49,59 +131,8 @@ export const EventLimitsTab = ({ eventType }: Pick<EventTypeSetupProps, "eventTy
|
|
|
|
|
defaultValue: periodType?.type,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const onMinimumNoticeDurationTypeChange = useMemo(
|
|
|
|
|
() => (durationType?: DurationType) => {
|
|
|
|
|
if (typeof durationType === "undefined") return;
|
|
|
|
|
|
|
|
|
|
// Store current selected type in ref to use in previous run for comparrison.
|
|
|
|
|
minimumBookingNoticeType.current = durationType;
|
|
|
|
|
|
|
|
|
|
// Uses the previous selected type and converts the value to the new type.
|
|
|
|
|
// Eg day was selected before, user selects hours now, so we multiple * 24 and round up.
|
|
|
|
|
const minimumBookingNoticeInDurationType = convertToNewDurationType(
|
|
|
|
|
prevBookingNoticeType.current,
|
|
|
|
|
durationType,
|
|
|
|
|
Number(minimumBookingNoticeInDurationTypeFormValue)
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// Uses the round up value in the new type to calculate the value in minutes.
|
|
|
|
|
// We should NOT use minimumBookingNoticeInDurationTypeFormValue here, since that
|
|
|
|
|
// would break in the case that the user enters 90 minutes, changes the type to
|
|
|
|
|
// hours (that will show 2 hours), but the value in minutes would then remain 90,
|
|
|
|
|
// which is something else than the user things they save.
|
|
|
|
|
const minimumBookingNoticeInMinutes = convertToNewDurationType(
|
|
|
|
|
durationType,
|
|
|
|
|
"minutes",
|
|
|
|
|
Number(minimumBookingNoticeInDurationType)
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// Updates both form values as well as stores the current type as previous type in ref.
|
|
|
|
|
formMethods.setValue("minimumBookingNotice", minimumBookingNoticeInMinutes);
|
|
|
|
|
formMethods.setValue("minimumBookingNoticeInDurationType", minimumBookingNoticeInDurationType);
|
|
|
|
|
prevBookingNoticeType.current = durationType;
|
|
|
|
|
},
|
|
|
|
|
[formMethods, minimumBookingNoticeInDurationTypeFormValue]
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* When the user inputs a new value for minimumBookingNoticeInDurationType,
|
|
|
|
|
* we calculate the value in minutes, and update this hidden field as well.
|
|
|
|
|
*/
|
|
|
|
|
const onMinimumNoticeChange = useMemo(
|
|
|
|
|
() => (duration: string) => {
|
|
|
|
|
const minimumBookingNoticeInMinutes = convertToNewDurationType(
|
|
|
|
|
minimumBookingNoticeType.current,
|
|
|
|
|
"minutes",
|
|
|
|
|
Number(duration)
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
formMethods.setValue("minimumBookingNotice", minimumBookingNoticeInMinutes);
|
|
|
|
|
},
|
|
|
|
|
[formMethods]
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div>
|
|
|
|
|
<div className="space-y-8">
|
|
|
|
|
<div className="flex flex-col space-y-4 lg:flex-row lg:space-y-0 lg:space-x-4">
|
|
|
|
|
<div className="w-full">
|
|
|
|
|
<Label htmlFor="beforeBufferTime">{t("before_event")} </Label>
|
|
|
|
@@ -168,56 +199,10 @@ export const EventLimitsTab = ({ eventType }: Pick<EventTypeSetupProps, "eventTy
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="flex flex-col space-y-4 pt-4 lg:flex-row lg:space-y-0 lg:space-x-4">
|
|
|
|
|
<div className="flex w-full items-end justify-end">
|
|
|
|
|
<Controller
|
|
|
|
|
name="minimumBookingNotice"
|
|
|
|
|
control={formMethods.control}
|
|
|
|
|
render={() => {
|
|
|
|
|
const durationTypeOptions = [
|
|
|
|
|
{
|
|
|
|
|
label: t("minutes"),
|
|
|
|
|
value: "minutes",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
label: t("hours"),
|
|
|
|
|
value: "hours",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
label: t("days"),
|
|
|
|
|
value: "days",
|
|
|
|
|
},
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<>
|
|
|
|
|
<div className="w-1/2 md:w-full">
|
|
|
|
|
<InputField
|
|
|
|
|
required
|
|
|
|
|
label={t("minimum_booking_notice")}
|
|
|
|
|
type="number"
|
|
|
|
|
placeholder="120"
|
|
|
|
|
className="mb-0 h-[38px] rounded-[4px] ltr:mr-2 rtl:ml-2"
|
|
|
|
|
{...formMethods.register("minimumBookingNoticeInDurationType", {
|
|
|
|
|
onChange: (event: React.ChangeEvent<HTMLInputElement>) =>
|
|
|
|
|
onMinimumNoticeChange(event.target.value),
|
|
|
|
|
})}
|
|
|
|
|
/>
|
|
|
|
|
<input type="hidden" {...formMethods.register("minimumBookingNotice")} />
|
|
|
|
|
</div>
|
|
|
|
|
<Select
|
|
|
|
|
isSearchable={false}
|
|
|
|
|
className="mb-0 ml-2 h-[38px] w-full capitalize md:min-w-[150px] md:max-w-[200px]"
|
|
|
|
|
defaultValue={durationTypeOptions.find(
|
|
|
|
|
(option) => option.value === minimumBookingNoticeType.current
|
|
|
|
|
)}
|
|
|
|
|
onChange={(input) => onMinimumNoticeDurationTypeChange(input?.value as DurationType)}
|
|
|
|
|
options={durationTypeOptions}
|
|
|
|
|
/>
|
|
|
|
|
</>
|
|
|
|
|
);
|
|
|
|
|
}}
|
|
|
|
|
/>
|
|
|
|
|
<div className="flex flex-col lg:flex-row lg:space-y-0 lg:space-x-4">
|
|
|
|
|
<div className="w-full">
|
|
|
|
|
<Label htmlFor="minimumBookingNotice">{t("minimum_booking_notice")} </Label>
|
|
|
|
|
<MinimumBookingNoticeInput {...formMethods.register("minimumBookingNotice")} />
|
|
|
|
|
</div>
|
|
|
|
|
<div className="w-full">
|
|
|
|
|
<Label htmlFor="slotInterval">{t("slot_interval")} </Label>
|
|
|
|
@@ -252,9 +237,7 @@ export const EventLimitsTab = ({ eventType }: Pick<EventTypeSetupProps, "eventTy
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<hr className="my-8" />
|
|
|
|
|
|
|
|
|
|
<hr />
|
|
|
|
|
<Controller
|
|
|
|
|
name="bookingLimits"
|
|
|
|
|
control={formMethods.control}
|
|
|
|
@@ -276,8 +259,7 @@ export const EventLimitsTab = ({ eventType }: Pick<EventTypeSetupProps, "eventTy
|
|
|
|
|
</SettingsToggle>
|
|
|
|
|
)}
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
<hr className="my-8" />
|
|
|
|
|
<hr />
|
|
|
|
|
<Controller
|
|
|
|
|
name="periodType"
|
|
|
|
|
control={formMethods.control}
|
|
|
|
|