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 { | ||||
|   ChevronRightIcon, | ||||
|   ClockIcon, | ||||
|  | @ -12,6 +12,7 @@ import { | |||
|   UserAddIcon, | ||||
|   UsersIcon, | ||||
| } from "@heroicons/react/solid"; | ||||
| import { zodResolver } from "@hookform/resolvers/zod"; | ||||
| import { MembershipRole } from "@prisma/client"; | ||||
| import { Availability, EventTypeCustomInput, PeriodType, Prisma, SchedulingType } from "@prisma/client"; | ||||
| 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 Select from "react-select"; | ||||
| import { JSONObject } from "superjson/dist/types"; | ||||
| import { z } from "zod"; | ||||
| 
 | ||||
| import { StripeData } from "@calcom/stripe/server"; | ||||
| import Switch from "@calcom/ui/Switch"; | ||||
|  | @ -119,6 +121,7 @@ const EventTypePage = (props: inferSSRProps<typeof getServerSideProps>) => { | |||
| 
 | ||||
|   const defaultLocations = [ | ||||
|     { value: LocationType.InPerson, label: t("in_person_meeting") }, | ||||
|     { value: LocationType.Link, label: t("link_meeting") }, | ||||
|     { value: LocationType.Jitsi, label: "Jitsi Meet" }, | ||||
|     { value: LocationType.Phone, label: t("phone_call") }, | ||||
|   ]; | ||||
|  | @ -273,6 +276,32 @@ const EventTypePage = (props: inferSSRProps<typeof getServerSideProps>) => { | |||
|             </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: | ||||
|         return <p className="text-sm">{t("cal_invitee_phone_number_scheduling")}</p>; | ||||
|       case LocationType.GoogleMeet: | ||||
|  | @ -351,7 +380,7 @@ const EventTypePage = (props: inferSSRProps<typeof getServerSideProps>) => { | |||
|     schedulingType: SchedulingType | null; | ||||
|     price: number; | ||||
|     hidden: boolean; | ||||
|     locations: { type: LocationType; address?: string }[]; | ||||
|     locations: { type: LocationType; address?: string; link?: string }[]; | ||||
|     customInputs: EventTypeCustomInput[]; | ||||
|     users: string[]; | ||||
|     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<{ | ||||
|     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 = () => { | ||||
|     return ( | ||||
|       <div className="w-full"> | ||||
|  | @ -422,6 +459,16 @@ const EventTypePage = (props: inferSSRProps<typeof getServerSideProps>) => { | |||
|                       /> | ||||
|                     </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 && ( | ||||
|                     <div className="flex flex-grow items-center"> | ||||
|                       <PhoneIcon className="h-6 w-6" /> | ||||
|  | @ -690,10 +737,12 @@ const EventTypePage = (props: inferSSRProps<typeof getServerSideProps>) => { | |||
|                       smartContractAddress, | ||||
|                       beforeBufferTime, | ||||
|                       afterBufferTime, | ||||
|                       locations, | ||||
|                       ...input | ||||
|                     } = values; | ||||
|                     updateMutation.mutate({ | ||||
|                       ...input, | ||||
|                       locations, | ||||
|                       availability: availabilityState, | ||||
|                       periodStartDate: periodDates.startDate, | ||||
|                       periodEndDate: periodDates.endDate, | ||||
|  | @ -1525,6 +1574,9 @@ const EventTypePage = (props: inferSSRProps<typeof getServerSideProps>) => { | |||
|                       details = { address: values.locationAddress }; | ||||
|                     } | ||||
| 
 | ||||
|                     if (newLocation === LocationType.Link) { | ||||
|                       details = { link: values.locationLink }; | ||||
|                     } | ||||
|                     const existingIdx = formMethods | ||||
|                       .getValues("locations") | ||||
|                       .findIndex((loc) => values.locationType === loc.type); | ||||
|  | @ -1541,7 +1593,6 @@ const EventTypePage = (props: inferSSRProps<typeof getServerSideProps>) => { | |||
|                         formMethods.getValues("locations").concat({ type: values.locationType, ...details }) | ||||
|                       ); | ||||
|                     } | ||||
| 
 | ||||
|                     setShowLocationModal(false); | ||||
|                   }}> | ||||
|                   <Controller | ||||
|  |  | |||
|  | @ -405,7 +405,8 @@ | |||
|   "share_additional_notes": "Please share anything that will help prepare for our meeting.", | ||||
|   "booking_confirmation": "Confirm 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_number": "Phone Number", | ||||
|   "enter_phone_number": "Enter phone number", | ||||
|  | @ -574,6 +575,7 @@ | |||
|   "calendar_days": "calendar days", | ||||
|   "business_days": "business days", | ||||
|   "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_provide_google_meet_location": "Cal will provide a Google Meet location.", | ||||
|   "cal_provide_zoom_meeting_url": "Cal will provide a Zoom meeting URL.", | ||||
|  |  | |||
|  | @ -1,6 +1,7 @@ | |||
| export enum LocationType { | ||||
|   InPerson = "inPerson", | ||||
|   Phone = "phone", | ||||
|   Link = "link", | ||||
|   GoogleMeet = "integrations:google:meet", | ||||
|   Zoom = "integrations:zoom", | ||||
|   Daily = "integrations:daily", | ||||
|  |  | |||
|  | @ -4,7 +4,11 @@ import { LocationType } from "@calcom/lib/location"; | |||
| import { slugify } from "@calcom/lib/slugify"; | ||||
| 
 | ||||
| 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())); | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue
	
	 sean-brydon
						sean-brydon