Skip to content

Conversation

@iharsh02
Copy link
Contributor

@iharsh02 iharsh02 commented Jul 16, 2025

What does this PR do?

Follow-up PR - #18770

This PR adds an email verification dialog to the /booking/uid page that:

  • Shows immediately when visiting the page without a valid session or email parameter
  • Validates the entered email against the booking's attendees and host
  • Only displays booking details after successful email verification
  • Ensures cancelledBy is always set when cancelling a booking

Updates since last revision

Addressed review feedback from @kart1ka:

  • Implemented server-side email validation: Added a new verifyBookingEmail tRPC endpoint that validates emails against the database instead of client-side validation
  • Removed async from updateSearchParams function
  • Removed await from router.replace call

Latest update: Moved prisma query to proper repository pattern:

  • Added findByUidIncludeUserEmailAndAttendeeEmails method to BookingRepository
  • Updated handler to use repository instead of importing prisma directly

Files changed:

  • packages/trpc/server/routers/publicViewer/verifyBookingEmail.schema.ts (new)
  • packages/trpc/server/routers/publicViewer/verifyBookingEmail.handler.ts (new)
  • packages/features/bookings/repositories/BookingRepository.ts (modified)

Visual Demo (For contributors especially)

cal-5045.mp4
image

Mandatory Tasks (DO NOT REMOVE)

  • I have self-reviewed the code (A decent size PR without self-review might be rejected).
  • I have updated the developer docs in /docs if this PR makes changes that would require a documentation change. If N/A, write N/A here and check the checkbox. - N/A
  • I confirm automated tests are in place that prove my fix is effective or that my feature works.

How should this be tested?

  1. Create a booking as a logged-in user
  2. Log out and visit /booking/{uid} directly
  3. Verify the email verification dialog appears immediately
  4. Enter an invalid email - should show error message
  5. Enter a valid attendee or host email - should show booking details
  6. Test cancellation flow - verify cancelledBy is set correctly

Human Review Checklist

  • Verify the new findByUidIncludeUserEmailAndAttendeeEmails repository method returns correct data
  • Verify the verifyBookingEmail tRPC endpoint correctly validates emails against booking attendees and host
  • Test loading states during email verification
  • Confirm dialog cannot be dismissed without valid email verification
  • Test the flow for both attendees and hosts

Checklist

  • I have read the contributing guide
  • My code follows the style guidelines of this project
  • I have checked if my changes generate no new warnings

Link to Devin run: https://app.devin.ai/sessions/733eb255221f498981f687837f05b1bc
Requested by: unknown ()

@vercel
Copy link

vercel bot commented Jul 16, 2025

@iharsh02 is attempting to deploy a commit to the cal Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jul 16, 2025

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

"""

Walkthrough

The changes introduce an email verification step before allowing booking cancellation or viewing booking details when the current user is not authenticated and no valid email parameter is present in the URL. The CancelBooking component refactors cancellation logic into a dedicated async function and accepts new optional props for userEmail and attendees. The Success component in the booking single view manages state for displaying an email verification dialog, validating the entered email against the booking's user and attendees, and conditionally obfuscates personally identifiable information when verification is absent. Localization strings are updated to reflect the broader verification context. End-to-end tests are updated to simulate the new email verification flow prior to cancellation or rescheduling actions.

Assessment against linked issues

Objective Addressed Explanation
Show a dialog to input email address if cancelledBy is not in URL and user is not logged in (CAL-5045, #18717) The Success component displays a verification dialog when no session or valid email param exists.
Prevent cancellation unless input matches booking user or attendee email (CAL-5045, #18717) The verification dialog validates the entered email against the booking's user and attendees.
Only allow dialog closure if email matches a booking email (CAL-5045, #18717) The dialog cannot close without successful email verification; errors are shown otherwise.
Always set cancelledBy when cancelling a booking (CAL-5045, #18717) The cancellation request includes the verified email as cancelledBy ensuring proper attribution.

Assessment against linked issues: Out-of-scope changes

No out-of-scope changes were found.
"""

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~15 minutes

