Link/In person location (#2104)
This commit is contained in:
parent
3e3e802b28
commit
f0b1767b3c
4 changed files with 66 additions and 8 deletions
|
@ -1,4 +1,4 @@
|
||||||
import { PhoneIcon, XIcon } from "@heroicons/react/outline";
|
import { GlobeAltIcon, PhoneIcon, XIcon } from "@heroicons/react/outline";
|
||||||
import {
|
import {
|
||||||
ChevronRightIcon,
|
ChevronRightIcon,
|
||||||
ClockIcon,
|
ClockIcon,
|
||||||
|
@ -12,6 +12,7 @@ import {
|
||||||
UserAddIcon,
|
UserAddIcon,
|
||||||
UsersIcon,
|
UsersIcon,
|
||||||
} from "@heroicons/react/solid";
|
} from "@heroicons/react/solid";
|
||||||
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import { MembershipRole } from "@prisma/client";
|
import { MembershipRole } from "@prisma/client";
|
||||||
import { Availability, EventTypeCustomInput, PeriodType, Prisma, SchedulingType } from "@prisma/client";
|
import { Availability, EventTypeCustomInput, PeriodType, Prisma, SchedulingType } from "@prisma/client";
|
||||||
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@radix-ui/react-collapsible";
|
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@radix-ui/react-collapsible";
|
||||||
|
@ -26,6 +27,7 @@ import { Controller, useForm } from "react-hook-form";
|
||||||
import { FormattedNumber, IntlProvider } from "react-intl";
|
import { FormattedNumber, IntlProvider } from "react-intl";
|
||||||
import Select from "react-select";
|
import Select from "react-select";
|
||||||
import { JSONObject } from "superjson/dist/types";
|
import { JSONObject } from "superjson/dist/types";
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
import { StripeData } from "@calcom/stripe/server";
|
import { StripeData } from "@calcom/stripe/server";
|
||||||
import Switch from "@calcom/ui/Switch";
|
import Switch from "@calcom/ui/Switch";
|
||||||
|
@ -119,6 +121,7 @@ const EventTypePage = (props: inferSSRProps<typeof getServerSideProps>) => {
|
||||||
|
|
||||||
const defaultLocations = [
|
const defaultLocations = [
|
||||||
{ value: LocationType.InPerson, label: t("in_person_meeting") },
|
{ value: LocationType.InPerson, label: t("in_person_meeting") },
|
||||||
|
{ value: LocationType.Link, label: t("link_meeting") },
|
||||||
{ value: LocationType.Jitsi, label: "Jitsi Meet" },
|
{ value: LocationType.Jitsi, label: "Jitsi Meet" },
|
||||||
{ value: LocationType.Phone, label: t("phone_call") },
|
{ value: LocationType.Phone, label: t("phone_call") },
|
||||||
];
|
];
|
||||||
|
@ -273,6 +276,32 @@ const EventTypePage = (props: inferSSRProps<typeof getServerSideProps>) => {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
case LocationType.Link:
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<label htmlFor="address" className="block text-sm font-medium text-gray-700">
|
||||||
|
{t("set_link_meeting")}
|
||||||
|
</label>
|
||||||
|
<div className="mt-1">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
{...locationFormMethods.register("locationLink")}
|
||||||
|
id="address"
|
||||||
|
required
|
||||||
|
className="focus:border-primary-500 focus:ring-primary-500 block w-full rounded-sm border-gray-300 shadow-sm sm:text-sm"
|
||||||
|
defaultValue={
|
||||||
|
formMethods.getValues("locations").find((location) => location.type === LocationType.Link)
|
||||||
|
?.link
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
{locationFormMethods.formState.errors.locationLink && (
|
||||||
|
<p className="mt-1 text-red-500">
|
||||||
|
{locationFormMethods.formState.errors.locationLink.message}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
case LocationType.Phone:
|
case LocationType.Phone:
|
||||||
return <p className="text-sm">{t("cal_invitee_phone_number_scheduling")}</p>;
|
return <p className="text-sm">{t("cal_invitee_phone_number_scheduling")}</p>;
|
||||||
case LocationType.GoogleMeet:
|
case LocationType.GoogleMeet:
|
||||||
|
@ -351,7 +380,7 @@ const EventTypePage = (props: inferSSRProps<typeof getServerSideProps>) => {
|
||||||
schedulingType: SchedulingType | null;
|
schedulingType: SchedulingType | null;
|
||||||
price: number;
|
price: number;
|
||||||
hidden: boolean;
|
hidden: boolean;
|
||||||
locations: { type: LocationType; address?: string }[];
|
locations: { type: LocationType; address?: string; link?: string }[];
|
||||||
customInputs: EventTypeCustomInput[];
|
customInputs: EventTypeCustomInput[];
|
||||||
users: string[];
|
users: string[];
|
||||||
availability: {
|
availability: {
|
||||||
|
@ -381,11 +410,19 @@ const EventTypePage = (props: inferSSRProps<typeof getServerSideProps>) => {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const locationFormSchema = z.object({
|
||||||
|
locationType: z.string(),
|
||||||
|
locationAddress: z.string().optional(),
|
||||||
|
locationLink: z.string().url().optional(), // URL validates as new URL() - which requires HTTPS:// In the input field
|
||||||
|
});
|
||||||
|
|
||||||
const locationFormMethods = useForm<{
|
const locationFormMethods = useForm<{
|
||||||
locationType: LocationType;
|
locationType: LocationType;
|
||||||
locationAddress: string;
|
locationAddress?: string; // TODO: We should validate address or fetch the address from googles api to see if its valid?
|
||||||
}>();
|
locationLink?: string; // Currently this only accepts links that are HTTPS://
|
||||||
|
}>({
|
||||||
|
resolver: zodResolver(locationFormSchema),
|
||||||
|
});
|
||||||
const Locations = () => {
|
const Locations = () => {
|
||||||
return (
|
return (
|
||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
|
@ -422,6 +459,16 @@ const EventTypePage = (props: inferSSRProps<typeof getServerSideProps>) => {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
{location.type === LocationType.Link && (
|
||||||
|
<div className="flex flex-grow items-center">
|
||||||
|
<GlobeAltIcon className="h-6 w-6" />
|
||||||
|
<input
|
||||||
|
disabled
|
||||||
|
className="w-full border-0 bg-transparent text-sm ltr:ml-2 rtl:mr-2"
|
||||||
|
value={location.link}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
{location.type === LocationType.Phone && (
|
{location.type === LocationType.Phone && (
|
||||||
<div className="flex flex-grow items-center">
|
<div className="flex flex-grow items-center">
|
||||||
<PhoneIcon className="h-6 w-6" />
|
<PhoneIcon className="h-6 w-6" />
|
||||||
|
@ -690,10 +737,12 @@ const EventTypePage = (props: inferSSRProps<typeof getServerSideProps>) => {
|
||||||
smartContractAddress,
|
smartContractAddress,
|
||||||
beforeBufferTime,
|
beforeBufferTime,
|
||||||
afterBufferTime,
|
afterBufferTime,
|
||||||
|
locations,
|
||||||
...input
|
...input
|
||||||
} = values;
|
} = values;
|
||||||
updateMutation.mutate({
|
updateMutation.mutate({
|
||||||
...input,
|
...input,
|
||||||
|
locations,
|
||||||
availability: availabilityState,
|
availability: availabilityState,
|
||||||
periodStartDate: periodDates.startDate,
|
periodStartDate: periodDates.startDate,
|
||||||
periodEndDate: periodDates.endDate,
|
periodEndDate: periodDates.endDate,
|
||||||
|
@ -1525,6 +1574,9 @@ const EventTypePage = (props: inferSSRProps<typeof getServerSideProps>) => {
|
||||||
details = { address: values.locationAddress };
|
details = { address: values.locationAddress };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (newLocation === LocationType.Link) {
|
||||||
|
details = { link: values.locationLink };
|
||||||
|
}
|
||||||
const existingIdx = formMethods
|
const existingIdx = formMethods
|
||||||
.getValues("locations")
|
.getValues("locations")
|
||||||
.findIndex((loc) => values.locationType === loc.type);
|
.findIndex((loc) => values.locationType === loc.type);
|
||||||
|
@ -1541,7 +1593,6 @@ const EventTypePage = (props: inferSSRProps<typeof getServerSideProps>) => {
|
||||||
formMethods.getValues("locations").concat({ type: values.locationType, ...details })
|
formMethods.getValues("locations").concat({ type: values.locationType, ...details })
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
setShowLocationModal(false);
|
setShowLocationModal(false);
|
||||||
}}>
|
}}>
|
||||||
<Controller
|
<Controller
|
||||||
|
|
|
@ -405,7 +405,8 @@
|
||||||
"share_additional_notes": "Please share anything that will help prepare for our meeting.",
|
"share_additional_notes": "Please share anything that will help prepare for our meeting.",
|
||||||
"booking_confirmation": "Confirm your {{eventTypeTitle}} with {{profileName}}",
|
"booking_confirmation": "Confirm your {{eventTypeTitle}} with {{profileName}}",
|
||||||
"booking_reschedule_confirmation": "Reschedule your {{eventTypeTitle}} with {{profileName}}",
|
"booking_reschedule_confirmation": "Reschedule your {{eventTypeTitle}} with {{profileName}}",
|
||||||
"in_person_meeting": "Link or In-person meeting",
|
"in_person_meeting": "In-person meeting",
|
||||||
|
"link_meeting":"Link meeting",
|
||||||
"phone_call": "Phone call",
|
"phone_call": "Phone call",
|
||||||
"phone_number": "Phone Number",
|
"phone_number": "Phone Number",
|
||||||
"enter_phone_number": "Enter phone number",
|
"enter_phone_number": "Enter phone number",
|
||||||
|
@ -574,6 +575,7 @@
|
||||||
"calendar_days": "calendar days",
|
"calendar_days": "calendar days",
|
||||||
"business_days": "business days",
|
"business_days": "business days",
|
||||||
"set_address_place": "Set an address or place",
|
"set_address_place": "Set an address or place",
|
||||||
|
"set_link_meeting": "Set a link to the meeting",
|
||||||
"cal_invitee_phone_number_scheduling": "Cal will ask your invitee to enter a phone number before scheduling.",
|
"cal_invitee_phone_number_scheduling": "Cal will ask your invitee to enter a phone number before scheduling.",
|
||||||
"cal_provide_google_meet_location": "Cal will provide a Google Meet location.",
|
"cal_provide_google_meet_location": "Cal will provide a Google Meet location.",
|
||||||
"cal_provide_zoom_meeting_url": "Cal will provide a Zoom meeting URL.",
|
"cal_provide_zoom_meeting_url": "Cal will provide a Zoom meeting URL.",
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
export enum LocationType {
|
export enum LocationType {
|
||||||
InPerson = "inPerson",
|
InPerson = "inPerson",
|
||||||
Phone = "phone",
|
Phone = "phone",
|
||||||
|
Link = "link",
|
||||||
GoogleMeet = "integrations:google:meet",
|
GoogleMeet = "integrations:google:meet",
|
||||||
Zoom = "integrations:zoom",
|
Zoom = "integrations:zoom",
|
||||||
Daily = "integrations:daily",
|
Daily = "integrations:daily",
|
||||||
|
|
|
@ -4,7 +4,11 @@ import { LocationType } from "@calcom/lib/location";
|
||||||
import { slugify } from "@calcom/lib/slugify";
|
import { slugify } from "@calcom/lib/slugify";
|
||||||
|
|
||||||
export const eventTypeLocations = z.array(
|
export const eventTypeLocations = z.array(
|
||||||
z.object({ type: z.nativeEnum(LocationType), address: z.string().optional() })
|
z.object({
|
||||||
|
type: z.nativeEnum(LocationType),
|
||||||
|
address: z.string().optional(),
|
||||||
|
link: z.string().url().optional(),
|
||||||
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
export const eventTypeSlug = z.string().transform((val) => slugify(val.trim()));
|
export const eventTypeSlug = z.string().transform((val) => slugify(val.trim()));
|
||||||
|
|
Loading…
Reference in a new issue