Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 5 additions & 8 deletions apps/web/components/booking/BookingListItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,12 @@ import dayjs from "dayjs";
import { useRouter } from "next/router";
import { useState } from "react";
import { useMutation } from "react-query";
import { Frequency as RRuleFrequency } from "rrule";

import { parseRecurringEvent } from "@calcom/lib";
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Runtime and type safety for an unknown object

import classNames from "@calcom/lib/classNames";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import showToast from "@calcom/lib/notification";
import { Frequency } from "@calcom/prisma/zod-utils";
import Button from "@calcom/ui/Button";
import { Dialog, DialogClose, DialogContent, DialogFooter, DialogHeader } from "@calcom/ui/Dialog";
import { Tooltip } from "@calcom/ui/Tooltip";
Expand Down Expand Up @@ -196,7 +197,7 @@ function BookingListItem(booking: BookingItemProps) {
[recurringStrings] = parseRecurringDates(
{
startDate: booking.startTime,
recurringEvent: booking.eventType.recurringEvent,
recurringEvent: parseRecurringEvent(booking.eventType.recurringEvent),
recurringCount: booking.recurringCount,
},
i18n
Expand Down Expand Up @@ -307,14 +308,10 @@ function BookingListItem(booking: BookingItemProps) {
<RefreshIcon className="mr-1 -mt-1 inline-block h-4 w-4 text-gray-400" />
{`${t("every_for_freq", {
freq: t(
`${RRuleFrequency[booking.eventType.recurringEvent.freq]
.toString()
.toLowerCase()}`
`${Frequency[booking.eventType.recurringEvent.freq].toString().toLowerCase()}`
),
})} ${booking.recurringCount} ${t(
`${RRuleFrequency[booking.eventType.recurringEvent.freq]
.toString()
.toLowerCase()}`,
`${Frequency[booking.eventType.recurringEvent.freq].toString().toLowerCase()}`,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This piece of very long and repeated string throughout the app will be refactored in an util as part of #2750

{ count: booking.recurringCount }
)}`}
</p>
Expand Down
30 changes: 11 additions & 19 deletions apps/web/components/booking/pages/AvailabilityPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import { TFunction } from "next-i18next";
import { useRouter } from "next/router";
import { useCallback, useEffect, useMemo, useState } from "react";
import { FormattedNumber, IntlProvider } from "react-intl";
import { Frequency as RRuleFrequency } from "rrule";

import { AppStoreLocationType, LocationObject, LocationType } from "@calcom/app-store/locations";
import {
Expand All @@ -35,6 +34,7 @@ import classNames from "@calcom/lib/classNames";
import { CAL_URL, WEBAPP_URL } from "@calcom/lib/constants";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { localStorage } from "@calcom/lib/webstorage";
import { Frequency } from "@calcom/prisma/zod-utils";

import { asStringOrNull } from "@lib/asStringOrNull";
import { timeZone } from "@lib/clock";
Expand Down Expand Up @@ -302,7 +302,7 @@ const AvailabilityPage = ({ profile, plan, eventType, workingHours, previousPage
<p className="mb-1 -ml-2 inline px-2 py-1">
{t("every_for_freq", {
freq: t(
`${RRuleFrequency[eventType.recurringEvent.freq].toString().toLowerCase()}`
`${Frequency[eventType.recurringEvent.freq].toString().toLowerCase()}`
),
})}
</p>
Expand All @@ -317,12 +317,9 @@ const AvailabilityPage = ({ profile, plan, eventType, workingHours, previousPage
}}
/>
<p className="inline text-gray-600 dark:text-white">
{t(
`${RRuleFrequency[eventType.recurringEvent.freq].toString().toLowerCase()}`,
{
count: recurringEventCount,
}
)}
{t(`${Frequency[eventType.recurringEvent.freq].toString().toLowerCase()}`, {
count: recurringEventCount,
})}
</p>
</div>
)}
Expand All @@ -344,7 +341,7 @@ const AvailabilityPage = ({ profile, plan, eventType, workingHours, previousPage
<p className="mb-1 -ml-2 inline px-2 py-1">
{t("every_for_freq", {
freq: t(
`${RRuleFrequency[eventType.recurringEvent.freq].toString().toLowerCase()}`
`${Frequency[eventType.recurringEvent.freq].toString().toLowerCase()}`
),
})}
</p>
Expand All @@ -359,12 +356,9 @@ const AvailabilityPage = ({ profile, plan, eventType, workingHours, previousPage
}}
/>
<p className="inline text-gray-600 dark:text-white">
{t(
`${RRuleFrequency[eventType.recurringEvent.freq].toString().toLowerCase()}`,
{
count: recurringEventCount,
}
)}
{t(`${Frequency[eventType.recurringEvent.freq].toString().toLowerCase()}`, {
count: recurringEventCount,
})}
</p>
</div>
)}
Expand Down Expand Up @@ -471,9 +465,7 @@ const AvailabilityPage = ({ profile, plan, eventType, workingHours, previousPage
<RefreshIcon className="mr-[10px] -mt-1 ml-[2px] inline-block h-4 w-4 text-gray-400" />
<p className="mb-1 -ml-2 inline px-2 py-1">
{t("every_for_freq", {
freq: t(
`${RRuleFrequency[eventType.recurringEvent.freq].toString().toLowerCase()}`
),
freq: t(`${Frequency[eventType.recurringEvent.freq].toString().toLowerCase()}`),
})}
</p>
<input
Expand All @@ -487,7 +479,7 @@ const AvailabilityPage = ({ profile, plan, eventType, workingHours, previousPage
}}
/>
<p className="inline text-gray-600 dark:text-white">
{t(`${RRuleFrequency[eventType.recurringEvent.freq].toString().toLowerCase()}`, {
{t(`${Frequency[eventType.recurringEvent.freq].toString().toLowerCase()}`, {
count: recurringEventCount,
})}
</p>
Expand Down
12 changes: 6 additions & 6 deletions apps/web/components/booking/pages/BookingPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import { Controller, useForm, useWatch } from "react-hook-form";
import { FormattedNumber, IntlProvider } from "react-intl";
import { ReactMultiEmail } from "react-multi-email";
import { useMutation } from "react-query";
import { Frequency as RRuleFrequency } from "rrule";
import { v4 as uuidv4 } from "uuid";
import { z } from "zod";

Expand All @@ -32,6 +31,7 @@ import {
import classNames from "@calcom/lib/classNames";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { HttpError } from "@calcom/lib/http-error";
import { Frequency } from "@calcom/prisma/zod-utils";
import { createPaymentLink } from "@calcom/stripe/client";
import { Button } from "@calcom/ui/Button";
import { Tooltip } from "@calcom/ui/Tooltip";
Expand Down Expand Up @@ -518,9 +518,9 @@ const BookingPage = ({
<RefreshIcon className="mr-[10px] -mt-1 ml-[2px] inline-block h-4 w-4 text-gray-400" />
<p className="mb-1 -ml-2 inline px-2 py-1">
{`${t("every_for_freq", {
freq: t(`${RRuleFrequency[eventType.recurringEvent.freq].toString().toLowerCase()}`),
freq: t(`${Frequency[eventType.recurringEvent.freq].toString().toLowerCase()}`),
})} ${recurringEventCount} ${t(
`${RRuleFrequency[eventType.recurringEvent.freq].toString().toLowerCase()}`,
`${Frequency[eventType.recurringEvent.freq].toString().toLowerCase()}`,
{ count: parseInt(recurringEventCount.toString()) }
)}`}
</p>
Expand All @@ -529,12 +529,12 @@ const BookingPage = ({
<div className="text-bookinghighlight mb-4 flex">
<CalendarIcon className="mr-[10px] ml-[2px] inline-block h-4 w-4" />
<div className="-mt-1">
{(rescheduleUid || !eventType.recurringEvent.freq) &&
{(rescheduleUid || !eventType.recurringEvent?.freq) &&
parseDate(dayjs(date).tz(timeZone()), i18n)}
{!rescheduleUid &&
eventType.recurringEvent.freq &&
eventType.recurringEvent?.freq &&
recurringStrings.slice(0, 5).map((aDate, key) => <p key={key}>{aDate}</p>)}
{!rescheduleUid && eventType.recurringEvent.freq && recurringStrings.length > 5 && (
{!rescheduleUid && eventType.recurringEvent?.freq && recurringStrings.length > 5 && (
<div className="flex">
<Tooltip
content={recurringStrings.slice(5).map((aDate, key) => (
Expand Down
6 changes: 3 additions & 3 deletions apps/web/components/eventtype/EventTypeDescription.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ import { Prisma, SchedulingType } from "@prisma/client";
import { useMemo } from "react";
import { FormattedNumber, IntlProvider } from "react-intl";

import { parseRecurringEvent } from "@calcom/lib";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { baseEventTypeSelect } from "@calcom/prisma/selects";
import { RecurringEvent } from "@calcom/types/Calendar";

import classNames from "@lib/classNames";

Expand All @@ -30,8 +30,8 @@ export type EventTypeDescriptionProps = {
export const EventTypeDescription = ({ eventType, className }: EventTypeDescriptionProps) => {
const { t } = useLocale();

const recurringEvent: RecurringEvent = useMemo(
() => (eventType.recurringEvent as RecurringEvent) || [],
const recurringEvent = useMemo(
() => parseRecurringEvent(eventType.recurringEvent),
[eventType.recurringEvent]
);

Expand Down
94 changes: 43 additions & 51 deletions apps/web/components/eventtype/RecurringEventController.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
import { Collapsible, CollapsibleContent } from "@radix-ui/react-collapsible";
import type { FormValues } from "pages/event-types/[type]";
import { useState } from "react";
import { UseFormReturn } from "react-hook-form";
import { Frequency as RRuleFrequency } from "rrule";

import { useLocale } from "@calcom/lib/hooks/useLocale";
import { Frequency } from "@calcom/prisma/zod-utils";
import { RecurringEvent } from "@calcom/types/Calendar";
import { Alert } from "@calcom/ui/Alert";

import Select from "@components/ui/form/Select";

type RecurringEventControllerProps = {
recurringEvent: RecurringEvent;
recurringEvent: RecurringEvent | null;
formMethods: UseFormReturn<FormValues>;
paymentEnabled: boolean;
onRecurringEventDefined: (value: boolean) => void;
Expand All @@ -24,20 +23,13 @@ export default function RecurringEventController({
onRecurringEventDefined,
}: RecurringEventControllerProps) {
const { t } = useLocale();

const [recurringEventDefined, setRecurringEventDefined] = useState(recurringEvent?.count !== undefined);

const [recurringEventInterval, setRecurringEventInterval] = useState(recurringEvent?.interval || 1);
const [recurringEventFrequency, setRecurringEventFrequency] = useState(
recurringEvent?.freq || RRuleFrequency.WEEKLY
);
const [recurringEventCount, setRecurringEventCount] = useState(recurringEvent?.count || 12);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Simplifying the logic to just rely on a state object

const [recurringEventState, setRecurringEventState] = useState<RecurringEvent | null>(recurringEvent);

/* Just yearly-0, monthly-1 and weekly-2 */
const recurringEventFreqOptions = Object.entries(RRuleFrequency)
const recurringEventFreqOptions = Object.entries(Frequency)
.filter(([key, value]) => isNaN(Number(key)) && Number(value) < 3)
.map(([key, value]) => ({
label: t(`${key.toString().toLowerCase()}`, { count: recurringEventInterval }),
label: t(`${key.toString().toLowerCase()}`, { count: recurringEventState?.interval }),
value: value.toString(),
}));

Expand All @@ -55,27 +47,23 @@ export default function RecurringEventController({
<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", {});
formMethods.setValue("recurringEvent", null);
setRecurringEventState(null);
} else {
formMethods.setValue(
"recurringEvent",
recurringEventDefined
? recurringEvent
: {
interval: 1,
count: 12,
freq: RRuleFrequency.WEEKLY,
}
);
const newVal = recurringEvent || {
interval: 1,
count: 12,
freq: Frequency.WEEKLY,
};
formMethods.setValue("recurringEvent", newVal);
setRecurringEventState(newVal);
}
recurringEvent = formMethods.getValues("recurringEvent");
}}
type="checkbox"
className="text-primary-600 h-4 w-4 rounded border-gray-300"
defaultChecked={recurringEventDefined}
defaultChecked={recurringEventState !== null}
data-testid="recurring-event-check"
id="recurringEvent"
/>
Expand All @@ -86,36 +74,37 @@ export default function RecurringEventController({
</label>
</div>
</div>
<Collapsible
open={recurringEventDefined}
data-testid="recurring-event-collapsible"
onOpenChange={() => setRecurringEventDefined(!recurringEventDefined)}>
<CollapsibleContent className="mt-4 text-sm">
{recurringEventState && (
<div data-testid="recurring-event-collapsible" 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}
defaultValue={recurringEventState.interval}
onChange={(event) => {
setRecurringEventInterval(parseInt(event?.target.value));
recurringEvent.interval = parseInt(event?.target.value);
formMethods.setValue("recurringEvent", recurringEvent);
const newVal = {
...recurringEventState,
interval: parseInt(event?.target.value),
};
formMethods.setValue("recurringEvent", newVal);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Controller can be used here to simplify, but not needed - this is already a great PR.

setRecurringEventState(newVal);
}}
/>
<Select
options={recurringEventFreqOptions}
value={recurringEventFreqOptions[recurringEventFrequency]}
value={recurringEventFreqOptions[recurringEventState.freq]}
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);
}
onChange={(event) => {
const newVal = {
...recurringEventState,
freq: parseInt(event?.value || `${Frequency.WEEKLY}`),
};
formMethods.setValue("recurringEvent", newVal);
setRecurringEventState(newVal);
}}
/>
</div>
Expand All @@ -126,21 +115,24 @@ export default function RecurringEventController({
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}
defaultValue={recurringEventState.count}
onChange={(event) => {
setRecurringEventCount(parseInt(event?.target.value));
recurringEvent.count = parseInt(event?.target.value);
formMethods.setValue("recurringEvent", recurringEvent);
const newVal = {
...recurringEventState,
count: parseInt(event?.target.value),
};
formMethods.setValue("recurringEvent", newVal);
setRecurringEventState(newVal);
}}
/>
<p className="mr-2 text-neutral-900">
{t(`${RRuleFrequency[recurringEventFrequency].toString().toLowerCase()}`, {
count: recurringEventCount,
{t(`${Frequency[recurringEventState.freq].toString().toLowerCase()}`, {
count: recurringEventState.count,
})}
</p>
</div>
</CollapsibleContent>
</Collapsible>
</div>
)}
</>
)}
</div>
Expand Down
Loading