From 4368ad02894eb18eb7d11ac3795d4f525dba79af Mon Sep 17 00:00:00 2001 From: Alex van Andel Date: Thu, 22 Jul 2021 22:52:27 +0000 Subject: [PATCH 1/3] Implement minimum booking notice --- components/booking/AvailableTimes.tsx | 2 ++ components/booking/DatePicker.tsx | 4 ++++ lib/slots.ts | 13 +++++++++---- pages/[user]/[type].tsx | 5 ++++- prisma/schema.prisma | 1 + 5 files changed, 20 insertions(+), 5 deletions(-) diff --git a/components/booking/AvailableTimes.tsx b/components/booking/AvailableTimes.tsx index 64cb4dd0..e2d6af3e 100644 --- a/components/booking/AvailableTimes.tsx +++ b/components/booking/AvailableTimes.tsx @@ -7,6 +7,7 @@ const AvailableTimes = ({ date, eventLength, eventTypeId, + minimumBookingNotice, workingHours, timeFormat, user, @@ -20,6 +21,7 @@ const AvailableTimes = ({ eventLength, workingHours, organizerTimeZone, + minimumBookingNotice, }); return ( diff --git a/components/booking/DatePicker.tsx b/components/booking/DatePicker.tsx index b49b887e..c6a49fa0 100644 --- a/components/booking/DatePicker.tsx +++ b/components/booking/DatePicker.tsx @@ -23,6 +23,7 @@ const DatePicker = ({ periodEndDate, periodDays, periodCountCalendarDays, + minimumBookingNotice, }) => { const [calendar, setCalendar] = useState([]); const [selectedMonth, setSelectedMonth] = useState(); @@ -77,6 +78,7 @@ const DatePicker = ({ !getSlots({ inviteeDate: date, frequency: eventLength, + minimumBookingNotice, workingHours, organizerTimeZone, }).length @@ -93,6 +95,7 @@ const DatePicker = ({ !getSlots({ inviteeDate: date, frequency: eventLength, + minimumBookingNotice, workingHours, organizerTimeZone, }).length @@ -106,6 +109,7 @@ const DatePicker = ({ !getSlots({ inviteeDate: date, frequency: eventLength, + minimumBookingNotice, workingHours, organizerTimeZone, }).length diff --git a/lib/slots.ts b/lib/slots.ts index 3c0d45a1..957d084b 100644 --- a/lib/slots.ts +++ b/lib/slots.ts @@ -1,7 +1,6 @@ import dayjs, { Dayjs } from "dayjs"; import utc from "dayjs/plugin/utc"; import timezone from "dayjs/plugin/timezone"; - dayjs.extend(utc); dayjs.extend(timezone); @@ -119,9 +118,15 @@ const getSlots = ({ workingHours, organizerTimeZone, }: GetSlots): Dayjs[] => { - const startTime = dayjs().utcOffset(inviteeDate.utcOffset()).isSame(inviteeDate, "day") - ? inviteeDate.hour() * 60 + inviteeDate.minute() + (minimumBookingNotice || 0) - : 0; + // current date in invitee tz + const currentDate = dayjs().utcOffset(inviteeDate.utcOffset()); + const startDate = currentDate.add(minimumBookingNotice, "minutes"); // + minimum notice period + // when the invitee date is not the same as the current date, reset the date to the start of day + if (inviteeDate.date() !== currentDate.date()) { + inviteeDate = inviteeDate.startOf("day"); + } + + const startTime = startDate.isAfter(inviteeDate) ? inviteeDate.hour() * 60 + inviteeDate.minute() : 0; const inviteeBounds = inviteeBoundary(startTime, inviteeDate.utcOffset(), frequency); diff --git a/pages/[user]/[type].tsx b/pages/[user]/[type].tsx index 04cdbd76..222db31e 100644 --- a/pages/[user]/[type].tsx +++ b/pages/[user]/[type].tsx @@ -167,14 +167,16 @@ export default function Type(props): Type { organizerTimeZone={props.eventType.timeZone || props.user.timeZone} inviteeTimeZone={timeZone()} eventLength={props.eventType.length} + minimumBookingNotice={props.eventType.minimumBookingNotice} /> {selectedDate && ( @@ -238,6 +240,7 @@ export const getServerSideProps: GetServerSideProps = async (context: GetServerS "periodStartDate", "periodEndDate", "periodCountCalendarDays", + "minimumBookingNotice", ] ); diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 83aed724..409c68af 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -31,6 +31,7 @@ model EventType { periodDays Int? periodCountCalendarDays Boolean? requiresConfirmation Boolean @default(false) + minimumBookingNotice Int @default(120) } model Credential { From 00550ac8ce4d1bd74a7ee972759519e328d9da6d Mon Sep 17 00:00:00 2001 From: Alex van Andel Date: Thu, 22 Jul 2021 22:55:15 +0000 Subject: [PATCH 2/3] Added migration for minimum booking notice --- .../20210722225431_minimum_booking_notice/migration.sql | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 prisma/migrations/20210722225431_minimum_booking_notice/migration.sql diff --git a/prisma/migrations/20210722225431_minimum_booking_notice/migration.sql b/prisma/migrations/20210722225431_minimum_booking_notice/migration.sql new file mode 100644 index 00000000..75c5a7de --- /dev/null +++ b/prisma/migrations/20210722225431_minimum_booking_notice/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "EventType" ADD COLUMN "minimumBookingNotice" INTEGER NOT NULL DEFAULT 120; From 9234f74bec4ce767e39c44bde9f32203f07c4d0f Mon Sep 17 00:00:00 2001 From: Alex van Andel Date: Fri, 23 Jul 2021 20:19:23 +0000 Subject: [PATCH 3/3] Added accompanying frontend --- lib/slots.ts | 8 +++++-- pages/api/availability/eventtype.ts | 1 + pages/availability/event/[type].tsx | 35 ++++++++++++++++++++--------- 3 files changed, 32 insertions(+), 12 deletions(-) diff --git a/lib/slots.ts b/lib/slots.ts index 957d084b..e4fdaa41 100644 --- a/lib/slots.ts +++ b/lib/slots.ts @@ -126,8 +126,12 @@ const getSlots = ({ inviteeDate = inviteeDate.startOf("day"); } - const startTime = startDate.isAfter(inviteeDate) ? inviteeDate.hour() * 60 + inviteeDate.minute() : 0; - + const startTime = startDate.isAfter(inviteeDate) + ? // block out everything when inviteeDate is less than startDate + startDate.date() > inviteeDate.date() + ? 1440 + : startDate.hour() * 60 + startDate.minute() + : 0; const inviteeBounds = inviteeBoundary(startTime, inviteeDate.utcOffset(), frequency); return getOverlaps( diff --git a/pages/api/availability/eventtype.ts b/pages/api/availability/eventtype.ts index 3add13cd..69dbac99 100644 --- a/pages/api/availability/eventtype.ts +++ b/pages/api/availability/eventtype.ts @@ -55,6 +55,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) periodStartDate: req.body.periodStartDate, periodEndDate: req.body.periodEndDate, periodCountCalendarDays: req.body.periodCountCalendarDays, + minimumBookingNotice: req.body.minimumBookingNotice, }; if (req.method == "POST") { diff --git a/pages/availability/event/[type].tsx b/pages/availability/event/[type].tsx index ae15a1a2..1fec9038 100644 --- a/pages/availability/event/[type].tsx +++ b/pages/availability/event/[type].tsx @@ -66,7 +66,8 @@ type EventTypeInput = { periodStartDate?: Date | string; periodEndDate?: Date | string; periodCountCalendarDays?: boolean; - enteredRequiresConfirmation: boolean; + requiresConfirmation: boolean; + minimumBookingNotice: number; }; const PERIOD_TYPES = [ @@ -92,7 +93,6 @@ export default function EventTypePage({ }: Props): JSX.Element { const router = useRouter(); - console.log(eventType); const inputOptions: OptionBase[] = [ { value: EventTypeCustomInputType.Text, label: "Text" }, { value: EventTypeCustomInputType.TextLong, label: "Multiline Text" }, @@ -174,6 +174,7 @@ export default function EventTypePage({ const lengthRef = useRef(); const isHiddenRef = useRef(); const requiresConfirmationRef = useRef(); + const minimumBookingNoticeRef = useRef(); const eventNameRef = useRef(); const periodDaysRef = useRef(); const periodDaysTypeRef = useRef(); @@ -190,6 +191,7 @@ export default function EventTypePage({ const enteredDescription: string = descriptionRef.current.value; const enteredLength: number = parseInt(lengthRef.current.value); const enteredIsHidden: boolean = isHiddenRef.current.checked; + const enteredMinimumBookingNotice: number = parseInt(minimumBookingNoticeRef.current.value); const enteredRequiresConfirmation: boolean = requiresConfirmationRef.current.checked; const enteredEventName: string = eventNameRef.current.value; @@ -200,14 +202,6 @@ export default function EventTypePage({ const enteredPeriodStartDate = periodStartDate ? periodStartDate.toDate() : null; const enteredPeriodEndDate = periodEndDate ? periodEndDate.toDate() : null; - console.log("values", { - type, - periodDaysTypeRef, - enteredPeriodDays, - enteredPeriodDaysType, - enteredPeriodStartDate, - enteredPeriodEndDate, - }); // TODO: Add validation const payload: EventTypeInput = { @@ -226,6 +220,7 @@ export default function EventTypePage({ periodStartDate: enteredPeriodStartDate, periodEndDate: enteredPeriodEndDate, periodCountCalendarDays: enteredPeriodDaysType, + minimumBookingNotice: enteredMinimumBookingNotice, requiresConfirmation: enteredRequiresConfirmation, }; @@ -671,6 +666,25 @@ export default function EventTypePage({
When can people book this event? +
+ +
+ +
+ minutes +
+
+

@@ -1019,6 +1033,7 @@ export const getServerSideProps: GetServerSideProps = async ({ req, query periodEndDate: true, periodCountCalendarDays: true, requiresConfirmation: true, + minimumBookingNotice: true, }, });