Skip to content

Conversation

@Anshumancanrock
Copy link
Contributor

What does this PR do?

This PR fixes the phone field in Booker Atom (Platform mode) to auto-detect the user's country, matching the behavior of the main web app. Previously, the phone field always defaulted to US (+1) regardless of the user's actual location.

Before:

  • Phone field in Booker Atom was hardcoded to always show US flag 🇺🇸 and +1 prefix
  • No country detection logic in the platform code path
  • Inconsistent behavior between web app (which worked correctly) and Booker Atom

After:

  • Created BasePhoneInputPlatform component that uses the same useDefaultCountry() hook as BasePhoneInputWeb
  • Both web and platform modes now use identical country detection logic
  • Maintains backward compatibility and all existing functionality

Visual Demo (For contributors especially)

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.
  • I confirm automated tests are in place that prove my fix is effective or that my feature works.

How should this be tested?

  • No special environment variables needed
  • Standard Cal.com development setup

Checklist

@vercel
Copy link

vercel bot commented Oct 2, 2025

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

A member of the Team first needs to authorize it.

@graphite-app graphite-app bot added the community Created by Linear-GitHub Sync label Oct 2, 2025
@graphite-app graphite-app bot requested a review from a team October 2, 2025 13:51
@github-actions github-actions bot added platform Anything related to our platform plan reactive⚡︎ ⚛️ atoms Created by Linear-GitHub Sync ✨ feature New feature or request labels Oct 2, 2025
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 2, 2025

Walkthrough

Replaces platform-specific rendering in PhoneInput.tsx with a single unified BasePhoneInput that always renders one PhoneInput. Removes useIsPlatform branching and BasePhoneInputWeb/platform variants. Derives defaultCountry via a new useDefaultCountry hook and updates the signature to Omit<PhoneInputProps, "defaultCountry">. Merges inputStyle and flagButtonStyle into the underlying component’s style/ button props and preserves value normalization and onChange (including prefix handling).

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run `@coderabbitai generate docstrings` to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Title Check ✅ Passed The title clearly summarizes the core change of the pull request by stating it adds automatic phone country code detection in the Booker Atom component, which aligns with the PR’s main objective of replicating the web app’s behavior.
Linked Issues Check ✅ Passed The implemented useDefaultCountry hook and unified BasePhoneInput component satisfy the linked issues’ requirement to replicate the web app’s country detection logic in Booker Atom and eliminate the hardcoded US default, fully addressing #24214 and CAL-6501.
Out of Scope Changes Check ✅ Passed All modifications—including removing platform-specific branches, updating the BasePhoneInput signature, and merging style props—directly support implementing automatic country detection and do not introduce any unrelated or out-of-scope changes.
Description Check ✅ Passed The description directly addresses the changes made to enable automatic country detection in the Booker Atom phone field, outlines the before and after behavior, references the relevant issues, and describes testing steps, so it is clearly related to the changeset.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 24f13ee and d95f760.

📒 Files selected for processing (1)
  • packages/features/components/phone-input/PhoneInput.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/features/components/phone-input/PhoneInput.tsx

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.

@graphite-app graphite-app bot requested a review from a team October 2, 2025 13:51
@dosubot dosubot bot added the booking-page area: booking page, public booking page, booker label Oct 2, 2025
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: 0

🧹 Nitpick comments (2)
packages/features/components/phone-input/PhoneInput.tsx (2)

71-120: Consider eliminating duplication between BasePhoneInputPlatform and BasePhoneInputWeb.

Both components share identical implementations (PhoneInput configuration, styling, value normalization, onChange handling). The conditional in BasePhoneInput could be removed entirely since both paths now use useDefaultCountry() the same way.

