From 307fd87664bd49d7894a92e2816ba33401eff19a Mon Sep 17 00:00:00 2001 From: Udit Takkar Date: Wed, 16 Jul 2025 02:36:01 +0530 Subject: [PATCH 01/13] refactor: Booking list items actions UI --- .../components/booking/BookingListItem.tsx | 205 ++++++++++++++++-- apps/web/public/static/locales/en/common.json | 3 +- 2 files changed, 185 insertions(+), 23 deletions(-) diff --git a/apps/web/components/booking/BookingListItem.tsx b/apps/web/components/booking/BookingListItem.tsx index 7f712dcfbd33cc..2bfa70c0679477 100644 --- a/apps/web/components/booking/BookingListItem.tsx +++ b/apps/web/components/booking/BookingListItem.tsx @@ -42,7 +42,6 @@ import { TextAreaField } from "@calcom/ui/components/form"; import { Icon } from "@calcom/ui/components/icon"; import { MeetingTimeInTimezones } from "@calcom/ui/components/popover"; import type { ActionType } from "@calcom/ui/components/table"; -import { TableActions } from "@calcom/ui/components/table"; import { showToast } from "@calcom/ui/components/toast"; import { Tooltip } from "@calcom/ui/components/tooltip"; @@ -491,6 +490,186 @@ function BookingListItem(booking: BookingItemProps) { ]; const showPendingPayment = paymentAppData.enabled && booking.payment.length && !booking.paid; + const Actions = () => { + // Build all possible actions, disabling as needed + const editEventActions: ActionType[] = [ + // Reschedule + { + id: "reschedule", + icon: "clock", + label: t("reschedule_booking"), + href: `/reschedule/${booking.uid}${ + booking.seatsReferences.length ? `?seatReferenceUid=${getSeatReferenceUid()}` : "" + }`, + disabled: isBookingInPast && !booking.eventType.allowReschedulingPastBookings, + }, + // Request reschedule + { + id: "reschedule_request", + icon: "send", + iconClassName: "rotate-45 w-[16px] -translate-x-0.5 ", + label: t("send_reschedule_request"), + onClick: () => setIsOpenRescheduleDialog(true), + disabled: isBookingInPast && !booking.eventType.allowReschedulingPastBookings, + }, + // Reroute (if team booking) + isBookingFromRoutingForm + ? { + id: "reroute", + label: t("reroute"), + onClick: () => setRerouteDialogIsOpen(true), + icon: "waypoints", + disabled: false, + } + : null, + // Edit location + { + id: "change_location", + label: t("edit_location"), + onClick: () => setIsOpenLocationDialog(true), + icon: "map-pin", + disabled: false, + }, + // Add guests + booking.eventType?.disableGuests + ? null + : { + id: "add_members", + label: t("additional_guests"), + onClick: () => setIsOpenAddGuestsDialog(true), + icon: "user-plus", + disabled: false, + }, + // Reassign (if round robin) + booking.eventType.schedulingType === SchedulingType.ROUND_ROBIN + ? { + id: "reassign", + label: t("reassign"), + onClick: () => setIsOpenReassignDialog(true), + icon: "users", + disabled: false, + } + : null, + ...(isPending ? pendingActions : []), + ].filter(Boolean) as ActionType[]; + + const cancelEventAction: ActionType = { + id: "cancel", + label: isTabRecurring && isRecurring ? t("cancel_all_remaining") : t("cancel_event"), + href: `/booking/${booking.uid}?cancel=true${ + isTabRecurring && isRecurring ? "&allRemainingBookings=true" : "" + }${booking.seatsReferences.length ? `&seatReferenceUid=${getSeatReferenceUid()}` : ""}`, + icon: "close", + color: "destructive", + disabled: isDisabledCancelling || (isBookingInPast && isPending && !isConfirmed), + }; + + // After event actions + const afterEventActions: ActionType[] = [ + { + id: "view_recordings", + label: t("view_recordings"), + onClick: () => setViewRecordingsDialogIsOpen(true), + icon: "video", + disabled: !(isBookingInPast && isConfirmed && isCalVideoLocation && booking.isRecorded), + }, + { + id: "meeting_session_details", + label: t("view_session_details"), + onClick: () => setMeetingSessionDetailsDialogIsOpen(true), + icon: "info", + disabled: !(isBookingInPast && isConfirmed && isCalVideoLocation), + }, + booking.status === "ACCEPTED" && booking.paid && booking.payment[0]?.paymentOption === "HOLD" + ? { + id: "charge_card", + label: cardCharged ? t("no_show_fee_charged") : t("collect_no_show_fee"), + onClick: () => setChargeCardDialogIsOpen(true), + icon: "credit-card", + disabled: cardCharged, + } + : null, + { + id: "no_show", + label: + attendeeList.length === 1 && attendeeList[0].noShow ? t("unmark_as_no_show") : t("mark_as_no_show"), + onClick: () => { + if (attendeeList.length === 1) { + const attendee = attendeeList[0]; + noShowMutation.mutate({ + bookingUid: booking.uid, + attendees: [{ email: attendee.email, noShow: !attendee.noShow }], + }); + return; + } + setIsNoShowDialogOpen(true); + }, + icon: attendeeList.length === 1 && attendeeList[0].noShow ? "eye" : "eye-off", + disabled: !(isBookingInPast || isOngoing), + }, + ].filter(Boolean) as ActionType[]; + + return ( + + +