From a8c4a9c4c2418f461bc80e9373468510fb8a3655 Mon Sep 17 00:00:00 2001 From: Hariom Balhara Date: Sat, 5 Nov 2022 23:30:10 +0530 Subject: [PATCH 1/7] Fix viewer.me avatar size (#5392) * Fix viewer.me avatar size * Fix for i18n-next typecheck Co-authored-by: Alex van Andel --- apps/web/lib/app-providers.tsx | 8 ++++---- packages/trpc/server/routers/viewer.tsx | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/apps/web/lib/app-providers.tsx b/apps/web/lib/app-providers.tsx index 3f3eda9d5b..c5c55a97de 100644 --- a/apps/web/lib/app-providers.tsx +++ b/apps/web/lib/app-providers.tsx @@ -1,7 +1,7 @@ import { TooltipProvider } from "@radix-ui/react-tooltip"; import { SessionProvider } from "next-auth/react"; import { EventCollectionProvider } from "next-collect/client"; -import { appWithTranslation } from "next-i18next"; +import { appWithTranslation, SSRConfig } from "next-i18next"; import { ThemeProvider } from "next-themes"; import type { AppProps as NextAppProps, AppProps as NextJsAppProps } from "next/app"; import { NextRouter } from "next/router"; @@ -14,9 +14,9 @@ import { MetaProvider } from "@calcom/ui/v2/core/Meta"; import usePublicPage from "@lib/hooks/usePublicPage"; -const I18nextAdapter = appWithTranslation(({ children }) => ( - <>{children} -)); +const I18nextAdapter = appWithTranslation & { children: React.ReactNode }>( + ({ children }) => <>{children} +); // Workaround for https://github.com/vercel/next.js/issues/8592 export type AppProps = Omit & { diff --git a/packages/trpc/server/routers/viewer.tsx b/packages/trpc/server/routers/viewer.tsx index cf0fd61815..c7c0229469 100644 --- a/packages/trpc/server/routers/viewer.tsx +++ b/packages/trpc/server/routers/viewer.tsx @@ -152,7 +152,7 @@ const loggedInViewerRouter = createProtectedRouter() locale: user.locale, timeFormat: user.timeFormat, timeZone: user.timeZone, - avatar: user.avatar, + avatar: `${CAL_URL}/${user.username}/avatar.png`, createdDate: user.createdDate, trialEndsAt: user.trialEndsAt, completedOnboarding: user.completedOnboarding, From 5954144b98396a66db01ebfff2344379833eb712 Mon Sep 17 00:00:00 2001 From: Hariom Balhara Date: Sat, 5 Nov 2022 23:43:34 +0530 Subject: [PATCH 2/7] Make sure body being jsoned is fresh (#5371) Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> Co-authored-by: alannnc --- packages/app-store/zoomvideo/lib/VideoApiAdapter.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/app-store/zoomvideo/lib/VideoApiAdapter.ts b/packages/app-store/zoomvideo/lib/VideoApiAdapter.ts index 3d2bce6b68..1ed1f2016c 100644 --- a/packages/app-store/zoomvideo/lib/VideoApiAdapter.ts +++ b/packages/app-store/zoomvideo/lib/VideoApiAdapter.ts @@ -326,6 +326,7 @@ const ZoomVideoApiAdapter = (credential: CredentialPayload): VideoApiAdapter => const handleZoomResponse = async (response: Response, credentialId: Credential["id"]) => { let _response = response.clone(); + const responseClone = response.clone(); if (_response.headers.get("content-encoding") === "gzip") { const responseString = await response.text(); _response = JSON.parse(responseString); @@ -339,7 +340,7 @@ const handleZoomResponse = async (response: Response, credentialId: Credential[" throw Error(response.statusText); } - return response.json(); + return responseClone.json(); }; const invalidateCredential = async (credentialId: Credential["id"]) => { From 7517feb62a535bac174d23cca4204bc715afc5fc Mon Sep 17 00:00:00 2001 From: alannnc Date: Sat, 5 Nov 2022 11:47:29 -0700 Subject: [PATCH 3/7] fix/success-page-seats-description-5338 (#5390) * Add validations for server side loading of bookingInfo for seats event types * Fix prop used for validating if booking has seats enabled * Fix types * Removed frontend filter for seatsShowAttendees as not needed anymore Co-authored-by: Alex van Andel --- apps/web/pages/success.tsx | 49 +++++++++++++++++++++++++------------- 1 file changed, 32 insertions(+), 17 deletions(-) diff --git a/apps/web/pages/success.tsx b/apps/web/pages/success.tsx index 9a0bf0f740..5fb9fa8172 100644 --- a/apps/web/pages/success.tsx +++ b/apps/web/pages/success.tsx @@ -369,23 +369,12 @@ export default function Success(props: SuccessProps) {

{bookingInfo.user.email}

)} - {!eventType.seatsShowAttendees - ? bookingInfo?.attendees - .filter((attendee) => attendee.email === email) - .map((attendee) => ( -
-

{attendee.name}

-

{attendee.email}

-
- )) - : bookingInfo?.attendees.map((attendee, index) => ( -
-

{attendee.name}

-

{attendee.email}

-
- ))} + {bookingInfo?.attendees.map((attendee, index) => ( +
+

{attendee.name}

+

{attendee.email}

+
+ ))} @@ -786,6 +775,28 @@ const schema = z.object({ bookingId: strToNumber, }); +const handleSeatsEventTypeOnBooking = ( + eventType: { + seatsPerTimeSlot?: boolean | null; + seatsShowAttendees: boolean | null; + [x: string | number | symbol]: unknown; + }, + booking: Partial< + Prisma.BookingGetPayload<{ include: { attendees: { select: { name: true; email: true } } } }> + >, + email: string +) => { + if (eventType?.seatsPerTimeSlot !== null) { + // @TODO: right now bookings with seats doesn't save every description that its entered by every user + delete booking.description; + } + if (!eventType.seatsShowAttendees) { + const attendee = booking?.attendees?.find((a) => a.email === email); + booking["attendees"] = attendee ? [attendee] : []; + } + return; +}; + export async function getServerSideProps(context: GetServerSidePropsContext) { const ssr = await ssrInit(context); const parsedQuery = schema.safeParse(context.query); @@ -884,6 +895,10 @@ export async function getServerSideProps(context: GetServerSidePropsContext) { }, }, }); + if (bookingInfo !== null && email) { + handleSeatsEventTypeOnBooking(eventType, bookingInfo, email); + } + let recurringBookings = null; if (recurringEventIdQuery) { // We need to get the dates for the bookings to be able to show them in the UI From dff08dc5b939a57ddeea8a1f4902a3c8b8ae0a00 Mon Sep 17 00:00:00 2001 From: alannnc Date: Sat, 5 Nov 2022 11:58:35 -0700 Subject: [PATCH 4/7] fix/google-calendar-removes-attendees-when-seats-5336 (#5366) * Adding missing code that copied booking attendees to event attendees * Clean up code * Fix types * Fix types * Doing deep clone for sending seats email --- .../v2/eventtype/EventAdvancedTab.tsx | 7 ++-- .../googlecalendar/lib/CalendarService.ts | 9 +++-- packages/core/EventManager.ts | 38 +++++++++++++------ .../features/bookings/lib/handleNewBooking.ts | 23 ++++++++--- .../components/TeamAvailabilityScreen.tsx | 2 +- .../components/v2/TeamAvailabilityScreen.tsx | 2 +- .../components/NewScheduleButton.tsx | 2 +- .../features/webhooks/pages/webhooks-view.tsx | 2 +- packages/ui/v2/core/apps/AppCard.tsx | 2 +- 9 files changed, 57 insertions(+), 30 deletions(-) diff --git a/apps/web/components/v2/eventtype/EventAdvancedTab.tsx b/apps/web/components/v2/eventtype/EventAdvancedTab.tsx index a6c882cac0..d73716c243 100644 --- a/apps/web/components/v2/eventtype/EventAdvancedTab.tsx +++ b/apps/web/components/v2/eventtype/EventAdvancedTab.tsx @@ -54,7 +54,7 @@ export const EventAdvancedTab = ({ eventType, team }: Pick {/** * Only display calendar selector if user has connected calendars AND if it's not - * a team event. Since we don't have logic to handle each attende calendar (for now). + * a team event. Since we don't have logic to handle each attendee calendar (for now). * This will fallback to each user selected destination calendar. */} {!!connectedCalendarsQuery.data?.connectedCalendars.length && !team && ( @@ -292,7 +292,7 @@ export const EventAdvancedTab = ({ eventType, team }: Pick { - // Enabling seats will disable guests and requiring confimation until fully supported + // Enabling seats will disable guests and requiring confirmation until fully supported if (e) { formMethods.setValue("disableGuests", true); formMethods.setValue("requiresConfirmation", false); @@ -316,9 +316,10 @@ export const EventAdvancedTab = ({ eventType, team }: Pick{t("seats")}} onChange={(e) => { - onChange(Number(e.target.value)); + onChange(Math.abs(Number(e.target.value))); }} />
diff --git a/packages/app-store/googlecalendar/lib/CalendarService.ts b/packages/app-store/googlecalendar/lib/CalendarService.ts index f6f8a1316d..1b81ca52f7 100644 --- a/packages/app-store/googlecalendar/lib/CalendarService.ts +++ b/packages/app-store/googlecalendar/lib/CalendarService.ts @@ -160,6 +160,10 @@ export default class GoogleCalendarService implements Calendar { async updateEvent(uid: string, event: CalendarEvent, externalCalendarId: string): Promise { return new Promise(async (resolve, reject) => { const myGoogleAuth = await this.auth.getToken(); + const eventAttendees = event.attendees.map(({ id, ...rest }) => ({ + ...rest, + responseStatus: "accepted", + })); const payload: calendar_v3.Schema$Event = { summary: event.title, description: getRichDescription(event), @@ -179,10 +183,7 @@ export default class GoogleCalendarService implements Calendar { responseStatus: "accepted", }, // eslint-disable-next-line - ...event.attendees.map(({ id, ...rest }) => ({ - ...rest, - responseStatus: "accepted", - })), + ...eventAttendees, ], reminders: { useDefault: true, diff --git a/packages/core/EventManager.ts b/packages/core/EventManager.ts index 67f5c5858b..8479718d94 100644 --- a/packages/core/EventManager.ts +++ b/packages/core/EventManager.ts @@ -7,8 +7,14 @@ import { FAKE_DAILY_CREDENTIAL } from "@calcom/app-store/dailyvideo/lib/VideoApi import { getEventLocationTypeFromApp } from "@calcom/app-store/locations"; import getApps from "@calcom/app-store/utils"; import prisma from "@calcom/prisma"; +import { Attendee } from "@calcom/prisma/client"; import { createdEventSchema } from "@calcom/prisma/zod-utils"; -import type { AdditionalInformation, CalendarEvent, NewCalendarEventType } from "@calcom/types/Calendar"; +import type { + AdditionalInformation, + CalendarEvent, + NewCalendarEventType, + Person, +} from "@calcom/types/Calendar"; import { CredentialPayload, CredentialWithAppName } from "@calcom/types/Credential"; import type { Event } from "@calcom/types/Event"; import type { @@ -294,6 +300,7 @@ export default class EventManager { } public async updateCalendarAttendees(event: CalendarEvent, booking: PartialBooking) { + // @NOTE: This function is only used for updating attendees on a calendar event. Can we remove this? await this.updateAllCalendarEvents(event, booking); } @@ -430,11 +437,14 @@ export default class EventManager { try { // Bookings should only have one calendar reference calendarReference = booking.references.filter((reference) => reference.type.includes("_calendar"))[0]; - if (!calendarReference) throw new Error("bookingRef"); - + if (!calendarReference) { + throw new Error("bookingRef"); + } const { uid: bookingRefUid, externalCalendarId: bookingExternalCalendarId } = calendarReference; - if (!bookingExternalCalendarId) throw new Error("externalCalendarId"); + if (!bookingExternalCalendarId) { + throw new Error("externalCalendarId"); + } let result = []; if (calendarReference.credentialId) { @@ -458,13 +468,15 @@ export default class EventManager { .map(async (cred) => { const calendarReference = booking.references.find((ref) => ref.type === cred.type); if (!calendarReference) - return { - appName: cred.appName, - type: cred.type, - success: false, - uid: "", - originalEvent: event, - }; + if (!calendarReference) { + return { + appName: cred.appName, + type: cred.type, + success: false, + uid: "", + originalEvent: event, + }; + } const { externalCalendarId: bookingExternalCalendarId, meetingId: bookingRefUid } = calendarReference; return await updateEvent(cred, event, bookingRefUid ?? null, bookingExternalCalendarId ?? null); @@ -474,7 +486,9 @@ export default class EventManager { return Promise.all(result); } catch (error) { let message = `Tried to 'updateAllCalendarEvents' but there was no '{thing}' for '${credential?.type}', userId: '${credential?.userId}', bookingId: '${booking?.id}'`; - if (error instanceof Error) message = message.replace("{thing}", error.message); + if (error instanceof Error) { + message = message.replace("{thing}", error.message); + } console.error(message); return Promise.resolve([ { diff --git a/packages/features/bookings/lib/handleNewBooking.ts b/packages/features/bookings/lib/handleNewBooking.ts index 80d477957f..d0c0fa4262 100644 --- a/packages/features/bookings/lib/handleNewBooking.ts +++ b/packages/features/bookings/lib/handleNewBooking.ts @@ -7,6 +7,7 @@ import { WebhookTriggerEvents, } from "@prisma/client"; import async from "async"; +import { cloneDeep } from "lodash"; import type { NextApiRequest } from "next"; import { RRule } from "rrule"; import short from "short-uuid"; @@ -482,8 +483,9 @@ async function handler(req: NextApiRequest & { userId?: number | undefined }) { // For seats, if the booking already exists then we want to add the new attendee to the existing booking if (reqBody.bookingUid) { - if (!eventType.seatsPerTimeSlot) + if (!eventType.seatsPerTimeSlot) { throw new HttpError({ statusCode: 404, message: "Event type does not have seats" }); + } const booking = await prisma.booking.findUnique({ where: { @@ -505,7 +507,9 @@ async function handler(req: NextApiRequest & { userId?: number | undefined }) { }, }, }); - if (!booking) throw new HttpError({ statusCode: 404, message: "Booking not found" }); + if (!booking) { + throw new HttpError({ statusCode: 404, message: "Booking not found" }); + } // Need to add translation for attendees to pass type checks. Since these values are never written to the db we can just use the new attendee language const bookingAttendees = booking.attendees.map((attendee) => { @@ -514,11 +518,13 @@ async function handler(req: NextApiRequest & { userId?: number | undefined }) { evt = { ...evt, attendees: [...bookingAttendees, invitee[0]] }; - if (eventType.seatsPerTimeSlot <= booking.attendees.length) + if (eventType.seatsPerTimeSlot <= booking.attendees.length) { throw new HttpError({ statusCode: 409, message: "Booking seats are full" }); + } - if (booking.attendees.some((attendee) => attendee.email === invitee[0].email)) + if (booking.attendees.find((attendee) => attendee.email === invitee[0].email)) { throw new HttpError({ statusCode: 409, message: "Already signed up for time slot" }); + } await prisma.booking.update({ where: { @@ -537,8 +543,13 @@ async function handler(req: NextApiRequest & { userId?: number | undefined }) { }); const newSeat = booking.attendees.length !== 0; - - await sendScheduledSeatsEmails(evt, invitee[0], newSeat, !!eventType.seatsShowAttendees); + /** + * Remember objects are passed into functions as references + * so if you modify it in a inner function it will be modified in the outer function + * deep cloning evt to avoid this + */ + const copyEvent = cloneDeep(evt); + await sendScheduledSeatsEmails(copyEvent, invitee[0], newSeat, !!eventType.seatsShowAttendees); const credentials = await refreshCredentials(organizerUser.credentials); const eventManager = new EventManager({ ...organizerUser, credentials }); diff --git a/packages/features/ee/teams/components/TeamAvailabilityScreen.tsx b/packages/features/ee/teams/components/TeamAvailabilityScreen.tsx index c5e7f70bbe..ea75cd1f5e 100644 --- a/packages/features/ee/teams/components/TeamAvailabilityScreen.tsx +++ b/packages/features/ee/teams/components/TeamAvailabilityScreen.tsx @@ -5,9 +5,9 @@ import { FixedSizeList as List } from "react-window"; import dayjs from "@calcom/dayjs"; import { CAL_URL } from "@calcom/lib/constants"; import { inferQueryOutput, trpc } from "@calcom/trpc/react"; +import { Avatar } from "@calcom/ui/components/avatar"; import Select from "@calcom/ui/form/Select"; import TimezoneSelect, { ITimezone } from "@calcom/ui/form/TimezoneSelect"; -import { Avatar } from "@calcom/ui/components/avatar"; import DatePicker from "@calcom/ui/v2/core/form/DatePicker"; import TeamAvailabilityTimes from "./TeamAvailabilityTimes"; diff --git a/packages/features/ee/teams/components/v2/TeamAvailabilityScreen.tsx b/packages/features/ee/teams/components/v2/TeamAvailabilityScreen.tsx index c5e7f70bbe..ea75cd1f5e 100644 --- a/packages/features/ee/teams/components/v2/TeamAvailabilityScreen.tsx +++ b/packages/features/ee/teams/components/v2/TeamAvailabilityScreen.tsx @@ -5,9 +5,9 @@ import { FixedSizeList as List } from "react-window"; import dayjs from "@calcom/dayjs"; import { CAL_URL } from "@calcom/lib/constants"; import { inferQueryOutput, trpc } from "@calcom/trpc/react"; +import { Avatar } from "@calcom/ui/components/avatar"; import Select from "@calcom/ui/form/Select"; import TimezoneSelect, { ITimezone } from "@calcom/ui/form/TimezoneSelect"; -import { Avatar } from "@calcom/ui/components/avatar"; import DatePicker from "@calcom/ui/v2/core/form/DatePicker"; import TeamAvailabilityTimes from "./TeamAvailabilityTimes"; diff --git a/packages/features/schedules/components/NewScheduleButton.tsx b/packages/features/schedules/components/NewScheduleButton.tsx index c7ef6c6152..c6df6cff18 100644 --- a/packages/features/schedules/components/NewScheduleButton.tsx +++ b/packages/features/schedules/components/NewScheduleButton.tsx @@ -6,8 +6,8 @@ import { HttpError } from "@calcom/lib/http-error"; import { trpc } from "@calcom/trpc/react"; import { Dialog, DialogClose, DialogContent, DialogTrigger } from "@calcom/ui/Dialog"; import { Icon } from "@calcom/ui/Icon"; -import { Form } from "@calcom/ui/form/fields"; import { Button } from "@calcom/ui/components/button"; +import { Form } from "@calcom/ui/form/fields"; import showToast from "@calcom/ui/v2/core/notifications"; export function NewScheduleButton({ name = "new-schedule" }: { name?: string }) { diff --git a/packages/features/webhooks/pages/webhooks-view.tsx b/packages/features/webhooks/pages/webhooks-view.tsx index 96a7ef378c..afcfdff33d 100644 --- a/packages/features/webhooks/pages/webhooks-view.tsx +++ b/packages/features/webhooks/pages/webhooks-view.tsx @@ -5,8 +5,8 @@ import { WEBAPP_URL } from "@calcom/lib/constants"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import { trpc } from "@calcom/trpc/react"; import { Icon } from "@calcom/ui"; -import { EmptyScreen, SkeletonText } from "@calcom/ui/v2"; import { Button } from "@calcom/ui/components/button"; +import { EmptyScreen, SkeletonText } from "@calcom/ui/v2"; import Meta from "@calcom/ui/v2/core/Meta"; import { getLayout } from "@calcom/ui/v2/core/layouts/SettingsLayout"; diff --git a/packages/ui/v2/core/apps/AppCard.tsx b/packages/ui/v2/core/apps/AppCard.tsx index 903251d456..7594f3571d 100644 --- a/packages/ui/v2/core/apps/AppCard.tsx +++ b/packages/ui/v2/core/apps/AppCard.tsx @@ -5,8 +5,8 @@ import { InstallAppButton } from "@calcom/app-store/components"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import { App } from "@calcom/types/App"; import { Icon } from "@calcom/ui/Icon"; -import { showToast } from "@calcom/ui/v2"; import { Button } from "@calcom/ui/components/button"; +import { showToast } from "@calcom/ui/v2"; interface AppCardProps { app: App; From a419613d293568b3209f593e9ddf01bb3b4f8c42 Mon Sep 17 00:00:00 2001 From: Nafees Nazik <84864519+G3root@users.noreply.github.com> Date: Sun, 6 Nov 2022 00:56:48 +0530 Subject: [PATCH 5/7] chore: convert anchor tag to p tag (#5370) Co-authored-by: Peer Richelsen --- apps/web/components/booking/AvailableEventLocations.tsx | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/apps/web/components/booking/AvailableEventLocations.tsx b/apps/web/components/booking/AvailableEventLocations.tsx index 531c0a142c..31dcada5e2 100644 --- a/apps/web/components/booking/AvailableEventLocations.tsx +++ b/apps/web/components/booking/AvailableEventLocations.tsx @@ -24,14 +24,7 @@ export function AvailableEventLocations({ locations }: { locations: Props["event alt={`${eventLocationType.label} icon`} /> - - {locationKeyToString(location)} - +

{locationKeyToString(location)}

); From 0fa7982084b41ad8e7ce992a029d868c32f46d5d Mon Sep 17 00:00:00 2001 From: Om Ray <38233712+om-ray@users.noreply.github.com> Date: Sat, 5 Nov 2022 16:10:10 -0400 Subject: [PATCH 6/7] 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 --- .../components/booking/pages/BookingPage.tsx | 43 +++++++++++++--- .../components/dialog/EditLocationDialog.tsx | 5 +- .../v2/eventtype/EditLocationDialog.tsx | 6 +++ apps/web/pages/event-types/[type]/index.tsx | 1 + packages/app-store/locations.ts | 29 +++++++++-- packages/ui/form/AddressInput.tsx | 51 +++++++++++++++++++ packages/ui/form/AddressInputLazy.tsx | 8 +++ 7 files changed, 130 insertions(+), 13 deletions(-) create mode 100644 packages/ui/form/AddressInput.tsx create mode 100644 packages/ui/form/AddressInputLazy.tsx 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 && (
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; From bf68ec1ad52b7bdc888a7830bd128c0b165020a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20M=C3=BCller?= <48084558+rene-mueller@users.noreply.github.com> Date: Sat, 5 Nov 2022 21:20:59 +0100 Subject: [PATCH 7/7] use LOGO constant instead of fixed string in OgImages (#5234) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * use LOGO constant instead of fixed string in OgImages * fixed linting error Co-authored-by: René Müller Co-authored-by: Peer Richelsen --- packages/lib/OgImages.tsx | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/packages/lib/OgImages.tsx b/packages/lib/OgImages.tsx index 92d433c2ac..071ef5a60a 100644 --- a/packages/lib/OgImages.tsx +++ b/packages/lib/OgImages.tsx @@ -1,6 +1,6 @@ import React from "react"; -import { CAL_URL } from "./constants"; +import { CAL_URL, LOGO } from "./constants"; // Ensures tw prop is typed. declare module "react" { @@ -119,7 +119,7 @@ export const Meeting = ({ title, users = [], profile }: MeetingImageProps) => {
- Logo + Logo {avatars.length > 0 &&
/
}
{avatars.slice(0, 3).map((avatar) => ( @@ -189,12 +189,7 @@ const VisualBlur = ({ visualSlug }: { visualSlug: string }) => { export const App = ({ name, description, slug }: AppImageProps) => ( - Logo + Logo