2022-04-27 15:19:04 +00:00
import { zodResolver } from "@hookform/resolvers/zod" ;
2022-09-29 16:58:29 +00:00
import { useMutation } from "@tanstack/react-query" ;
2022-03-15 14:39:20 +00:00
import { useSession } from "next-auth/react" ;
2023-03-02 18:15:28 +00:00
import dynamic from "next/dynamic" ;
2021-09-22 19:52:38 +00:00
import Head from "next/head" ;
import { useRouter } from "next/router" ;
2023-03-22 10:15:16 +00:00
import { useEffect , useMemo , useReducer , useState } from "react" ;
2023-03-02 18:15:28 +00:00
import { useForm , useFormContext } from "react-hook-form" ;
2022-05-05 21:16:25 +00:00
import { v4 as uuidv4 } from "uuid" ;
2022-04-27 15:19:04 +00:00
import { z } from "zod" ;
2021-09-22 19:52:38 +00:00
2022-10-14 16:24:43 +00:00
import BookingPageTagManager from "@calcom/app-store/BookingPageTagManager" ;
2023-02-16 22:39:57 +00:00
import type { EventLocationType } from "@calcom/app-store/locations" ;
2022-07-28 19:58:26 +00:00
import { createPaymentLink } from "@calcom/app-store/stripepayment/lib/client" ;
2022-10-14 16:24:43 +00:00
import { getEventTypeAppData } from "@calcom/app-store/utils" ;
2023-02-16 22:39:57 +00:00
import type { LocationObject } from "@calcom/core/location" ;
2022-06-28 20:40:58 +00:00
import dayjs from "@calcom/dayjs" ;
2022-05-27 15:37:02 +00:00
import {
useEmbedNonStylesConfig ,
2022-12-13 07:23:26 +00:00
useEmbedUiConfig ,
2022-05-27 15:37:02 +00:00
useIsBackgroundTransparent ,
useIsEmbed ,
} from "@calcom/embed-core/embed-iframe" ;
New Booker Component (preparations for booker atom) (#6792)
* Wip on booker atom
* Wip on booker atom
* Added correct icon imports
* Fixed build
* Responsive improvements
* Removed package lock
* Responsive tweaks
* Animation improvements and cleanup
* Animation improvements and event meta layout improvements.
* Tweaked margins.
* Added more event meta blocks
* Layout tweaks
* Converted booker layout to css grid and implemented multiple layout options
* cleanup
* Fixed build
* Fixed build
* Added temporary api route to enable/disable new booker
* Added sticky behavior
* Reverted yarn.lock and reinstalled new packages to see if this fixes build on vercel.
* Ensure divider lines always have 100% height.
* Improved animation config + initial load
* Ensure to pass eventid to getschedule, otherwise custom availability schedule wont work and wont return any availability
* Fixed divider line heights in booker
* Fixed timezone select positioning
* Added ability to view multiple days of timeslots
* Added icons to booker toggle
* Always show timeslots in timeslots view, also if no date is selected yet. In that case we show upcoming 5 days.
* Fixed timeslots in small calendar view
* Show selected day in calendar
* Fixed booker timeslots view
* Wip in making booking form work
* Moved most of the booker atom stuff to features, since it belongs there. Atom should be a rather small wrapper.
* Added create event functionality to booker form.
* Added guests toggle to booker form and styled input addons in dark mode.
* Added dynamic weekstart to booker
* Added seats limit feature to timeslots.
* Removed todo
* Added correct event avatars
* Added correct event name and icons
* Added correct translation for minutes text in multi duration
* Add rescheduling functionality to new booker.
* Added selected booking time to booking meta in sidebar.
* Abstracted away timeformat to custom hook
* Added correct key props to all components in booker.
* Fix build
* Create some new custom hooks to have a lot less repitition in code.
* Moved bookerform component inside booker directory since it is tied to it.
* Added error messages to booker form, plus fixed bug in recurring events.
* Added some comments <3
* Fixed todos in booker form.
* Added loading state for timeslot selector, and added prefetching of next month, in case of multi day view showing 2 months at the same time.
* Fixed import paths
* Added away view
* Validate uniqueness of event attendees.
* Tweaked comment
* #5798 added correct date format and style for selected date in booker.
* UI improvements
* Enable possibility to add booking values via query params.
* Added functionality to update query params when user selects date/duration etc in booker
* First steps in adding e2e test.
* Fixes after merge with main, and added new form builder.
* Implemented new form types and validation to booker, confirming new form builder. Validation still throwing wrong error keys though.
* Added search to timezone dropdown
* Added e2e test for booker (copy of current booker tests, only enabling cookie), plus fixed reschedule view.
* Updated yarn.lock
* Added new booker for team pages.
* Fixed input addon (hover) styles.
* Added dynamic booking.
* Hide timeformat select for multi day view for now.
* Cleanup and ui tweaks
* removed log
* Mobile improvements
* Cleanup
* Small design tweaks after talking to ciaran.
* Text color and weight tweaks in booker
* Added rainbow gates to new booker.
* Added in default values which fixes form vallidation (???).
* Added empty defaults for name and email
* Added metadata
* Reset yarn.lock
* Fixed booker zod validation after change in main.
* Icon tweak
* Fixed timezone select styles after new classnames have been merged.
* Updated seat availability styles.
* Update yarn.lock
* Added explanation for alchemy key to .env.example
* Added tooltip to booker month/week/multiday toggle
* Fixed timezoneselect styles in booker after select updates.
* Updates bookingfields component by taking changes from current booker component
* Removed remaining booker todos
* Fix bookeventform
* Fix for recurring event meta
* Type fixes
* Typefixes
* Team event fixes
* Avoid hydration errors by only rendering date picker client side. Remove web3 gates since we dont offer them anymore. Prevent timeslot select from staying open when switching to a different month.
* Don't show calendar on mobile booker during booking.
* Always align booker buttons to bottom
* Don't show backend messages in error, rather show a helpful text like the current booker does as well.
* Do invisible next rewrite based on cookie from next.config.js (#7949)
* Do invisible next rewrite based on cookie from next.config.js
* Name embed link instead of bookerPath
* Rewrites only dynamic user pages
---------
Co-authored-by: zomars <zomars@me.com>
* Don't allow change of timezone when bookerform is visible
* Don't add duration to query param if the event is not a multi duration event.
* Update next.config.js
* Added correct timezone formatting to event meta when timeslot is selected.
* removed .env variable that isn't needed anymore.
* Update Gates.tsx
* Type fixes
* Allows to run all tests with the new booker
* Fixed timezone select styles after merge.
* Don't throw error when event doesn't have hosts, rather return no users, which will result in no availability in UI.
* Make booker errors of severity info instead of warning.
* Ensure team avatars are shown, as well as filter on uniqueness of avatars.
* Added all booked today message to timeslots.
* Added cal.com logo to booker.
* Fixed fragment classname error, minor mobile animation tweaks plus make all booked today text smaller for multi day layout.
* Improved timezone select styles, and updated arguments of getbooking function after updates in main.
* Prevent infinite loop in rewriting new booker.
* Prevent infinite loop in rewriting new booker.
* Moved new-booker pages to their own directory to prevent regexes confusing next and thus nut running getserversideprops after rewrite. Also adding clearing of old date in booker store, that could stick around when user immediately navigates back to the same page after booking.
* Fixed cal logo color in darkmode for new booker.
* Implemented new color tokens and theme variables. Also small design tweaks after merge with main.
* Minor style tweaks
* Show multiple locations in tooltip on booker #8222
* Radio button style tweaks
* Fixed build
* Updated calendar imports to new lucide names
* Removed resetting of selected times logic, because otherwise url params wouldnt be taken into account which is actually what we want. So old values sticking around when navigating back is actually the desired behavior.
* Updated tests to instead of always run the new booker in tests, have a utility to run both the new and old booker for specified tests.
* Added comment and eslint disable for if statement in booker test.
* Update packages/features/bookings/components/event-meta/Details.tsx
Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
* Fix badge types
* Lazy loaded timezone select to save 85kb in bundle size.
* Upgraded framer to latest. Als moved framer and react sticky deps to features instead of atoms.
* Added new pagewrapper logic
* Simplified rescheduling ssr fetches, this now also supports multi seat rescheduling.
* Unset selected time when user is rescheduling directly after a new booking, otherwise it would show the form instead of new time selection.
* Updated form builder logic as per form builder in current booker.
* Updated form builder prefill logic as per logic in current booker.
* Updated getbooking function to fetch correct details when a reschedule uid is used
* Fixed booking questions test by NOT waiting for /book page because the new booker doesnt have this.
* Added former meeting time to reschedule view.
* Fixed types
* Undo playwright config update by mistake.
* Fixed event types test by only waiting for /book page in old booker
* Set new booker cookie to one year in the future instead of 2050
* added reset mockdate to test
* Temporary disabled test to see if this solves the out of memory error.
* Deleted test to see if that fixes the memory error
* Select first day when switching months in booker
---------
Co-authored-by: zomars <zomars@me.com>
Co-authored-by: Alex van Andel <me@alexvanandel.com>
Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
Co-authored-by: Sean Brydon <sean@cal.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2023-04-24 14:32:30 +00:00
import { createBooking , createRecurringBooking } from "@calcom/features/bookings/lib" ;
2023-05-23 07:50:47 +00:00
import { useTimePreferences } from "@calcom/features/bookings/lib" ;
2023-03-02 18:15:28 +00:00
import {
getBookingFieldsWithSystemFields ,
SystemField ,
} from "@calcom/features/bookings/lib/getBookingFields" ;
import getBookingResponsesSchema , {
getBookingResponsesPartialSchema ,
} from "@calcom/features/bookings/lib/getBookingResponsesSchema" ;
2023-04-06 08:17:53 +00:00
import getLocationOptionsForSelect from "@calcom/features/bookings/lib/getLocationOptionsForSelect" ;
2023-03-02 18:15:28 +00:00
import { FormBuilderField } from "@calcom/features/form-builder/FormBuilder" ;
2023-04-11 02:20:15 +00:00
import { bookingSuccessRedirect } from "@calcom/lib/bookingSuccessRedirect" ;
2022-04-08 05:33:24 +00:00
import classNames from "@calcom/lib/classNames" ;
2023-04-13 19:55:26 +00:00
import { APP_NAME , MINUTES_TO_BOOK } from "@calcom/lib/constants" ;
2023-04-05 18:14:46 +00:00
import useGetBrandingColours from "@calcom/lib/getBrandColours" ;
2022-04-06 17:20:30 +00:00
import { useLocale } from "@calcom/lib/hooks/useLocale" ;
2022-07-28 19:58:26 +00:00
import useTheme from "@calcom/lib/hooks/useTheme" ;
2023-03-22 10:15:16 +00:00
import { useTypedQuery } from "@calcom/lib/hooks/useTypedQuery" ;
2022-03-24 02:27:35 +00:00
import { HttpError } from "@calcom/lib/http-error" ;
2023-05-23 07:50:47 +00:00
import { parseDate , parseDateTimeWithTimeZone , parseRecurringDates } from "@calcom/lib/parse-dates" ;
2022-06-10 20:38:06 +00:00
import { getEveryFreqFor } from "@calcom/lib/recurringStrings" ;
2023-04-16 21:58:47 +00:00
import { telemetryEventTypes , useTelemetry } from "@calcom/lib/telemetry" ;
2023-03-22 10:15:16 +00:00
import { TimeFormat } from "@calcom/lib/timeFormat" ;
2023-04-13 19:55:26 +00:00
import { trpc } from "@calcom/trpc" ;
2023-04-05 18:14:46 +00:00
import { Button , Form , Tooltip , useCalcomTheme } from "@calcom/ui" ;
2023-04-12 15:26:31 +00:00
import { AlertTriangle , Calendar , RefreshCw , User } from "@calcom/ui/components/icon" ;
2021-09-22 19:52:38 +00:00
2021-09-14 08:45:28 +00:00
import { timeZone } from "@lib/clock" ;
2022-11-28 18:14:01 +00:00
import useRouterQuery from "@lib/hooks/useRouterQuery" ;
2021-09-22 19:52:38 +00:00
2023-02-16 22:39:57 +00:00
import type { Gate , GateState } from "@components/Gates" ;
import Gates from "@components/Gates" ;
2022-10-12 08:39:14 +00:00
import BookingDescription from "@components/booking/BookingDescription" ;
2021-09-22 19:52:38 +00:00
2023-02-16 22:39:57 +00:00
import type { BookPageProps } from "../../../pages/[user]/book" ;
import type { HashLinkPageProps } from "../../../pages/d/[link]/book" ;
import type { TeamBookingPageProps } from "../../../pages/team/[slug]/book" ;
2021-09-14 08:45:28 +00:00
2023-03-17 10:32:02 +00:00
const Toaster = dynamic ( ( ) = > import ( "react-hot-toast" ) . then ( ( mod ) = > mod . Toaster ) , { ssr : false } ) ;
2023-03-02 18:15:28 +00:00
/** These are like 40kb that not every user needs */
const BookingDescriptionPayment = dynamic (
( ) = > import ( "@components/booking/BookingDescriptionPayment" )
) as unknown as typeof import ( "@components/booking/BookingDescriptionPayment" ) . default ;
2023-04-05 18:14:46 +00:00
const useBrandColors = ( { brandColor , darkBrandColor } : { brandColor? : string ; darkBrandColor? : string } ) = > {
const brandTheme = useGetBrandingColours ( {
lightVal : brandColor ,
darkVal : darkBrandColor ,
} ) ;
useCalcomTheme ( brandTheme ) ;
} ;
2022-08-26 00:48:50 +00:00
type BookingPageProps = BookPageProps | TeamBookingPageProps | HashLinkPageProps ;
2023-03-02 18:15:28 +00:00
const BookingFields = ( {
fields ,
locations ,
rescheduleUid ,
isDynamicGroupBooking ,
} : {
fields : BookingPageProps [ "eventType" ] [ "bookingFields" ] ;
locations : LocationObject [ ] ;
rescheduleUid? : string ;
isDynamicGroupBooking : boolean ;
} ) = > {
const { t } = useLocale ( ) ;
const { watch , setValue } = useFormContext ( ) ;
const locationResponse = watch ( "responses.location" ) ;
2023-03-16 05:10:20 +00:00
const currentView = rescheduleUid ? "reschedule" : "" ;
2021-09-22 18:36:13 +00:00
2023-03-02 18:15:28 +00:00
return (
// TODO: It might make sense to extract this logic into BookingFields config, that would allow to quickly configure system fields and their editability in fresh booking and reschedule booking view
< div >
{ fields . map ( ( field , index ) = > {
// During reschedule by default all system fields are readOnly. Make them editable on case by case basis.
// Allowing a system field to be edited might require sending emails to attendees, so we need to be careful
let readOnly =
( field . editable === "system" || field . editable === "system-but-optional" ) && ! ! rescheduleUid ;
2023-03-16 05:10:20 +00:00
2023-03-02 18:15:28 +00:00
let noLabel = false ;
let hidden = ! ! field . hidden ;
2023-03-16 05:10:20 +00:00
const fieldViews = field . views ;
if ( fieldViews && ! fieldViews . find ( ( view ) = > view . id === currentView ) ) {
return null ;
}
2023-03-02 18:15:28 +00:00
if ( field . name === SystemField . Enum . rescheduleReason ) {
// rescheduleReason is a reschedule specific field and thus should be editable during reschedule
readOnly = false ;
}
if ( field . name === SystemField . Enum . smsReminderNumber ) {
// `smsReminderNumber` and location.optionValue when location.value===phone are the same data point. We should solve it in a better way in the Form Builder itself.
// I think we should have a way to connect 2 fields together and have them share the same value in Form Builder
if ( locationResponse ? . value === "phone" ) {
setValue ( ` responses. ${ SystemField . Enum . smsReminderNumber } ` , locationResponse ? . optionValue ) ;
// Just don't render the field now, as the value is already connected to attendee phone location
return null ;
}
// `smsReminderNumber` can be edited during reschedule even though it's a system field
readOnly = false ;
}
if ( field . name === SystemField . Enum . guests ) {
// No matter what user configured for Guests field, we don't show it for dynamic group booking as that doesn't support guests
hidden = isDynamicGroupBooking ? true : ! ! field . hidden ;
}
// We don't show `notes` field during reschedule
2023-03-14 04:19:05 +00:00
if (
( field . name === SystemField . Enum . notes || field . name === SystemField . Enum . guests ) &&
! ! rescheduleUid
) {
2023-03-02 18:15:28 +00:00
return null ;
}
// Dynamically populate location field options
if ( field . name === SystemField . Enum . location && field . type === "radioInput" ) {
if ( ! field . optionsInputs ) {
throw new Error ( "radioInput must have optionsInputs" ) ;
}
const optionsInputs = field . optionsInputs ;
2023-04-06 08:17:53 +00:00
// TODO: Instead of `getLocationOptionsForSelect` options should be retrieved from dataStore[field.getOptionsAt]. It would make it agnostic of the `name` of the field.
const options = getLocationOptionsForSelect ( locations , t ) ;
options . forEach ( ( option ) = > {
const optionInput = optionsInputs [ option . value as keyof typeof optionsInputs ] ;
2023-03-02 18:15:28 +00:00
if ( optionInput ) {
2023-04-06 08:17:53 +00:00
optionInput . placeholder = option . inputPlaceholder ;
2023-03-02 18:15:28 +00:00
}
} ) ;
field . options = options . filter (
( location ) : location is NonNullable < ( typeof options ) [ number ] > = > ! ! location
) ;
// If we have only one option and it has an input, we don't show the field label because Option name acts as label.
// e.g. If it's just Attendee Phone Number option then we don't show `Location` label
if ( field . options . length === 1 ) {
if ( field . optionsInputs [ field . options [ 0 ] . value ] ) {
noLabel = true ;
} else {
// If there's only one option and it doesn't have an input, we don't show the field at all because it's visible in the left side bar
hidden = true ;
}
}
}
const label = noLabel ? "" : field . label || t ( field . defaultLabel || "" ) ;
const placeholder = field . placeholder || t ( field . defaultPlaceholder || "" ) ;
return (
< FormBuilderField
className = "mb-4"
field = { { . . . field , label , placeholder , hidden } }
readOnly = { readOnly }
key = { index }
/ >
) ;
} ) }
< / div >
) ;
2022-02-01 21:48:40 +00:00
} ;
2023-03-22 10:15:16 +00:00
const routerQuerySchema = z
. object ( {
timeFormat : z.nativeEnum ( TimeFormat ) ,
rescheduleUid : z.string ( ) . optional ( ) ,
date : z
. string ( )
. optional ( )
. transform ( ( date ) = > {
if ( date === undefined ) {
return null ;
}
return date ;
} ) ,
} )
. passthrough ( ) ;
2022-04-06 17:20:30 +00:00
const BookingPage = ( {
eventType ,
booking ,
2023-03-27 09:34:41 +00:00
currentSlotBooking ,
2022-04-06 17:20:30 +00:00
profile ,
isDynamicGroupBooking ,
2022-05-05 21:16:25 +00:00
recurringEventCount ,
2022-04-28 15:44:26 +00:00
hasHashedBookingLink ,
hashedLink ,
2022-10-19 21:25:03 +00:00
. . . restProps
2022-04-06 17:20:30 +00:00
} : BookingPageProps ) = > {
2023-04-13 19:55:26 +00:00
const removeSelectedSlotMarkMutation = trpc . viewer . public . slots . removeSelectedSlotMark . useMutation ( ) ;
const reserveSlotMutation = trpc . viewer . public . slots . reserveSlot . useMutation ( ) ;
2021-10-25 13:05:21 +00:00
const { t , i18n } = useLocale ( ) ;
2022-11-28 18:14:01 +00:00
const { duration : queryDuration } = useRouterQuery ( "duration" ) ;
2023-04-13 19:55:26 +00:00
const { date : queryDate } = useRouterQuery ( "date" ) ;
2022-10-19 21:25:03 +00:00
const isEmbed = useIsEmbed ( restProps . isEmbed ) ;
2022-12-13 07:23:26 +00:00
const embedUiConfig = useEmbedUiConfig ( ) ;
2022-04-25 04:33:00 +00:00
const shouldAlignCentrallyInEmbed = useEmbedNonStylesConfig ( "align" ) !== "left" ;
const shouldAlignCentrally = ! isEmbed || shouldAlignCentrallyInEmbed ;
2021-09-14 08:45:28 +00:00
const router = useRouter ( ) ;
2022-03-15 14:39:20 +00:00
const { data : session } = useSession ( ) ;
2022-04-08 05:33:24 +00:00
const isBackgroundTransparent = useIsBackgroundTransparent ( ) ;
2022-05-14 13:49:39 +00:00
const telemetry = useTelemetry ( ) ;
2022-09-05 21:10:58 +00:00
const [ gateState , gateDispatcher ] = useReducer (
( state : GateState , newState : Partial < GateState > ) = > ( {
. . . state ,
. . . newState ,
} ) ,
{ }
) ;
2023-05-09 10:02:19 +00:00
2023-05-23 07:50:47 +00:00
const { timezone } = useTimePreferences ( ) ;
2023-04-13 19:55:26 +00:00
const reserveSlot = ( ) = > {
if ( queryDuration ) {
reserveSlotMutation . mutate ( {
eventTypeId : eventType.id ,
slotUtcStartDate : dayjs ( queryDate ) . utc ( ) . format ( ) ,
slotUtcEndDate : dayjs ( queryDate ) . utc ( ) . add ( parseInt ( queryDuration ) , "minutes" ) . format ( ) ,
2023-05-22 09:30:24 +00:00
bookingUid : currentSlotBooking?.uid ,
2023-04-13 19:55:26 +00:00
} ) ;
}
} ;
2022-11-28 18:14:01 +00:00
// Define duration now that we support multiple duration eventTypes
let duration = eventType . length ;
2023-02-01 22:19:37 +00:00
if (
queryDuration &&
! isNaN ( Number ( queryDuration ) ) &&
eventType . metadata ? . multipleDuration &&
eventType . metadata ? . multipleDuration . includes ( Number ( queryDuration ) )
) {
2022-11-28 18:14:01 +00:00
duration = Number ( queryDuration ) ;
}
2022-05-11 05:14:08 +00:00
useEffect ( ( ) = > {
2023-04-16 21:58:47 +00:00
/ * i f ( t o p ! = = w i n d o w ) {
2022-06-02 16:19:01 +00:00
//page_view will be collected automatically by _middleware.ts
telemetry . event (
telemetryEventTypes . embedView ,
2022-05-11 05:14:08 +00:00
collectPageParameters ( "/book" , { isTeamBooking : document.URL.includes ( "team/" ) } )
2022-06-02 16:19:01 +00:00
) ;
2023-04-16 21:58:47 +00:00
} * /
2023-04-13 19:55:26 +00:00
reserveSlot ( ) ;
const interval = setInterval ( reserveSlot , parseInt ( MINUTES_TO_BOOK ) * 60 * 1000 - 2000 ) ;
return ( ) = > {
clearInterval ( interval ) ;
removeSelectedSlotMarkMutation . mutate ( ) ;
} ;
2022-05-14 13:49:39 +00:00
// eslint-disable-next-line react-hooks/exhaustive-deps
2022-05-11 05:14:08 +00:00
} , [ ] ) ;
2021-12-03 10:15:20 +00:00
const mutation = useMutation ( createBooking , {
2022-02-18 16:53:45 +00:00
onSuccess : async ( responseData ) = > {
2023-03-14 04:19:05 +00:00
const { uid } = responseData ;
2023-02-08 20:36:22 +00:00
2023-03-14 04:19:05 +00:00
if ( "paymentUid" in responseData && ! ! responseData . paymentUid ) {
2021-12-03 10:15:20 +00:00
return await router . push (
createPaymentLink ( {
2023-03-14 04:19:05 +00:00
paymentUid : responseData.paymentUid ,
2021-12-03 10:15:20 +00:00
date ,
2023-03-02 18:15:28 +00:00
name : bookingForm.getValues ( "responses.name" ) ,
email : bookingForm.getValues ( "responses.email" ) ,
2021-12-03 10:15:20 +00:00
absolute : false ,
} )
) ;
}
2023-04-11 02:20:15 +00:00
const query = {
isSuccessBookingPage : true ,
email : bookingForm.getValues ( "responses.email" ) ,
eventTypeSlug : eventType.slug ,
seatReferenceUid : "seatReferenceUid" in responseData ? responseData.seatReferenceUid : null ,
. . . ( rescheduleUid && booking ? . startTime && { formerTime : booking.startTime.toString ( ) } ) ,
} ;
return bookingSuccessRedirect ( {
router ,
successRedirectUrl : eventType.successRedirectUrl ,
query ,
bookingUid : uid ,
2021-12-03 10:15:20 +00:00
} ) ;
} ,
} ) ;
2022-05-05 21:16:25 +00:00
const recurringMutation = useMutation ( createRecurringBooking , {
onSuccess : async ( responseData = [ ] ) = > {
2022-11-15 19:00:02 +00:00
const { uid } = responseData [ 0 ] || { } ;
2023-04-11 02:20:15 +00:00
const query = {
isSuccessBookingPage : true ,
allRemainingBookings : true ,
email : bookingForm.getValues ( "responses.email" ) ,
eventTypeSlug : eventType.slug ,
formerTime : booking?.startTime.toString ( ) ,
} ;
return bookingSuccessRedirect ( {
router ,
successRedirectUrl : eventType.successRedirectUrl ,
query ,
bookingUid : uid ,
2022-05-05 21:16:25 +00:00
} ) ;
} ,
} ) ;
2023-03-22 10:15:16 +00:00
const {
data : { timeFormat , rescheduleUid , date } ,
} = useTypedQuery ( routerQuerySchema ) ;
2022-07-26 08:27:57 +00:00
useTheme ( profile . theme ) ;
2023-04-05 18:14:46 +00:00
useBrandColors ( {
brandColor : profile.brandColor ,
darkBrandColor : profile.darkBrandColor ,
} ) ;
2023-03-22 10:15:16 +00:00
2023-03-02 18:15:28 +00:00
const querySchema = getBookingResponsesPartialSchema ( {
2023-03-16 05:10:20 +00:00
eventType : {
bookingFields : getBookingFieldsWithSystemFields ( eventType ) ,
} ,
view : rescheduleUid ? "reschedule" : "booking" ,
2023-03-02 18:15:28 +00:00
} ) ;
const parsedQuery = querySchema . parse ( {
. . . router . query ,
// `guest` because we need to support legacy URL with `guest` query param support
// `guests` because the `name` of the corresponding bookingField is `guests`
guests : router.query.guests || router . query . guest ,
} ) ;
2021-09-14 08:45:28 +00:00
2021-12-03 10:15:20 +00:00
// it would be nice if Prisma at some point in the future allowed for Json<Location>; as of now this is not the case.
2022-05-25 20:34:08 +00:00
const locations : LocationObject [ ] = useMemo (
( ) = > ( eventType . locations as LocationObject [ ] ) || [ ] ,
2022-03-15 14:39:20 +00:00
[ eventType . locations ]
2021-09-14 08:45:28 +00:00
) ;
2023-03-02 18:15:28 +00:00
const [ isClientTimezoneAvailable , setIsClientTimezoneAvailable ] = useState ( false ) ;
2021-12-03 10:15:20 +00:00
useEffect ( ( ) = > {
2023-03-02 18:15:28 +00:00
// THis is to fix hydration error that comes because of different timezone on server and client
setIsClientTimezoneAvailable ( true ) ;
} , [ ] ) ;
2021-09-14 08:45:28 +00:00
2022-05-18 21:05:49 +00:00
const loggedInIsOwner = eventType ? . users [ 0 ] ? . id === session ? . user ? . id ;
2022-04-14 21:25:24 +00:00
2022-08-05 17:08:47 +00:00
// There should only exists one default userData variable for primaryAttendee.
const defaultUserValues = {
2023-03-02 18:15:28 +00:00
email : rescheduleUid ? booking ? . attendees [ 0 ] . email : parsedQuery [ "email" ] ,
name : rescheduleUid ? booking ? . attendees [ 0 ] . name : parsedQuery [ "name" ] ,
2022-08-05 17:08:47 +00:00
} ;
2022-01-10 23:25:06 +00:00
const defaultValues = ( ) = > {
if ( ! rescheduleUid ) {
2023-03-02 18:15:28 +00:00
const defaults = {
responses : { } as Partial < z.infer < typeof bookingFormSchema > [ "responses" ] > ,
} ;
const responses = eventType . bookingFields . reduce ( ( responses , field ) = > {
return {
. . . responses ,
[ field . name ] : parsedQuery [ field . name ] ,
} ;
} , { } ) ;
defaults . responses = {
. . . responses ,
2022-08-05 17:08:47 +00:00
name : defaultUserValues.name || ( ! loggedInIsOwner && session ? . user ? . name ) || "" ,
email : defaultUserValues.email || ( ! loggedInIsOwner && session ? . user ? . email ) || "" ,
2022-01-10 23:25:06 +00:00
} ;
2023-03-02 18:15:28 +00:00
return defaults ;
2022-01-10 23:25:06 +00:00
}
2023-03-02 18:15:28 +00:00
2022-03-15 14:39:20 +00:00
if ( ! booking || ! booking . attendees . length ) {
2022-01-10 23:25:06 +00:00
return { } ;
}
2022-03-15 14:39:20 +00:00
const primaryAttendee = booking . attendees [ 0 ] ;
2022-01-10 23:25:06 +00:00
if ( ! primaryAttendee ) {
return { } ;
}
2022-05-18 21:05:49 +00:00
2023-03-02 18:15:28 +00:00
const defaults = {
responses : { } as Partial < z.infer < typeof bookingFormSchema > [ "responses" ] > ,
} ;
const responses = eventType . bookingFields . reduce ( ( responses , field ) = > {
return {
. . . responses ,
[ field . name ] : booking . responses [ field . name ] ,
} ;
} , { } ) ;
defaults . responses = {
. . . responses ,
name : defaultUserValues.name || ( ! loggedInIsOwner && session ? . user ? . name ) || "" ,
email : defaultUserValues.email || ( ! loggedInIsOwner && session ? . user ? . email ) || "" ,
2022-01-10 23:25:06 +00:00
} ;
2023-03-02 18:15:28 +00:00
return defaults ;
2022-01-10 23:25:06 +00:00
} ;
2022-04-27 21:21:18 +00:00
const bookingFormSchema = z
. object ( {
2023-03-02 18:15:28 +00:00
responses : getBookingResponsesSchema ( {
2023-03-16 05:10:20 +00:00
eventType : { bookingFields : getBookingFieldsWithSystemFields ( eventType ) } ,
view : rescheduleUid ? "reschedule" : "booking" ,
2023-03-02 18:15:28 +00:00
} ) ,
2022-04-27 21:21:18 +00:00
} )
. passthrough ( ) ;
2022-04-27 15:19:04 +00:00
2023-03-02 18:15:28 +00:00
type BookingFormValues = {
locationType? : EventLocationType [ "type" ] ;
responses : z.infer < typeof bookingFormSchema > [ "responses" ] ;
} ;
2021-12-03 10:15:20 +00:00
const bookingForm = useForm < BookingFormValues > ( {
2022-01-10 23:25:06 +00:00
defaultValues : defaultValues ( ) ,
2022-04-27 21:21:18 +00:00
resolver : zodResolver ( bookingFormSchema ) , // Since this isn't set to strict we only validate the fields in the schema
2021-12-03 10:15:20 +00:00
} ) ;
2021-09-22 18:36:13 +00:00
2022-05-05 21:16:25 +00:00
// Calculate the booking date(s)
let recurringStrings : string [ ] = [ ] ,
recurringDates : Date [ ] = [ ] ;
if ( eventType . recurringEvent ? . freq && recurringEventCount !== null ) {
2023-03-22 10:15:16 +00:00
[ recurringStrings , recurringDates ] = parseRecurringDates (
{
startDate : date ,
timeZone : timeZone ( ) ,
recurringEvent : eventType.recurringEvent ,
recurringCount : parseInt ( recurringEventCount . toString ( ) ) ,
selectedTimeFormat : timeFormat ,
} ,
New Booker Component (preparations for booker atom) (#6792)
* Wip on booker atom
* Wip on booker atom
* Added correct icon imports
* Fixed build
* Responsive improvements
* Removed package lock
* Responsive tweaks
* Animation improvements and cleanup
* Animation improvements and event meta layout improvements.
* Tweaked margins.
* Added more event meta blocks
* Layout tweaks
* Converted booker layout to css grid and implemented multiple layout options
* cleanup
* Fixed build
* Fixed build
* Added temporary api route to enable/disable new booker
* Added sticky behavior
* Reverted yarn.lock and reinstalled new packages to see if this fixes build on vercel.
* Ensure divider lines always have 100% height.
* Improved animation config + initial load
* Ensure to pass eventid to getschedule, otherwise custom availability schedule wont work and wont return any availability
* Fixed divider line heights in booker
* Fixed timezone select positioning
* Added ability to view multiple days of timeslots
* Added icons to booker toggle
* Always show timeslots in timeslots view, also if no date is selected yet. In that case we show upcoming 5 days.
* Fixed timeslots in small calendar view
* Show selected day in calendar
* Fixed booker timeslots view
* Wip in making booking form work
* Moved most of the booker atom stuff to features, since it belongs there. Atom should be a rather small wrapper.
* Added create event functionality to booker form.
* Added guests toggle to booker form and styled input addons in dark mode.
* Added dynamic weekstart to booker
* Added seats limit feature to timeslots.
* Removed todo
* Added correct event avatars
* Added correct event name and icons
* Added correct translation for minutes text in multi duration
* Add rescheduling functionality to new booker.
* Added selected booking time to booking meta in sidebar.
* Abstracted away timeformat to custom hook
* Added correct key props to all components in booker.
* Fix build
* Create some new custom hooks to have a lot less repitition in code.
* Moved bookerform component inside booker directory since it is tied to it.
* Added error messages to booker form, plus fixed bug in recurring events.
* Added some comments <3
* Fixed todos in booker form.
* Added loading state for timeslot selector, and added prefetching of next month, in case of multi day view showing 2 months at the same time.
* Fixed import paths
* Added away view
* Validate uniqueness of event attendees.
* Tweaked comment
* #5798 added correct date format and style for selected date in booker.
* UI improvements
* Enable possibility to add booking values via query params.
* Added functionality to update query params when user selects date/duration etc in booker
* First steps in adding e2e test.
* Fixes after merge with main, and added new form builder.
* Implemented new form types and validation to booker, confirming new form builder. Validation still throwing wrong error keys though.
* Added search to timezone dropdown
* Added e2e test for booker (copy of current booker tests, only enabling cookie), plus fixed reschedule view.
* Updated yarn.lock
* Added new booker for team pages.
* Fixed input addon (hover) styles.
* Added dynamic booking.
* Hide timeformat select for multi day view for now.
* Cleanup and ui tweaks
* removed log
* Mobile improvements
* Cleanup
* Small design tweaks after talking to ciaran.
* Text color and weight tweaks in booker
* Added rainbow gates to new booker.
* Added in default values which fixes form vallidation (???).
* Added empty defaults for name and email
* Added metadata
* Reset yarn.lock
* Fixed booker zod validation after change in main.
* Icon tweak
* Fixed timezone select styles after new classnames have been merged.
* Updated seat availability styles.
* Update yarn.lock
* Added explanation for alchemy key to .env.example
* Added tooltip to booker month/week/multiday toggle
* Fixed timezoneselect styles in booker after select updates.
* Updates bookingfields component by taking changes from current booker component
* Removed remaining booker todos
* Fix bookeventform
* Fix for recurring event meta
* Type fixes
* Typefixes
* Team event fixes
* Avoid hydration errors by only rendering date picker client side. Remove web3 gates since we dont offer them anymore. Prevent timeslot select from staying open when switching to a different month.
* Don't show calendar on mobile booker during booking.
* Always align booker buttons to bottom
* Don't show backend messages in error, rather show a helpful text like the current booker does as well.
* Do invisible next rewrite based on cookie from next.config.js (#7949)
* Do invisible next rewrite based on cookie from next.config.js
* Name embed link instead of bookerPath
* Rewrites only dynamic user pages
---------
Co-authored-by: zomars <zomars@me.com>
* Don't allow change of timezone when bookerform is visible
* Don't add duration to query param if the event is not a multi duration event.
* Update next.config.js
* Added correct timezone formatting to event meta when timeslot is selected.
* removed .env variable that isn't needed anymore.
* Update Gates.tsx
* Type fixes
* Allows to run all tests with the new booker
* Fixed timezone select styles after merge.
* Don't throw error when event doesn't have hosts, rather return no users, which will result in no availability in UI.
* Make booker errors of severity info instead of warning.
* Ensure team avatars are shown, as well as filter on uniqueness of avatars.
* Added all booked today message to timeslots.
* Added cal.com logo to booker.
* Fixed fragment classname error, minor mobile animation tweaks plus make all booked today text smaller for multi day layout.
* Improved timezone select styles, and updated arguments of getbooking function after updates in main.
* Prevent infinite loop in rewriting new booker.
* Prevent infinite loop in rewriting new booker.
* Moved new-booker pages to their own directory to prevent regexes confusing next and thus nut running getserversideprops after rewrite. Also adding clearing of old date in booker store, that could stick around when user immediately navigates back to the same page after booking.
* Fixed cal logo color in darkmode for new booker.
* Implemented new color tokens and theme variables. Also small design tweaks after merge with main.
* Minor style tweaks
* Show multiple locations in tooltip on booker #8222
* Radio button style tweaks
* Fixed build
* Updated calendar imports to new lucide names
* Removed resetting of selected times logic, because otherwise url params wouldnt be taken into account which is actually what we want. So old values sticking around when navigating back is actually the desired behavior.
* Updated tests to instead of always run the new booker in tests, have a utility to run both the new and old booker for specified tests.
* Added comment and eslint disable for if statement in booker test.
* Update packages/features/bookings/components/event-meta/Details.tsx
Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
* Fix badge types
* Lazy loaded timezone select to save 85kb in bundle size.
* Upgraded framer to latest. Als moved framer and react sticky deps to features instead of atoms.
* Added new pagewrapper logic
* Simplified rescheduling ssr fetches, this now also supports multi seat rescheduling.
* Unset selected time when user is rescheduling directly after a new booking, otherwise it would show the form instead of new time selection.
* Updated form builder logic as per form builder in current booker.
* Updated form builder prefill logic as per logic in current booker.
* Updated getbooking function to fetch correct details when a reschedule uid is used
* Fixed booking questions test by NOT waiting for /book page because the new booker doesnt have this.
* Added former meeting time to reschedule view.
* Fixed types
* Undo playwright config update by mistake.
* Fixed event types test by only waiting for /book page in old booker
* Set new booker cookie to one year in the future instead of 2050
* added reset mockdate to test
* Temporary disabled test to see if this solves the out of memory error.
* Deleted test to see if that fixes the memory error
* Select first day when switching months in booker
---------
Co-authored-by: zomars <zomars@me.com>
Co-authored-by: Alex van Andel <me@alexvanandel.com>
Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
Co-authored-by: Sean Brydon <sean@cal.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2023-04-24 14:32:30 +00:00
i18n . language
2022-05-05 21:16:25 +00:00
) ;
}
2023-03-14 04:19:05 +00:00
const bookEvent = ( bookingValues : BookingFormValues ) = > {
2022-06-02 16:19:01 +00:00
telemetry . event (
top !== window ? telemetryEventTypes.embedBookingConfirmed : telemetryEventTypes.bookingConfirmed ,
{ isTeamBooking : document.URL.includes ( "team/" ) }
2021-12-03 10:15:20 +00:00
) ;
// "metadata" is a reserved key to allow for connecting external users without relying on the email address.
// <...url>&metadata[user_id]=123 will be send as a custom input field as the hidden type.
2022-02-01 21:48:40 +00:00
// @TODO: move to metadata
2021-12-03 10:15:20 +00:00
const metadata = Object . keys ( router . query )
. filter ( ( key ) = > key . startsWith ( "metadata" ) )
. reduce (
( metadata , key ) = > ( {
. . . metadata ,
[ key . substring ( "metadata[" . length , key . length - 1 ) ] : router . query [ key ] ,
} ) ,
{ }
) ;
2021-09-14 08:45:28 +00:00
2022-05-05 21:16:25 +00:00
if ( recurringDates . length ) {
// Identify set of bookings to one intance of recurring event to support batch changes
const recurringEventId = uuidv4 ( ) ;
const recurringBookings = recurringDates . map ( ( recurringDate ) = > ( {
2023-03-14 04:19:05 +00:00
. . . bookingValues ,
2023-03-17 11:53:05 +00:00
start : dayjs ( recurringDate ) . utc ( ) . format ( ) ,
end : dayjs ( recurringDate ) . utc ( ) . add ( duration , "minute" ) . format ( ) ,
2022-05-05 21:16:25 +00:00
eventTypeId : eventType.id ,
eventTypeSlug : eventType.slug ,
recurringEventId ,
// Added to track down the number of actual occurrences selected by the user
recurringCount : recurringDates.length ,
timeZone : timeZone ( ) ,
language : i18n.language ,
rescheduleUid ,
user : router.query.user ,
metadata ,
hasHashedBookingLink ,
hashedLink ,
2022-09-05 21:10:58 +00:00
ethSignature : gateState.rainbowToken ,
2022-05-05 21:16:25 +00:00
} ) ) ;
recurringMutation . mutate ( recurringBookings ) ;
} else {
mutation . mutate ( {
2023-03-14 04:19:05 +00:00
. . . bookingValues ,
2023-03-17 11:53:05 +00:00
start : dayjs ( date ) . utc ( ) . format ( ) ,
end : dayjs ( date ) . utc ( ) . add ( duration , "minute" ) . format ( ) ,
2022-05-05 21:16:25 +00:00
eventTypeId : eventType.id ,
eventTypeSlug : eventType.slug ,
timeZone : timeZone ( ) ,
language : i18n.language ,
rescheduleUid ,
2023-03-14 04:19:05 +00:00
bookingUid : ( router . query . bookingUid as string ) || booking ? . uid ,
2022-05-05 21:16:25 +00:00
user : router.query.user ,
metadata ,
hasHashedBookingLink ,
hashedLink ,
2022-09-05 21:10:58 +00:00
ethSignature : gateState.rainbowToken ,
2023-03-14 04:19:05 +00:00
seatReferenceUid : router.query.seatReferenceUid as string ,
2022-05-05 21:16:25 +00:00
} ) ;
}
2021-09-14 08:45:28 +00:00
} ;
2022-12-13 07:23:26 +00:00
const showEventTypeDetails = ( isEmbed && ! embedUiConfig . hideEventTypeDetails ) || ! isEmbed ;
2022-10-14 16:24:43 +00:00
const rainbowAppData = getEventTypeAppData ( eventType , "rainbow" ) || { } ;
2022-07-14 00:10:45 +00:00
2022-09-05 21:10:58 +00:00
// Define conditional gates here
const gates = [
// Rainbow gate is only added if the event has both a `blockchainId` and a `smartContractAddress`
2022-10-14 16:24:43 +00:00
rainbowAppData && rainbowAppData . blockchainId && rainbowAppData . smartContractAddress
2022-09-05 21:10:58 +00:00
? ( "rainbow" as Gate )
: undefined ,
] ;
2021-09-14 08:45:28 +00:00
return (
2022-10-14 16:24:43 +00:00
< Gates gates = { gates } appData = { rainbowAppData } dispatch = { gateDispatcher } >
2021-09-24 22:11:30 +00:00
< Head >
< title >
2021-10-08 11:43:48 +00:00
{ rescheduleUid
? t ( "booking_reschedule_confirmation" , {
2022-03-15 14:39:20 +00:00
eventTypeTitle : eventType.title ,
profileName : profile.name ,
2021-10-08 11:43:48 +00:00
} )
: t ( "booking_confirmation" , {
2022-03-15 14:39:20 +00:00
eventTypeTitle : eventType.title ,
profileName : profile.name ,
2021-10-08 11:43:48 +00:00
} ) } { " " }
2022-11-30 21:52:56 +00:00
| { APP_NAME }
2021-09-24 22:11:30 +00:00
< / title >
2023-01-23 23:08:01 +00:00
< link rel = "icon" href = "/favico.ico" / >
2021-09-24 22:11:30 +00:00
< / Head >
2022-10-14 16:24:43 +00:00
< BookingPageTagManager eventType = { eventType } / >
2022-04-08 05:33:24 +00:00
< main
2022-04-14 02:47:34 +00:00
className = { classNames (
2022-04-25 04:33:00 +00:00
shouldAlignCentrally ? "mx-auto" : "" ,
isEmbed ? "" : "sm:my-24" ,
2022-12-13 07:23:26 +00:00
"my-0 max-w-3xl"
2022-04-14 02:47:34 +00:00
) } >
2022-07-26 08:27:57 +00:00
< div
className = { classNames (
2023-03-07 07:55:06 +00:00
"main" ,
2023-04-16 21:47:07 +00:00
isBackgroundTransparent ? "" : "bg-default dark:bg-muted" ,
2023-04-13 18:26:31 +00:00
"border-booker sm:border-booker-width rounded-md"
2022-07-26 08:27:57 +00:00
) } >
2022-08-05 08:46:44 +00:00
< div className = "sm:flex" >
2022-12-13 07:23:26 +00:00
{ showEventTypeDetails && (
2023-05-08 19:03:24 +00:00
< div className = "sm:border-subtle text-default flex flex-col px-6 pt-6 pb-0 sm:w-1/2 sm:border-r sm:pb-6" >
2022-12-13 07:23:26 +00:00
< BookingDescription isBookingPage profile = { profile } eventType = { eventType } >
2023-04-19 21:38:06 +00:00
< BookingDescriptionPayment eventType = { eventType } t = { t } i18n = { i18n } / >
2022-12-13 07:23:26 +00:00
{ ! rescheduleUid && eventType . recurringEvent ? . freq && recurringEventCount && (
2023-05-02 21:10:47 +00:00
< div className = "text-default items-start text-sm font-medium" >
2023-04-12 15:26:31 +00:00
< RefreshCw className = "ml-[2px] inline-block h-4 w-4 ltr:mr-[10px] rtl:ml-[10px]" / >
2022-12-13 07:23:26 +00:00
< p className = "-ml-2 inline-block items-center px-2" >
{ getEveryFreqFor ( {
t ,
recurringEvent : eventType.recurringEvent ,
recurringCount : recurringEventCount ,
} ) }
< / p >
< / div >
) }
2022-09-02 21:16:36 +00:00
< div className = "text-bookinghighlight flex items-start text-sm" >
2023-04-12 15:26:31 +00:00
< Calendar className = "ml-[2px] mt-[2px] inline-block h-4 w-4 ltr:mr-[10px] rtl:ml-[10px]" / >
2022-12-13 07:23:26 +00:00
< div className = "text-sm font-medium" >
2023-03-02 18:15:28 +00:00
{ isClientTimezoneAvailable &&
( rescheduleUid || ! eventType . recurringEvent ? . freq ) &&
New Booker Component (preparations for booker atom) (#6792)
* Wip on booker atom
* Wip on booker atom
* Added correct icon imports
* Fixed build
* Responsive improvements
* Removed package lock
* Responsive tweaks
* Animation improvements and cleanup
* Animation improvements and event meta layout improvements.
* Tweaked margins.
* Added more event meta blocks
* Layout tweaks
* Converted booker layout to css grid and implemented multiple layout options
* cleanup
* Fixed build
* Fixed build
* Added temporary api route to enable/disable new booker
* Added sticky behavior
* Reverted yarn.lock and reinstalled new packages to see if this fixes build on vercel.
* Ensure divider lines always have 100% height.
* Improved animation config + initial load
* Ensure to pass eventid to getschedule, otherwise custom availability schedule wont work and wont return any availability
* Fixed divider line heights in booker
* Fixed timezone select positioning
* Added ability to view multiple days of timeslots
* Added icons to booker toggle
* Always show timeslots in timeslots view, also if no date is selected yet. In that case we show upcoming 5 days.
* Fixed timeslots in small calendar view
* Show selected day in calendar
* Fixed booker timeslots view
* Wip in making booking form work
* Moved most of the booker atom stuff to features, since it belongs there. Atom should be a rather small wrapper.
* Added create event functionality to booker form.
* Added guests toggle to booker form and styled input addons in dark mode.
* Added dynamic weekstart to booker
* Added seats limit feature to timeslots.
* Removed todo
* Added correct event avatars
* Added correct event name and icons
* Added correct translation for minutes text in multi duration
* Add rescheduling functionality to new booker.
* Added selected booking time to booking meta in sidebar.
* Abstracted away timeformat to custom hook
* Added correct key props to all components in booker.
* Fix build
* Create some new custom hooks to have a lot less repitition in code.
* Moved bookerform component inside booker directory since it is tied to it.
* Added error messages to booker form, plus fixed bug in recurring events.
* Added some comments <3
* Fixed todos in booker form.
* Added loading state for timeslot selector, and added prefetching of next month, in case of multi day view showing 2 months at the same time.
* Fixed import paths
* Added away view
* Validate uniqueness of event attendees.
* Tweaked comment
* #5798 added correct date format and style for selected date in booker.
* UI improvements
* Enable possibility to add booking values via query params.
* Added functionality to update query params when user selects date/duration etc in booker
* First steps in adding e2e test.
* Fixes after merge with main, and added new form builder.
* Implemented new form types and validation to booker, confirming new form builder. Validation still throwing wrong error keys though.
* Added search to timezone dropdown
* Added e2e test for booker (copy of current booker tests, only enabling cookie), plus fixed reschedule view.
* Updated yarn.lock
* Added new booker for team pages.
* Fixed input addon (hover) styles.
* Added dynamic booking.
* Hide timeformat select for multi day view for now.
* Cleanup and ui tweaks
* removed log
* Mobile improvements
* Cleanup
* Small design tweaks after talking to ciaran.
* Text color and weight tweaks in booker
* Added rainbow gates to new booker.
* Added in default values which fixes form vallidation (???).
* Added empty defaults for name and email
* Added metadata
* Reset yarn.lock
* Fixed booker zod validation after change in main.
* Icon tweak
* Fixed timezone select styles after new classnames have been merged.
* Updated seat availability styles.
* Update yarn.lock
* Added explanation for alchemy key to .env.example
* Added tooltip to booker month/week/multiday toggle
* Fixed timezoneselect styles in booker after select updates.
* Updates bookingfields component by taking changes from current booker component
* Removed remaining booker todos
* Fix bookeventform
* Fix for recurring event meta
* Type fixes
* Typefixes
* Team event fixes
* Avoid hydration errors by only rendering date picker client side. Remove web3 gates since we dont offer them anymore. Prevent timeslot select from staying open when switching to a different month.
* Don't show calendar on mobile booker during booking.
* Always align booker buttons to bottom
* Don't show backend messages in error, rather show a helpful text like the current booker does as well.
* Do invisible next rewrite based on cookie from next.config.js (#7949)
* Do invisible next rewrite based on cookie from next.config.js
* Name embed link instead of bookerPath
* Rewrites only dynamic user pages
---------
Co-authored-by: zomars <zomars@me.com>
* Don't allow change of timezone when bookerform is visible
* Don't add duration to query param if the event is not a multi duration event.
* Update next.config.js
* Added correct timezone formatting to event meta when timeslot is selected.
* removed .env variable that isn't needed anymore.
* Update Gates.tsx
* Type fixes
* Allows to run all tests with the new booker
* Fixed timezone select styles after merge.
* Don't throw error when event doesn't have hosts, rather return no users, which will result in no availability in UI.
* Make booker errors of severity info instead of warning.
* Ensure team avatars are shown, as well as filter on uniqueness of avatars.
* Added all booked today message to timeslots.
* Added cal.com logo to booker.
* Fixed fragment classname error, minor mobile animation tweaks plus make all booked today text smaller for multi day layout.
* Improved timezone select styles, and updated arguments of getbooking function after updates in main.
* Prevent infinite loop in rewriting new booker.
* Prevent infinite loop in rewriting new booker.
* Moved new-booker pages to their own directory to prevent regexes confusing next and thus nut running getserversideprops after rewrite. Also adding clearing of old date in booker store, that could stick around when user immediately navigates back to the same page after booking.
* Fixed cal logo color in darkmode for new booker.
* Implemented new color tokens and theme variables. Also small design tweaks after merge with main.
* Minor style tweaks
* Show multiple locations in tooltip on booker #8222
* Radio button style tweaks
* Fixed build
* Updated calendar imports to new lucide names
* Removed resetting of selected times logic, because otherwise url params wouldnt be taken into account which is actually what we want. So old values sticking around when navigating back is actually the desired behavior.
* Updated tests to instead of always run the new booker in tests, have a utility to run both the new and old booker for specified tests.
* Added comment and eslint disable for if statement in booker test.
* Update packages/features/bookings/components/event-meta/Details.tsx
Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
* Fix badge types
* Lazy loaded timezone select to save 85kb in bundle size.
* Upgraded framer to latest. Als moved framer and react sticky deps to features instead of atoms.
* Added new pagewrapper logic
* Simplified rescheduling ssr fetches, this now also supports multi seat rescheduling.
* Unset selected time when user is rescheduling directly after a new booking, otherwise it would show the form instead of new time selection.
* Updated form builder logic as per form builder in current booker.
* Updated form builder prefill logic as per logic in current booker.
* Updated getbooking function to fetch correct details when a reschedule uid is used
* Fixed booking questions test by NOT waiting for /book page because the new booker doesnt have this.
* Added former meeting time to reschedule view.
* Fixed types
* Undo playwright config update by mistake.
* Fixed event types test by only waiting for /book page in old booker
* Set new booker cookie to one year in the future instead of 2050
* added reset mockdate to test
* Temporary disabled test to see if this solves the out of memory error.
* Deleted test to see if that fixes the memory error
* Select first day when switching months in booker
---------
Co-authored-by: zomars <zomars@me.com>
Co-authored-by: Alex van Andel <me@alexvanandel.com>
Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
Co-authored-by: Sean Brydon <sean@cal.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2023-04-24 14:32:30 +00:00
` ${ parseDate ( date , i18n . language , { selectedTimeFormat : timeFormat } )} ` }
2023-03-02 18:15:28 +00:00
{ isClientTimezoneAvailable &&
! rescheduleUid &&
2022-12-13 07:23:26 +00:00
eventType . recurringEvent ? . freq &&
recurringStrings . slice ( 0 , 5 ) . map ( ( timeFormatted , key ) = > {
return < p key = { key } > { timeFormatted } < / p > ;
} ) }
{ ! rescheduleUid && eventType . recurringEvent ? . freq && recurringStrings . length > 5 && (
< div className = "flex" >
< Tooltip
content = { recurringStrings . slice ( 5 ) . map ( ( timeFormatted , key ) = > (
< p key = { key } > { timeFormatted } < / p >
) ) } >
2023-04-05 18:14:46 +00:00
< p className = " text-sm" >
2022-12-13 07:23:26 +00:00
+ { t ( "plus_more" , { count : recurringStrings.length - 5 } ) }
< / p >
< / Tooltip >
< / div >
) }
< / div >
2022-09-02 21:16:36 +00:00
< / div >
2022-12-13 07:23:26 +00:00
{ booking ? . startTime && rescheduleUid && (
< div >
< p className = "mt-8 mb-2 text-sm " data - testid = "former_time_p" >
{ t ( "former_time" ) }
< / p >
< p className = "line-through " >
2023-04-12 15:26:31 +00:00
< Calendar className = "ml-[2px] -mt-1 inline-block h-4 w-4 ltr:mr-[10px] rtl:ml-[10px]" / >
2023-03-02 18:15:28 +00:00
{ isClientTimezoneAvailable &&
typeof booking . startTime === "string" &&
2023-05-23 07:50:47 +00:00
parseDateTimeWithTimeZone ( booking . startTime , i18n . language , timezone , {
New Booker Component (preparations for booker atom) (#6792)
* Wip on booker atom
* Wip on booker atom
* Added correct icon imports
* Fixed build
* Responsive improvements
* Removed package lock
* Responsive tweaks
* Animation improvements and cleanup
* Animation improvements and event meta layout improvements.
* Tweaked margins.
* Added more event meta blocks
* Layout tweaks
* Converted booker layout to css grid and implemented multiple layout options
* cleanup
* Fixed build
* Fixed build
* Added temporary api route to enable/disable new booker
* Added sticky behavior
* Reverted yarn.lock and reinstalled new packages to see if this fixes build on vercel.
* Ensure divider lines always have 100% height.
* Improved animation config + initial load
* Ensure to pass eventid to getschedule, otherwise custom availability schedule wont work and wont return any availability
* Fixed divider line heights in booker
* Fixed timezone select positioning
* Added ability to view multiple days of timeslots
* Added icons to booker toggle
* Always show timeslots in timeslots view, also if no date is selected yet. In that case we show upcoming 5 days.
* Fixed timeslots in small calendar view
* Show selected day in calendar
* Fixed booker timeslots view
* Wip in making booking form work
* Moved most of the booker atom stuff to features, since it belongs there. Atom should be a rather small wrapper.
* Added create event functionality to booker form.
* Added guests toggle to booker form and styled input addons in dark mode.
* Added dynamic weekstart to booker
* Added seats limit feature to timeslots.
* Removed todo
* Added correct event avatars
* Added correct event name and icons
* Added correct translation for minutes text in multi duration
* Add rescheduling functionality to new booker.
* Added selected booking time to booking meta in sidebar.
* Abstracted away timeformat to custom hook
* Added correct key props to all components in booker.
* Fix build
* Create some new custom hooks to have a lot less repitition in code.
* Moved bookerform component inside booker directory since it is tied to it.
* Added error messages to booker form, plus fixed bug in recurring events.
* Added some comments <3
* Fixed todos in booker form.
* Added loading state for timeslot selector, and added prefetching of next month, in case of multi day view showing 2 months at the same time.
* Fixed import paths
* Added away view
* Validate uniqueness of event attendees.
* Tweaked comment
* #5798 added correct date format and style for selected date in booker.
* UI improvements
* Enable possibility to add booking values via query params.
* Added functionality to update query params when user selects date/duration etc in booker
* First steps in adding e2e test.
* Fixes after merge with main, and added new form builder.
* Implemented new form types and validation to booker, confirming new form builder. Validation still throwing wrong error keys though.
* Added search to timezone dropdown
* Added e2e test for booker (copy of current booker tests, only enabling cookie), plus fixed reschedule view.
* Updated yarn.lock
* Added new booker for team pages.
* Fixed input addon (hover) styles.
* Added dynamic booking.
* Hide timeformat select for multi day view for now.
* Cleanup and ui tweaks
* removed log
* Mobile improvements
* Cleanup
* Small design tweaks after talking to ciaran.
* Text color and weight tweaks in booker
* Added rainbow gates to new booker.
* Added in default values which fixes form vallidation (???).
* Added empty defaults for name and email
* Added metadata
* Reset yarn.lock
* Fixed booker zod validation after change in main.
* Icon tweak
* Fixed timezone select styles after new classnames have been merged.
* Updated seat availability styles.
* Update yarn.lock
* Added explanation for alchemy key to .env.example
* Added tooltip to booker month/week/multiday toggle
* Fixed timezoneselect styles in booker after select updates.
* Updates bookingfields component by taking changes from current booker component
* Removed remaining booker todos
* Fix bookeventform
* Fix for recurring event meta
* Type fixes
* Typefixes
* Team event fixes
* Avoid hydration errors by only rendering date picker client side. Remove web3 gates since we dont offer them anymore. Prevent timeslot select from staying open when switching to a different month.
* Don't show calendar on mobile booker during booking.
* Always align booker buttons to bottom
* Don't show backend messages in error, rather show a helpful text like the current booker does as well.
* Do invisible next rewrite based on cookie from next.config.js (#7949)
* Do invisible next rewrite based on cookie from next.config.js
* Name embed link instead of bookerPath
* Rewrites only dynamic user pages
---------
Co-authored-by: zomars <zomars@me.com>
* Don't allow change of timezone when bookerform is visible
* Don't add duration to query param if the event is not a multi duration event.
* Update next.config.js
* Added correct timezone formatting to event meta when timeslot is selected.
* removed .env variable that isn't needed anymore.
* Update Gates.tsx
* Type fixes
* Allows to run all tests with the new booker
* Fixed timezone select styles after merge.
* Don't throw error when event doesn't have hosts, rather return no users, which will result in no availability in UI.
* Make booker errors of severity info instead of warning.
* Ensure team avatars are shown, as well as filter on uniqueness of avatars.
* Added all booked today message to timeslots.
* Added cal.com logo to booker.
* Fixed fragment classname error, minor mobile animation tweaks plus make all booked today text smaller for multi day layout.
* Improved timezone select styles, and updated arguments of getbooking function after updates in main.
* Prevent infinite loop in rewriting new booker.
* Prevent infinite loop in rewriting new booker.
* Moved new-booker pages to their own directory to prevent regexes confusing next and thus nut running getserversideprops after rewrite. Also adding clearing of old date in booker store, that could stick around when user immediately navigates back to the same page after booking.
* Fixed cal logo color in darkmode for new booker.
* Implemented new color tokens and theme variables. Also small design tweaks after merge with main.
* Minor style tweaks
* Show multiple locations in tooltip on booker #8222
* Radio button style tweaks
* Fixed build
* Updated calendar imports to new lucide names
* Removed resetting of selected times logic, because otherwise url params wouldnt be taken into account which is actually what we want. So old values sticking around when navigating back is actually the desired behavior.
* Updated tests to instead of always run the new booker in tests, have a utility to run both the new and old booker for specified tests.
* Added comment and eslint disable for if statement in booker test.
* Update packages/features/bookings/components/event-meta/Details.tsx
Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
* Fix badge types
* Lazy loaded timezone select to save 85kb in bundle size.
* Upgraded framer to latest. Als moved framer and react sticky deps to features instead of atoms.
* Added new pagewrapper logic
* Simplified rescheduling ssr fetches, this now also supports multi seat rescheduling.
* Unset selected time when user is rescheduling directly after a new booking, otherwise it would show the form instead of new time selection.
* Updated form builder logic as per form builder in current booker.
* Updated form builder prefill logic as per logic in current booker.
* Updated getbooking function to fetch correct details when a reschedule uid is used
* Fixed booking questions test by NOT waiting for /book page because the new booker doesnt have this.
* Added former meeting time to reschedule view.
* Fixed types
* Undo playwright config update by mistake.
* Fixed event types test by only waiting for /book page in old booker
* Set new booker cookie to one year in the future instead of 2050
* added reset mockdate to test
* Temporary disabled test to see if this solves the out of memory error.
* Deleted test to see if that fixes the memory error
* Select first day when switching months in booker
---------
Co-authored-by: zomars <zomars@me.com>
Co-authored-by: Alex van Andel <me@alexvanandel.com>
Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
Co-authored-by: Sean Brydon <sean@cal.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2023-04-24 14:32:30 +00:00
selectedTimeFormat : timeFormat ,
} ) }
2022-12-13 07:23:26 +00:00
< / p >
< / div >
) }
{ ! ! eventType . seatsPerTimeSlot && (
< div className = "text-bookinghighlight flex items-start text-sm" >
2023-04-12 15:26:31 +00:00
< User
2023-01-04 07:38:45 +00:00
className = { ` ml-[2px] mt-[2px] inline-block h-4 w-4 ltr:mr-[10px] rtl:ml-[10px] ${
2023-03-27 09:34:41 +00:00
currentSlotBooking &&
currentSlotBooking . attendees . length / eventType . seatsPerTimeSlot >= 0.5
2022-12-13 07:23:26 +00:00
? "text-rose-600"
2023-03-27 09:34:41 +00:00
: currentSlotBooking &&
currentSlotBooking . attendees . length / eventType . seatsPerTimeSlot >= 0.33
2022-12-13 07:23:26 +00:00
? "text-yellow-500"
: "text-bookinghighlight"
} ` }
/ >
< p
className = { ` ${
2023-03-27 09:34:41 +00:00
currentSlotBooking &&
currentSlotBooking . attendees . length / eventType . seatsPerTimeSlot >= 0.5
2022-12-13 07:23:26 +00:00
? "text-rose-600"
2023-03-27 09:34:41 +00:00
: currentSlotBooking &&
currentSlotBooking . attendees . length / eventType . seatsPerTimeSlot >= 0.33
2022-12-13 07:23:26 +00:00
? "text-yellow-500"
: "text-bookinghighlight"
} mb - 2 font - medium ` }>
2023-03-27 09:34:41 +00:00
{ currentSlotBooking
? eventType . seatsPerTimeSlot - currentSlotBooking . attendees . length
2022-12-13 07:23:26 +00:00
: eventType . seatsPerTimeSlot } { " " }
2023-05-24 09:29:49 +00:00
/ { e v e n t T y p e . s e a t s P e r T i m e S l o t } { " " }
{ t ( "seats_available" , {
count : currentSlotBooking
? eventType . seatsPerTimeSlot - currentSlotBooking . attendees . length
: eventType . seatsPerTimeSlot ,
} ) }
2022-12-13 07:23:26 +00:00
< / p >
< / div >
) }
< / BookingDescription >
< / div >
) }
< div className = { classNames ( "p-6" , showEventTypeDetails ? "sm:w-1/2" : "w-full" ) } >
2023-03-02 18:15:28 +00:00
< Form form = { bookingForm } noValidate handleSubmit = { bookEvent } >
< BookingFields
isDynamicGroupBooking = { isDynamicGroupBooking }
fields = { eventType . bookingFields }
locations = { locations }
rescheduleUid = { rescheduleUid }
/ >
< div
className = { classNames (
"flex justify-end space-x-2 rtl:space-x-reverse" ,
// HACK: If the last field is guests, we need to move Cancel, Submit buttons up because "Add Guests" being present on the left and the buttons on the right, spacing is not required
eventType . bookingFields [ eventType . bookingFields . length - 1 ] . name ===
SystemField . Enum . guests
? "-mt-4"
: ""
) } >
2023-01-17 12:21:05 +00:00
< Button color = "minimal" type = "button" onClick = { ( ) = > router . back ( ) } >
2022-09-02 21:16:36 +00:00
{ t ( "cancel" ) }
< / Button >
2022-07-26 08:27:57 +00:00
< Button
type = "submit"
data - testid = { rescheduleUid ? "confirm-reschedule-button" : "confirm-book-button" }
loading = { mutation . isLoading || recurringMutation . isLoading } >
{ rescheduleUid ? t ( "reschedule" ) : t ( "confirm" ) }
< / Button >
< / div >
< / Form >
2023-04-13 19:55:26 +00:00
{ mutation . isError || recurringMutation . isError ? (
2022-07-26 08:27:57 +00:00
< ErrorMessage error = { mutation . error || recurringMutation . error } / >
2023-04-13 19:55:26 +00:00
) : null }
2021-09-14 08:45:28 +00:00
< / div >
< / div >
2022-07-26 08:27:57 +00:00
< / div >
2021-09-24 22:11:30 +00:00
< / main >
2023-03-17 10:32:02 +00:00
< Toaster position = "bottom-right" / >
2022-09-05 21:10:58 +00:00
< / Gates >
2021-09-14 08:45:28 +00:00
) ;
} ;
export default BookingPage ;
2022-06-10 18:38:46 +00:00
function ErrorMessage ( { error } : { error : unknown } ) {
const { t } = useLocale ( ) ;
const { query : { rescheduleUid } = { } } = useRouter ( ) ;
2023-03-09 13:11:07 +00:00
const router = useRouter ( ) ;
2022-06-10 18:38:46 +00:00
return (
2023-03-09 13:11:07 +00:00
< div data - testid = "booking-fail" className = "mt-2 border-l-4 border-blue-400 bg-blue-50 p-4" >
2022-06-10 18:38:46 +00:00
< div className = "flex" >
< div className = "flex-shrink-0" >
2023-04-12 15:26:31 +00:00
< AlertTriangle className = "h-5 w-5 text-blue-400" aria - hidden = "true" / >
2022-06-10 18:38:46 +00:00
< / div >
2023-04-09 09:13:57 +00:00
< div className = "ms-3" >
2023-03-09 13:11:07 +00:00
< p className = "text-sm text-blue-700" >
2022-06-10 18:38:46 +00:00
{ rescheduleUid ? t ( "reschedule_fail" ) : t ( "booking_fail" ) } { " " }
2023-03-09 13:11:07 +00:00
{ error instanceof HttpError || error instanceof Error ? (
< >
{ t ( "can_you_try_again" ) } { " " }
< span className = "cursor-pointer underline" onClick = { ( ) = > router . back ( ) } >
{ t ( "go_back" ) }
< / span >
.
< / > /* t(error.message) */
) : (
"Unknown error"
) }
2022-06-10 18:38:46 +00:00
< / p >
< / div >
< / div >
< / div >
) ;
}