Guests will be able to add their own location for in person meetings (#5282)

* Guests will be able to add their own location or in person meetings

* fix type error

* fixed issue with attendees location not showing on success page

* google meet booking will now go through, removed unnecessary change

* filter out attendee address when editing location

Co-authored-by: Peer Richelsen <peeroke@gmail.com>
This commit is contained in:
Om RayandPeer Richelsen authored and GitHub committed 2022-11-05 20:10:10 +00:00
1 parent a419613d29
commit 0fa7982084
7 files changed
+130 -13

No files matched your search

@@ -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 && (
<div className="mb-4">
<label
htmlFor="phone"
htmlFor={
selectedLocationType === LocationType.Phone
? "phone"
: selectedLocationType === LocationType.AttendeeInPerson
? "attendeeAddress"
: ""
}
className="block text-sm font-medium text-gray-700 dark:text-white">
{t("phone_number")}
{selectedLocationType === LocationType.Phone
? t("phone_number")
: selectedLocationType === LocationType.AttendeeInPerson
? t("Address")
: ""}
</label>
<div className="mt-1">
<AttendeeInput<BookingFormValues>
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}
/>
@@ -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
@@ -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,
@@ -56,6 +56,7 @@ export type FormValues = {
locations: {
type: EventLocationType["type"];
address?: string;
attendeeAddress?: string;
link?: string;
hostPhoneNumber?: string;
displayLocationPublicly?: boolean;
+24 -5
View File
@@ -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<Record<"address" | "link" | "hostPhoneNumber" | "phone", string>>;
} & Partial<Record<"address" | "attendeeAddress" | "link" | "hostPhoneNumber" | "phone", string>>;
// integrations:jitsi | 919999999999 | Delhi | https://manual.meeting.link | Around Video
export type BookingLocationValue = string;
+51
View File
@@ -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<FormValues> = Props<
{
value: string;
id: string;
placeholder: string;
required: boolean;
bookingForm: UseFormReturn<BookingFormValues>;
},
FormValues
>;
function AddressInput<FormValues>({ bookingForm, name, className, ...rest }: AddressInputProps<FormValues>) {
return (
<div className="relative ">
<Icon.FiMapPin color="#D2D2D2" className="absolute top-1/2 left-0.5 ml-3 h-6 -translate-y-1/2" />
<input
{...rest}
{...bookingForm.register("attendeeAddress")}
name={name}
color="#D2D2D2"
className={`${className} border-1 focus-within:border-brand dark:bg-darkgray-100 dark:border-darkgray-300 block h-10 w-full rounded-md border border-gray-300 py-px pl-10 text-sm outline-none ring-black focus-within:ring-1 disabled:text-gray-500 disabled:opacity-50 dark:text-white dark:placeholder-gray-500 dark:selection:bg-green-500 disabled:dark:text-gray-500`}
/>
</div>
);
}
export default AddressInput;
+8
View File
@@ -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;