Mutually exclusive options (#2755)
Co-authored-by: Omar López <zomars@me.com> Co-authored-by: Peer Richelsen <peeroke@gmail.com> Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>pull/2768/head
parent
f2a6d00348
commit
9e662aa202
|
@ -8,11 +8,18 @@ import { RecurringEvent } from "@calcom/types/Calendar";
|
||||||
|
|
||||||
import Select from "@components/ui/form/Select";
|
import Select from "@components/ui/form/Select";
|
||||||
|
|
||||||
type RecurringEventControllerProps = { recurringEvent: RecurringEvent; formMethods: UseFormReturn<any, any> };
|
type RecurringEventControllerProps = {
|
||||||
|
recurringEvent: RecurringEvent;
|
||||||
|
formMethods: UseFormReturn<any, any>;
|
||||||
|
paymentEnabled: boolean;
|
||||||
|
onRecurringEventDefined: Function;
|
||||||
|
};
|
||||||
|
|
||||||
export default function RecurringEventController({
|
export default function RecurringEventController({
|
||||||
recurringEvent,
|
recurringEvent,
|
||||||
formMethods,
|
formMethods,
|
||||||
|
paymentEnabled,
|
||||||
|
onRecurringEventDefined,
|
||||||
}: RecurringEventControllerProps) {
|
}: RecurringEventControllerProps) {
|
||||||
const { t } = useLocale();
|
const { t } = useLocale();
|
||||||
|
|
||||||
|
@ -33,100 +40,105 @@ export default function RecurringEventController({
|
||||||
}));
|
}));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="block items-start sm:flex">
|
<>
|
||||||
<div className="min-w-48 mb-4 sm:mb-0">
|
{!paymentEnabled && (
|
||||||
<label htmlFor="recurringEvent" className="flex text-sm font-medium text-neutral-700">
|
<div className="block items-start sm:flex">
|
||||||
{t("recurring_event")}
|
<div className="min-w-48 mb-4 sm:mb-0">
|
||||||
</label>
|
<label htmlFor="recurringEvent" className="flex text-sm font-medium text-neutral-700">
|
||||||
</div>
|
{t("recurring_event")}
|
||||||
<div className="w-full">
|
</label>
|
||||||
<div className="relative flex items-start">
|
|
||||||
<div className="flex h-5 items-center">
|
|
||||||
<input
|
|
||||||
onChange={(event) => {
|
|
||||||
setRecurringEventDefined(event?.target.checked);
|
|
||||||
if (!event?.target.checked) {
|
|
||||||
formMethods.setValue("recurringEvent", {});
|
|
||||||
} else {
|
|
||||||
formMethods.setValue(
|
|
||||||
"recurringEvent",
|
|
||||||
recurringEventDefined
|
|
||||||
? recurringEvent
|
|
||||||
: {
|
|
||||||
interval: 1,
|
|
||||||
count: 12,
|
|
||||||
freq: RRuleFrequency.WEEKLY,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
recurringEvent = formMethods.getValues("recurringEvent");
|
|
||||||
}}
|
|
||||||
type="checkbox"
|
|
||||||
className="text-primary-600 h-4 w-4 rounded border-gray-300"
|
|
||||||
defaultChecked={recurringEventDefined}
|
|
||||||
data-testid="recurring-event-check"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="text-sm ltr:ml-3 rtl:mr-3">
|
<div className="w-full">
|
||||||
<p className="text-neutral-900">{t("recurring_event_description")}</p>
|
<div className="relative flex items-start">
|
||||||
|
<div className="flex h-5 items-center">
|
||||||
|
<input
|
||||||
|
onChange={(event) => {
|
||||||
|
setRecurringEventDefined(event?.target.checked);
|
||||||
|
onRecurringEventDefined(event?.target.checked);
|
||||||
|
if (!event?.target.checked) {
|
||||||
|
formMethods.setValue("recurringEvent", {});
|
||||||
|
} else {
|
||||||
|
formMethods.setValue(
|
||||||
|
"recurringEvent",
|
||||||
|
recurringEventDefined
|
||||||
|
? recurringEvent
|
||||||
|
: {
|
||||||
|
interval: 1,
|
||||||
|
count: 12,
|
||||||
|
freq: RRuleFrequency.WEEKLY,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
recurringEvent = formMethods.getValues("recurringEvent");
|
||||||
|
}}
|
||||||
|
type="checkbox"
|
||||||
|
className="text-primary-600 h-4 w-4 rounded border-gray-300"
|
||||||
|
defaultChecked={recurringEventDefined}
|
||||||
|
data-testid="recurring-event-check"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="text-sm ltr:ml-3 rtl:mr-3">
|
||||||
|
<p className="text-neutral-900">{t("recurring_event_description")}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Collapsible
|
||||||
|
open={recurringEventDefined}
|
||||||
|
data-testid="recurring-event-collapsible"
|
||||||
|
onOpenChange={() => setRecurringEventDefined(!recurringEventDefined)}>
|
||||||
|
<CollapsibleContent className="mt-4 text-sm">
|
||||||
|
<div className="flex items-center">
|
||||||
|
<p className="mr-2 text-neutral-900">{t("repeats_every")}</p>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
min="1"
|
||||||
|
max="20"
|
||||||
|
className="block w-16 rounded-sm border-gray-300 shadow-sm [appearance:textfield] ltr:mr-2 rtl:ml-2 sm:text-sm"
|
||||||
|
defaultValue={recurringEvent?.interval || 1}
|
||||||
|
onChange={(event) => {
|
||||||
|
setRecurringEventInterval(parseInt(event?.target.value));
|
||||||
|
recurringEvent.interval = parseInt(event?.target.value);
|
||||||
|
formMethods.setValue("recurringEvent", recurringEvent);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Select
|
||||||
|
options={recurringEventFreqOptions}
|
||||||
|
value={recurringEventFreqOptions[recurringEventFrequency]}
|
||||||
|
isSearchable={false}
|
||||||
|
className="w-18 block min-w-0 rounded-sm sm:text-sm"
|
||||||
|
onChange={(e) => {
|
||||||
|
if (e?.value) {
|
||||||
|
setRecurringEventFrequency(parseInt(e?.value));
|
||||||
|
recurringEvent.freq = parseInt(e?.value);
|
||||||
|
formMethods.setValue("recurringEvent", recurringEvent);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="mt-4 flex items-center">
|
||||||
|
<p className="mr-2 text-neutral-900">{t("max")}</p>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
min="1"
|
||||||
|
max="20"
|
||||||
|
className="block w-16 rounded-sm border-gray-300 shadow-sm [appearance:textfield] ltr:mr-2 rtl:ml-2 sm:text-sm"
|
||||||
|
defaultValue={recurringEvent?.count || 12}
|
||||||
|
onChange={(event) => {
|
||||||
|
setRecurringEventCount(parseInt(event?.target.value));
|
||||||
|
recurringEvent.count = parseInt(event?.target.value);
|
||||||
|
formMethods.setValue("recurringEvent", recurringEvent);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<p className="mr-2 text-neutral-900">
|
||||||
|
{t(`${RRuleFrequency[recurringEventFrequency].toString().toLowerCase()}`, {
|
||||||
|
count: recurringEventCount,
|
||||||
|
})}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</CollapsibleContent>
|
||||||
|
</Collapsible>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Collapsible
|
)}
|
||||||
open={recurringEventDefined}
|
</>
|
||||||
data-testid="recurring-event-collapsible"
|
|
||||||
onOpenChange={() => setRecurringEventDefined(!recurringEventDefined)}>
|
|
||||||
<CollapsibleContent className="mt-4 text-sm">
|
|
||||||
<div className="flex items-center">
|
|
||||||
<p className="mr-2 text-neutral-900">{t("repeats_every")}</p>
|
|
||||||
<input
|
|
||||||
type="number"
|
|
||||||
min="1"
|
|
||||||
max="20"
|
|
||||||
className="block w-16 rounded-sm border-gray-300 shadow-sm [appearance:textfield] ltr:mr-2 rtl:ml-2 sm:text-sm"
|
|
||||||
defaultValue={recurringEvent?.interval || 1}
|
|
||||||
onChange={(event) => {
|
|
||||||
setRecurringEventInterval(parseInt(event?.target.value));
|
|
||||||
recurringEvent.interval = parseInt(event?.target.value);
|
|
||||||
formMethods.setValue("recurringEvent", recurringEvent);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Select
|
|
||||||
options={recurringEventFreqOptions}
|
|
||||||
value={recurringEventFreqOptions[recurringEventFrequency]}
|
|
||||||
isSearchable={false}
|
|
||||||
className="w-18 block min-w-0 rounded-sm sm:text-sm"
|
|
||||||
onChange={(e) => {
|
|
||||||
if (e?.value) {
|
|
||||||
setRecurringEventFrequency(parseInt(e?.value));
|
|
||||||
recurringEvent.freq = parseInt(e?.value);
|
|
||||||
formMethods.setValue("recurringEvent", recurringEvent);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="mt-4 flex items-center">
|
|
||||||
<p className="mr-2 text-neutral-900">{t("max")}</p>
|
|
||||||
<input
|
|
||||||
type="number"
|
|
||||||
min="1"
|
|
||||||
max="20"
|
|
||||||
className="block w-16 rounded-sm border-gray-300 shadow-sm [appearance:textfield] ltr:mr-2 rtl:ml-2 sm:text-sm"
|
|
||||||
defaultValue={recurringEvent?.count || 12}
|
|
||||||
onChange={(event) => {
|
|
||||||
setRecurringEventCount(parseInt(event?.target.value));
|
|
||||||
recurringEvent.count = parseInt(event?.target.value);
|
|
||||||
formMethods.setValue("recurringEvent", recurringEvent);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<p className="mr-2 text-neutral-900">
|
|
||||||
{t(`${RRuleFrequency[recurringEventFrequency].toString().toLowerCase()}`, {
|
|
||||||
count: recurringEventCount,
|
|
||||||
})}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</CollapsibleContent>
|
|
||||||
</Collapsible>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -282,8 +282,9 @@ const EventTypePage = (props: inferSSRProps<typeof getServerSideProps>) => {
|
||||||
|
|
||||||
const [advancedSettingsVisible, setAdvancedSettingsVisible] = useState(false);
|
const [advancedSettingsVisible, setAdvancedSettingsVisible] = useState(false);
|
||||||
|
|
||||||
const [requirePayment, setRequirePayment] = useState(
|
const [requirePayment, setRequirePayment] = useState(eventType.price > 0);
|
||||||
eventType.price > 0 && eventType.recurringEvent?.count !== undefined
|
const [recurringEventDefined, setRecurringEventDefined] = useState(
|
||||||
|
eventType.recurringEvent?.count !== undefined
|
||||||
);
|
);
|
||||||
|
|
||||||
const [hashedLinkVisible, setHashedLinkVisible] = useState(!!eventType.hashedLink);
|
const [hashedLinkVisible, setHashedLinkVisible] = useState(!!eventType.hashedLink);
|
||||||
|
@ -1412,6 +1413,8 @@ const EventTypePage = (props: inferSSRProps<typeof getServerSideProps>) => {
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<RecurringEventController
|
<RecurringEventController
|
||||||
|
paymentEnabled={hasPaymentIntegration && requirePayment}
|
||||||
|
onRecurringEventDefined={setRecurringEventDefined}
|
||||||
recurringEvent={eventType.recurringEvent}
|
recurringEvent={eventType.recurringEvent}
|
||||||
formMethods={formMethods}
|
formMethods={formMethods}
|
||||||
/>
|
/>
|
||||||
|
@ -1729,7 +1732,7 @@ const EventTypePage = (props: inferSSRProps<typeof getServerSideProps>) => {
|
||||||
<SuccessRedirectEdit<typeof formMethods>
|
<SuccessRedirectEdit<typeof formMethods>
|
||||||
formMethods={formMethods}
|
formMethods={formMethods}
|
||||||
eventType={eventType}></SuccessRedirectEdit>
|
eventType={eventType}></SuccessRedirectEdit>
|
||||||
{hasPaymentIntegration && eventType.recurringEvent?.count !== undefined && (
|
{hasPaymentIntegration && !recurringEventDefined && (
|
||||||
<>
|
<>
|
||||||
<hr className="border-neutral-200" />
|
<hr className="border-neutral-200" />
|
||||||
<div className="block sm:flex">
|
<div className="block sm:flex">
|
||||||
|
|
Loading…
Reference in New Issue