✨ Finishing Touches
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link
Contributor

github-actions bot commented Jul 16, 2025

Hey there and thank you for opening this pull request! 👋🏼

We require pull request titles to follow the Conventional Commits specification and it looks like your proposed title needs to be adjusted.

Details:

No release type found in pull request title "feat : Add email input dialog to /booking/uid page". Add a prefix to indicate what kind of release this pull request corresponds to. For reference, see https://www.conventionalcommits.org/

Available types:
 - feat: A new feature
 - fix: A bug fix
 - docs: Documentation only changes
 - style: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)
 - refactor: A code change that neither fixes a bug nor adds a feature
 - perf: A code change that improves performance
 - test: Adding missing tests or correcting existing tests
 - build: Changes that affect the build system or external dependencies (example scopes: gulp, broccoli, npm)
 - ci: Changes to our CI configuration files and scripts (example scopes: Travis, Circle, BrowserStack, SauceLabs)
 - chore: Other changes that don't modify src or test files
 - revert: Reverts a previous commit

@graphite-app graphite-app bot added the community Created by Linear-GitHub Sync label Jul 16, 2025
@graphite-app graphite-app bot requested a review from a team July 16, 2025 07:03
@github-actions github-actions bot added Medium priority Created by Linear-GitHub Sync ✨ feature New feature or request labels Jul 16, 2025
@graphite-app
Copy link

graphite-app bot commented Jul 16, 2025

Graphite Automations

"Add consumer team as reviewer" took an action on this PR • (07/16/25)

1 reviewer was added to this PR based on Keith Williams's automation.

"Add community label" took an action on this PR • (07/16/25)

1 label was added to this PR based on Keith Williams's automation.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🔭 Outside diff range comments (1)
apps/web/modules/bookings/views/bookings-single-view.tsx (1)

865-872: Avoid unsafe cast & ensure fallbacks for optional fields

bookingInfo?.user?.email is optional. Casting it to string hides the undefined case at compile-time but the runtime value can still be undefined, which will break the new e-mail-verification flow inside CancelBooking.
Likewise, bookingInfo?.attendees can be undefined, yet CancelBooking presumably iterates over it.

-                              attendees: bookingInfo?.attendees,
-                              userEmail: bookingInfo?.user?.email as string,
+                              attendees: bookingInfo.attendees ?? [],
+                              userEmail:
+                                bookingInfo?.user?.email ??
+                                bookingInfo?.userPrimaryEmail ??
+                                "", // empty string handled inside CancelBooking

Adjust the CancelBooking prop types if they currently mark these fields as required.
This prevents runtime errors and keeps the types honest.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a84e898 and e03902b.

📒 Files selected for processing (5)
  • apps/web/components/booking/CancelBooking.tsx (6 hunks)
  • apps/web/modules/bookings/views/bookings-single-view.tsx (1 hunks)
  • apps/web/playwright/booking-seats.e2e.ts (2 hunks)
  • apps/web/public/static/locales/en/common.json (1 hunks)
  • packages/features/schedules/components/Schedule.tsx (0 hunks)
💤 Files with no reviewable changes (1)
  • packages/features/schedules/components/Schedule.tsx
🧰 Additional context used
🧠 Learnings (3)
apps/web/modules/bookings/views/bookings-single-view.tsx (1)
Learnt from: eunjae-lee
PR: calcom/cal.com#22106
File: packages/features/insights/components/FailedBookingsByField.tsx:65-71
Timestamp: 2025-07-15T12:59:34.341Z
Learning: In the FailedBookingsByField component (packages/features/insights/components/FailedBookingsByField.tsx), although routingFormId is typed as optional in useInsightsParameters, the system automatically enforces a routing form filter, so routingFormId is always present in practice. This means the data always contains only one entry, making the single-entry destructuring approach safe.
apps/web/public/static/locales/en/common.json (1)

undefined

