feat: calendar view atom v1 (#23840)
* fix: refactor * add `isMonthViewProp` to header * feat: init v1 for calendar view atom * test breaking toggle buttons * fix: make sure week start is always sunday for calendar view atom * fixup * fix: remove extra comments * fix: add calendar view page in examples app * chore: add changesets * fix: coderabbit feedback * fixup
This commit is contained in:
1 parent
c5942bcfcd
commit
63740c02c7
15 files changed
+413
-56
No files matched your search
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@calcom/atoms": minor
|
||||
---
|
||||
|
||||
This PR adds a new atom called `CalendarView` which is a read only calendar view component for a user.
|
||||
@@ -5,6 +5,7 @@ import { useIsPlatform } from "@calcom/atoms/hooks/useIsPlatform";
|
||||
import dayjs from "@calcom/dayjs";
|
||||
import { useIsEmbed } from "@calcom/embed-core/embed-iframe";
|
||||
import { useBookerStoreContext } from "@calcom/features/bookings/Booker/BookerStoreProvider";
|
||||
import { useInitializeWeekStart } from "@calcom/features/bookings/Booker/components/hooks/useInitializeWeekStart";
|
||||
import { WEBAPP_URL } from "@calcom/lib/constants";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { BookerLayouts } from "@calcom/prisma/zod-utils";
|
||||
@@ -25,6 +26,7 @@ export function Header({
|
||||
eventSlug,
|
||||
isMyLink,
|
||||
renderOverlay,
|
||||
isCalendarView,
|
||||
}: {
|
||||
extraDays: number;
|
||||
isMobile: boolean;
|
||||
@@ -33,14 +35,16 @@ export function Header({
|
||||
eventSlug: string;
|
||||
isMyLink: boolean;
|
||||
renderOverlay?: () => JSX.Element | null;
|
||||
isCalendarView?: boolean;
|
||||
}) {
|
||||
const { t, i18n } = useLocale();
|
||||
const isEmbed = useIsEmbed();
|
||||
const isPlatform = useIsPlatform();
|
||||
const [layout, setLayout] = useBookerStoreContext((state) => [state.layout, state.setLayout], shallow);
|
||||
const selectedDateString = useBookerStoreContext((state) => state.selectedDate);
|
||||
const setSelectedDate = useBookerStoreContext((state) => state.setSelectedDate);
|
||||
const addToSelectedDate = useBookerStoreContext((state) => state.addToSelectedDate);
|
||||
const isMonthView = layout === BookerLayouts.MONTH_VIEW;
|
||||
const isMonthView = isCalendarView !== undefined ? !isCalendarView : layout === BookerLayouts.MONTH_VIEW;
|
||||
const today = dayjs();
|
||||
const selectedDate = selectedDateString ? dayjs(selectedDateString) : today;
|
||||
const selectedDateMin3DaysDifference = useMemo(() => {
|
||||
@@ -48,6 +52,8 @@ export function Header({
|
||||
return diff > 3 || diff < -3;
|
||||
}, [today, selectedDate]);
|
||||
|
||||
useInitializeWeekStart(isPlatform, isCalendarView ?? false);
|
||||
|
||||
const onLayoutToggle = useCallback(
|
||||
(newLayout: string) => {
|
||||
if (layout === newLayout || !newLayout) return;
|
||||
@@ -130,7 +136,10 @@ export function Header({
|
||||
<Button
|
||||
className="capitalize ltr:ml-2 rtl:mr-2"
|
||||
color="secondary"
|
||||
onClick={() => setSelectedDate({ date: today.format("YYYY-MM-DD") })}>
|
||||
onClick={() => {
|
||||
const selectedDate = (isCalendarView ? today.startOf("week") : today).format("YYYY-MM-DD");
|
||||
setSelectedDate({ date: selectedDate });
|
||||
}}>
|
||||
{t("today")}
|
||||
</Button>
|
||||
)}
|
||||
|
||||
@@ -2,10 +2,10 @@ import { useMemo, useEffect } 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 type { CalendarAvailableTimeslots } from "@calcom/features/calendars/weeklyview/types/state";
|
||||
import { localStorage } from "@calcom/lib/webstorage";
|
||||
|
||||
import type { useScheduleForEventReturnType } from "../utils/event";
|
||||
@@ -34,24 +34,7 @@ export const LargeCalendar = ({
|
||||
|
||||
const eventDuration = selectedEventDuration || event?.data?.length || 30;
|
||||
|
||||
const availableSlots = useMemo(() => {
|
||||
const availableTimeslots: CalendarAvailableTimeslots = {};
|
||||
if (!schedule) return availableTimeslots;
|
||||
if (!schedule.slots) return availableTimeslots;
|
||||
|
||||
for (const day in schedule.slots) {
|
||||
availableTimeslots[day] = schedule.slots[day].map((slot) => {
|
||||
const { time, ...rest } = slot;
|
||||
return {
|
||||
start: dayjs(time).toDate(),
|
||||
end: dayjs(time).add(eventDuration, "minutes").toDate(),
|
||||
...rest,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
return availableTimeslots;
|
||||
}, [schedule, eventDuration]);
|
||||
const availableSlots = useAvailableTimeSlots({ schedule, eventDuration });
|
||||
|
||||
const startDate = selectedDate ? dayjs(selectedDate).toDate() : dayjs().toDate();
|
||||
const endDate = dayjs(startDate)
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
import { useMemo } from "react";
|
||||
|
||||
import dayjs from "@calcom/dayjs";
|
||||
import type { CalendarAvailableTimeslots } from "@calcom/features/calendars/weeklyview/types/state";
|
||||
import type { IGetAvailableSlots } from "@calcom/trpc/server/routers/viewer/slots/util";
|
||||
|
||||
interface UseAvailableTimeSlotsProps {
|
||||
eventDuration: number;
|
||||
schedule?: IGetAvailableSlots;
|
||||
}
|
||||
|
||||
export const useAvailableTimeSlots = ({ schedule, eventDuration }: UseAvailableTimeSlotsProps) => {
|
||||
return useMemo(() => {
|
||||
const availableTimeslots: CalendarAvailableTimeslots = {};
|
||||
if (!schedule || !schedule.slots) return availableTimeslots;
|
||||
|
||||
for (const day in schedule.slots) {
|
||||
availableTimeslots[day] = schedule.slots[day].map((slot) => {
|
||||
const { time, ...rest } = slot;
|
||||
return {
|
||||
start: dayjs(time).toDate(),
|
||||
end: dayjs(time).add(eventDuration, "minutes").toDate(),
|
||||
...rest,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
return availableTimeslots;
|
||||
}, [schedule, eventDuration]);
|
||||
};
|
||||
@@ -0,0 +1,17 @@
|
||||
import { useEffect } from "react";
|
||||
|
||||
import dayjs from "@calcom/dayjs";
|
||||
import { useBookerStoreContext } from "@calcom/features/bookings/Booker/BookerStoreProvider";
|
||||
|
||||
export const useInitializeWeekStart = (isPlatform: boolean, isCalendarView: boolean) => {
|
||||
const today = dayjs();
|
||||
const weekStart = today.startOf("week").format("YYYY-MM-DD");
|
||||
const setSelectedDate = useBookerStoreContext((state) => state.setSelectedDate);
|
||||
|
||||
useEffect(() => {
|
||||
if (isPlatform && isCalendarView) {
|
||||
setSelectedDate({ date: weekStart, omitUpdatingParams: true });
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
};
|
||||
@@ -0,0 +1,79 @@
|
||||
import { useMemo, useEffect } 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 { useOverlayCalendarStore } from "../bookings/Booker/components/OverlayCalendar/store";
|
||||
import type { useScheduleForEventReturnType } from "../bookings/Booker/utils/event";
|
||||
import { getQueryParam } from "../bookings/Booker/utils/query-param";
|
||||
|
||||
export const LargeCalendar = ({
|
||||
extraDays,
|
||||
schedule,
|
||||
isLoading,
|
||||
event,
|
||||
}: {
|
||||
extraDays: number;
|
||||
schedule?: useScheduleForEventReturnType["data"];
|
||||
isLoading: boolean;
|
||||
event: {
|
||||
data?: Pick<BookerEvent, "length"> | 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");
|
||||
|
||||
const eventDuration = selectedEventDuration || event?.data?.length || 30;
|
||||
|
||||
const availableSlots = useAvailableTimeSlots({ schedule, eventDuration });
|
||||
|
||||
const startDate = selectedDate ? dayjs(selectedDate).toDate() : dayjs().toDate();
|
||||
const endDate = dayjs(startDate)
|
||||
.add(extraDays - 1, "day")
|
||||
.toDate();
|
||||
|
||||
// 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) => {
|
||||
return {
|
||||
id,
|
||||
start: dayjs(event.start).toDate(),
|
||||
end: dayjs(event.end).toDate(),
|
||||
title: "Busy",
|
||||
options: {
|
||||
status: "ACCEPTED",
|
||||
},
|
||||
} as CalendarEvent;
|
||||
});
|
||||
}, [overlayEvents, displayOverlay]);
|
||||
|
||||
return (
|
||||
<div className="h-full [--calendar-dates-sticky-offset:66px]">
|
||||
<Calendar
|
||||
isPending={isLoading}
|
||||
availableTimeslots={availableSlots}
|
||||
startHour={0}
|
||||
endHour={23}
|
||||
events={overlayEventsForDate}
|
||||
startDate={startDate}
|
||||
endDate={endDate}
|
||||
gridCellsPerHour={60 / eventDuration}
|
||||
hoverEventDuration={eventDuration}
|
||||
hideHeader
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -2,8 +2,8 @@ import { useSession } from "next-auth/react";
|
||||
import { useMemo } from "react";
|
||||
|
||||
import dayjs from "@calcom/dayjs";
|
||||
import { useAvailableTimeSlots } from "@calcom/features/bookings/Booker/components/hooks/useAvailableTimeSlots";
|
||||
import { Calendar } from "@calcom/features/calendars/weeklyview";
|
||||
import type { CalendarAvailableTimeslots } from "@calcom/features/calendars/weeklyview/types/state";
|
||||
import { BookingStatus } from "@calcom/prisma/enums";
|
||||
import { trpc } from "@calcom/trpc";
|
||||
|
||||
@@ -48,22 +48,7 @@ export const LargeCalendar = ({ extraDays }: { extraDays: number }) => {
|
||||
.add(extraDays - 1, "day")
|
||||
.toDate();
|
||||
|
||||
const availableSlots = useMemo(() => {
|
||||
const availableTimeslots: CalendarAvailableTimeslots = {};
|
||||
if (!schedule) return availableTimeslots;
|
||||
if (!schedule?.slots) return availableTimeslots;
|
||||
|
||||
for (const day in schedule.slots) {
|
||||
availableTimeslots[day] = schedule.slots[day].map((slot) => ({
|
||||
start: dayjs(slot.time).toDate(),
|
||||
end: dayjs(slot.time)
|
||||
.add(event?.duration ?? 30, "minutes")
|
||||
.toDate(),
|
||||
}));
|
||||
}
|
||||
|
||||
return availableTimeslots;
|
||||
}, [schedule, event]);
|
||||
const availableSlots = useAvailableTimeSlots({ schedule, eventDuration: event?.duration ?? 30 });
|
||||
|
||||
const events = useMemo(() => {
|
||||
if (!busyEvents?.busy) return [];
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import { useQueryClient } from "@tanstack/react-query";
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import debounce from "lodash/debounce";
|
||||
@@ -594,7 +595,7 @@ export const BookerPlatformWrapper = (
|
||||
);
|
||||
};
|
||||
|
||||
function formatUsername(username: string | string[]): string {
|
||||
export function formatUsername(username: string | string[]): string {
|
||||
if (typeof username === "string") {
|
||||
return username;
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
export { CalendarViewPlatformWrapper } from "./wrappers/CalendarViewPlatformWrapper";
|
||||
@@ -0,0 +1,223 @@
|
||||
import { AtomsWrapper } from "@/components/atoms-wrapper";
|
||||
import { useMemo, useEffect, useState } from "react";
|
||||
import { shallow } from "zustand/shallow";
|
||||
|
||||
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 { 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 { BookerLayouts } from "@calcom/prisma/zod-utils";
|
||||
|
||||
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,
|
||||
view = "MONTH_VIEW",
|
||||
} = props;
|
||||
|
||||
const teamId: number | undefined = props.isTeamEvent ? props.teamId : undefined;
|
||||
const username = useMemo(() => {
|
||||
if (props.username) {
|
||||
return formatUsername(props.username);
|
||||
}
|
||||
return "";
|
||||
}, [props.username]);
|
||||
|
||||
const { isPending } = useEventType(username, eventSlug, isTeamEvent);
|
||||
const { isPending: isTeamPending } = useTeamEventType(teamId, eventSlug, isTeamEvent, hostsLimit);
|
||||
|
||||
const setSelectedDuration = useBookerStoreContext((state) => state.setSelectedDuration);
|
||||
const selectedDuration = useBookerStoreContext((state) => state.selectedDuration);
|
||||
|
||||
const event = useAtomGetPublicEvent({
|
||||
username,
|
||||
eventSlug: props.eventSlug,
|
||||
isTeamEvent: props.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 monthCount =
|
||||
((bookerLayout.layout !== BookerLayouts.WEEK_VIEW && bookerState === "selecting_time") ||
|
||||
bookerLayout.layout === BookerLayouts.COLUMN_VIEW) &&
|
||||
dayjs(date).add(1, "month").month() !==
|
||||
dayjs(date).add(bookerLayout.columnViewExtraDays.current, "day").month()
|
||||
? 2
|
||||
: undefined;
|
||||
const month = useBookerStoreContext((state) => state.month);
|
||||
const [dayCount] = useBookerStoreContext((state) => [state.dayCount, state.setDayCount], shallow);
|
||||
|
||||
const prefetchNextMonth =
|
||||
(bookerLayout.layout === BookerLayouts.WEEK_VIEW &&
|
||||
!!bookerLayout.extraDays &&
|
||||
dayjs(date).month() !== dayjs(date).add(bookerLayout.extraDays, "day").month()) ||
|
||||
(bookerLayout.layout === BookerLayouts.COLUMN_VIEW &&
|
||||
dayjs(date).month() !== dayjs(date).add(bookerLayout.columnViewExtraDays.current, "day").month());
|
||||
|
||||
const [startTime, endTime] = useTimesForSchedule({
|
||||
month,
|
||||
monthCount,
|
||||
dayCount,
|
||||
prefetchNextMonth,
|
||||
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);
|
||||
const layout = BookerLayouts[view];
|
||||
|
||||
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: layout,
|
||||
org: props.entity?.orgSlug,
|
||||
username,
|
||||
bookingData,
|
||||
isPlatform: true,
|
||||
allowUpdatingUrlParams,
|
||||
});
|
||||
|
||||
const schedule = useAvailableSlots({
|
||||
usernameList: getUsernameList(username),
|
||||
eventTypeId: event?.data?.id ?? 0,
|
||||
startTime,
|
||||
endTime,
|
||||
timeZone: timezone,
|
||||
duration: selectedDuration ?? undefined,
|
||||
rescheduleUid: props.rescheduleUid,
|
||||
teamMemberEmail: props.teamMemberEmail ?? undefined,
|
||||
...(props.isTeamEvent
|
||||
? {
|
||||
isTeamEvent: props.isTeamEvent,
|
||||
teamId: teamId,
|
||||
}
|
||||
: {}),
|
||||
enabled:
|
||||
Boolean(teamId || username) &&
|
||||
Boolean(month) &&
|
||||
Boolean(timezone) &&
|
||||
(props.isTeamEvent ? !isTeamPending : !isPending) &&
|
||||
Boolean(event?.data?.id),
|
||||
orgSlug: props.entity?.orgSlug ?? undefined,
|
||||
eventTypeSlug: isDynamic ? "dynamic" : eventSlug || "",
|
||||
...routingParams,
|
||||
});
|
||||
|
||||
return (
|
||||
<AtomsWrapper>
|
||||
<BookerSection area="header" className="bg-default dark:bg-muted sticky top-0 z-10">
|
||||
<Header
|
||||
isCalendarView={true}
|
||||
isMyLink={true}
|
||||
eventSlug={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} schedule={schedule.data} isLoading={schedule.isPending} event={event} />
|
||||
</BookerSection>
|
||||
</AtomsWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export const CalendarViewPlatformWrapper = (
|
||||
props: BookerPlatformWrapperAtomPropsForIndividual | BookerPlatformWrapperAtomPropsForTeam
|
||||
) => {
|
||||
return (
|
||||
<BookerStoreProvider>
|
||||
<CalendarViewPlatformWrapperComponent {...props} />
|
||||
</BookerStoreProvider>
|
||||
);
|
||||
};
|
||||
@@ -3,6 +3,7 @@
|
||||
import { useQueryClient } from "@tanstack/react-query";
|
||||
import { useRef, useState, useEffect, forwardRef, useImperativeHandle, useCallback } from "react";
|
||||
|
||||
import { BookerStoreProvider } from "@calcom/features/bookings/Booker/BookerStoreProvider";
|
||||
import type { ChildrenEventType } from "@calcom/features/eventtypes/components/ChildrenEventTypeSelect";
|
||||
import { EventType as EventTypeComponent } from "@calcom/features/eventtypes/components/EventType";
|
||||
import ManagedEventTypeDialog from "@calcom/features/eventtypes/components/dialogs/ManagedEventDialog";
|
||||
@@ -417,20 +418,22 @@ export const EventTypePlatformWrapper = forwardRef<
|
||||
if (!eventTypeQueryData) return null;
|
||||
|
||||
return (
|
||||
<EventType
|
||||
{...eventTypeQueryData}
|
||||
id={id}
|
||||
tabs={tabs}
|
||||
onSuccess={onSuccess}
|
||||
onError={onError}
|
||||
onDeleteSuccess={onDeleteSuccess}
|
||||
onDeleteError={onDeleteError}
|
||||
allowDelete={allowDelete}
|
||||
customClassNames={customClassNames}
|
||||
isDryRun={isDryRun}
|
||||
onFormStateChange={onFormStateChange}
|
||||
ref={ref}
|
||||
disableToasts={disableToasts}
|
||||
/>
|
||||
<BookerStoreProvider>
|
||||
<EventType
|
||||
{...eventTypeQueryData}
|
||||
id={id}
|
||||
tabs={tabs}
|
||||
onSuccess={onSuccess}
|
||||
onError={onError}
|
||||
onDeleteSuccess={onDeleteSuccess}
|
||||
onDeleteError={onDeleteError}
|
||||
allowDelete={allowDelete}
|
||||
customClassNames={customClassNames}
|
||||
isDryRun={isDryRun}
|
||||
onFormStateChange={onFormStateChange}
|
||||
ref={ref}
|
||||
disableToasts={disableToasts}
|
||||
/>
|
||||
</BookerStoreProvider>
|
||||
);
|
||||
});
|
||||
@@ -41,3 +41,4 @@ export { useCreateTeamEventType } from "./hooks/event-types/private/useCreateTea
|
||||
|
||||
export { useOrganizationBookings } from "./hooks/organizations/bookings/useOrganizationBookings";
|
||||
export { useOrganizationUserBookings } from "./hooks/organizations/bookings/useOrganizationUserBookings";
|
||||
export { CalendarViewPlatformWrapper as CalendarView } from "./calendar-view/index";
|
||||
+1
-1
@@ -15,12 +15,12 @@ import { Dropdown, DropdownMenuContent, DropdownMenuTrigger } from "@calcom/ui/c
|
||||
import { Switch } from "@calcom/ui/components/form";
|
||||
import { List } from "@calcom/ui/components/list";
|
||||
|
||||
import * as Connect from "../../connect";
|
||||
import { AppleConnect } from "../../connect/apple/AppleConnect";
|
||||
import { useAddSelectedCalendar } from "../../hooks/calendars/useAddSelectedCalendar";
|
||||
import { useDeleteCalendarCredentials } from "../../hooks/calendars/useDeleteCalendarCredentials";
|
||||
import { useRemoveSelectedCalendar } from "../../hooks/calendars/useRemoveSelectedCalendar";
|
||||
import { useConnectedCalendars } from "../../hooks/useConnectedCalendars";
|
||||
import { Connect } from "../../index";
|
||||
import { AtomsWrapper } from "../../src/components/atoms-wrapper";
|
||||
import { useToast } from "../../src/components/ui/use-toast";
|
||||
import { SelectedCalendarsSettings } from "../SelectedCalendarsSettings";
|
||||
|
||||
@@ -3,6 +3,7 @@ import Link from "next/link";
|
||||
|
||||
const poppins = Poppins({ subsets: ["latin"], weight: ["400", "800"] });
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
export function Navbar({ username }: { username?: string }) {
|
||||
return (
|
||||
<nav className="flex h-[75px] w-[100%] items-center justify-between bg-black px-14 py-3 text-white">
|
||||
@@ -13,9 +14,11 @@ export function Navbar({ username }: { username?: string }) {
|
||||
</h1>
|
||||
</Link>
|
||||
</div>
|
||||
{username && <div className="capitalize">👤 {username}</div>}
|
||||
<div className={`${poppins.className}`}>
|
||||
<ul className="flex gap-x-7">
|
||||
<li>
|
||||
<Link href="/calendar-view">Week View</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link href="/calendars">Calendar</Link>
|
||||
</li>
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
import { Navbar } from "@/components/Navbar";
|
||||
import { Inter } from "next/font/google";
|
||||
|
||||
import { CalendarView } from "@calcom/atoms";
|
||||
|
||||
const inter = Inter({ subsets: ["latin"] });
|
||||
|
||||
export default function CalendarViewAtom(props: { calUsername: string; calEmail: string }) {
|
||||
return (
|
||||
<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" />
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user