cal.pub0.org/apps/web/components/booking/SlotPicker.tsx

199 lines
6.4 KiB
TypeScript
Raw Normal View History

import type { EventType } from "@prisma/client";
import dynamic from "next/dynamic";
import { useRouter } from "next/router";
import { useEffect, useState } from "react";
import type { z } from "zod";
import type { Dayjs } from "@calcom/dayjs";
import dayjs from "@calcom/dayjs";
import DatePicker from "@calcom/features/calendars/DatePicker";
import classNames from "@calcom/lib/classNames";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import type { TimeFormat } from "@calcom/lib/timeFormat";
import type { EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils";
import { trpc } from "@calcom/trpc/react";
import useRouterQuery from "@lib/hooks/useRouterQuery";
const AvailableTimes = dynamic(() => import("@components/booking/AvailableTimes"));
const getRefetchInterval = (refetchCount: number): number => {
const intervals = [3000, 3000, 5000, 10000, 20000, 30000] as const;
return intervals[refetchCount] || intervals[intervals.length - 1];
};
const useSlots = ({
eventTypeId,
eventTypeSlug,
startTime,
endTime,
usernameList,
timeZone,
duration,
enabled = true,
}: {
eventTypeId: number;
eventTypeSlug: string;
startTime?: Dayjs;
endTime?: Dayjs;
usernameList: string[];
timeZone?: string;
duration?: string;
enabled?: boolean;
}) => {
const [refetchCount, setRefetchCount] = useState(0);
const refetchInterval = getRefetchInterval(refetchCount);
const { data, isLoading, isPaused, fetchStatus } = trpc.viewer.public.slots.getSchedule.useQuery(
{
eventTypeId,
eventTypeSlug,
usernameList,
startTime: startTime?.toISOString() || "",
endTime: endTime?.toISOString() || "",
timeZone,
duration,
},
{
enabled: !!startTime && !!endTime && enabled,
refetchInterval,
trpc: { context: { skipBatch: true } },
}
);
useEffect(() => {
if (!!data && fetchStatus === "idle") {
setRefetchCount(refetchCount + 1);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [fetchStatus, data]);
// The very first time isPaused is set if auto-fetch is disabled, so isPaused should also be considered a loading state.
return { slots: data?.slots || {}, isLoading: isLoading || isPaused };
};
export const SlotPicker = ({
eventType,
timeFormat,
onTimeFormatChange,
timeZone,
recurringEventCount,
users,
seatsPerTimeSlot,
Seated booking rescheduling. (#5427) * WIP-already-reschedule-success-emails-missing * WIP now saving bookingSeatsReferences and identifyin on reschedule/book page * Remove logs and created test * WIP saving progress * Select second slot to pass test * Delete attendee from event * Clean up * Update with main changes * Fix emails not being sent * Changed test end url from success to booking * Remove unused pkg * Fix new booking reschedule * remove log * Renable test * remove unused pkg * rename table name * review changes * Fix and and other test to reschedule with seats * Fix api for cancel booking * Typings * Update [uid].tsx * Abstracted common pattern into maybeGetBookingUidFromSeat * Reverts * Nitpicks * Update handleCancelBooking.ts * Adds missing cascades * Improve booking seats changes (#6858) * Create sendCancelledSeatEmails * Draft attendee cancelled seat email * Send no longer attendee email to attendee * Send email to organizer when attendee cancels * Pass cloned event data to emails * Send booked email for first seat * Add seat reference uid from email link * Query for seatReferenceUId and add to cancel & reschedule links * WIP * Display proper attendee when rescheduling seats * Remove console.logs * Only check for already invited when not rescheduling * WIP sending reschedule email to just single attendee and owner * Merge branch 'main' into send-email-on-seats-attendee-changes * Remove console.logs * Add cloned event to seat emails * Do not show manage link for calendar event * First seat, have both attendees on calendar * WIP refactor booking seats reschedule logic * WIP Refactor handleSeats * Change relation of attendee & seat reference to a one-to-one * Migration with relationship change * Booking page handling unique seat references * Abstract to handleSeats * Remove console.logs and clean up * New migration file, delete on cascade * Check if attendee is already a part of the booking * Move deleting booking logic to `handleSeats` * When owner reschedule, move whole booking * Prevent owner from rescheduling if not enough seats * Add owner reschedule * Send reschedule email when moving to new timeslot * Add event data to reschedule email for seats * Remove DB changes from event manager * When a booking has no attendees then delete * Update calendar when merging bookings * Move both attendees and seat references when merging * Remove guest list from seats booking page * Update original booking when moving an attendee * Delete calendar and video events if no more attendees * Update or delete integrations when attendees cancel * Show no longer attendee if a single attendee cancels * Change booking to accepted if an attendee books on an empty booking * If booking in same slot then just return the booking * Clean up * Clean up * Remove booking select * Typos --------- Co-authored-by: zomars <zomars@me.com> * Fix migration table name * Add missing trpc import * Rename bookingSeatReferences to bookingSeat * Change relationship between Attendee & BookingSeat to one to one * Fix some merge conflicts * Minor merge artifact fixup * Add the right 'Person' type * Check on email, less (although still) editable than name * Removed calEvent.attendeeUniqueId * rename referenceUId -> referenceUid * Squashes migrations * Run cached installs Should still be faster. Ensures prisma client is up to date. * Solve attendee form on booking page * Remove unused code * Some type fixes * Squash migrations * Type fixes * Fix for reschedule/[uid] redirect * Fix e2e test * Solve double declaration of host * Solve lint errors * Drop constraint only if exists * Renamed UId to Uid * Explicit vs. implicit * Attempt to work around text flakiness by adding a little break between animations * Various bugfixes * Persistently apply seatReferenceUid (#7545) * Persistently apply seatReferenceUid * Small ts fix * Setup guards correctly * Type fixes * Fix render 0 in conditional * Test refactoring * Fix type on handleSeats * Fix handle seats conditional * Fix type inference * Update packages/features/bookings/lib/handleNewBooking.ts * Update apps/web/components/booking/pages/BookingPage.tsx * Fix type and missing logic for reschedule * Fix delete of calendar event and booking * Add handleSeats return type * Fix seats booking creation * Fall through normal booking for initial booking, handleSeats for secondary/reschedule * Simplification of fetching booking * Enable seats for round-robin events * A lot harder than I expected * ignore-owner-if-seat-reference-given * Return seatReferenceUid when second seat * negate userIsOwner * Fix booking seats with a link without bookingUid * Needed a time check otherwise the attendee will be in the older booking * Can't open dialog twice in test.. * Allow passing the booking ID from the server * Fixed isCancelled check, fixed test * Delete through cascade instead of multiple deletes --------- Co-authored-by: Joe Au-Yeung <j.auyeung419@gmail.com> Co-authored-by: Peer Richelsen <peer@cal.com> Co-authored-by: zomars <zomars@me.com> Co-authored-by: Joe Au-Yeung <65426560+joeauyeung@users.noreply.github.com> Co-authored-by: Alex van Andel <me@alexvanandel.com> Co-authored-by: Efraín Rochín <roae.85@gmail.com> Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2023-03-14 04:19:05 +00:00
bookingAttendees,
weekStart = 0,
}: {
eventType: Pick<
EventType & { metadata: z.infer<typeof EventTypeMetaDataSchema> },
"id" | "schedulingType" | "slug" | "length" | "metadata"
>;
timeFormat: TimeFormat;
onTimeFormatChange: (is24Hour: boolean) => void;
timeZone?: string;
seatsPerTimeSlot?: number;
Seated booking rescheduling. (#5427) * WIP-already-reschedule-success-emails-missing * WIP now saving bookingSeatsReferences and identifyin on reschedule/book page * Remove logs and created test * WIP saving progress * Select second slot to pass test * Delete attendee from event * Clean up * Update with main changes * Fix emails not being sent * Changed test end url from success to booking * Remove unused pkg * Fix new booking reschedule * remove log * Renable test * remove unused pkg * rename table name * review changes * Fix and and other test to reschedule with seats * Fix api for cancel booking * Typings * Update [uid].tsx * Abstracted common pattern into maybeGetBookingUidFromSeat * Reverts * Nitpicks * Update handleCancelBooking.ts * Adds missing cascades * Improve booking seats changes (#6858) * Create sendCancelledSeatEmails * Draft attendee cancelled seat email * Send no longer attendee email to attendee * Send email to organizer when attendee cancels * Pass cloned event data to emails * Send booked email for first seat * Add seat reference uid from email link * Query for seatReferenceUId and add to cancel & reschedule links * WIP * Display proper attendee when rescheduling seats * Remove console.logs * Only check for already invited when not rescheduling * WIP sending reschedule email to just single attendee and owner * Merge branch 'main' into send-email-on-seats-attendee-changes * Remove console.logs * Add cloned event to seat emails * Do not show manage link for calendar event * First seat, have both attendees on calendar * WIP refactor booking seats reschedule logic * WIP Refactor handleSeats * Change relation of attendee & seat reference to a one-to-one * Migration with relationship change * Booking page handling unique seat references * Abstract to handleSeats * Remove console.logs and clean up * New migration file, delete on cascade * Check if attendee is already a part of the booking * Move deleting booking logic to `handleSeats` * When owner reschedule, move whole booking * Prevent owner from rescheduling if not enough seats * Add owner reschedule * Send reschedule email when moving to new timeslot * Add event data to reschedule email for seats * Remove DB changes from event manager * When a booking has no attendees then delete * Update calendar when merging bookings * Move both attendees and seat references when merging * Remove guest list from seats booking page * Update original booking when moving an attendee * Delete calendar and video events if no more attendees * Update or delete integrations when attendees cancel * Show no longer attendee if a single attendee cancels * Change booking to accepted if an attendee books on an empty booking * If booking in same slot then just return the booking * Clean up * Clean up * Remove booking select * Typos --------- Co-authored-by: zomars <zomars@me.com> * Fix migration table name * Add missing trpc import * Rename bookingSeatReferences to bookingSeat * Change relationship between Attendee & BookingSeat to one to one * Fix some merge conflicts * Minor merge artifact fixup * Add the right 'Person' type * Check on email, less (although still) editable than name * Removed calEvent.attendeeUniqueId * rename referenceUId -> referenceUid * Squashes migrations * Run cached installs Should still be faster. Ensures prisma client is up to date. * Solve attendee form on booking page * Remove unused code * Some type fixes * Squash migrations * Type fixes * Fix for reschedule/[uid] redirect * Fix e2e test * Solve double declaration of host * Solve lint errors * Drop constraint only if exists * Renamed UId to Uid * Explicit vs. implicit * Attempt to work around text flakiness by adding a little break between animations * Various bugfixes * Persistently apply seatReferenceUid (#7545) * Persistently apply seatReferenceUid * Small ts fix * Setup guards correctly * Type fixes * Fix render 0 in conditional * Test refactoring * Fix type on handleSeats * Fix handle seats conditional * Fix type inference * Update packages/features/bookings/lib/handleNewBooking.ts * Update apps/web/components/booking/pages/BookingPage.tsx * Fix type and missing logic for reschedule * Fix delete of calendar event and booking * Add handleSeats return type * Fix seats booking creation * Fall through normal booking for initial booking, handleSeats for secondary/reschedule * Simplification of fetching booking * Enable seats for round-robin events * A lot harder than I expected * ignore-owner-if-seat-reference-given * Return seatReferenceUid when second seat * negate userIsOwner * Fix booking seats with a link without bookingUid * Needed a time check otherwise the attendee will be in the older booking * Can't open dialog twice in test.. * Allow passing the booking ID from the server * Fixed isCancelled check, fixed test * Delete through cascade instead of multiple deletes --------- Co-authored-by: Joe Au-Yeung <j.auyeung419@gmail.com> Co-authored-by: Peer Richelsen <peer@cal.com> Co-authored-by: zomars <zomars@me.com> Co-authored-by: Joe Au-Yeung <65426560+joeauyeung@users.noreply.github.com> Co-authored-by: Alex van Andel <me@alexvanandel.com> Co-authored-by: Efraín Rochín <roae.85@gmail.com> Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2023-03-14 04:19:05 +00:00
bookingAttendees?: number;
recurringEventCount?: number;
users: string[];
weekStart?: 0 | 1 | 2 | 3 | 4 | 5 | 6;
}) => {
const [selectedDate, setSelectedDate] = useState<Dayjs>();
const [browsingDate, setBrowsingDate] = useState<Dayjs>();
let { duration = eventType.length.toString() } = useRouterQuery("duration");
const { date, setQuery: setDate } = useRouterQuery("date");
const { month, setQuery: setMonth } = useRouterQuery("month");
const router = useRouter();
if (!eventType.metadata?.multipleDuration) {
duration = eventType.length.toString();
}
useEffect(() => {
if (!router.isReady) return;
// Etc/GMT is not actually a timeZone, so handle this select option explicitly to prevent a hard crash.
if (timeZone === "Etc/GMT") {
setBrowsingDate(dayjs.utc(month).set("date", 1).set("hour", 0).set("minute", 0).set("second", 0));
if (date) {
setSelectedDate(dayjs.utc(date));
}
} else {
// Set the start of the month without shifting time like startOf() may do.
setBrowsingDate(
dayjs.tz(month, timeZone).set("date", 1).set("hour", 0).set("minute", 0).set("second", 0)
);
if (date) {
// It's important to set the date immediately to the timeZone, dayjs(date) will convert to browsertime.
setSelectedDate(dayjs.tz(date, timeZone));
}
}
}, [router.isReady, month, date, duration, timeZone]);
const { i18n, isLocaleReady } = useLocale();
const { slots: monthSlots, isLoading } = useSlots({
eventTypeId: eventType.id,
eventTypeSlug: eventType.slug,
usernameList: users,
startTime: dayjs().startOf("day"),
endTime: browsingDate?.endOf("month"),
timeZone,
duration,
});
const { slots: selectedDateSlots, isLoading: _isLoadingSelectedDateSlots } = useSlots({
eventTypeId: eventType.id,
eventTypeSlug: eventType.slug,
usernameList: users,
startTime: selectedDate?.startOf("day"),
endTime: selectedDate?.endOf("day"),
timeZone,
duration,
/** Prevent refetching is we already have this data from month slots */
enabled: !!selectedDate,
});
/** Hide skeleton if we have the slot loaded in the month query */
const isLoadingSelectedDateSlots = (() => {
if (!selectedDate) return _isLoadingSelectedDateSlots;
if (!!selectedDateSlots[selectedDate.format("YYYY-MM-DD")]) return false;
if (!!monthSlots[selectedDate.format("YYYY-MM-DD")]) return false;
return false;
})();
return (
<>
<DatePicker
isLoading={isLoading}
className={classNames(
"mt-8 px-4 pb-4 sm:mt-0 md:min-w-[300px] md:px-4 lg:min-w-[455px]",
selectedDate ? " border-subtle sm:border-r sm:p-4 sm:pr-6" : "sm:p-4"
)}
includedDates={Object.keys(monthSlots).filter((k) => monthSlots[k].length > 0)}
locale={isLocaleReady ? i18n.language : "en"}
selected={selectedDate}
onChange={(newDate) => {
setDate(newDate.format("YYYY-MM-DD"));
}}
onMonthChange={(newMonth) => {
setMonth(newMonth.format("YYYY-MM"));
}}
browsingDate={browsingDate}
weekStart={weekStart}
/>
<AvailableTimes
isLoading={isLoadingSelectedDateSlots}
slots={
selectedDate &&
(selectedDateSlots[selectedDate.format("YYYY-MM-DD")] ||
monthSlots[selectedDate.format("YYYY-MM-DD")])
}
date={selectedDate}
timeFormat={timeFormat}
onTimeFormatChange={onTimeFormatChange}
eventTypeId={eventType.id}
eventTypeSlug={eventType.slug}
seatsPerTimeSlot={seatsPerTimeSlot}
Seated booking rescheduling. (#5427) * WIP-already-reschedule-success-emails-missing * WIP now saving bookingSeatsReferences and identifyin on reschedule/book page * Remove logs and created test * WIP saving progress * Select second slot to pass test * Delete attendee from event * Clean up * Update with main changes * Fix emails not being sent * Changed test end url from success to booking * Remove unused pkg * Fix new booking reschedule * remove log * Renable test * remove unused pkg * rename table name * review changes * Fix and and other test to reschedule with seats * Fix api for cancel booking * Typings * Update [uid].tsx * Abstracted common pattern into maybeGetBookingUidFromSeat * Reverts * Nitpicks * Update handleCancelBooking.ts * Adds missing cascades * Improve booking seats changes (#6858) * Create sendCancelledSeatEmails * Draft attendee cancelled seat email * Send no longer attendee email to attendee * Send email to organizer when attendee cancels * Pass cloned event data to emails * Send booked email for first seat * Add seat reference uid from email link * Query for seatReferenceUId and add to cancel & reschedule links * WIP * Display proper attendee when rescheduling seats * Remove console.logs * Only check for already invited when not rescheduling * WIP sending reschedule email to just single attendee and owner * Merge branch 'main' into send-email-on-seats-attendee-changes * Remove console.logs * Add cloned event to seat emails * Do not show manage link for calendar event * First seat, have both attendees on calendar * WIP refactor booking seats reschedule logic * WIP Refactor handleSeats * Change relation of attendee & seat reference to a one-to-one * Migration with relationship change * Booking page handling unique seat references * Abstract to handleSeats * Remove console.logs and clean up * New migration file, delete on cascade * Check if attendee is already a part of the booking * Move deleting booking logic to `handleSeats` * When owner reschedule, move whole booking * Prevent owner from rescheduling if not enough seats * Add owner reschedule * Send reschedule email when moving to new timeslot * Add event data to reschedule email for seats * Remove DB changes from event manager * When a booking has no attendees then delete * Update calendar when merging bookings * Move both attendees and seat references when merging * Remove guest list from seats booking page * Update original booking when moving an attendee * Delete calendar and video events if no more attendees * Update or delete integrations when attendees cancel * Show no longer attendee if a single attendee cancels * Change booking to accepted if an attendee books on an empty booking * If booking in same slot then just return the booking * Clean up * Clean up * Remove booking select * Typos --------- Co-authored-by: zomars <zomars@me.com> * Fix migration table name * Add missing trpc import * Rename bookingSeatReferences to bookingSeat * Change relationship between Attendee & BookingSeat to one to one * Fix some merge conflicts * Minor merge artifact fixup * Add the right 'Person' type * Check on email, less (although still) editable than name * Removed calEvent.attendeeUniqueId * rename referenceUId -> referenceUid * Squashes migrations * Run cached installs Should still be faster. Ensures prisma client is up to date. * Solve attendee form on booking page * Remove unused code * Some type fixes * Squash migrations * Type fixes * Fix for reschedule/[uid] redirect * Fix e2e test * Solve double declaration of host * Solve lint errors * Drop constraint only if exists * Renamed UId to Uid * Explicit vs. implicit * Attempt to work around text flakiness by adding a little break between animations * Various bugfixes * Persistently apply seatReferenceUid (#7545) * Persistently apply seatReferenceUid * Small ts fix * Setup guards correctly * Type fixes * Fix render 0 in conditional * Test refactoring * Fix type on handleSeats * Fix handle seats conditional * Fix type inference * Update packages/features/bookings/lib/handleNewBooking.ts * Update apps/web/components/booking/pages/BookingPage.tsx * Fix type and missing logic for reschedule * Fix delete of calendar event and booking * Add handleSeats return type * Fix seats booking creation * Fall through normal booking for initial booking, handleSeats for secondary/reschedule * Simplification of fetching booking * Enable seats for round-robin events * A lot harder than I expected * ignore-owner-if-seat-reference-given * Return seatReferenceUid when second seat * negate userIsOwner * Fix booking seats with a link without bookingUid * Needed a time check otherwise the attendee will be in the older booking * Can't open dialog twice in test.. * Allow passing the booking ID from the server * Fixed isCancelled check, fixed test * Delete through cascade instead of multiple deletes --------- Co-authored-by: Joe Au-Yeung <j.auyeung419@gmail.com> Co-authored-by: Peer Richelsen <peer@cal.com> Co-authored-by: zomars <zomars@me.com> Co-authored-by: Joe Au-Yeung <65426560+joeauyeung@users.noreply.github.com> Co-authored-by: Alex van Andel <me@alexvanandel.com> Co-authored-by: Efraín Rochín <roae.85@gmail.com> Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2023-03-14 04:19:05 +00:00
bookingAttendees={bookingAttendees}
recurringCount={recurringEventCount}
duration={parseInt(duration)}
/>
</>
);
};