feat: CalendarView atom v2 (#24896)
* refactor: make individual component for generic and event type specific calendar views * fixup * update examples app * fix: show busy times from calendars in week view * fix: use calendar busy times not working * chore: add changesets * fixup * chore: implement PR feedback * fix: merge conflicts * chore: implement PR feedback * fixup * chore: implement PR feedback
This commit is contained in:
1 parent
150e93dc6e
commit
957d19740e
7 files changed
+332
-174
No files matched your search
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@calcom/atoms": minor
|
||||
---
|
||||
|
||||
This PR introduces two variants for the calendar view atom, a generic calendar view and an event type specific view containing bookings data and busy times from connected calendars.
|
||||
@@ -2,27 +2,28 @@ import { useEffect, useMemo } from "react";
|
||||
|
||||
import dayjs from "@calcom/dayjs";
|
||||
import { useBookerStoreContext } from "@calcom/features/bookings/Booker/BookerStoreProvider";
|
||||
import { useAvailableTimeSlots } from "@calcom/features/bookings/Booker/components/hooks/useAvailableTimeSlots";
|
||||
import { useBookerTime } from "@calcom/features/bookings/Booker/components/hooks/useBookerTime";
|
||||
import type { BookerEvent } from "@calcom/features/bookings/types";
|
||||
import { Calendar } from "@calcom/features/calendars/weeklyview";
|
||||
import type { CalendarAvailableTimeslots } from "@calcom/features/calendars/weeklyview/types/state";
|
||||
import { localStorage } from "@calcom/lib/webstorage";
|
||||
import type { BookingStatus } from "@calcom/prisma/enums";
|
||||
|
||||
import { useBookings } from "../../platform/atoms/hooks/bookings/useBookings";
|
||||
import type { useScheduleForEventReturnType } from "../bookings/Booker/utils/event";
|
||||
import { useCalendarsBusyTimes } from "../../platform/atoms/hooks/useCalendarsBusyTimes";
|
||||
import { useConnectedCalendars } from "../../platform/atoms/hooks/useConnectedCalendars";
|
||||
import { getQueryParam } from "../bookings/Booker/utils/query-param";
|
||||
|
||||
export const LargeCalendar = ({
|
||||
extraDays,
|
||||
schedule,
|
||||
availableTimeslots,
|
||||
isLoading,
|
||||
event,
|
||||
}: {
|
||||
extraDays: number;
|
||||
schedule?: useScheduleForEventReturnType["data"];
|
||||
availableTimeslots?: CalendarAvailableTimeslots | undefined;
|
||||
isLoading: boolean;
|
||||
event: {
|
||||
event?: {
|
||||
data?: Pick<BookerEvent, "length" | "id"> | null;
|
||||
};
|
||||
}) => {
|
||||
@@ -34,20 +35,43 @@ export const LargeCalendar = ({
|
||||
|
||||
const eventDuration = selectedEventDuration || event?.data?.length || 30;
|
||||
|
||||
const availableSlots = useAvailableTimeSlots({ schedule, eventDuration });
|
||||
const availableSlots = availableTimeslots !== undefined ? availableTimeslots : undefined;
|
||||
|
||||
const startDate = selectedDate ? dayjs(selectedDate).toDate() : dayjs().toDate();
|
||||
const endDate = dayjs(startDate)
|
||||
.add(extraDays - 1, "day")
|
||||
.toDate();
|
||||
|
||||
const { data: upcomingBookings } = useBookings({
|
||||
const { data: bookings, isPending: _isFetchingBookings } = useBookings({
|
||||
take: 150,
|
||||
skip: 0,
|
||||
status: ["upcoming", "past", "recurring"],
|
||||
eventTypeId: event?.data?.id,
|
||||
afterStart: startDate.toISOString(),
|
||||
beforeEnd: endDate.toISOString(),
|
||||
status: ["upcoming", "past", "recurring"],
|
||||
});
|
||||
|
||||
const { data: connectedCalendars, isPending: isFetchingConnectedCalendars } = useConnectedCalendars({
|
||||
enabled: true,
|
||||
});
|
||||
|
||||
const calendarsToLoad = connectedCalendars?.connectedCalendars.flatMap((connectedCalendar) => {
|
||||
return (
|
||||
connectedCalendar.calendars
|
||||
?.filter((calendar) => calendar.isSelected === true)
|
||||
.map((cal) => ({
|
||||
credentialId: cal.credentialId,
|
||||
externalId: cal.externalId,
|
||||
})) ?? []
|
||||
);
|
||||
});
|
||||
|
||||
const { data: overlayBusyDates } = useCalendarsBusyTimes({
|
||||
loggedInUsersTz: timezone,
|
||||
dateFrom: startDate.toISOString(),
|
||||
dateTo: endDate.toISOString(),
|
||||
calendarsToLoad: calendarsToLoad ?? [],
|
||||
enabled: Boolean(!isFetchingConnectedCalendars && !!calendarsToLoad?.length),
|
||||
});
|
||||
|
||||
// HACK: force rerender when overlay events change
|
||||
@@ -55,23 +79,66 @@ export const LargeCalendar = ({
|
||||
|
||||
useEffect(() => {}, [displayOverlay]);
|
||||
|
||||
const overlayEventsForDate = useMemo(() => {
|
||||
if (!upcomingBookings) return [];
|
||||
const sortedBookingRanges = useMemo(
|
||||
() =>
|
||||
(bookings ?? [])
|
||||
.map((booking) => ({
|
||||
booking,
|
||||
startTime: dayjs(booking.start).valueOf(),
|
||||
endTime: dayjs(booking.end).valueOf(),
|
||||
}))
|
||||
.sort((a, b) => a.startTime - b.startTime),
|
||||
[bookings]
|
||||
);
|
||||
|
||||
return upcomingBookings?.map((booking) => {
|
||||
return {
|
||||
id: booking.id,
|
||||
title: booking.title ?? `Busy`,
|
||||
start: new Date(booking.start),
|
||||
end: new Date(booking.end),
|
||||
options: {
|
||||
status: booking.status.toUpperCase() as BookingStatus,
|
||||
"data-test-id": "troubleshooter-busy-event",
|
||||
className: "border-[1.5px]",
|
||||
},
|
||||
};
|
||||
const filteredBusyDates = useMemo(() => {
|
||||
const overlay = overlayBusyDates?.data ?? [];
|
||||
if (!sortedBookingRanges.length) return overlay;
|
||||
|
||||
return overlay.filter((busy) => {
|
||||
const busyStart = dayjs(busy.start).valueOf();
|
||||
const busyEnd = dayjs(busy.end).valueOf();
|
||||
|
||||
for (const { startTime, endTime } of sortedBookingRanges) {
|
||||
if (startTime >= busyEnd) break;
|
||||
if (endTime <= busyStart) continue;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
}, [upcomingBookings]);
|
||||
}, [overlayBusyDates?.data, sortedBookingRanges]);
|
||||
|
||||
const overlayEventsForDate = useMemo(() => {
|
||||
const allBookings = bookings ?? [];
|
||||
// since busy dates comes straight from the calendar, it contains slots have bookings and also slots that are marked as busy by user but are not bookings
|
||||
// hence we filter overlayBusyDates to exclude anything that overlaps with bookings
|
||||
const busyEvents = filteredBusyDates.map((busyData, index) => ({
|
||||
id: index,
|
||||
title: `Busy`,
|
||||
start: new Date(busyData.start),
|
||||
end: new Date(busyData.end),
|
||||
options: {
|
||||
status: "ACCEPTED" as BookingStatus,
|
||||
"data-test-id": "troubleshooter-busy-event",
|
||||
className: "border-[1.5px]",
|
||||
},
|
||||
}));
|
||||
|
||||
const bookingEvents = allBookings.map((booking) => ({
|
||||
id: booking.id,
|
||||
title: booking.title ?? `Busy`,
|
||||
start: new Date(booking.start),
|
||||
end: new Date(booking.end),
|
||||
options: {
|
||||
status: booking.status.toUpperCase() as BookingStatus,
|
||||
"data-test-id": "troubleshooter-busy-event",
|
||||
className: "border-[1.5px]",
|
||||
},
|
||||
}));
|
||||
|
||||
return [...bookingEvents, ...busyEvents];
|
||||
}, [bookings, filteredBusyDates]);
|
||||
|
||||
return (
|
||||
<div className="h-full [--calendar-dates-sticky-offset:66px]">
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
import { Header } from "@calcom/features/bookings/Booker/components/Header";
|
||||
import { BookerSection } from "@calcom/features/bookings/Booker/components/Section";
|
||||
import { LargeCalendar } from "@calcom/features/calendar-view/LargeCalendar";
|
||||
import { bookerLayoutOptions } from "@calcom/prisma/zod-utils";
|
||||
|
||||
import { AtomsWrapper } from "../src/components/atoms-wrapper";
|
||||
|
||||
export const CalendarViewComponent = () => {
|
||||
return (
|
||||
<AtomsWrapper>
|
||||
<BookerSection area="header" className="bg-default dark:bg-muted sticky top-0 z-10">
|
||||
<Header
|
||||
isCalendarView={true}
|
||||
isMyLink={true}
|
||||
eventSlug="calendar-view"
|
||||
enabledLayouts={bookerLayoutOptions}
|
||||
extraDays={7}
|
||||
isMobile={false}
|
||||
nextSlots={6}
|
||||
/>
|
||||
</BookerSection>
|
||||
<BookerSection
|
||||
key="large-calendar"
|
||||
area="main"
|
||||
visible={true}
|
||||
className="border-subtle sticky top-0 ml-px h-full md:border-l">
|
||||
<LargeCalendar extraDays={7} isLoading={false} />
|
||||
</BookerSection>
|
||||
</AtomsWrapper>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,159 @@
|
||||
import { useMemo } from "react";
|
||||
import { shallow } from "zustand/shallow";
|
||||
|
||||
import dayjs from "@calcom/dayjs";
|
||||
import {
|
||||
useBookerStoreContext,
|
||||
useInitializeBookerStoreContext,
|
||||
} from "@calcom/features/bookings/Booker/BookerStoreProvider";
|
||||
import { Header } from "@calcom/features/bookings/Booker/components/Header";
|
||||
import { BookerSection } from "@calcom/features/bookings/Booker/components/Section";
|
||||
import { useAvailableTimeSlots } from "@calcom/features/bookings/Booker/components/hooks/useAvailableTimeSlots";
|
||||
import { useBookerLayout } from "@calcom/features/bookings/Booker/components/hooks/useBookerLayout";
|
||||
import { usePrefetch } from "@calcom/features/bookings/Booker/components/hooks/usePrefetch";
|
||||
import { useTimePreferences } from "@calcom/features/bookings/lib";
|
||||
import { LargeCalendar } from "@calcom/features/calendar-view/LargeCalendar";
|
||||
import { getUsernameList } from "@calcom/features/eventtypes/lib/defaultEvents";
|
||||
import { useTimesForSchedule } from "@calcom/features/schedules/lib/use-schedule/useTimesForSchedule";
|
||||
|
||||
import { formatUsername } from "../booker/BookerPlatformWrapper";
|
||||
import type {
|
||||
CalendarViewPlatformWrapperAtomPropsForIndividual,
|
||||
CalendarViewPlatformWrapperAtomPropsForTeam,
|
||||
} from "../calendar-view/wrappers/CalendarViewPlatformWrapper";
|
||||
import { useAtomGetPublicEvent } from "../hooks/event-types/public/useAtomGetPublicEvent";
|
||||
import { useEventType } from "../hooks/event-types/public/useEventType";
|
||||
import { useTeamEventType } from "../hooks/event-types/public/useTeamEventType";
|
||||
import { useAvailableSlots } from "../hooks/useAvailableSlots";
|
||||
import { AtomsWrapper } from "../src/components/atoms-wrapper";
|
||||
|
||||
export const EventTypeCalendarViewComponent = (
|
||||
props:
|
||||
| (CalendarViewPlatformWrapperAtomPropsForIndividual & {
|
||||
teamId?: number;
|
||||
})
|
||||
| (CalendarViewPlatformWrapperAtomPropsForTeam & { username?: string | string[] })
|
||||
) => {
|
||||
const isTeamEvent = !!props.teamId;
|
||||
const teamId: number | undefined = props.teamId ? props.teamId : undefined;
|
||||
const username = useMemo(() => {
|
||||
if (props.username) {
|
||||
return formatUsername(props.username);
|
||||
}
|
||||
return "";
|
||||
}, [props.username]);
|
||||
|
||||
const { isPending } = useEventType(username, props.eventSlug, isTeamEvent);
|
||||
|
||||
const { isPending: isTeamPending } = useTeamEventType(teamId, props.eventSlug, isTeamEvent);
|
||||
|
||||
const selectedDuration = useBookerStoreContext((state) => state.selectedDuration);
|
||||
|
||||
const event = useAtomGetPublicEvent({
|
||||
username,
|
||||
eventSlug: props.eventSlug,
|
||||
isTeamEvent: isTeamEvent,
|
||||
teamId,
|
||||
selectedDuration,
|
||||
});
|
||||
|
||||
const bookerLayout = useBookerLayout(event.data?.profile?.bookerLayouts);
|
||||
|
||||
const [bookerState, _setBookerState] = useBookerStoreContext(
|
||||
(state) => [state.state, state.setState],
|
||||
shallow
|
||||
);
|
||||
const selectedDate = useBookerStoreContext((state) => state.selectedDate);
|
||||
const date = dayjs(selectedDate).format("YYYY-MM-DD");
|
||||
const month = useBookerStoreContext((state) => state.month);
|
||||
const [dayCount] = useBookerStoreContext((state) => [state.dayCount, state.setDayCount], shallow);
|
||||
|
||||
const { prefetchNextMonth, monthCount } = usePrefetch({
|
||||
date,
|
||||
month,
|
||||
bookerLayout,
|
||||
bookerState,
|
||||
});
|
||||
|
||||
const [startTime, endTime] = useTimesForSchedule({
|
||||
month,
|
||||
monthCount,
|
||||
dayCount,
|
||||
prefetchNextMonth,
|
||||
selectedDate,
|
||||
});
|
||||
|
||||
const { timezone } = useTimePreferences();
|
||||
const isDynamic = useMemo(() => {
|
||||
return getUsernameList(username ?? "").length > 1;
|
||||
}, [username]);
|
||||
|
||||
const bookingData = useBookerStoreContext((state) => state.bookingData);
|
||||
|
||||
useInitializeBookerStoreContext({
|
||||
...props,
|
||||
eventId: event?.data?.id,
|
||||
layout: "week_view",
|
||||
username,
|
||||
bookingData,
|
||||
isPlatform: true,
|
||||
allowUpdatingUrlParams: false,
|
||||
});
|
||||
|
||||
const schedule = useAvailableSlots({
|
||||
usernameList: getUsernameList(username),
|
||||
eventTypeId: event?.data?.id ?? 0,
|
||||
startTime,
|
||||
endTime,
|
||||
timeZone: timezone,
|
||||
duration: selectedDuration ?? undefined,
|
||||
teamMemberEmail: undefined,
|
||||
...(isTeamEvent
|
||||
? {
|
||||
isTeamEvent: isTeamEvent,
|
||||
teamId: teamId,
|
||||
}
|
||||
: {}),
|
||||
enabled:
|
||||
Boolean(teamId || username) &&
|
||||
Boolean(month) &&
|
||||
Boolean(timezone) &&
|
||||
(isTeamEvent ? !isTeamPending : !isPending) &&
|
||||
Boolean(event?.data?.id),
|
||||
orgSlug: undefined,
|
||||
eventTypeSlug: isDynamic ? "dynamic" : props.eventSlug || "",
|
||||
});
|
||||
|
||||
const selectedEventDuration = useBookerStoreContext((state) => state.selectedDuration);
|
||||
const eventDuration = selectedEventDuration || event?.data?.length || 30;
|
||||
|
||||
const availableTimeSlots = useAvailableTimeSlots({ schedule: schedule.data, eventDuration });
|
||||
|
||||
return (
|
||||
<AtomsWrapper>
|
||||
<BookerSection area="header" className="bg-default dark:bg-muted sticky top-0 z-10">
|
||||
<Header
|
||||
isCalendarView={true}
|
||||
isMyLink={true}
|
||||
eventSlug={props.eventSlug}
|
||||
enabledLayouts={bookerLayout.bookerLayouts.enabledLayouts}
|
||||
extraDays={7}
|
||||
isMobile={false}
|
||||
nextSlots={6}
|
||||
/>
|
||||
</BookerSection>
|
||||
<BookerSection
|
||||
key="large-calendar"
|
||||
area="main"
|
||||
visible={true}
|
||||
className="border-subtle sticky top-0 ml-[-1px] h-full md:border-l">
|
||||
<LargeCalendar
|
||||
extraDays={7}
|
||||
availableTimeslots={availableTimeSlots}
|
||||
isLoading={schedule.isPending}
|
||||
event={event}
|
||||
/>
|
||||
</BookerSection>
|
||||
</AtomsWrapper>
|
||||
);
|
||||
};
|
||||
@@ -1,166 +1,38 @@
|
||||
import { AtomsWrapper } from "@/components/atoms-wrapper";
|
||||
import { useMemo } from "react";
|
||||
import { shallow } from "zustand/shallow";
|
||||
import { BookerStoreProvider } from "@calcom/features/bookings/Booker/BookerStoreProvider";
|
||||
|
||||
import dayjs from "@calcom/dayjs";
|
||||
import {
|
||||
BookerStoreProvider,
|
||||
useBookerStoreContext,
|
||||
useInitializeBookerStoreContext,
|
||||
} from "@calcom/features/bookings/Booker/BookerStoreProvider";
|
||||
import { Header } from "@calcom/features/bookings/Booker/components/Header";
|
||||
import { BookerSection } from "@calcom/features/bookings/Booker/components/Section";
|
||||
import { useBookerLayout } from "@calcom/features/bookings/Booker/components/hooks/useBookerLayout";
|
||||
import { usePrefetch } from "@calcom/features/bookings/Booker/components/hooks/usePrefetch";
|
||||
import { useTimePreferences } from "@calcom/features/bookings/lib";
|
||||
import { LargeCalendar } from "@calcom/features/calendar-view/LargeCalendar";
|
||||
import { getUsernameList } from "@calcom/features/eventtypes/lib/defaultEvents";
|
||||
import { useTimesForSchedule } from "@calcom/features/schedules/lib/use-schedule/useTimesForSchedule";
|
||||
import { CalendarViewComponent } from "../CalendarViewComponent";
|
||||
import { EventTypeCalendarViewComponent } from "../EventTypeCalendarViewComponent";
|
||||
|
||||
import { formatUsername } from "../../booker/BookerPlatformWrapper";
|
||||
import type {
|
||||
BookerPlatformWrapperAtomPropsForIndividual,
|
||||
BookerPlatformWrapperAtomPropsForTeam,
|
||||
} from "../../booker/types";
|
||||
import { useAtomGetPublicEvent } from "../../hooks/event-types/public/useAtomGetPublicEvent";
|
||||
import { useEventType } from "../../hooks/event-types/public/useEventType";
|
||||
import { useTeamEventType } from "../../hooks/event-types/public/useTeamEventType";
|
||||
import { useAvailableSlots } from "../../hooks/useAvailableSlots";
|
||||
|
||||
type CalendarViewPlatformWrapperAtomPropsForIndividual = {
|
||||
export type CalendarViewPlatformWrapperAtomPropsForIndividual = {
|
||||
username: string | string[];
|
||||
eventSlug: string;
|
||||
};
|
||||
|
||||
type CalendarViewPlatformWrapperAtomPropsForTeam = {
|
||||
export type CalendarViewPlatformWrapperAtomPropsForTeam = {
|
||||
teamId: number;
|
||||
eventSlug: string;
|
||||
};
|
||||
|
||||
const CalendarViewPlatformWrapperComponent = (
|
||||
props:
|
||||
| (CalendarViewPlatformWrapperAtomPropsForIndividual & { teamId?: number })
|
||||
| (CalendarViewPlatformWrapperAtomPropsForTeam & { username?: string | string[] })
|
||||
) => {
|
||||
const isTeamEvent = !!props.teamId;
|
||||
const teamId: number | undefined = props.teamId ? props.teamId : undefined;
|
||||
const username = useMemo(() => {
|
||||
if (props.username) {
|
||||
return formatUsername(props.username);
|
||||
}
|
||||
return "";
|
||||
}, [props.username]);
|
||||
type EventTypeCalendarViewProps = { isEventTypeView: true } & (
|
||||
| (CalendarViewPlatformWrapperAtomPropsForIndividual & { teamId?: number })
|
||||
| (CalendarViewPlatformWrapperAtomPropsForTeam & { username?: string | string[] })
|
||||
);
|
||||
|
||||
const { isPending } = useEventType(username, props.eventSlug, isTeamEvent);
|
||||
|
||||
const { isPending: isTeamPending } = useTeamEventType(teamId, props.eventSlug, isTeamEvent);
|
||||
|
||||
const selectedDuration = useBookerStoreContext((state) => state.selectedDuration);
|
||||
|
||||
const event = useAtomGetPublicEvent({
|
||||
username,
|
||||
eventSlug: props.eventSlug,
|
||||
isTeamEvent: isTeamEvent,
|
||||
teamId,
|
||||
selectedDuration,
|
||||
});
|
||||
|
||||
const bookerLayout = useBookerLayout(event.data?.profile?.bookerLayouts);
|
||||
|
||||
const [bookerState, _setBookerState] = useBookerStoreContext(
|
||||
(state) => [state.state, state.setState],
|
||||
shallow
|
||||
);
|
||||
const selectedDate = useBookerStoreContext((state) => state.selectedDate);
|
||||
const date = dayjs(selectedDate).format("YYYY-MM-DD");
|
||||
const month = useBookerStoreContext((state) => state.month);
|
||||
const [dayCount] = useBookerStoreContext((state) => [state.dayCount, state.setDayCount], shallow);
|
||||
|
||||
const { prefetchNextMonth, monthCount } = usePrefetch({
|
||||
date,
|
||||
month,
|
||||
bookerLayout,
|
||||
bookerState,
|
||||
});
|
||||
|
||||
const [startTime, endTime] = useTimesForSchedule({
|
||||
month,
|
||||
monthCount,
|
||||
dayCount,
|
||||
prefetchNextMonth,
|
||||
selectedDate,
|
||||
});
|
||||
|
||||
const { timezone } = useTimePreferences();
|
||||
const isDynamic = useMemo(() => {
|
||||
return getUsernameList(username ?? "").length > 1;
|
||||
}, [username]);
|
||||
|
||||
const bookingData = useBookerStoreContext((state) => state.bookingData);
|
||||
|
||||
useInitializeBookerStoreContext({
|
||||
...props,
|
||||
eventId: event?.data?.id,
|
||||
layout: "week_view",
|
||||
username,
|
||||
bookingData,
|
||||
isPlatform: true,
|
||||
allowUpdatingUrlParams: false,
|
||||
});
|
||||
|
||||
const schedule = useAvailableSlots({
|
||||
usernameList: getUsernameList(username),
|
||||
eventTypeId: event?.data?.id ?? 0,
|
||||
startTime,
|
||||
endTime,
|
||||
timeZone: timezone,
|
||||
duration: selectedDuration ?? undefined,
|
||||
teamMemberEmail: undefined,
|
||||
...(isTeamEvent
|
||||
? {
|
||||
isTeamEvent: isTeamEvent,
|
||||
teamId: teamId,
|
||||
}
|
||||
: {}),
|
||||
enabled:
|
||||
Boolean(teamId || username) &&
|
||||
Boolean(month) &&
|
||||
Boolean(timezone) &&
|
||||
(isTeamEvent ? !isTeamPending : !isPending) &&
|
||||
Boolean(event?.data?.id),
|
||||
orgSlug: undefined,
|
||||
eventTypeSlug: isDynamic ? "dynamic" : props.eventSlug || "",
|
||||
});
|
||||
|
||||
return (
|
||||
<AtomsWrapper>
|
||||
<BookerSection area="header" className="bg-default dark:bg-cal-muted sticky top-0 z-10">
|
||||
<Header
|
||||
isCalendarView={true}
|
||||
isMyLink={true}
|
||||
eventSlug={props.eventSlug}
|
||||
enabledLayouts={bookerLayout.bookerLayouts.enabledLayouts}
|
||||
extraDays={7}
|
||||
isMobile={false}
|
||||
nextSlots={6}
|
||||
/>
|
||||
</BookerSection>
|
||||
<BookerSection
|
||||
key="large-calendar"
|
||||
area="main"
|
||||
visible={true}
|
||||
className="border-subtle sticky top-0 -ml-px h-full md:border-l">
|
||||
<LargeCalendar extraDays={7} schedule={schedule.data} isLoading={schedule.isPending} event={event} />
|
||||
</BookerSection>
|
||||
</AtomsWrapper>
|
||||
);
|
||||
type CalendarViewComponentProps = {
|
||||
isEventTypeView?: false;
|
||||
};
|
||||
|
||||
export const CalendarViewPlatformWrapper = (
|
||||
props:
|
||||
| (BookerPlatformWrapperAtomPropsForIndividual & { teamId?: number })
|
||||
| Omit<BookerPlatformWrapperAtomPropsForTeam, "isTeamEvent">
|
||||
) => {
|
||||
type CalendarViewProps = EventTypeCalendarViewProps | CalendarViewComponentProps;
|
||||
|
||||
const CalendarViewPlatformWrapperComponent = (props: CalendarViewProps) => {
|
||||
if (!props.isEventTypeView) {
|
||||
return <CalendarViewComponent />;
|
||||
}
|
||||
|
||||
return <EventTypeCalendarViewComponent {...props} />;
|
||||
};
|
||||
|
||||
export const CalendarViewPlatformWrapper = (props: CalendarViewProps) => {
|
||||
return (
|
||||
<BookerStoreProvider>
|
||||
<CalendarViewPlatformWrapperComponent {...props} />
|
||||
|
||||
@@ -26,6 +26,29 @@ export const useCalendarsBusyTimes = ({ onError, enabled, ...rest }: UseCalendar
|
||||
return http
|
||||
.get<ApiResponse<EventBusyDate[]>>("/calendars/busy-times", {
|
||||
params: rest,
|
||||
paramsSerializer: (params) => {
|
||||
const searchParams = new URLSearchParams();
|
||||
|
||||
if (params.dateFrom) searchParams.append("dateFrom", params.dateFrom);
|
||||
if (params.dateTo) searchParams.append("dateTo", params.dateTo);
|
||||
if (params.loggedInUsersTz) searchParams.append("loggedInUsersTz", params.loggedInUsersTz);
|
||||
|
||||
// calendarsToLoad expects an array of objects but since this is a GET request, we cant pass the data directly inside the body
|
||||
// hence serializing calendarsToLoad array of objects in the query params
|
||||
if (params.calendarsToLoad && Array.isArray(params.calendarsToLoad)) {
|
||||
params.calendarsToLoad.forEach(
|
||||
(calendar: { credentialId: number; externalId: string }, index: number) => {
|
||||
searchParams.append(
|
||||
`calendarsToLoad[${index}][credentialId]`,
|
||||
String(calendar.credentialId)
|
||||
);
|
||||
searchParams.append(`calendarsToLoad[${index}][externalId]`, calendar.externalId);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
return searchParams.toString();
|
||||
},
|
||||
})
|
||||
.then((res) => {
|
||||
if (res.data.status === SUCCESS_STATUS) {
|
||||
|
||||
@@ -10,7 +10,8 @@ export default function CalendarViewAtom(props: { calUsername: string; calEmail:
|
||||
<main className={`flex min-h-screen flex-col ${inter.className}`}>
|
||||
<Navbar username={props.calUsername} />
|
||||
<div data-testid="calendars-settings-atom">
|
||||
<CalendarView username={props.calUsername} eventSlug="sixty-minutes" />
|
||||
{/* <CalendarView isEventTypeView={true} username={props.calUsername} eventSlug="sixty-minutes" /> */}
|
||||
<CalendarView />
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
|
||||
Reference in new issue
Block a user