cal.pub0.org/apps/web/lib/slots.ts

115 lines
4.0 KiB
TypeScript
Raw Normal View History

import dayjs, { Dayjs } from "dayjs";
import isBetween from "dayjs/plugin/isBetween";
import isToday from "dayjs/plugin/isToday";
import utc from "dayjs/plugin/utc";
import { getWorkingHours } from "./availability";
Feature/booking page refactor (#3035) * Extracted UI related logic on the DatePicker, stripped out all logic * wip * fixed small regression due to merge * Fix alignment of the chevrons * Added isToday dot, added onMonthChange so we can fetch this month slots * Added includedDates to inverse excludedDates * removed trpcState * Improvements to the state * All params are now dynamic * This builds the flat map so not all paths block on every new build * Added requiresConfirmation * Correctly take into account getFilteredTimes to make the calendar function * Rewritten team availability, seems to work * Circumvent i18n flicker by showing the loader instead * 'You can remove this code. Its not being used now' - Hariom * Nailed a persistent little bug, new Date() caused the current day to flicker on and off * TS fixes * Fix some eventType details in AvailableTimes * '5 / 6 Seats Available' instead of '6 / Seats Available' * More type fixes * Removed unrelated merge artifact * Use WEBAPP_URL instead of hardcoded * Next round of TS fixes * I believe this was mistyped * Temporarily disabled rescheduling 'this is when you originally scheduled', so removed dep * Sorting some dead code * This page has a lot of red, not all related to this PR * A PR to your PR (#3067) * Cleanup * Cleanup * Uses zod to parse params * Type fixes * Fixes ISR * E2E fixes * Disabled dynamic bookings until post v1.7 * More test fixes * Fixed border position (transparent border) to prevent dot from jumping - and possibly fix spacing * Disabled style nitpicks * Delete useSlots.ts Removed early design artifact * Unlock DatePicker locale * Adds mini spinner to DatePicker Co-authored-by: Peer Richelsen <peeroke@gmail.com> Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> Co-authored-by: zomars <zomars@me.com>
2022-06-15 20:54:31 +00:00
import { WorkingHours } from "./types/schedule";
dayjs.extend(isToday);
dayjs.extend(utc);
dayjs.extend(isBetween);
export type GetSlots = {
inviteeDate: Dayjs;
frequency: number;
workingHours: WorkingHours[];
minimumBookingNotice: number;
eventLength: number;
2021-06-29 22:35:13 +00:00
};
export type WorkingHoursTimeFrame = { startTime: number; endTime: number };
const splitAvailableTime = (
startTimeMinutes: number,
endTimeMinutes: number,
frequency: number,
eventLength: number
): Array<WorkingHoursTimeFrame> => {
let initialTime = startTimeMinutes;
const finalizationTime = endTimeMinutes;
const result = [] as Array<WorkingHoursTimeFrame>;
while (initialTime < finalizationTime) {
const periodTime = initialTime + frequency;
const slotEndTime = initialTime + eventLength;
/*
check if the slot end time surpasses availability end time of the user
1 minute is added to round up the hour mark so that end of the slot is considered in the check instead of x9
eg: if finalization time is 11:59, slotEndTime is 12:00, we ideally want the slot to be available
*/
if (slotEndTime <= finalizationTime + 1) result.push({ startTime: initialTime, endTime: periodTime });
initialTime += frequency;
}
return result;
2021-06-29 22:35:13 +00:00
};
Feature/booking page refactor (#3035) * Extracted UI related logic on the DatePicker, stripped out all logic * wip * fixed small regression due to merge * Fix alignment of the chevrons * Added isToday dot, added onMonthChange so we can fetch this month slots * Added includedDates to inverse excludedDates * removed trpcState * Improvements to the state * All params are now dynamic * This builds the flat map so not all paths block on every new build * Added requiresConfirmation * Correctly take into account getFilteredTimes to make the calendar function * Rewritten team availability, seems to work * Circumvent i18n flicker by showing the loader instead * 'You can remove this code. Its not being used now' - Hariom * Nailed a persistent little bug, new Date() caused the current day to flicker on and off * TS fixes * Fix some eventType details in AvailableTimes * '5 / 6 Seats Available' instead of '6 / Seats Available' * More type fixes * Removed unrelated merge artifact * Use WEBAPP_URL instead of hardcoded * Next round of TS fixes * I believe this was mistyped * Temporarily disabled rescheduling 'this is when you originally scheduled', so removed dep * Sorting some dead code * This page has a lot of red, not all related to this PR * A PR to your PR (#3067) * Cleanup * Cleanup * Uses zod to parse params * Type fixes * Fixes ISR * E2E fixes * Disabled dynamic bookings until post v1.7 * More test fixes * Fixed border position (transparent border) to prevent dot from jumping - and possibly fix spacing * Disabled style nitpicks * Delete useSlots.ts Removed early design artifact * Unlock DatePicker locale * Adds mini spinner to DatePicker Co-authored-by: Peer Richelsen <peeroke@gmail.com> Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> Co-authored-by: zomars <zomars@me.com>
2022-06-15 20:54:31 +00:00
const getSlots = ({ inviteeDate, frequency, minimumBookingNotice, workingHours, eventLength }: GetSlots) => {
// current date in invitee tz
2021-12-16 15:20:38 +00:00
const startDate = dayjs().add(minimumBookingNotice, "minute");
const startOfDay = dayjs.utc().startOf("day");
const startOfInviteeDay = inviteeDate.startOf("day");
// checks if the start date is in the past
2021-12-16 15:20:38 +00:00
if (inviteeDate.isBefore(startDate, "day")) {
return [];
}
const localWorkingHours = getWorkingHours(
{ utcOffset: -inviteeDate.utcOffset() },
workingHours.map((schedule) => ({
days: schedule.days,
startTime: startOfDay.add(schedule.startTime, "minute"),
endTime: startOfDay.add(schedule.endTime, "minute"),
}))
).filter((hours) => hours.days.includes(inviteeDate.day()));
const slots: Dayjs[] = [];
const slotsTimeFrameAvailable = [] as Array<WorkingHoursTimeFrame>;
// Here we split working hour in chunks for every frequency available that can fit in whole working hours
const computedLocalWorkingHours: WorkingHoursTimeFrame[] = [];
let tempComputeTimeFrame: WorkingHoursTimeFrame | undefined;
const computeLength = localWorkingHours.length - 1;
const makeTimeFrame = (item: typeof localWorkingHours[0]): WorkingHoursTimeFrame => ({
startTime: item.startTime,
endTime: item.endTime,
});
localWorkingHours.forEach((item, index) => {
if (!tempComputeTimeFrame) {
tempComputeTimeFrame = makeTimeFrame(item);
} else {
// please check the comment in splitAvailableTime func for the added 1 minute
if (tempComputeTimeFrame.endTime + 1 === item.startTime) {
// to deal with time that across the day, e.g. from 11:59 to to 12:01
tempComputeTimeFrame.endTime = item.endTime;
} else {
computedLocalWorkingHours.push(tempComputeTimeFrame);
tempComputeTimeFrame = makeTimeFrame(item);
}
}
if (index == computeLength) {
computedLocalWorkingHours.push(tempComputeTimeFrame);
}
});
2022-05-17 20:43:27 +00:00
computedLocalWorkingHours.forEach((item) => {
slotsTimeFrameAvailable.push(...splitAvailableTime(item.startTime, item.endTime, frequency, eventLength));
});
slotsTimeFrameAvailable.forEach((item) => {
const slot = startOfInviteeDay.add(item.startTime, "minute");
// Validating slot its not on the past
if (!slot.isBefore(startDate)) {
slots.push(slot);
}
});
const uniq = (a: Dayjs[]) => {
const seen: Record<string, boolean> = {};
return a.filter((item) => {
return seen.hasOwnProperty(item.format()) ? false : (seen[item.format()] = true);
});
};
return uniq(slots);
};
export default getSlots;