* fix: deep link reschedule audit log to booking drawer history tab Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com> * fix: make booking drawer tab-agnostic for cross-tab deep links Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com> * fix: use status-agnostic /bookings URL for audit log deep links - Update audit service URLs from /bookings/upcoming?uid=... to /bookings?uid=... - Add /bookings/page.tsx redirect that routes to /bookings/upcoming preserving query params - Update tests to expect new URL format Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com> * fixes * refactor: use client-side replaceState instead of server redirect for booking deep links Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com> * fix: use router.replace instead of replaceState to update tab and booking list Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com> * improvements * refactor: extract deep link logic from BookingListContainer into usePreSelectedBooking hook Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com> * fix: use proper BookingOutput status type in test helper Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com> * fix: decouple getTabForBooking from BookingOutput type for simpler testing Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com> * fix: accept Date | string for endTime in BookingForTabResolution interface Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com> * refactor: eliminate initialBookingUid prop drilling and revert formatting-only changes Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com> * fix: use AuditDeepLink wrapper to preserve target=_blank through ServerTrans cloneElement Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com> * Improve code organization * refactor: rename usePreSelectedBooking to useSwitchToCorrectStatusTab Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com> * fix: push preSelectedBooking into store so drawer opens on direct navigation Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com> * fixes * fixes * fixes * fix --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com>
104 lines
3.3 KiB
TypeScript
104 lines
3.3 KiB
TypeScript
import { trpc } from "@calcom/trpc/react";
|
|
import { usePathname, useRouter, useSearchParams } from "next/navigation";
|
|
import { useEffect, useState, useTransition } from "react";
|
|
import { validStatuses } from "../lib/validStatuses";
|
|
import type { BookingListingStatus, BookingOutput } from "../types";
|
|
|
|
interface BookingForTabResolution {
|
|
status: string;
|
|
endTime: Date | string;
|
|
recurringEventId: string | null;
|
|
}
|
|
|
|
interface UseSwitchToCorrectTabResult {
|
|
resolvedTabStatus: BookingListingStatus;
|
|
isResolvingTabStatus: boolean;
|
|
preSelectedBooking: BookingOutput | null;
|
|
}
|
|
|
|
export function getTabForBooking(booking: BookingForTabResolution): BookingListingStatus {
|
|
const isPast = new Date(booking.endTime) <= new Date();
|
|
|
|
if (booking.status === "CANCELLED" || booking.status === "REJECTED") {
|
|
return "cancelled";
|
|
}
|
|
if (booking.status === "PENDING" && !isPast) {
|
|
return "unconfirmed";
|
|
}
|
|
if (isPast) {
|
|
return "past";
|
|
}
|
|
if (booking.recurringEventId) {
|
|
return "recurring";
|
|
}
|
|
return "upcoming";
|
|
}
|
|
|
|
/**
|
|
* Ensures that the correct status tab is selected based on the pre-selected booking through query params
|
|
*/
|
|
export function useSwitchToCorrectStatusTab({
|
|
defaultStatus,
|
|
}: {
|
|
defaultStatus: BookingListingStatus;
|
|
}): UseSwitchToCorrectTabResult {
|
|
const {
|
|
preSelectedBookingUid,
|
|
preSelectedBooking,
|
|
preSelectedBookingFull,
|
|
isPending: isFetchingPreSelectedBooking,
|
|
} = usePreSelectedBooking();
|
|
const pathname = usePathname();
|
|
const router = useRouter();
|
|
const [resolvedTabStatus, setResolvedTab] = useState<BookingListingStatus>(defaultStatus);
|
|
const [isNavigatingToCorrectTab, startNavigationToCorrectTab] = useTransition();
|
|
|
|
useEffect(() => {
|
|
if (!preSelectedBooking) return;
|
|
const correctTab = getTabForBooking(preSelectedBooking);
|
|
const currentTab = pathname?.match(/\/bookings\/(\w+)/)?.[1];
|
|
const shouldNavigate = correctTab && currentTab && correctTab !== currentTab;
|
|
if (!shouldNavigate) return;
|
|
startNavigationToCorrectTab(() => {
|
|
const newPath = pathname.replace(`/bookings/${currentTab}`, `/bookings/${correctTab}`);
|
|
router.replace(`${newPath}${window.location.search}`);
|
|
});
|
|
setResolvedTab(correctTab);
|
|
}, [preSelectedBooking, pathname, router]);
|
|
|
|
const isResolvingTabStatus = preSelectedBookingUid
|
|
? isFetchingPreSelectedBooking || isNavigatingToCorrectTab
|
|
: false;
|
|
|
|
return { resolvedTabStatus, isResolvingTabStatus, preSelectedBooking: preSelectedBookingFull };
|
|
}
|
|
|
|
export function usePreSelectedBooking(): {
|
|
preSelectedBookingUid: string | undefined;
|
|
preSelectedBooking: BookingForTabResolution | null;
|
|
preSelectedBookingFull: BookingOutput | null;
|
|
isPending: boolean;
|
|
}{
|
|
const searchParams = useSearchParams();
|
|
const preSelectedBookingUid = searchParams?.get("uid") ?? undefined;
|
|
|
|
const { data: preSelectedBookingData, isPending } = trpc.viewer.bookings.get.useQuery(
|
|
{
|
|
limit: 1,
|
|
offset: 0,
|
|
filters: {
|
|
bookingUid: preSelectedBookingUid,
|
|
statuses: [...validStatuses],
|
|
},
|
|
},
|
|
{
|
|
enabled: !!preSelectedBookingUid,
|
|
staleTime: 5 * 60 * 1000,
|
|
}
|
|
);
|
|
|
|
const preSelectedBookingFull = preSelectedBookingData?.bookings?.[0] ?? null;
|
|
const preSelectedBooking: BookingForTabResolution | null = preSelectedBookingFull;
|
|
return { preSelectedBookingUid, preSelectedBooking, preSelectedBookingFull, isPending };
|
|
}
|