Consider this refactor to eliminate ~100 lines of duplication:

 function BasePhoneInput({
   name,
   className = "",
   onChange,
   value,
-  defaultCountry = "us",
+  inputStyle,
+  flagButtonStyle,
   ...rest
 }: PhoneInputProps) {
-  const isPlatform = useIsPlatform();
+  const defaultCountry = useDefaultCountry();
 
   // This is to trigger validation on prefill value changes
   useEffect(() => {
     if (!value) return;
 
     const sanitized = value
       .trim()
       .replace(/[^\d+]/g, "")
       .replace(/^\+?/, "+");
 
     if (sanitized === "+" || sanitized === "") return;
 
     if (value !== sanitized) {
       onChange(sanitized);
     }
     // eslint-disable-next-line react-hooks/exhaustive-deps
   }, []);
 
-  if (!isPlatform) {
-    return (
-      <BasePhoneInputWeb name={name} className={className} onChange={onChange} value={value} {...rest} />
-    );
-  }
-
-  return (
-    <BasePhoneInputPlatform
-      name={name}
-      className={className}
-      onChange={onChange}
-      value={value}
-      {...rest}
-    />
-  );
-}
-
-function BasePhoneInputPlatform({
-  name,
-  className = "",
-  onChange,
-  value,
-  inputStyle,
-  flagButtonStyle,
-  ...rest
-}: Omit<PhoneInputProps, "defaultCountry">) {
-  const defaultCountry = useDefaultCountry();
-
   return (
     <PhoneInput
       {...rest}
       value={value ? value.trim().replace(/^\+?/, "+") : undefined}
       country={value ? undefined : defaultCountry}
       enableSearch
       disableSearchIcon
       inputProps={{
         name,
         required: rest.required,
         placeholder: rest.placeholder,
       }}
       onChange={(val: string) => {
         onChange(`+${val}`);
       }}
       containerClass={classNames(
         "hover:border-emphasis dark:focus:border-emphasis border-default !bg-default rounded-md border focus-within:outline-none focus-within:ring-2 focus-within:ring-brand-default disabled:cursor-not-allowed",
         className
       )}
       inputClass="text-sm focus:ring-0 !bg-default text-default placeholder:text-muted"
       buttonClass="text-emphasis !bg-default hover:!bg-emphasis"
       buttonStyle={{ ...flagButtonStyle }}
       searchClass="!text-default !bg-default hover:!bg-emphasis"
       dropdownClass="!text-default !bg-default"
       inputStyle={{ width: "inherit", border: 0, ...inputStyle }}
       searchStyle={{
         display: "flex",
         flexDirection: "row",
         alignItems: "center",
         padding: "6px 12px",
         gap: "8px",
         width: "296px",
         height: "28px",
         marginLeft: "-4px",
       }}
       dropdownStyle={{ width: "max-content" }}
     />
   );
 }
-
-function BasePhoneInputWeb({
-  name,
-  className = "",
-  onChange,
-  value,
-  inputStyle,
-  flagButtonStyle,
-  ...rest
-}: Omit<PhoneInputProps, "defaultCountry">) {
-  const defaultCountry = useDefaultCountry();
-
-  return (
-    <PhoneInput
-      {...rest}
-      value={value ? value.trim().replace(/^\+?/, "+") : undefined}
-      country={value ? undefined : defaultCountry}
-      enableSearch
-      disableSearchIcon
-      inputProps={{
-        name,
-        required: rest.required,
-        placeholder: rest.placeholder,
-      }}
-      onChange={(val: string) => {
-        onChange(`+${val}`);
-      }}
-      containerClass={classNames(
-        "hover:border-emphasis dark:focus:border-emphasis border-default !bg-default rounded-md border focus-within:outline-none focus-within:ring-2 focus-within:ring-brand-default disabled:cursor-not-allowed",
-        className
-      )}
-      inputClass="text-sm focus:ring-0 !bg-default text-default placeholder:text-muted"
-      buttonClass="text-emphasis !bg-default hover:!bg-emphasis"
-      buttonStyle={{ ...flagButtonStyle }}
-      searchClass="!text-default !bg-default hover:!bg-emphasis"
-      dropdownClass="!text-default !bg-default"
-      inputStyle={{ width: "inherit", border: 0, ...inputStyle }}
-      searchStyle={{
-        display: "flex",
-        flexDirection: "row",
-        alignItems: "center",
-        padding: "6px 12px",
-        gap: "8px",
-        width: "296px",
-        height: "28px",
-        marginLeft: "-4px",
-      }}
-      dropdownStyle={{ width: "max-content" }}
-    />
-  );
-}

198-198: Prefer named export over default export.

As per coding guidelines, named exports provide better tree-shaking, easier refactoring, and clearer imports. While default exports are acceptable for pages and layouts, this shared component should use a named export.

