diff --git a/apps/web/components/booking/actions/bookingActions.test.ts b/apps/web/components/booking/actions/bookingActions.test.ts index 9ec5a81ba3..1c8386d091 100644 --- a/apps/web/components/booking/actions/bookingActions.test.ts +++ b/apps/web/components/booking/actions/bookingActions.test.ts @@ -374,6 +374,87 @@ describe("Booking Actions", () => { expect(rescheduleAction?.disabled).toBe(true); expect(rescheduleRequestAction?.disabled).toBe(true); }); + + it("should disable change_location for past bookings", () => { + const context = createMockContext({ isBookingInPast: true }); + const actions = getEditEventActions(context); + + const changeLocationAction = actions.find((a) => a.id === "change_location"); + expect(changeLocationAction?.disabled).toBe(true); + }); + + it("should disable change_location for cancelled bookings", () => { + const context = createMockContext({ isCancelled: true }); + const actions = getEditEventActions(context); + + const changeLocationAction = actions.find((a) => a.id === "change_location"); + expect(changeLocationAction?.disabled).toBe(true); + }); + + it("should disable add_members for rejected bookings", () => { + const context = createMockContext({ isRejected: true }); + const actions = getEditEventActions(context); + + const addMembersAction = actions.find((a) => a.id === "add_members"); + expect(addMembersAction?.disabled).toBe(true); + }); + + it("should disable reroute for past bookings from routing form", () => { + const context = createMockContext({ + isBookingFromRoutingForm: true, + isBookingInPast: true, + }); + const actions = getEditEventActions(context); + + const rerouteAction = actions.find((a) => a.id === "reroute"); + expect(rerouteAction?.disabled).toBe(true); + }); + + it("should disable reassign for cancelled round robin bookings", () => { + const context = createMockContext({ + isCancelled: true, + booking: { + ...createMockContext().booking, + eventType: { + ...createMockContext().booking.eventType, + schedulingType: SchedulingType.ROUND_ROBIN, + hostGroups: [], + }, + }, + }); + const actions = getEditEventActions(context); + + const reassignAction = actions.find((a) => a.id === "reassign"); + expect(reassignAction?.disabled).toBe(true); + }); + + it("should enable edit actions for upcoming active bookings", () => { + const context = createMockContext({ + isBookingInPast: false, + isCancelled: false, + isRejected: false, + isBookingFromRoutingForm: true, + booking: { + ...createMockContext().booking, + eventType: { + ...createMockContext().booking.eventType, + schedulingType: SchedulingType.ROUND_ROBIN, + hostGroups: [], + }, + }, + }); + const actions = getEditEventActions(context); + + const changeLocationAction = actions.find((a) => a.id === "change_location"); + const addMembersAction = actions.find((a) => a.id === "add_members"); + const rerouteAction = actions.find((a) => a.id === "reroute"); + const reassignAction = actions.find((a) => a.id === "reassign"); + + expect(changeLocationAction?.disabled).toBe(false); + expect(addMembersAction?.disabled).toBe(false); + expect(rerouteAction?.disabled).toBe(false); + expect(reassignAction?.disabled).toBe(false); + }); }); describe("getAfterEventActions", () => { @@ -523,6 +604,134 @@ describe("Booking Actions", () => { expect(isActionDisabled("charge_card", context)).toBe(true); }); + + describe("change_location action", () => { + it("should be disabled for past bookings", () => { + const context = createMockContext({ isBookingInPast: true }); + expect(isActionDisabled("change_location", context)).toBe(true); + }); + + it("should be disabled for cancelled bookings", () => { + const context = createMockContext({ isCancelled: true }); + expect(isActionDisabled("change_location", context)).toBe(true); + }); + + it("should be disabled for rejected bookings", () => { + const context = createMockContext({ isRejected: true }); + expect(isActionDisabled("change_location", context)).toBe(true); + }); + + it("should be enabled for upcoming active bookings", () => { + const context = createMockContext({ + isBookingInPast: false, + isCancelled: false, + isRejected: false, + }); + expect(isActionDisabled("change_location", context)).toBe(false); + }); + }); + + describe("add_members action", () => { + it("should be disabled for past bookings", () => { + const context = createMockContext({ isBookingInPast: true }); + expect(isActionDisabled("add_members", context)).toBe(true); + }); + + it("should be disabled for cancelled bookings", () => { + const context = createMockContext({ isCancelled: true }); + expect(isActionDisabled("add_members", context)).toBe(true); + }); + + it("should be disabled for rejected bookings", () => { + const context = createMockContext({ isRejected: true }); + expect(isActionDisabled("add_members", context)).toBe(true); + }); + + it("should be enabled for upcoming active bookings", () => { + const context = createMockContext({ + isBookingInPast: false, + isCancelled: false, + isRejected: false, + }); + expect(isActionDisabled("add_members", context)).toBe(false); + }); + }); + + describe("reroute action", () => { + it("should be disabled for past bookings", () => { + const context = createMockContext({ isBookingInPast: true }); + expect(isActionDisabled("reroute", context)).toBe(true); + }); + + it("should be disabled for cancelled bookings", () => { + const context = createMockContext({ isCancelled: true }); + expect(isActionDisabled("reroute", context)).toBe(true); + }); + + it("should be disabled for rejected bookings", () => { + const context = createMockContext({ isRejected: true }); + expect(isActionDisabled("reroute", context)).toBe(true); + }); + + it("should be enabled for upcoming active bookings", () => { + const context = createMockContext({ + isBookingInPast: false, + isCancelled: false, + isRejected: false, + }); + expect(isActionDisabled("reroute", context)).toBe(false); + }); + }); + + describe("reassign action", () => { + it("should be disabled for past bookings", () => { + const context = createMockContext({ isBookingInPast: true }); + expect(isActionDisabled("reassign", context)).toBe(true); + }); + + it("should be disabled for cancelled bookings", () => { + const context = createMockContext({ isCancelled: true }); + expect(isActionDisabled("reassign", context)).toBe(true); + }); + + it("should be disabled for rejected bookings", () => { + const context = createMockContext({ isRejected: true }); + expect(isActionDisabled("reassign", context)).toBe(true); + }); + + it("should be enabled for upcoming active bookings", () => { + const context = createMockContext({ + isBookingInPast: false, + isCancelled: false, + isRejected: false, + }); + expect(isActionDisabled("reassign", context)).toBe(false); + }); + }); + + describe("cancel action with cancelled/rejected states", () => { + it("should be disabled for already cancelled bookings", () => { + const context = createMockContext({ isCancelled: true }); + expect(isActionDisabled("cancel", context)).toBe(true); + }); + + it("should be disabled for rejected bookings", () => { + const context = createMockContext({ isRejected: true }); + expect(isActionDisabled("cancel", context)).toBe(true); + }); + }); + + describe("reschedule_request action with cancelled/rejected states", () => { + it("should be disabled for cancelled bookings", () => { + const context = createMockContext({ isCancelled: true }); + expect(isActionDisabled("reschedule_request", context)).toBe(true); + }); + + it("should be disabled for rejected bookings", () => { + const context = createMockContext({ isRejected: true }); + expect(isActionDisabled("reschedule_request", context)).toBe(true); + }); + }); }); describe("getActionLabel", () => { diff --git a/apps/web/components/booking/actions/bookingActions.ts b/apps/web/components/booking/actions/bookingActions.ts index bd76e4c7e4..97544f63fd 100644 --- a/apps/web/components/booking/actions/bookingActions.ts +++ b/apps/web/components/booking/actions/bookingActions.ts @@ -148,14 +148,14 @@ export function getEditEventActions(context: BookingActionContext): ActionType[] id: "reroute", label: t("reroute"), icon: "waypoints", - disabled: false, + disabled: isActionDisabled("reroute", context), } : null, { id: "change_location", label: t("edit_location"), icon: "map-pin", - disabled: false, + disabled: isActionDisabled("change_location", context), }, booking.eventType?.disableGuests ? null @@ -163,14 +163,14 @@ export function getEditEventActions(context: BookingActionContext): ActionType[] id: "add_members", label: t("additional_guests"), icon: "user-plus", - disabled: false, + disabled: isActionDisabled("add_members", context), }, isReassignable ? { id: "reassign", label: t("reassign"), icon: "users", - disabled: false, + disabled: isActionDisabled("reassign", context), } : null, ]; @@ -237,7 +237,7 @@ export function shouldShowIndividualReportButton(context: BookingActionContext): } export function isActionDisabled(actionId: string, context: BookingActionContext): boolean { - const { booking, isBookingInPast, isDisabledRescheduling, isDisabledCancelling, isAttendee } = context; + const { booking, isBookingInPast, isDisabledRescheduling, isDisabledCancelling, isAttendee, isCancelled, isRejected } = context; switch (actionId) { case "reschedule": @@ -256,18 +256,25 @@ export function isActionDisabled(actionId: string, context: BookingActionContext booking.eventType.minimumRescheduleNotice ?? null ); return ( + isCancelled || + isRejected || (isBookingInPast && !booking.eventType.allowReschedulingPastBookings) || isDisabledRescheduling || isWithinMinimumNotice ); case "cancel": - return isDisabledCancelling || isBookingInPast; + return isDisabledCancelling || isBookingInPast || isCancelled || isRejected; case "view_recordings": return !(isBookingInPast && booking.status === BookingStatus.ACCEPTED && context.isCalVideoLocation); case "meeting_session_details": return !(isBookingInPast && booking.status === BookingStatus.ACCEPTED && context.isCalVideoLocation); case "charge_card": return context.cardCharged; + case "reroute": + case "reassign": + case "change_location": + case "add_members": + return isBookingInPast || isCancelled || isRejected; default: return false; }