Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
6b0cbc8
fix: Hosts not able to cancel/reschedule the event when Disable Resch…
asadath1395 Jul 7, 2025
fe5cc0b
Fix logic bypass
asadath1395 Jul 7, 2025
cb4fb4c
Fix cubic comment
asadath1395 Jul 7, 2025
9fad7a2
Merge branch 'main' of https://github.com/calcom/cal.com into fix/dis…
asadath1395 Jul 8, 2025
f4adb8d
Merge branch 'main' into fix/disable-rescheduling-cancelling
anikdhabal Jul 8, 2025
8edc297
Fix team/org admins/owners mot able to reschedule/cancel when they ar…
asadath1395 Jul 9, 2025
8542dca
Merge branch 'fix/disable-rescheduling-cancelling' of https://github.…
asadath1395 Jul 9, 2025
2e8b63b
Fix type error
asadath1395 Jul 9, 2025
a88aa94
Merge branch 'main' of https://github.com/calcom/cal.com into fix/dis…
asadath1395 Jul 9, 2025
15a5262
Improve imports and function params typing
asadath1395 Jul 9, 2025
b134b7a
Remove duplicate permission check, fix auth logic to check whether us…
asadath1395 Jul 9, 2025
bab5c12
Remove duplicate permission check
asadath1395 Jul 9, 2025
4ae3d00
Extract checkTeamOrOrgPermissions into a separate function
asadath1395 Jul 9, 2025
b0405f3
Fix reschedule/cancel not shown in bookings page for team/org owner/h…
asadath1395 Jul 10, 2025
1d2888f
Merge branch 'main' into fix/disable-rescheduling-cancelling
kart1ka Aug 5, 2025
63f7b5f
Merge branch 'main' into fix/disable-rescheduling-cancelling
kart1ka Aug 6, 2025
eace6c2
Improve user host logic check
asadath1395 Aug 18, 2025
5f9fa38
Merge branch 'fix/disable-rescheduling-cancelling' of https://github.…
asadath1395 Aug 18, 2025
505726f
Merge branch 'main' of https://github.com/calcom/cal.com into fix/dis…
asadath1395 Aug 18, 2025
7e84114
Fix type error
asadath1395 Aug 18, 2025
054eba3
Merge branch 'main' into fix/disable-rescheduling-cancelling
asadath1395 Aug 18, 2025
7d20cff
Merge branch 'main' of https://github.com/calcom/cal.com into fix/dis…
asadath1395 Aug 19, 2025
32a7faf
Fix non-logged in users able to reschedule and extended item showing …
asadath1395 Aug 19, 2025
0026554
Merge branch 'main' of https://github.com/calcom/cal.com into fix/dis…
asadath1395 Aug 19, 2025
e89107f
Merge branch 'main' into fix/disable-rescheduling-cancelling
kart1ka Aug 22, 2025
bab71ec
Merge branch 'main' into fix/disable-rescheduling-cancelling
kart1ka Aug 22, 2025
25875b6
Merge branch 'main' of https://github.com/calcom/cal.com into fix/dis…
asadath1395 Aug 29, 2025
85ca61a
Merge branch 'main' of https://github.com/calcom/cal.com into fix/dis…
asadath1395 Sep 4, 2025
02ebfd8
Simplify team or org permission check
asadath1395 Sep 4, 2025
610e162
Fix userId undefined
asadath1395 Sep 4, 2025
02fb9da
Fix type error
asadath1395 Sep 4, 2025
6077874
Merge branch 'main' of https://github.com/calcom/cal.com into fix/dis…
asadath1395 Sep 4, 2025
0b26e3a
Fix org owner/admin not able to reschedule/cancel a booking when both…
asadath1395 Sep 4, 2025
98594cb
Merge branch 'main' of https://github.com/calcom/cal.com into fix/dis…
asadath1395 Sep 15, 2025
166a5f7
Add org tests to validate owner/admin able to reschedule/cancel indiv…
asadath1395 Sep 15, 2025
5f89e3d
Merge branch 'main' into fix/disable-rescheduling-cancelling
asadath1395 Sep 15, 2025
c163681
Fix type in tests
asadath1395 Sep 15, 2025
41c26b4
Merge branch 'fix/disable-rescheduling-cancelling' of https://github.…
asadath1395 Sep 15, 2025
b81a194
Merge branch 'main' into fix/disable-rescheduling-cancelling
asadath1395 Sep 17, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions apps/web/app/api/cancel/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ async function handler(req: NextRequest) {
const result = await handleCancelBooking({
bookingData,
userId: session?.user?.id || -1,
userOrgRole: session?.user?.org?.role,
});

const statusCode = result.success ? 200 : 400;
Expand Down
14 changes: 13 additions & 1 deletion apps/web/components/booking/BookingListItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,14 @@ type BookingListingStatus = RouterInputs["viewer"]["bookings"]["get"]["filters"]

type BookingItem = RouterOutputs["viewer"]["bookings"]["get"]["bookings"][number];

export type BookingItemProps = BookingItem & {
type EnrichedBookingItem = BookingItem & {
eventType: BookingItem["eventType"] & {
isUserHostOrOwner?: boolean;
hasTeamOrOrgPermissions?: boolean;
};
};

export type BookingItemProps = EnrichedBookingItem & {
listingStatus: BookingListingStatus;
recurringInfo: RouterOutputs["viewer"]["bookings"]["get"]["recurringInfo"][number] | undefined;
loggedInUser: {
Expand Down Expand Up @@ -205,6 +212,9 @@ function BookingListItem(booking: BookingItemProps) {
const isDisabledCancelling = booking.eventType.disableCancelling;
const isDisabledRescheduling = booking.eventType.disableRescheduling;

const isHostOrOwner = booking.eventType?.isUserHostOrOwner || false;
const hasTeamOrOrgPermissions = booking.eventType?.hasTeamOrOrgPermissions || false;

const bookingConfirm = async (confirm: boolean) => {
let body = {
bookingId: booking.id,
Expand Down Expand Up @@ -250,6 +260,8 @@ function BookingListItem(booking: BookingItemProps) {
(typeof booking.location === "string" && booking.location.trim() === ""),
showPendingPayment: paymentAppData.enabled && booking.payment.length && !booking.paid,
cardCharged,
isHostOrOwner,
hasTeamOrOrgPermissions,
attendeeList,
getSeatReferenceUid,
t,
Expand Down
17 changes: 12 additions & 5 deletions apps/web/components/booking/bookingActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ export interface BookingActionContext {
isCalVideoLocation: boolean;
showPendingPayment: boolean;
cardCharged: boolean;
isHostOrOwner: boolean;
hasTeamOrOrgPermissions: boolean;
attendeeList: Array<{
Comment on lines +25 to 27
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

New context flags are appropriate; confirm all call sites provide them

isHostOrOwner and hasTeamOrOrgPermissions are required booleans. Ensure all callers constructing BookingActionContext set these fields; otherwise, TS will flag.

Use:


🏁 Script executed:

#!/bin/bash
# Find places we construct or pass BookingActionContext-like objects
rg -n -C2 --type=ts --type=tsx 'get(Edit|After|Pending|Cancel)EventAction[s]?\('
rg -n -C3 --type=ts --type=tsx 'as\s+BookingActionContext|<BookingActionContext>|:\s*BookingActionContext'

Length of output: 247


🏁 Script executed:

#!/bin/bash
set -e

# Locate the bookingActions.ts file
echo "Locating bookingActions.ts..."
file=$(fd bookingActions.ts)
echo "Found file: $file"

# Show the interface BookingActionContext definition
echo -e "\n=== Interface BookingActionContext ==="
sed -n '1,200p' "$file" | sed -n -n '/interface BookingActionContext {/,/}/p'

# List exported get*EventAction functions in that file
echo -e "\n=== Exported get*EventAction functions ==="
rg -n 'export function get.*EventAction' "$file"

# Find all call sites of those functions across the repo
echo -e "\n=== Call sites for get*EventAction functions ==="
rg -n 'get.*EventAction' -g '*.ts' -g '*.tsx'

# Find every reference to the BookingActionContext type in the repo
echo -e "\n=== References to BookingActionContext type ==="
rg -n 'BookingActionContext' -g '*.ts' -g '*.tsx'

Length of output: 7118


🏁 Script executed:

#!/bin/bash
set -e

# Show the context object in BookingListItem.tsx
echo -e "\n=== BookingListItem.tsx context (lines 230-290) ==="
sed -n '230,290p' apps/web/components/booking/BookingListItem.tsx

# Show createMockContext implementation in bookingActions.test.ts
echo -e "\n=== bookingActions.test.ts createMockContext (lines 1-120) ==="
sed -n '1,120p' apps/web/components/booking/bookingActions.test.ts

Length of output: 5301


🏁 Script executed:

#!/bin/bash
set -e

echo -e "\n=== bookingActions.test.ts createMockContext continuation (lines 120-180) ==="
sed -n '120,180p' apps/web/components/booking/bookingActions.test.ts

Length of output: 1966


Add missing context flags in tests

The new flags isHostOrOwner and hasTeamOrOrgPermissions are required on every BookingActionContext. Verification shows:

  • In BookingListItem.tsx, both flags (and attendeeList) are provided.
  • In bookingActions.test.ts, createMockContext omits those fields and uses a type assertion—so at runtime context.isHostOrOwner and context.hasTeamOrOrgPermissions will be undefined.

Please update createMockContext in apps/web/components/booking/bookingActions.test.ts to include default values for these flags, for example:

 function createMockContext(overrides: Partial<BookingActionContext> = {}): BookingActionContext {
   // …existing properties…
   cardCharged: false,
   attendeeList: [ /*…*/ ],
   getSeatReferenceUid: () => undefined,
   t: mockT,
+  isHostOrOwner: false,
+  hasTeamOrOrgPermissions: false,
   ...overrides,
 } as BookingActionContext;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
isHostOrOwner: boolean;
hasTeamOrOrgPermissions: boolean;
attendeeList: Array<{
function createMockContext(overrides: Partial<BookingActionContext> = {}): BookingActionContext {
// …existing properties…
cardCharged: false,
attendeeList: [ /*…*/ ],
getSeatReferenceUid: () => undefined,
t: mockT,
isHostOrOwner: false,
hasTeamOrOrgPermissions: false,
...overrides,
} as BookingActionContext;
🤖 Prompt for AI Agents
In apps/web/components/booking/bookingActions.test.ts update the
createMockContext used in tests to include the missing BookingActionContext
flags so they aren't undefined at runtime: add isHostOrOwner: false and
hasTeamOrOrgPermissions: false (or appropriate defaults) to the returned mock
context object (and keep attendeeList as present), replacing the current type
assertion workaround.

name: string;
email: string;
Expand Down Expand Up @@ -101,6 +103,8 @@ export function getEditEventActions(context: BookingActionContext): ActionType[]
isBookingInPast,
isDisabledRescheduling,
isBookingFromRoutingForm,
isHostOrOwner,
hasTeamOrOrgPermissions,
getSeatReferenceUid,
t,
} = context;
Expand All @@ -114,15 +118,17 @@ export function getEditEventActions(context: BookingActionContext): ActionType[]
booking.seatsReferences.length ? `?seatReferenceUid=${getSeatReferenceUid()}` : ""
}`,
disabled:
(isBookingInPast && !booking.eventType.allowReschedulingPastBookings) || isDisabledRescheduling,
(isBookingInPast && !booking.eventType.allowReschedulingPastBookings) ||
(isDisabledRescheduling && !isHostOrOwner && !hasTeamOrOrgPermissions),
},
{
id: "reschedule_request",
icon: "send",
iconClassName: "rotate-45 w-[16px] -translate-x-0.5 ",
label: t("send_reschedule_request"),
disabled:
(isBookingInPast && !booking.eventType.allowReschedulingPastBookings) || isDisabledRescheduling,
(isBookingInPast && !booking.eventType.allowReschedulingPastBookings) ||
(isDisabledRescheduling && !isHostOrOwner && !hasTeamOrOrgPermissions),
},
isBookingFromRoutingForm
? {
Expand Down Expand Up @@ -202,15 +208,16 @@ export function shouldShowRecurringCancelAction(context: BookingActionContext):
}

export function isActionDisabled(actionId: string, context: BookingActionContext): boolean {
const { booking, isBookingInPast, isDisabledRescheduling, isDisabledCancelling, isPending, isConfirmed } =
const { booking, isBookingInPast, isDisabledRescheduling, isDisabledCancelling, isPending, isConfirmed, isHostOrOwner, hasTeamOrOrgPermissions } =
context;

switch (actionId) {
case "reschedule":
case "reschedule_request":
return (isBookingInPast && !booking.eventType.allowReschedulingPastBookings) || isDisabledRescheduling;
return (isBookingInPast && !booking.eventType.allowReschedulingPastBookings) ||
(isDisabledRescheduling && !isHostOrOwner && !hasTeamOrOrgPermissions);
case "cancel":
return isDisabledCancelling || isBookingInPast;
return (isDisabledCancelling && !isHostOrOwner && !hasTeamOrOrgPermissions) || isBookingInPast;
case "view_recordings":
return !(isBookingInPast && booking.status === BookingStatus.ACCEPTED && context.isCalVideoLocation);
case "meeting_session_details":
Expand Down
1 change: 1 addition & 0 deletions apps/web/lib/booking.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ export const getEventTypesFromDB = async (id: number) => {
slug: true,
name: true,
hideBranding: true,
parentId: true,
parent: {
select: {
hideBranding: true,
Expand Down
48 changes: 43 additions & 5 deletions apps/web/lib/reschedule/[uid]/getServerSideProps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,13 @@ import type { GetServerSidePropsContext } from "next";
import { URLSearchParams } from "url";
import { z } from "zod";

import { checkAdminOrOwner } from "@calcom/features/auth/lib/checkAdminOrOwner";
import { getServerSession } from "@calcom/features/auth/lib/getServerSession";
import { getFullName } from "@calcom/features/form-builder/utils";
import { buildEventUrlFromBooking } from "@calcom/lib/bookings/buildEventUrlFromBooking";
import { getDefaultEvent } from "@calcom/lib/defaultEvents";
import { checkIfUserIsHost } from "@calcom/lib/event-types/utils/checkIfUserIsHost";
import { isTeamAdmin } from "@calcom/lib/server/queries/teams";
import { getSafe } from "@calcom/lib/getSafe";
import { maybeGetBookingUidFromSeat } from "@calcom/lib/server/maybeGetBookingUidFromSeat";
import { UserRepository } from "@calcom/lib/server/repository/user";
Expand Down Expand Up @@ -55,6 +58,8 @@ export async function getServerSideProps(context: GetServerSidePropsContext) {
users: {
select: {
username: true,
email: true,
id: true,
},
},
slug: true,
Expand All @@ -63,6 +68,7 @@ export async function getServerSideProps(context: GetServerSidePropsContext) {
allowReschedulingCancelledBookings: true,
team: {
select: {
id: true,
parentId: true,
slug: true,
},
Expand All @@ -79,6 +85,7 @@ export async function getServerSideProps(context: GetServerSidePropsContext) {
user: {
select: {
id: true,
email: true,
},
},
},
Expand Down Expand Up @@ -122,7 +129,28 @@ export async function getServerSideProps(context: GetServerSidePropsContext) {
const isNonRescheduleableBooking =
booking.status === BookingStatus.CANCELLED || booking.status === BookingStatus.REJECTED;

if (isDisabledRescheduling) {
// Check if user is a host or owner of the event type
const userId = session?.user?.id;
const userIsHost = userId
? checkIfUserIsHost(
userId,
{
user: booking.user,
attendees: booking.attendees,
},
{
users: booking.eventType?.users,
hosts: booking.eventType?.hosts,
}
)
: false;
const userIsOwnerOfEventType = userId !== undefined && booking?.eventType?.owner?.id === userId;

const hasTeamOrOrgPermissions = userId !== undefined ? !!(await isTeamAdmin(userId, booking?.eventType?.team?.id ?? 0)) : false;
const isOrgAdminOrOwner = checkAdminOrOwner(session?.user?.org?.role);

const isHostOrOwner = !!userIsHost || !!userIsOwnerOfEventType || !!hasTeamOrOrgPermissions || isOrgAdminOrOwner;
if (isDisabledRescheduling && !isHostOrOwner) {
return {
redirect: {
destination: `/booking/${uid}`,
Expand Down Expand Up @@ -182,13 +210,23 @@ export async function getServerSideProps(context: GetServerSidePropsContext) {
},
};
}
const userIsHost = booking?.eventType.hosts.find((host) => {
if (host.user.id === userId) return true;
});
const userIsHost = userId
? checkIfUserIsHost(
userId,
{
user: booking.user,
attendees: booking.attendees,
},
{
users: booking.eventType?.users,
hosts: booking.eventType?.hosts,
}
)
: false;

const userIsOwnerOfEventType = booking?.eventType.owner?.id === userId;

if (!userIsHost && !userIsOwnerOfEventType) {
if (!userIsHost && !userIsOwnerOfEventType && !hasTeamOrOrgPermissions && !isOrgAdminOrOwner) {
return {
notFound: true,
} as {
Expand Down
32 changes: 31 additions & 1 deletion apps/web/lib/team/[slug]/[type]/getServerSideProps.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
import type { GetServerSidePropsContext } from "next";
import { z } from "zod";

import { checkAdminOrOwner } from "@calcom/features/auth/lib/checkAdminOrOwner";
import { getServerSession } from "@calcom/features/auth/lib/getServerSession";
import type { GetBookingType } from "@calcom/features/bookings/lib/get-booking";
import { getBookingForReschedule } from "@calcom/features/bookings/lib/get-booking";
import { getSlugOrRequestedSlug, orgDomainConfig } from "@calcom/features/ee/organizations/lib/orgDomains";
import { getOrganizationSEOSettings } from "@calcom/features/ee/organizations/lib/orgSettings";
import { FeaturesRepository } from "@calcom/features/flags/features.repository";
import { getPlaceholderAvatar } from "@calcom/lib/defaultAvatarImage";
import { isTeamAdmin } from "@calcom/lib/server/queries/teams";
import { shouldHideBrandingForTeamEvent } from "@calcom/lib/hideBranding";
import { BookingRepository } from "@calcom/lib/server/repository/booking";
import slugify from "@calcom/lib/slugify";
import { prisma } from "@calcom/prisma";
import type { User } from "@calcom/prisma/client";
Expand Down Expand Up @@ -58,7 +61,23 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
return { notFound: true } as const;
}

if (rescheduleUid && eventData.disableRescheduling) {
const userId = session?.user?.id;
const hasTeamOrOrgPermissions = userId !== undefined ? !!(await isTeamAdmin(userId, eventData.team?.id ?? 0)) : false;
const isOrgAdminOrOwner = checkAdminOrOwner(session?.user?.org?.role);

let isHostOrOwner = (eventData.owner?.id && eventData.owner?.id === userId) || hasTeamOrOrgPermissions || isOrgAdminOrOwner;

// We will only check the database if the user is not the owner or has team or org permissions
if (userId && rescheduleUid && !isHostOrOwner) {
const bookingRepo = new BookingRepository(prisma);
const userIsHost = await bookingRepo.checkIfUserIsHost({
userId,
bookingId: `${rescheduleUid}`,
});
isHostOrOwner = !!userIsHost;
}

if (rescheduleUid && eventData.disableRescheduling && !isHostOrOwner) {
return { redirect: { destination: `/booking/${rescheduleUid}`, permanent: false } };
}

Expand Down Expand Up @@ -242,6 +261,17 @@ const getTeamWithEventsData = async (
},
},
},
team: {
select: {
id: true,
parentId: true,
},
},
owner: {
select: {
id: true,
},
},
},
},
isOrganization: true,
Expand Down
1 change: 1 addition & 0 deletions apps/web/modules/bookings/views/bookings-listing-view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@ function BookingsContent({ status }: BookingsProps) {
? dayjs(dateRange?.startDate).startOf("day").toISOString()
: undefined,
beforeEndDate: dateRange?.endDate ? dayjs(dateRange?.endDate).endOf("day").toISOString() : undefined,
includeHostAndTeamPermissions: true,
},
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { orgDomainConfig } from "@calcom/ee/organizations/lib/orgDomains";
import { getServerSession } from "@calcom/features/auth/lib/getServerSession";
import getBookingInfo from "@calcom/features/bookings/lib/getBookingInfo";
import { getDefaultEvent } from "@calcom/lib/defaultEvents";
import { isTeamAdmin } from "@calcom/lib/server/queries/teams";
import { shouldHideBrandingForEvent } from "@calcom/lib/hideBranding";
import { parseRecurringEvent } from "@calcom/lib/isRecurringEvent";
import { markdownToSafeHTML } from "@calcom/lib/markdownToSafeHTML";
Expand All @@ -15,6 +16,7 @@ import { BookingRepository } from "@calcom/lib/server/repository/booking";
import prisma from "@calcom/prisma";
import { customInputSchema } from "@calcom/prisma/zod-utils";
import { meRouter } from "@calcom/trpc/server/routers/viewer/me/_router";
import { checkAdminOrOwner } from "@calcom/features/auth/lib/checkAdminOrOwner";

import type { inferSSRProps } from "@lib/types/inferSSRProps";

Expand Down Expand Up @@ -175,6 +177,8 @@ export async function getServerSideProps(context: GetServerSidePropsContext) {
};

const isLoggedInUserHost = checkIfUserIsHost(userId);
const isOrgAdminOrOwner = checkAdminOrOwner(session?.user?.org?.role);
const hasTeamOrOrgPermissions = userId !== undefined ? !!(await isTeamAdmin(userId, eventType.team?.id ?? 0)) : false;

if (bookingInfo !== null && eventType.seatsPerTimeSlot) {
await handleSeatsEventTypeOnBooking(eventType, bookingInfo, seatReferenceUid, isLoggedInUserHost);
Expand Down Expand Up @@ -256,6 +260,7 @@ export async function getServerSideProps(context: GetServerSidePropsContext) {
requiresLoginToUpdate,
rescheduledToUid,
isLoggedInUserHost,
hasTeamOrOrgPermissions: hasTeamOrOrgPermissions || isOrgAdminOrOwner,
internalNotePresets: internalNotes,
},
};
Expand Down
11 changes: 7 additions & 4 deletions apps/web/modules/bookings/views/bookings-single-view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ export default function Success(props: PageProps) {
);
const { data: session } = useSession();
const isHost = props.isLoggedInUserHost;
const isAdminOrOwner = props.hasTeamOrOrgPermissions;

Comment on lines +156 to 157
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Include event-type owner in isAdminOrOwner (currently only checks team/org admin)

isAdminOrOwner is derived solely from hasTeamOrOrgPermissions, so event-type owners who are not hosts or admins won’t see cancel/reschedule actions. Align this with server-side gates by folding owner check into the flag.

Apply this diff to cover owners without relying on the later userIsOwner declaration:

-  const isAdminOrOwner = props.hasTeamOrOrgPermissions;
+  const isAdminOrOwner =
+    props.hasTeamOrOrgPermissions || (!!session?.user?.id && eventType.owner?.id === session.user.id);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const isAdminOrOwner = props.hasTeamOrOrgPermissions;
const isAdminOrOwner =
props.hasTeamOrOrgPermissions || (
!!session?.user?.id &&
eventType.owner?.id === session.user.id
);
🤖 Prompt for AI Agents
In apps/web/modules/bookings/views/bookings-single-view.tsx around lines 154 to
155, isAdminOrOwner is currently set only from props.hasTeamOrOrgPermissions
which excludes event-type owners; change the assignment to include the owner
check so the flag reads true for either team/org perms OR event owner. Update
the expression to fold the existing user-owner check (e.g. props.userIsOwner or
equivalent event.owner check) into isAdminOrOwner so downstream UI logic uses
the unified flag and owners can see cancel/reschedule actions.

const [showUtmParams, setShowUtmParams] = useState(false);

Expand Down Expand Up @@ -377,11 +378,13 @@ export default function Success(props: PageProps) {
const isRerouting = searchParams?.get("cal.rerouting") === "true";
const isRescheduled = bookingInfo?.rescheduled;

const canCancelOrReschedule = !eventType?.disableCancelling || !eventType?.disableRescheduling;
const canCancelAndReschedule = !eventType?.disableCancelling && !eventType?.disableRescheduling;
const canCancelOrReschedule =
!eventType?.disableCancelling || !eventType?.disableRescheduling || isHost || isAdminOrOwner;
const canCancelAndReschedule =
(!eventType?.disableCancelling && !eventType?.disableRescheduling) || isHost || isAdminOrOwner;

const canCancel = !eventType?.disableCancelling;
const canReschedule = !eventType?.disableRescheduling;
const canCancel = !eventType?.disableCancelling || isHost || isAdminOrOwner;
const canReschedule = !eventType?.disableRescheduling || isHost || isAdminOrOwner;

const successPageHeadline = (() => {
if (needsConfirmationAndReschedulable) {
Expand Down
Loading
Loading