"use client"; import { useReactTable, getCoreRowModel, getSortedRowModel } from "@tanstack/react-table"; import { useRouter } from "next/navigation"; import React, { useState, useMemo, useEffect, useCallback } from "react"; import dayjs from "@calcom/dayjs"; import { useDataTable, DataTableFilters, DataTableSegment, useDisplayedFilterCount, } from "@calcom/features/data-table"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import { trpc } from "@calcom/trpc/react"; import useMeQuery from "@calcom/trpc/react/hooks/useMeQuery"; import { Alert } from "@calcom/ui/components/alert"; import { Badge } from "@calcom/ui/components/badge"; import { Button } from "@calcom/ui/components/button"; import { ToggleGroup } from "@calcom/ui/components/form"; import { WipeMyCalActionButton } from "@calcom/web/components/apps/wipemycalother/wipeMyCalActionButton"; import { useBookingFilters } from "~/bookings/hooks/useBookingFilters"; import { useBookingListColumns } from "~/bookings/hooks/useBookingListColumns"; import { useBookingListData } from "~/bookings/hooks/useBookingListData"; import { useBookingStatusTab } from "~/bookings/hooks/useBookingStatusTab"; import { useFacetedUniqueValues } from "~/bookings/hooks/useFacetedUniqueValues"; import { useListAutoSelector } from "~/bookings/hooks/useListAutoSelector"; import { useListNavigationCapabilities } from "~/bookings/hooks/useListNavigationCapabilities"; import { BookingDetailsSheetStoreProvider, useBookingDetailsSheetStore, } from "../store/bookingDetailsSheetStore"; import type { RowData, BookingListingStatus, BookingsGetOutput } from "../types"; import { BookingDetailsSheet } from "./BookingDetailsSheet"; import { BookingList } from "./BookingList"; import { ViewToggleButton } from "./ViewToggleButton"; interface FilterButtonProps { table: ReturnType>; displayedFilterCount: number; setShowFilters: (value: boolean | ((prev: boolean) => boolean)) => void; } function FilterButton({ table, displayedFilterCount, setShowFilters }: FilterButtonProps) { const { t } = useLocale(); if (displayedFilterCount === 0) { return ; } return ( ); } interface BookingListContainerProps { status: BookingListingStatus; permissions: { canReadOthersBookings: boolean; }; bookingsV3Enabled: boolean; } interface BookingListInnerProps extends BookingListContainerProps { data?: BookingsGetOutput; isPending: boolean; hasError: boolean; errorMessage?: string; totalRowCount?: number; bookings: BookingsGetOutput["bookings"]; } function BookingListInner({ status, permissions, bookings, bookingsV3Enabled, data, isPending, hasError, errorMessage, totalRowCount, }: BookingListInnerProps) { const { t } = useLocale(); const user = useMeQuery().data; const setSelectedBookingUid = useBookingDetailsSheetStore((state) => state.setSelectedBookingUid); const router = useRouter(); const [showFilters, setShowFilters] = useState(true); // Handle auto-selection for list view useListAutoSelector(bookings); const ErrorView = errorMessage ? ( ) : undefined; const handleBookingClick = useCallback( (bookingUid: string) => { setSelectedBookingUid(bookingUid); }, [setSelectedBookingUid] ); const columns = useBookingListColumns({ user, status, canReadOthersBookings: permissions.canReadOthersBookings, bookingsV3Enabled, handleBookingClick, }); const finalData = useBookingListData({ data, status, userTimeZone: user?.timeZone }); const getFacetedUniqueValues = useFacetedUniqueValues(); const displayedFilterCount = useDisplayedFilterCount(); const { currentTab, tabOptions } = useBookingStatusTab(); useEffect(() => { if (displayedFilterCount === 0) { // reset to true, so it shows filters as soon as any filter is applied setShowFilters(true); } }, [displayedFilterCount]); const table = useReactTable({ data: finalData, columns, initialState: { columnVisibility: { eventTypeId: false, teamId: false, userId: false, attendeeName: false, attendeeEmail: false, dateRange: false, bookingUid: false, }, }, getCoreRowModel: getCoreRowModel(), getSortedRowModel: getSortedRowModel(), getFacetedUniqueValues, }); const isEmpty = !data?.bookings || data.bookings.length === 0; return ( <>
{/* Desktop: full width on first row, Mobile: full width on first row with horizontal scroll */}
{ if (!value) return; const selectedTab = tabOptions.find((tab) => tab.value === value); if (selectedTab?.href) { router.push(selectedTab.href); } }} options={tabOptions} />
{/* Desktop: second item on first row, Mobile: first item on second row */} {/* Desktop: auto-pushed to right via flex-grow spacer, Mobile: continue on second row */}
{bookingsV3Enabled && }
{displayedFilterCount > 0 && showFilters && (
{/* Desktop: auto-pushed to right via flex-grow spacer */}
)} {status === "upcoming" && !isEmpty && ( )}
{bookingsV3Enabled && ( )} ); } export function BookingListContainer(props: BookingListContainerProps) { const { limit, offset, setPageIndex } = useDataTable(); const { eventTypeIds, teamIds, userIds, dateRange, attendeeName, attendeeEmail, bookingUid } = useBookingFilters(); // Build query input once - shared between query and prefetching const queryInput = useMemo( () => ({ limit, offset, filters: { statuses: [props.status], eventTypeIds, teamIds, userIds, attendeeName, attendeeEmail, bookingUid, afterStartDate: dateRange?.startDate ? dayjs(dateRange?.startDate).startOf("day").toISOString() : undefined, beforeEndDate: dateRange?.endDate ? dayjs(dateRange?.endDate).endOf("day").toISOString() : undefined, }, }), [ limit, offset, props.status, eventTypeIds, teamIds, userIds, attendeeName, attendeeEmail, bookingUid, dateRange, ] ); const query = trpc.viewer.bookings.get.useQuery(queryInput, { staleTime: 5 * 60 * 1000, // 5 minutes - data is considered fresh gcTime: 30 * 60 * 1000, // 30 minutes - cache retention time }); const bookings = useMemo(() => query.data?.bookings ?? [], [query.data?.bookings]); // Always call the hook and provide navigation capabilities // The BookingDetailsSheet is only rendered when bookingsV3Enabled is true (see line 212) const capabilities = useListNavigationCapabilities({ limit, offset, totalCount: query.data?.totalCount, setPageIndex, queryInput, }); return ( ); }