Apply this diff:

-export default BasePhoneInput;
+export { BasePhoneInput };

Then update imports across the codebase from:

import BasePhoneInput from "...";

to:

import { BasePhoneInput } from "...";

As per coding guidelines.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 2f763dc and 24f13ee.

📒 Files selected for processing (1)
  • packages/features/components/phone-input/PhoneInput.tsx (2 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.tsx

📄 CodeRabbit inference engine (.cursor/rules/review.mdc)

Always use t() for text localization in frontend code; direct text embedding should trigger a warning

Files:

  • packages/features/components/phone-input/PhoneInput.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/review.mdc)

Flag excessive Day.js use in performance-critical code; prefer native Date or Day.js .utc() in hot paths like loops

Files:

  • packages/features/components/phone-input/PhoneInput.tsx
**/*.{ts,tsx,js,jsx}

⚙️ CodeRabbit configuration file

Flag default exports and encourage named exports. Named exports provide better tree-shaking, easier refactoring, and clearer imports. Exempt main components like pages, layouts, and components that serve as the primary export of a module.

Files:

  • packages/features/components/phone-input/PhoneInput.tsx
⏰ Context from checks skipped due to timeout of 180000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Type check / check-types
  • GitHub Check: Tests / Unit
  • GitHub Check: Linters / lint
🔇 Additional comments (2)
packages/features/components/phone-input/PhoneInput.tsx (2)

60-69: LGTM!

The conditional rendering cleanly separates platform and web paths, with appropriate prop forwarding to the new BasePhoneInputPlatform component.


173-196: LGTM!

The country detection logic correctly falls back from API response → navigator.language → "us", meeting the PR's objective to unify country detection between web and platform.

Copy link
Member

@dhairyashiil dhairyashiil left a comment

Choose a reason for hiding this comment

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

The two functions BasePhoneInputWeb and BasePhoneInputPlatform do the same thing. Please remove the web/platform split and keep only one function called BasePhoneInput

// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

if (!isPlatform) {
Copy link
Member

Choose a reason for hiding this comment

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

Combine them into a single version that works for both web and platform, and remove the isPlatform check.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@dhairyashiil Thanks for the feedback! I’ve made the changes as requested, removed the extra isPlatform check and the duplicate BasePhoneInputWeb / BasePhoneInputPlatform functions. Now everything’s handled in a single BasePhoneInput component that uses useDefaultCountry for both web and platform modes.

@Anshumancanrock
Copy link
Contributor Author

@dhairyashiil I’ve made the changes as requested. Please review now. Thanks !

Copy link
Member

@dhairyashiil dhairyashiil left a comment

Choose a reason for hiding this comment

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

LGTM 👍🏼

@github-actions
Copy link
Contributor

github-actions bot commented Oct 3, 2025

E2E results are ready!

@Anshumancanrock
Copy link
Contributor Author

Hi @supalarry , Could you please review this PR? Thank you !

1 similar comment
@TwanBox
Copy link

TwanBox commented Oct 27, 2025

Hi @supalarry , Could you please review this PR? Thank you !

Copy link
Member

@dhairyashiil dhairyashiil left a comment

Choose a reason for hiding this comment

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

@Anshumancanrock , please resolve merge conflicts, I will ask team member for the review on monday, and sorry for delay 🙏🏼

@dhairyashiil dhairyashiil marked this pull request as draft November 15, 2025 21:30
@Anshumancanrock Anshumancanrock requested review from a team as code owners November 25, 2025 16:42
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.

2 issues found across 8 files

Prompt for AI agents (all 2 issues)

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


<file name="packages/features/components/phone-input/PhoneInputPlatformWrapper.tsx">

<violation number="1" location="packages/features/components/phone-input/PhoneInputPlatformWrapper.tsx:22">
Locale fallback grabs the second locale segment, so locales with a script subtag (e.g., zh-Hant-TW) resolve to an invalid country code. Use the last hyphen-delimited segment (or another region-aware parser) when deriving the fallback country.</violation>
</file>

<file name="packages/features/components/phone-input/PhoneInputWebWrapper.tsx">

<violation number="1" location="packages/features/components/phone-input/PhoneInputWebWrapper.tsx:28">
Validate the browser-derived fallback country before passing it to the phone input; `navigator.language` can contain non-country tokens (e.g., scripts or UN regions), so currently an invalid `defaultCountry` is set and the phone field loses its default flag.</violation>
</file>

Reply to cubic to teach it or ask questions. Re-run a review with @cubic-dev-ai review this PR

@TwanBox
Copy link

TwanBox commented Dec 9, 2025

Hey guys,
We'd really appreciate this simple fix, we've kinda been forced to use the regular text Input field on platform.
Since it does not make sense at all for our (European) user to have to see & update a US phone field..

Thanks again!

@TeleCalendar
Copy link

Hi everyone,
Would it be possible to get this PR merged and released soon? For platform users in Europe the current behaviour makes the phone field unusable, since it always defaults to a US number. We are forced to fall back to a plain text input, which is a poor experience.

This change would unblock real usage of the phone field on platform and aligns it with the web behaviour. Thanks a lot for taking another look.

@github-actions
Copy link
Contributor

github-actions bot commented Jan 9, 2026

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.

@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.

@Ryukemeister
Copy link
Contributor

hi @TwanBox and @TeleCalendar i'll try to finish the remaning work in this PR and get it past the line

@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.

@devin-ai-integration devin-ai-integration bot added the Medium priority Created by Linear-GitHub Sync label Jan 19, 2026
@Ryukemeister Ryukemeister dismissed stale reviews from dhairyashiil and supalarry January 19, 2026 11:34

implemented feedback

@@ -1,8 +0,0 @@
import dynamic from "next/dynamic";
Copy link
Contributor

Choose a reason for hiding this comment

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

this is causing lazy loading for both atoms and web wrapper, for some reason api calls were not being made correctly with vite, hence shifted lazy loading inside web wrapper. this way it only lazy loads for web and not for atoms.

import { PhoneInputPlatformWrapper } from "./PhoneInputPlatformWrapper";

/** These are like 40kb that not every user needs */
const PhoneInputWebWrapper: ComponentType<Omit<PhoneInputProps, "defaultCountry">> = dynamic(() =>
Copy link
Contributor

Choose a reason for hiding this comment

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

we only wanna lazy loading for web wrapper

Copy link
Contributor

@Ryukemeister Ryukemeister left a comment

Choose a reason for hiding this comment

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

looks good on atoms side, tested with web app as well and looking good. @Udit-takkar needs an extra pair of eyes from you here

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.

1 issue found across 6 files (changes from recent commits).

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="packages/features/components/phone-input/PhoneInputPlatformWrapper.tsx">

<violation number="1" location="packages/features/components/phone-input/PhoneInputPlatformWrapper.tsx:23">
P2: Browser locale fallback always fails country support check due to lowercased code passed to case-sensitive isSupportedCountry, forcing default to US.</violation>
</file>

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

setDefaultCountry(countryCode.toLowerCase());
} else {
const browserCountry = navigator.language.split("-").pop()?.toLowerCase();
if (browserCountry && isSupportedCountry(browserCountry)) {
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Jan 19, 2026

Choose a reason for hiding this comment

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

P2: Browser locale fallback always fails country support check due to lowercased code passed to case-sensitive isSupportedCountry, forcing default to US.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/features/components/phone-input/PhoneInputPlatformWrapper.tsx, line 23:

<comment>Browser locale fallback always fails country support check due to lowercased code passed to case-sensitive isSupportedCountry, forcing default to US.</comment>

<file context>
@@ -21,7 +20,11 @@ export function PhoneInputPlatformWrapper(props: Omit<PhoneInputProps, "defaultC
     } else {
       const browserCountry = navigator.language.split("-").pop()?.toLowerCase();
-      setDefaultCountry(browserCountry && isSupportedCountry(browserCountry) ? browserCountry : "us");
+      if (browserCountry && isSupportedCountry(browserCountry)) {
+        setDefaultCountry(browserCountry);
+      } else {
</file context>
Fix with Cubic

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

⚛️ atoms Created by Linear-GitHub Sync booking-page area: booking page, public booking page, booker community Created by Linear-GitHub Sync ✨ feature New feature or request Medium priority Created by Linear-GitHub Sync platform Anything related to our platform plan reactive⚡︎ ready-for-e2e size/L

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: booker atom automatic phone country code detection

9 participants