import { useAutoAnimate } from "@formkit/auto-animate/react"; import type { GetStaticPaths, GetStaticProps } from "next"; import { useRouter } from "next/router"; import { Fragment } from "react"; import { z } from "zod"; import { WipeMyCalActionButton } from "@calcom/app-store/wipemycalother/components"; import BookingLayout from "@calcom/features/bookings/layout/BookingLayout"; import type { filterQuerySchema } from "@calcom/features/bookings/lib/useFilterQuery"; import { useFilterQuery } from "@calcom/features/bookings/lib/useFilterQuery"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import type { RouterOutputs } from "@calcom/trpc/react"; import { trpc } from "@calcom/trpc/react"; import { Alert, Button, EmptyScreen } from "@calcom/ui"; import { Calendar } from "@calcom/ui/components/icon"; import { useInViewObserver } from "@lib/hooks/useInViewObserver"; import BookingListItem from "@components/booking/BookingListItem"; import SkeletonLoader from "@components/booking/SkeletonLoader"; import { ssgInit } from "@server/lib/ssg"; type BookingListingStatus = z.infer["status"]; type BookingOutput = RouterOutputs["viewer"]["bookings"]["get"]["bookings"][0]; type RecurringInfo = { recurringEventId: string | null; count: number; firstDate: Date | null; bookings: { [key: string]: Date[] }; }; const validStatuses = ["upcoming", "recurring", "past", "cancelled", "unconfirmed"] as const; const descriptionByStatus: Record = { upcoming: "upcoming_bookings", recurring: "recurring_bookings", past: "past_bookings", cancelled: "cancelled_bookings", unconfirmed: "unconfirmed_bookings", }; const querySchema = z.object({ status: z.enum(validStatuses), }); export default function Bookings() { const { data: filterQuery } = useFilterQuery(); const router = useRouter(); const { status } = router.isReady ? querySchema.parse(router.query) : { status: "upcoming" as const }; const { t } = useLocale(); const query = trpc.viewer.bookings.get.useInfiniteQuery( { limit: 10, filters: { ...filterQuery, status: filterQuery.status ?? status, }, }, { // first render has status `undefined` enabled: router.isReady, getNextPageParam: (lastPage) => lastPage.nextCursor, } ); // Animate page (tab) tranistions to look smoothing const buttonInView = useInViewObserver(() => { if (!query.isFetching && query.hasNextPage && query.status === "success") { query.fetchNextPage(); } }); const isEmpty = !query.data?.pages[0]?.bookings.length; const shownBookings: Record = {}; const filterBookings = (booking: BookingOutput) => { if (status === "recurring" || status == "unconfirmed" || status === "cancelled") { if (!booking.recurringEventId) { return true; } if ( shownBookings[booking.recurringEventId] !== undefined && shownBookings[booking.recurringEventId].length > 0 ) { shownBookings[booking.recurringEventId].push(booking); return false; } shownBookings[booking.recurringEventId] = [booking]; } else if (status === "upcoming") { return new Date(booking.startTime).toDateString() !== new Date().toDateString(); } return true; }; let recurringInfoToday: RecurringInfo | undefined; const bookingsToday = query.data?.pages.map((page) => page.bookings.filter((booking: BookingOutput) => { recurringInfoToday = page.recurringInfo.find( (info) => info.recurringEventId === booking.recurringEventId ); return new Date(booking.startTime).toDateString() === new Date().toDateString(); }) )[0] || []; const [animationParentRef] = useAutoAnimate(); return (
{query.status === "error" && ( )} {(query.status === "loading" || query.isPaused) && } {query.status === "success" && !isEmpty && ( <> {!!bookingsToday.length && status === "upcoming" && (

{t("today")}

{bookingsToday.map((booking: BookingOutput) => ( ))}
)}
{query.data.pages.map((page, index) => ( {page.bookings.filter(filterBookings).map((booking: BookingOutput) => { const recurringInfo = page.recurringInfo.find( (info) => info.recurringEventId === booking.recurringEventId ); return ( ); })} ))}
)} {query.status === "success" && isEmpty && (
)}
); } export const getStaticProps: GetStaticProps = async (ctx) => { const params = querySchema.safeParse(ctx.params); const ssg = await ssgInit(ctx); if (!params.success) return { notFound: true }; return { props: { status: params.data.status, trpcState: ssg.dehydrate(), }, }; }; export const getStaticPaths: GetStaticPaths = () => { return { paths: validStatuses.map((status) => ({ params: { status }, locale: "en", })), fallback: "blocking", }; };