diff --git a/.changeset/eighty-walls-send.md b/.changeset/eighty-walls-send.md new file mode 100644 index 0000000000..2572eb3c11 --- /dev/null +++ b/.changeset/eighty-walls-send.md @@ -0,0 +1,5 @@ +--- +"@calcom/atoms": minor +--- + +This PR adds ability to display bookings of a user event for calendar view atom diff --git a/docs/platform/atoms/calendar-view.mdx b/docs/platform/atoms/calendar-view.mdx index 52bd1a063e..2531ae0ce9 100644 --- a/docs/platform/atoms/calendar-view.mdx +++ b/docs/platform/atoms/calendar-view.mdx @@ -30,7 +30,7 @@ import { CalendarView } from "@calcom/atoms"; export default function Booker( props : BookerProps ) { return (
- +
) } @@ -55,7 +55,6 @@ Below is a list of props that can be passed to the create event type atom | Name | Required | Description | |:----------------------------|:----------|:-------------------------------------------------------------------------------------------------------| -| username | Yes | Username of the person whose schedule is to be displayed | -| eventSlug | Yes | Unique slug created for a particular event | | -| isTeamEvent | No | Boolean indicating if it is a team event, to be passed only for team events | -| teamId | No | The id of the team for which the event is created, to be passed only for team events | \ No newline at end of file +| eventSlug | Yes | Unique slug created for a particular event | +| username | Optional | Username of the person whose schedule is to be displayed, required only for individual events | | | +| teamId | Optional | Id of the team for which the event is created, required only for team events | \ No newline at end of file diff --git a/packages/features/calendar-view/LargeCalendar.tsx b/packages/features/calendar-view/LargeCalendar.tsx index 0659df79da..0884424e9d 100644 --- a/packages/features/calendar-view/LargeCalendar.tsx +++ b/packages/features/calendar-view/LargeCalendar.tsx @@ -1,14 +1,14 @@ -import { useMemo, useEffect } from "react"; +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 type { BookerEvent } from "@calcom/features/bookings/types"; import { Calendar } from "@calcom/features/calendars/weeklyview"; -import type { CalendarEvent } from "@calcom/features/calendars/weeklyview/types/events"; import { localStorage } from "@calcom/lib/webstorage"; +import type { BookingStatus } from "@calcom/prisma/enums"; -import { useOverlayCalendarStore } from "../bookings/Booker/components/OverlayCalendar/store"; +import { useBookings } from "../../platform/atoms/hooks/bookings/useBookings"; import type { useScheduleForEventReturnType } from "../bookings/Booker/utils/event"; import { getQueryParam } from "../bookings/Booker/utils/query-param"; @@ -22,12 +22,11 @@ export const LargeCalendar = ({ schedule?: useScheduleForEventReturnType["data"]; isLoading: boolean; event: { - data?: Pick | null; + data?: Pick | null; }; }) => { const selectedDate = useBookerStoreContext((state) => state.selectedDate); const selectedEventDuration = useBookerStoreContext((state) => state.selectedDuration); - const overlayEvents = useOverlayCalendarStore((state) => state.overlayBusyDates); const displayOverlay = getQueryParam("overlayCalendar") === "true" || localStorage?.getItem("overlayCalendarSwitchDefault"); @@ -40,25 +39,38 @@ export const LargeCalendar = ({ .add(extraDays - 1, "day") .toDate(); + const { data: upcomingBookings } = useBookings({ + take: 150, + skip: 0, + status: ["upcoming", "past", "recurring"], + eventTypeId: event?.data?.id, + afterStart: startDate.toISOString(), + beforeEnd: endDate.toISOString(), + }); + // HACK: force rerender when overlay events change // Sine we dont use react router here we need to force rerender (ATOM SUPPORT) - // eslint-disable-next-line @typescript-eslint/no-empty-function + useEffect(() => {}, [displayOverlay]); const overlayEventsForDate = useMemo(() => { - if (!overlayEvents || !displayOverlay) return []; - return overlayEvents.map((event, id) => { + if (!upcomingBookings) return []; + + return upcomingBookings?.map((booking) => { return { - id, - start: dayjs(event.start).toDate(), - end: dayjs(event.end).toDate(), - title: "Busy", + id: booking.id, + title: booking.title ?? `Busy`, + start: new Date(booking.start), + end: new Date(booking.end), options: { - status: "ACCEPTED", + borderColor: "black", + status: booking.status.toUpperCase() as BookingStatus, + "data-test-id": "troubleshooter-busy-event", + className: "border-[1.5px]", }, - } as CalendarEvent; + }; }); - }, [overlayEvents, displayOverlay]); + }, [upcomingBookings]); return (
diff --git a/packages/platform/atoms/calendar-view/wrappers/CalendarViewPlatformWrapper.tsx b/packages/platform/atoms/calendar-view/wrappers/CalendarViewPlatformWrapper.tsx index 952d94d094..fc73fc72e3 100644 --- a/packages/platform/atoms/calendar-view/wrappers/CalendarViewPlatformWrapper.tsx +++ b/packages/platform/atoms/calendar-view/wrappers/CalendarViewPlatformWrapper.tsx @@ -1,5 +1,5 @@ import { AtomsWrapper } from "@/components/atoms-wrapper"; -import { useMemo, useEffect, useState } from "react"; +import { useMemo } from "react"; import { shallow } from "zustand/shallow"; import dayjs from "@calcom/dayjs"; @@ -16,33 +16,34 @@ 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 { getRoutedTeamMemberIdsFromSearchParams } from "@calcom/lib/bookings/getRoutedTeamMemberIdsFromSearchParams"; import { formatUsername } from "../../booker/BookerPlatformWrapper"; import type { BookerPlatformWrapperAtomPropsForIndividual, BookerPlatformWrapperAtomPropsForTeam, } from "../../booker/types"; -import { useGetBookingForReschedule } from "../../hooks/bookings/useGetBookingForReschedule"; 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"; -const CalendarViewPlatformWrapperComponent = ( - props: BookerPlatformWrapperAtomPropsForIndividual | BookerPlatformWrapperAtomPropsForTeam -) => { - const { - eventSlug, - isTeamEvent, - hostsLimit, - allowUpdatingUrlParams = false, - teamMemberEmail, - crmAppSlug, - crmOwnerRecordType, - } = props; +type CalendarViewPlatformWrapperAtomPropsForIndividual = { + username: string | string[]; + eventSlug: string; +}; - const teamId: number | undefined = props.isTeamEvent ? props.teamId : undefined; +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); @@ -50,16 +51,16 @@ const CalendarViewPlatformWrapperComponent = ( return ""; }, [props.username]); - const { isPending } = useEventType(username, eventSlug, isTeamEvent); - const { isPending: isTeamPending } = useTeamEventType(teamId, eventSlug, isTeamEvent, hostsLimit); + const { isPending } = useEventType(username, props.eventSlug, isTeamEvent); + + const { isPending: isTeamPending } = useTeamEventType(teamId, props.eventSlug, isTeamEvent); - const setSelectedDuration = useBookerStoreContext((state) => state.setSelectedDuration); const selectedDuration = useBookerStoreContext((state) => state.selectedDuration); const event = useAtomGetPublicEvent({ username, eventSlug: props.eventSlug, - isTeamEvent: props.isTeamEvent, + isTeamEvent: isTeamEvent, teamId, selectedDuration, }); @@ -90,67 +91,21 @@ const CalendarViewPlatformWrapperComponent = ( selectedDate, }); - useEffect(() => { - setSelectedDuration(props.duration ?? null); - }, [props.duration, setSelectedDuration]); - const { timezone } = useTimePreferences(); const isDynamic = useMemo(() => { return getUsernameList(username ?? "").length > 1; }, [username]); - const [routingParams, setRoutingParams] = useState<{ - routedTeamMemberIds?: number[]; - _shouldServeCache?: boolean; - skipContactOwner?: boolean; - isBookingDryRun?: boolean; - }>({}); - - useEffect(() => { - const searchParams = props.routingFormSearchParams - ? new URLSearchParams(props.routingFormSearchParams) - : new URLSearchParams(window.location.search); - - const routedTeamMemberIds = getRoutedTeamMemberIdsFromSearchParams(searchParams); - const skipContactOwner = searchParams.get("cal.skipContactOwner") === "true"; - - const _cacheParam = searchParams?.get("cal.cache"); - const _shouldServeCache = _cacheParam ? _cacheParam === "true" : undefined; - const isBookingDryRun = - searchParams?.get("cal.isBookingDryRun")?.toLowerCase() === "true" || - searchParams?.get("cal.sandbox")?.toLowerCase() === "true"; - setRoutingParams({ - ...(skipContactOwner ? { skipContactOwner } : {}), - ...(routedTeamMemberIds ? { routedTeamMemberIds } : {}), - ...(_shouldServeCache ? { _shouldServeCache } : {}), - ...(isBookingDryRun ? { isBookingDryRun } : {}), - }); - }, [props.routingFormSearchParams]); const bookingData = useBookerStoreContext((state) => state.bookingData); - const setBookingData = useBookerStoreContext((state) => state.setBookingData); - - useGetBookingForReschedule({ - uid: props.rescheduleUid ?? props.bookingUid ?? "", - onSuccess: (data) => { - setBookingData(data); - }, - }); useInitializeBookerStoreContext({ ...props, - teamMemberEmail, - crmAppSlug, - crmOwnerRecordType, - crmRecordId: props.crmRecordId, eventId: event?.data?.id, - rescheduleUid: props.rescheduleUid ?? null, - bookingUid: props.bookingUid ?? null, layout: "week_view", - org: props.entity?.orgSlug, username, bookingData, isPlatform: true, - allowUpdatingUrlParams, + allowUpdatingUrlParams: false, }); const schedule = useAvailableSlots({ @@ -160,11 +115,10 @@ const CalendarViewPlatformWrapperComponent = ( endTime, timeZone: timezone, duration: selectedDuration ?? undefined, - rescheduleUid: props.rescheduleUid, - teamMemberEmail: props.teamMemberEmail ?? undefined, - ...(props.isTeamEvent + teamMemberEmail: undefined, + ...(isTeamEvent ? { - isTeamEvent: props.isTeamEvent, + isTeamEvent: isTeamEvent, teamId: teamId, } : {}), @@ -172,11 +126,10 @@ const CalendarViewPlatformWrapperComponent = ( Boolean(teamId || username) && Boolean(month) && Boolean(timezone) && - (props.isTeamEvent ? !isTeamPending : !isPending) && + (isTeamEvent ? !isTeamPending : !isPending) && Boolean(event?.data?.id), - orgSlug: props.entity?.orgSlug ?? undefined, - eventTypeSlug: isDynamic ? "dynamic" : eventSlug || "", - ...routingParams, + orgSlug: undefined, + eventTypeSlug: isDynamic ? "dynamic" : props.eventSlug || "", }); return ( @@ -185,7 +138,7 @@ const CalendarViewPlatformWrapperComponent = (
) => { return (