Open Baloun1 opened 1 month ago
To resolve the "Invalid Input" error for a USA-formatted phone number in the 'phone call' event type with the location set to 'attendee phone number', ensure the phone number is correctly formatted and validated using the isValidPhoneNumber
function from libphonenumber-js
. The phone number should be in the format +1 (XXX) XXX-XXXX
.
Here are the key steps:
Phone Number Validation:
Ensure the phone number is validated using the isValidPhoneNumber
function:
const locationFormSchema = z.object({
locationPhoneNumber: z
.string()
.nullable()
.refine((val) => val !== null && isValidPhoneNumber(val))
.optional(),
});
Phone Input Component:
Ensure the phone number input is correctly passed to the PhoneInput
component:
<Controller
name={eventLocationType.variable}
control={control}
defaultValue={defaultValue}
render={({ field: { onChange, value } }) => (
<PhoneInput onChange={onChange} value={value} {...rest} />
)}
/>
Integration in Components:
Ensure the PhoneInput
component is correctly integrated within the AIEventController
and AISettings
components:
<Controller
name="aiPhoneCallConfig.yourPhoneNumber"
render={({ field: { onChange, value } }) => (
<PhoneInput
required
placeholder={t("your_phone_number")}
id="aiPhoneCallConfig.yourPhoneNumber"
name="aiPhoneCallConfig.yourPhoneNumber"
value={value}
onChange={(val) => onChange(val)}
/>
)}
/>
Default Event Location Type:
Ensure the DefaultEventLocationTypeEnum
and defaultLocations
are correctly set up:
export const defaultLocations: DefaultEventLocationType[] = [
{
default: true,
type: DefaultEventLocationTypeEnum.Phone,
label: "attendee_phone_number",
variable: "phone",
organizerInputType: null,
attendeeInputType: "phone",
attendeeInputPlaceholder: `enter_phone_number`,
defaultValueVariable: "phone",
messageForOrganizer: "Cal will ask your invitee to enter a phone number before scheduling.",
iconUrl: "/phone.svg",
category: "phone",
},
];
Phone Input Configuration:
Ensure the PhoneInput
component is correctly configured to handle the default country and formatting:
import { isSupportedCountry } from "libphonenumber-js";
import { useState, useEffect } from "react";
import PhoneInput from "react-phone-input-2";
import "react-phone-input-2/lib/style.css";
const useDefaultCountry = () => {
const [defaultCountry, setDefaultCountry] = useState("us");
const query = trpc.viewer.public.countryCode.useQuery(undefined, {
refetchOnWindowFocus: false,
refetchOnReconnect: false,
retry: false,
});
useEffect(() => {
const data = query.data;
if (!data?.countryCode) return;
isSupportedCountry(data?.countryCode)
? setDefaultCountry(data.countryCode.toLowerCase())
: setDefaultCountry(navigator.language.split("-")[1]?.toLowerCase() || "us");
}, [query.data]);
return defaultCountry;
};
function BasePhoneInput({ name, className = "", onChange, value, ...rest }) {
const defaultCountry = useDefaultCountry();
return (
<PhoneInput
{...rest}
value={value ? value.trim().replace(/^\+?/, "+") : undefined}
country={value ? undefined : defaultCountry}
enableSearch
disableSearchIcon
inputProps={{
name: name,
required: rest.required,
placeholder: rest.placeholder,
}}
onChange={(value) => onChange(`+${value}`)}
containerClass="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"
inputClass="text-sm focus:ring-0 !bg-default text-default"
buttonClass="text-emphasis !bg-default hover:!bg-emphasis"
searchClass="!text-default !bg-default hover:!bg-emphasis"
dropdownClass="!text-default !bg-default"
inputStyle={{ width: "inherit", border: 0 }}
searchStyle={{
display: "flex",
flexDirection: "row",
alignItems: "center",
padding: "6px 12px",
gap: "8px",
width: "296px",
height: "28px",
marginLeft: "-4px",
}}
dropdownStyle={{ width: "max-content" }}
/>
);
}
export default BasePhoneInput;
Test Cases: Ensure the phone number does not contain leading spaces or other invalid characters:
describe("validate phone type field", () => {
test(`should fail parsing if invalid phone provided`, async ({}) => {
const schema = getBookingResponsesSchema({
bookingFields: [
{ name: "name", type: "name", required: true },
{ name: "email", type: "email", required: true },
{ name: "testPhone", type: "phone", required: true },
],
view: "ALL_VIEWS",
});
const parsedResponses = await schema.safeParseAsync({
email: "test@test.com",
name: "test",
testPhone: "1234567890",
});
expect(parsedResponses.success).toBe(false);
if (parsedResponses.success) throw new Error("Should not reach here");
expect(parsedResponses.error.issues[0]).toEqual(
expect.objectContaining({
code: "custom",
message: `{testPhone}${CUSTOM_PHONE_VALIDATION_ERROR_MSG}`,
})
);
});
test(`should successfully give responses if phone type field value is valid`, async ({}) => {
const schema = getBookingResponsesSchema({
bookingFields: [
{ name: "name", type: "name", required: true },
{ name: "email", type: "email", required: true },
{ name: "testPhone", type: "phone", required: true },
],
view: "ALL_VIEWS",
});
const parsedResponses = await schema.safeParseAsync({
email: "test@test.com",
name: "test",
testPhone: "+919999999999",
});
expect(parsedResponses.success).toBe(true);
if (!parsedResponses.success) throw new Error("Should not reach here");
expect(parsedResponses.data).toEqual({
email: "test@test.com",
name: "test",
testPhone: "+919999999999",
});
});
test(`should give parsed response if phone type field value starts with a space`, async ({}) => {
const schema = getBookingResponsesSchema({
bookingFields: [
{ name: "name", type: "name", required: true },
{ name: "email", type: "email", required: true },
{ name: "testPhone", type: "phone", required: true },
],
view: "ALL_VIEWS",
});
const parsedResponses = await schema.safeParseAsync({
email: "test@test.com",
name: "test",
testPhone: " 919999999999",
});
expect(parsedResponses.success).toBe(true);
if (!parsedResponses.success) throw new Error("Should not reach here");
expect(parsedResponses.data).toEqual({
email: "test@test.com",
name: "test",
testPhone: "+919999999999",
});
const parsedResponses2 = await schema.safeParseAsync({
email: "test@test.com",
name: "test",
testPhone: " 919999999999",
});
expect(parsedResponses2.success).toBe true;
if (!parsedResponses2.success) throw new Error("Should not reach here");
expect(parsedResponses2.data).toEqual({
email: "test@test.com",
name: "test",
testPhone: "+919999999999",
});
});
});
These steps should help resolve the "Invalid Input" error by ensuring the phone number is correctly formatted and validated. If the issue persists, double-check the implementation of the PhoneInput
component and the isValidPhoneNumber
function.
To continue the conversation, mention @dosu.
@Udit-takkar and @zomars lets fix the phone number input once and for all. too many issues
Can i work on thia ?
Currently with event type 'phone call', location 'attendee phone number'. A USA formatted phone number is returning a "invalid Input" error. Country Code dropdown has USA +1 selected, data input field auto formats the raw phone number input to +1 (XXX) XXX-XXXX.
May relate to closed issue: https://github.com/calcom/cal.com/pull/15046
CAL-3833