<retrieved_learning>
Learnt from: bandhan-majumder
PR: #22359
File: packages/lib/server/locales/en/common.json:1336-1339
Timestamp: 2025-07-14T16:31:45.201Z
Learning: When making localization changes for new features, it's often safer to add new strings rather than modify existing ones to avoid breaking existing functionality that depends on the original strings. This approach allows for feature-specific customization while maintaining backward compatibility.
</retrieved_learning>

apps/web/components/booking/CancelBooking.tsx (1)
Learnt from: eunjae-lee
PR: calcom/cal.com#22106
File: packages/features/insights/components/FailedBookingsByField.tsx:65-71
Timestamp: 2025-07-15T12:59:34.341Z
Learning: In the FailedBookingsByField component (packages/features/insights/components/FailedBookingsByField.tsx), although routingFormId is typed as optional in useInsightsParameters, the system automatically enforces a routing form filter, so routingFormId is always present in practice. This means the data always contains only one entry, making the single-entry destructuring approach safe.
🧬 Code Graph Analysis (1)
apps/web/components/booking/CancelBooking.tsx (1)
packages/ui/components/form/index.ts (1)
  • Input (21-21)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Codacy Static Code Analysis
  • GitHub Check: Install dependencies / Yarn install & cache
  • GitHub Check: Security Check
🔇 Additional comments (5)
apps/web/public/static/locales/en/common.json (1)

2504-2506: Colon alignment & style inconsistency—remove leading spaces before :

Across this i18n file, key-value pairs are written without a space before the colon.
The two new entries proceed_with_cancellation_description and proceed_with_cancellation_error introduce a leading space before the colon, breaking the prevailing style and creating noisy diffs later.

-"proceed_with_cancellation_description" : "Please enter the email address used for this booking to proceed with cancellation",
-"proceed_with_cancellation_error" : "Email doesn't match the booking email",
+"proceed_with_cancellation_description": "Please enter the email address used for this booking to proceed with cancellation",
+"proceed_with_cancellation_error": "Email doesn't match the booking email",

[ suggest_nitpick ]

apps/web/playwright/booking-seats.e2e.ts (1)

127-170: LGTM! Test correctly implements the new email verification flow.

The test properly handles the email verification requirement by:

  1. Destructuring the user object to access the email
  2. Following the correct sequence: confirm → verify email → submit

This aligns with the new CancelBooking component behavior.

apps/web/components/booking/CancelBooking.tsx (3)

12-13: Imports and type updates look good.

The Dialog components and Input are correctly imported, and the booking type additions support the email verification functionality.

Also applies to: 80-81


124-126: Well-structured refactoring with proper state management.

The extraction of cancellation logic into handleCancellation improves code organization. The verifyAndCancel function correctly implements conditional verification based on whether currentUserEmail is available (logged-in users skip verification).

Also applies to: 137-183


286-324: Well-implemented verification dialog.

The Dialog component is properly structured with:

  • Appropriate test IDs that match the e2e tests
  • Clear error handling and display
  • Proper state cleanup on cancel
  • Accessible form inputs

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Copy link
Contributor

@kart1ka kart1ka left a comment

Choose a reason for hiding this comment

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

Left a few comments.

Comment on lines 424 to 432
const isValidBookingEmail = useCallback(
(email: string) => {
return (
bookingInfo?.attendees?.some((attendee) => attendee.email.toLowerCase() === email.toLowerCase()) ||
email.toLowerCase() === bookingInfo?.user?.email.toLowerCase()
);
},
[bookingInfo]
);
Copy link
Contributor

Choose a reason for hiding this comment

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

We should do a server side validation instead of checking the email client side.

}, [session, sessionStatus, isValidBookingEmail, emailParam]);

