diff --git a/apps/web/components/booking/pages/BookingPage.tsx b/apps/web/components/booking/pages/BookingPage.tsx
index 478c83fc31..eab526bf66 100644
--- a/apps/web/components/booking/pages/BookingPage.tsx
+++ b/apps/web/components/booking/pages/BookingPage.tsx
@@ -40,6 +40,7 @@ import { getEveryFreqFor } from "@calcom/lib/recurringStrings";
import { collectPageParameters, telemetryEventTypes, useTelemetry } from "@calcom/lib/telemetry";
import { Icon } from "@calcom/ui/Icon";
import { Tooltip } from "@calcom/ui/Tooltip";
+import AddressInput from "@calcom/ui/form/AddressInputLazy";
import { Button } from "@calcom/ui/components";
import PhoneInput from "@calcom/ui/form/PhoneInputLazy";
import { EmailInput, Form } from "@calcom/ui/form/fields";
@@ -68,6 +69,8 @@ type BookingFormValues = {
notes?: string;
locationType?: EventLocationType["type"];
guests?: string[];
+ address?: string;
+ attendeeAddress?: string;
phone?: string;
hostPhoneNumber?: string; // Maybe come up with a better way to name this to distingish between two types of phone numbers
customInputs?: {
@@ -269,6 +272,7 @@ const BookingPage = ({
.refine((val) => isValidPhoneNumber(val))
.optional()
.nullable(),
+ attendeeAddress: z.string().optional().nullable(),
smsReminderNumber: z
.string()
.refine((val) => isValidPhoneNumber(val))
@@ -297,10 +301,10 @@ const BookingPage = ({
const selectedLocation = getEventLocationType(selectedLocationType);
const AttendeeInput =
- selectedLocation?.attendeeInputType === "text"
- ? "input"
- : selectedLocation?.attendeeInputType === "phone"
+ selectedLocation?.attendeeInputType === "phone"
? PhoneInput
+ : selectedLocation?.attendeeInputType === "attendeeAddress"
+ ? AddressInput
: null;
// Calculate the booking date(s)
@@ -356,6 +360,7 @@ const BookingPage = ({
location: getEventLocationValue(locations, {
type: booking.locationType ? booking.locationType : selectedLocationType || "",
phone: booking.phone,
+ attendeeAddress: booking.attendeeAddress,
}),
metadata,
customInputs: Object.keys(booking.customInputs || {}).map((inputId) => ({
@@ -386,6 +391,7 @@ const BookingPage = ({
location: getEventLocationValue(locations, {
type: (booking.locationType ? booking.locationType : selectedLocationType) || "",
phone: booking.phone,
+ attendeeAddress: booking.attendeeAddress,
}),
metadata,
customInputs: Object.keys(booking.customInputs || {}).map((inputId) => ({
@@ -648,16 +654,39 @@ const BookingPage = ({
{AttendeeInput && (
- {t("phone_number")}
+ {selectedLocationType === LocationType.Phone
+ ? t("phone_number")
+ : selectedLocationType === LocationType.AttendeeInPerson
+ ? t("Address")
+ : ""}
control={bookingForm.control}
- name="phone"
+ bookingForm={bookingForm}
+ name={
+ selectedLocationType === LocationType.Phone
+ ? "phone"
+ : selectedLocationType === LocationType.AttendeeInPerson
+ ? "attendeeAddress"
+ : ""
+ }
placeholder={t(selectedLocation?.attendeeInputPlaceholder || "")}
- id="phone"
+ id={
+ selectedLocationType === LocationType.Phone
+ ? "phone"
+ : selectedLocationType === LocationType.AttendeeInPerson
+ ? "attendeeAddress"
+ : ""
+ }
required
disabled={disableInput}
/>
diff --git a/apps/web/components/dialog/EditLocationDialog.tsx b/apps/web/components/dialog/EditLocationDialog.tsx
index 69410a1b45..12a6c868d7 100644
--- a/apps/web/components/dialog/EditLocationDialog.tsx
+++ b/apps/web/components/dialog/EditLocationDialog.tsx
@@ -293,7 +293,10 @@ export const EditLocationDialog = (props: ISetLocationDialog) => {
defaultValue={selection}
options={
booking
- ? locationOptions.filter((location) => location.value !== "phone")
+ ? locationOptions.filter(
+ (location) =>
+ location.value !== "phone" && location.value !== "attendeeInPerson"
+ )
: locationOptions
}
isSearchable
diff --git a/apps/web/components/v2/eventtype/EditLocationDialog.tsx b/apps/web/components/v2/eventtype/EditLocationDialog.tsx
index 7a1f6e19a2..c27f11ed4c 100644
--- a/apps/web/components/v2/eventtype/EditLocationDialog.tsx
+++ b/apps/web/components/v2/eventtype/EditLocationDialog.tsx
@@ -232,6 +232,12 @@ export const EditLocationDialog = (props: ISetLocationDialog) => {
const { locationType: newLocation, displayLocationPublicly } = values;
let details = {};
+ if (newLocation === LocationType.AttendeeInPerson) {
+ details = {
+ address: values.locationAddress,
+ };
+ }
+
if (newLocation === LocationType.InPerson) {
details = {
address: values.locationAddress,
diff --git a/apps/web/pages/event-types/[type]/index.tsx b/apps/web/pages/event-types/[type]/index.tsx
index 4b1fa3050b..647351ec2d 100644
--- a/apps/web/pages/event-types/[type]/index.tsx
+++ b/apps/web/pages/event-types/[type]/index.tsx
@@ -56,6 +56,7 @@ export type FormValues = {
locations: {
type: EventLocationType["type"];
address?: string;
+ attendeeAddress?: string;
link?: string;
hostPhoneNumber?: string;
displayLocationPublicly?: boolean;
diff --git a/packages/app-store/locations.ts b/packages/app-store/locations.ts
index 5d90ea20ee..7c753113ab 100644
--- a/packages/app-store/locations.ts
+++ b/packages/app-store/locations.ts
@@ -16,8 +16,8 @@ export type DefaultEventLocationType = {
iconUrl: string;
// HACK: `variable` and `defaultValueVariable` are required due to legacy reason where different locations were stored in different places.
- variable: "locationType" | "locationAddress" | "locationLink" | "locationPhoneNumber" | "phone";
- defaultValueVariable: "address" | "link" | "hostPhoneNumber" | "phone";
+ variable: "locationType" | "locationAddress" | "address" | "locationLink" | "locationPhoneNumber" | "phone";
+ defaultValueVariable: "address" | "attendeeAddress" | "link" | "hostPhoneNumber" | "phone";
} & (
| {
organizerInputType: "phone" | "text" | null;
@@ -26,7 +26,7 @@ export type DefaultEventLocationType = {
attendeeInputPlaceholder?: null;
}
| {
- attendeeInputType: "phone" | "text" | null;
+ attendeeInputType: "phone" | "attendeeAddress" | null;
attendeeInputPlaceholder: string;
organizerInputType?: null;
organizerInputPlaceholder?: null;
@@ -40,6 +40,13 @@ export type EventLocationType = DefaultEventLocationType | EventLocationTypeFrom
export const DailyLocationType = "integrations:daily";
export enum DefaultEventLocationTypeEnum {
+ /**
+ * Booker Address
+ */
+ AttendeeInPerson = "attendeeInPerson",
+ /**
+ * Organizer Address
+ */
InPerson = "inPerson",
/**
* Booker Phone
@@ -53,10 +60,22 @@ export enum DefaultEventLocationTypeEnum {
}
export const defaultLocations: DefaultEventLocationType[] = [
+ {
+ default: true,
+ type: DefaultEventLocationTypeEnum.AttendeeInPerson,
+ label: "In Person (Attendee Address)",
+ variable: "address",
+ organizerInputType: null,
+ messageForOrganizer: "Cal will ask your invitee to enter an address before scheduling.",
+ attendeeInputType: "attendeeAddress",
+ attendeeInputPlaceholder: `Enter Address`,
+ defaultValueVariable: "attendeeAddress",
+ iconUrl: "/map-pin.svg",
+ },
{
default: true,
type: DefaultEventLocationTypeEnum.InPerson,
- label: "In Person",
+ label: "In Person (Organizer Address)",
organizerInputType: "text",
messageForOrganizer: "Provide an Address or Place",
// HACK:
@@ -103,7 +122,7 @@ export const defaultLocations: DefaultEventLocationType[] = [
export type LocationObject = {
type: string;
displayLocationPublicly?: boolean;
-} & Partial>;
+} & Partial>;
// integrations:jitsi | 919999999999 | Delhi | https://manual.meeting.link | Around Video
export type BookingLocationValue = string;
diff --git a/packages/ui/form/AddressInput.tsx b/packages/ui/form/AddressInput.tsx
new file mode 100644
index 0000000000..ebb1201d71
--- /dev/null
+++ b/packages/ui/form/AddressInput.tsx
@@ -0,0 +1,51 @@
+import { UseFormReturn } from "react-hook-form";
+import { Props } from "react-phone-number-input/react-hook-form";
+
+import { EventLocationType } from "@calcom/app-store/locations";
+
+import { Icon } from "../Icon";
+
+type BookingFormValues = {
+ name: string;
+ email: string;
+ notes?: string;
+ locationType?: EventLocationType["type"];
+ guests?: string[];
+ address?: string;
+ attendeeAddress?: string;
+ phone?: string;
+ hostPhoneNumber?: string; // Maybe come up with a better way to name this to distingish between two types of phone numbers
+ customInputs?: {
+ [key: string]: string | boolean;
+ };
+ rescheduleReason?: string;
+ smsReminderNumber?: string;
+};
+
+export type AddressInputProps = Props<
+ {
+ value: string;
+ id: string;
+ placeholder: string;
+ required: boolean;
+ bookingForm: UseFormReturn;
+ },
+ FormValues
+>;
+
+function AddressInput({ bookingForm, name, className, ...rest }: AddressInputProps) {
+ return (
+
+
+
+
+ );
+}
+
+export default AddressInput;
diff --git a/packages/ui/form/AddressInputLazy.tsx b/packages/ui/form/AddressInputLazy.tsx
new file mode 100644
index 0000000000..ce2280d353
--- /dev/null
+++ b/packages/ui/form/AddressInputLazy.tsx
@@ -0,0 +1,8 @@
+import dynamic from "next/dynamic";
+
+/** These are like 40kb that not every user needs */
+const AddressInput = dynamic(
+ () => import("./AddressInput")
+) as unknown as typeof import("./AddressInput").default;
+
+export default AddressInput;