const updateSearchParams = useCallback(
async (email: string) => {
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
async (email: string) => {
(email: string) => {

async (email: string) => {
const params = new URLSearchParams(searchParams.toString());
params.set("email", email);
await router.replace(`?${params.toString()}`, { scroll: false });
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
await router.replace(`?${params.toString()}`, { scroll: false });
router.replace(`?${params.toString()}`, { scroll: false });

@github-actions
Copy link
Contributor

This PR is being marked as stale due to inactivity.

@github-actions github-actions bot added the Stale label Aug 26, 2025
@kart1ka kart1ka self-assigned this Aug 26, 2025
@github-actions github-actions bot removed the Stale label Aug 27, 2025
@github-actions
Copy link
Contributor

Devin AI is resolving merge conflicts

This PR has merge conflicts with the main branch. A Devin session has been created to automatically resolve them.

View Devin Session

Devin will:

  1. Merge the latest main into this branch
  2. Resolve any conflicts intelligently
  3. Run lint/type checks to ensure validity
  4. Push the resolved changes

If you prefer to resolve conflicts manually, you can close the Devin session and handle it yourself.

…erification feature

Co-Authored-By: unknown <>
@github-actions
Copy link
Contributor

Devin AI is completing this stale PR

This PR by @iharsh02 has been marked as stale. A Devin session has been created to complete the remaining work.

View Devin Session


Devin will review the PR, address any feedback, and push updates to complete this PR.

- Add verifyBookingEmail tRPC endpoint for server-side email validation
- Replace client-side email validation with server-side validation
- Remove async/await from updateSearchParams as per review feedback
- Address review comment by @kart1ka for server-side validation

Co-authored-by: iharsh02 <[email protected]>
Co-Authored-By: unknown <>
@devin-ai-integration
Copy link
Contributor

Summary of Changes

I've completed the remaining work on this PR to address the review feedback from @kart1ka.

Key Changes

Server-side email validation (addressing the main review comment):

  • Added a new verifyBookingEmail tRPC endpoint in packages/trpc/server/routers/publicViewer/ that validates emails against the database
  • The endpoint queries the booking's attendees and host email to verify if the provided email is valid
  • Updated bookings-single-view.tsx to use this server-side validation instead of client-side validation

Minor fixes (addressing other review comments):

  • Removed async from updateSearchParams function
  • Removed await from router.replace call

Files Added

  • packages/trpc/server/routers/publicViewer/verifyBookingEmail.schema.ts
  • packages/trpc/server/routers/publicViewer/verifyBookingEmail.handler.ts

Files Modified

  • packages/trpc/server/routers/publicViewer/_router.tsx - Added the new endpoint
  • apps/web/modules/bookings/views/bookings-single-view.tsx - Updated to use server-side validation

The PR is now ready for another review. The email verification dialog still works as before, but now the validation happens securely on the server side.


Completed by Devin on behalf of @iharsh02

@calcom-bot calcom-bot removed the Stale label Jan 14, 2026
@calcom-bot calcom-bot marked this pull request as ready for review January 14, 2026 16:44
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

4 issues found across 8 files

Prompt for AI agents (all issues)

Check if these issues are valid — if so, understand the root cause of each and fix them.


<file name="apps/web/components/booking/CancelBooking.tsx">

<violation number="1" location="apps/web/components/booking/CancelBooking.tsx:182">
P2: handleCancellation lacks try/catch or res.ok guard, so fetch/JSON errors leave loading stuck true and throw unhandled rejections</violation>
</file>

<file name="packages/trpc/server/routers/publicViewer/verifyBookingEmail.schema.ts">

<violation number="1" location="packages/trpc/server/routers/publicViewer/verifyBookingEmail.schema.ts:10">
P2: Email input is validated without trimming; pasted emails with whitespace will be rejected as invalid</violation>
</file>

<file name="apps/web/modules/bookings/views/bookings-single-view.tsx">

<violation number="1" location="apps/web/modules/bookings/views/bookings-single-view.tsx:531">
P1: Email verification state persists across email changes; unverified emails can view booking details after one successful verify.</violation>

<violation number="2" location="apps/web/modules/bookings/views/bookings-single-view.tsx:565">
P1: Email-gated booking details are only hidden in the UI; SSR still delivers full booking data to unauthenticated users</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

}
};

const showBookingInfo = useMemo(() => {
Copy link
Contributor

Choose a reason for hiding this comment

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

P1: Email verification state persists across email changes; unverified emails can view booking details after one successful verify.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/web/modules/bookings/views/bookings-single-view.tsx, line 531:

<comment>Email verification state persists across email changes; unverified emails can view booking details after one successful verify.</comment>

<file context>
@@ -450,6 +461,79 @@ export default function Success(props: PageProps) {
+    }
+  };
+
+  const showBookingInfo = useMemo(() => {
+    if (session) return true;
+    if (!emailParam) return false;
</file context>

eventType={{ ...eventType, metadata: eventTypeMetaDataSchemaWithTypedApps.parse(eventType.metadata) }}
/>
<main className={classNames(shouldAlignCentrally ? "mx-auto" : "", isEmbed ? "" : "max-w-3xl")}>
{showBookingInfo ? (
Copy link
Contributor

Choose a reason for hiding this comment

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

P1: Email-gated booking details are only hidden in the UI; SSR still delivers full booking data to unauthenticated users

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/web/modules/bookings/views/bookings-single-view.tsx, line 565:

<comment>Email-gated booking details are only hidden in the UI; SSR still delivers full booking data to unauthenticated users</comment>

<file context>
@@ -478,7 +562,8 @@ export default function Success(props: PageProps) {
         eventType={{ ...eventType, metadata: eventTypeMetaDataSchemaWithTypedApps.parse(eventType.metadata) }}
       />
-      <main className={classNames(shouldAlignCentrally ? "mx-auto" : "", isEmbed ? "" : "max-w-3xl")}>
+      {showBookingInfo ? (
+        <main className={classNames(shouldAlignCentrally ? "mx-auto" : "", isEmbed ? "" : "max-w-3xl")}>
         <div className={classNames("overflow-y-auto", isEmbed ? "" : "z-50 ")}>
</file context>


const isRenderedAsCancelDialog = props.renderContext === "dialog";

const handleCancellation = async () => {
Copy link
Contributor

Choose a reason for hiding this comment

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

P2: handleCancellation lacks try/catch or res.ok guard, so fetch/JSON errors leave loading stuck true and throw unhandled rejections

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/web/components/booking/CancelBooking.tsx, line 182:

<comment>handleCancellation lacks try/catch or res.ok guard, so fetch/JSON errors leave loading stuck true and throw unhandled rejections</comment>

<file context>
@@ -177,6 +179,61 @@ export default function CancelBooking(props: Props) {
 
   const isRenderedAsCancelDialog = props.renderContext === "dialog";
 
+  const handleCancellation = async () => {
+    setLoading(true);
+
</file context>


export const ZVerifyBookingEmailInputSchema: z.ZodType<TVerifyBookingEmailInputSchema> = z.object({
bookingUid: z.string(),
email: z.string().email(),
Copy link
Contributor

Choose a reason for hiding this comment

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

P2: Email input is validated without trimming; pasted emails with whitespace will be rejected as invalid

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/trpc/server/routers/publicViewer/verifyBookingEmail.schema.ts, line 10:

<comment>Email input is validated without trimming; pasted emails with whitespace will be rejected as invalid</comment>

<file context>
@@ -0,0 +1,15 @@
+
+export const ZVerifyBookingEmailInputSchema: z.ZodType<TVerifyBookingEmailInputSchema> = z.object({
+  bookingUid: z.string(),
+  email: z.string().email(),
+});
+
</file context>

Fix confidence (alpha): 8/10

Suggested change
email: z.string().email(),
email: z.string().trim().email(),

- Add findByUidIncludeUserEmailAndAttendeeEmails method to BookingRepository
- Update verifyBookingEmail handler to use repository instead of prisma directly
- Follow Cal.com's repository pattern for data access

Co-Authored-By: unknown <>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

community Created by Linear-GitHub Sync ✨ feature New feature or request Medium priority Created by Linear-GitHub Sync ready-for-e2e size/L

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[CAL-5045] Add email input dialog to /booking/uid page

7 participants