Untitled
unknown
plain_text
2 years ago
14 kB
11
Indexable
"use node";
import { PMSPlatform, Integration } from "../../types";
import { fetchCalendarForListingIdInRange as fetchHostawayCalendarForListingIdInRange } from "../integrations/hostaway";
import { fetchCalendarForListingIdInRange as fetchGuestyCalendarForListingIdInRange } from "../integrations/guesty";
import { internalAction } from "../../_generated/server";
import { Doc, Id } from "../../_generated/dataModel";
import { internal } from "../../_generated/api";
import { logSlackError } from "./slackLogger";
export const changeDateWithMonthDelta = (
dateString: string,
delta: number
): string => {
const date = new Date(dateString);
date.setMonth(date.getMonth() + delta);
const formatted =
date.getFullYear() + "-" + (date.getMonth() + 1) + "-" + date.getDate();
return formatted;
};
export const changeDateWithDayDelta = (
dateString: string,
delta: number
): string => {
const date = new Date(dateString);
date.setDate(date.getDate() + delta);
const formatted =
date.getFullYear() + "-" + (date.getMonth() + 1) + "-" + date.getDate();
return formatted;
};
export const getDateWithMonthDelta = (delta: number): string => {
const date = new Date();
date.setMonth(date.getMonth() + delta);
const formatted =
date.getFullYear() + "-" + (date.getMonth() + 1) + "-" + date.getDate();
return formatted;
};
export const getCurrentDateString = () => {
const today = new Date();
const date =
today.getFullYear() +
"-" +
(today.getMonth() + 1) +
"-" +
today.getDate();
return date;
};
function formatDateString(dateString: string): string {
let parts = dateString.split("-");
// Ensure the year, month, and day parts exist
if (parts.length !== 3) {
throw new Error("Invalid date format");
}
let [year, month, day] = parts;
// Pad month and day with leading zero if necessary
month = month.length === 1 ? `0${month}` : month;
day = day.length === 1 ? `0${day}` : day;
return `${year}-${month}-${day}`;
}
export const getAvailabilityResponseWithStartEnd = internalAction(
async (
{ runQuery },
{
userId,
listing,
reservation,
startDate,
endDate,
}: {
userId: Id<"users">;
listing: Doc<"listings">;
reservation: Doc<"reservations">;
startDate: string;
endDate: string;
}
): Promise<{ response: string; possible: boolean }> => {
const pmsPlatform = listing?.pmsPlatform;
const pmsIntegration: Integration | null = await runQuery(
internal.integrations.getForUser,
{
userId,
pmsPlatform: pmsPlatform as PMSPlatform,
}
);
if (!pmsIntegration) {
throw new Error(
`No integration found for ${pmsPlatform}. Please connect to ${pmsPlatform} first.`
);
}
let passedStartDate = startDate;
let passedEndDate = endDate;
// if there isn't a start date,
// if there is an end date, set start date to 10 days before end date
// else set start date to 1 month before today
if (!startDate)
passedStartDate = endDate
? changeDateWithDayDelta(endDate, -10)
: getDateWithMonthDelta(-1);
// if there isn't an end date, set end date to 10 days after start date
if (!endDate) passedEndDate = changeDateWithDayDelta(startDate, 10);
let calendar: any[] = [];
// if call is gonna be invalid, swap dates
if (new Date(passedStartDate) > new Date(passedEndDate)) {
const temp = passedStartDate;
passedStartDate = passedEndDate;
passedEndDate = temp;
}
passedStartDate = formatDateString(passedStartDate);
passedEndDate = formatDateString(passedEndDate);
try {
switch (pmsPlatform) {
case PMSPlatform.HOSTAWAY:
calendar = await fetchHostawayCalendarForListingIdInRange(
pmsIntegration.accessToken,
listing.listingId as number,
passedStartDate,
passedEndDate
);
break;
case PMSPlatform.HOSTFULLY:
break;
case PMSPlatform.GUESTY:
calendar = await fetchGuestyCalendarForListingIdInRange(
pmsIntegration.accessToken,
listing.listingId as number,
passedStartDate,
passedEndDate
);
calendar = calendar.map((item) => {
return {
...item,
isAvailable: item.status === "available" ? 1 : 0,
};
});
break;
default:
break;
}
// const maxDate = new Date(Math.max(...dates.map(date => date.getTime())));
if (startDate && endDate) {
return checkAvailabilityWithStartEnd(
reservation,
startDate,
endDate,
calendar
);
}
if (startDate && !endDate) {
const reservationStart = new Date(reservation.arrivalDate);
if (
new Date(startDate).getTime() === reservationStart.getTime()
) {
return {
response: `The requested date of ${startDate} is available.`,
possible: true,
};
}
return checkSingleDateAvailability(startDate, calendar);
}
if (!startDate && endDate) {
const reservationEnd = new Date(reservation.departureDate);
if (new Date(endDate).getTime() === reservationEnd.getTime()) {
return {
response: `The requested date of ${endDate} is available.`,
possible: true,
};
}
return checkSingleDateAvailability(endDate, calendar);
}
return {
response: "",
possible: false,
};
} catch (e: any) {
console.log(e);
await logSlackError(
e.message,
"getAvailabilityResponseWithStartEnd"
);
return {
response: "",
possible: false,
};
}
}
);
export const getAvailabilityResponseWithSingleDate = internalAction(
async (
{ runQuery },
{
userId,
listing,
date,
request,
}: {
userId: Id<"users">;
listing: Doc<"listings">;
date: string;
request: string;
}
): Promise<{ response: string; possible: boolean }> => {
const pmsPlatform = listing?.pmsPlatform;
const pmsIntegration: Integration | null = await runQuery(
internal.integrations.getForUser,
{
userId,
pmsPlatform: pmsPlatform as PMSPlatform,
}
);
if (!pmsIntegration) {
throw new Error(
`No integration found for ${pmsPlatform}. Please connect to ${pmsPlatform} first.`
);
}
let calendar: any[] = [];
const formattedStartDate = formatDateString(date);
const formattedEndDate = formatDateString(
changeDateWithDayDelta(date, 1)
);
try {
switch (pmsPlatform) {
case PMSPlatform.HOSTAWAY:
calendar = await fetchHostawayCalendarForListingIdInRange(
pmsIntegration.accessToken,
listing.listingId as number,
formattedStartDate,
formattedEndDate
);
break;
case PMSPlatform.HOSTFULLY:
break;
case PMSPlatform.GUESTY:
calendar = await fetchGuestyCalendarForListingIdInRange(
pmsIntegration.accessToken,
listing.listingId as number,
formattedStartDate,
formattedEndDate
);
calendar = calendar.map((item) => {
return {
...item,
isAvailable: item.status === "available" ? 1 : 0,
};
});
break;
default:
break;
}
// Check dates
if (!date) {
return { response: `No requested date.`, possible: false };
}
return checkSingleDateAvailability(date, calendar, request);
} catch (e: any) {
console.log(e);
await logSlackError(
e.message,
"getAvailabilityResponseWithStartEnd"
);
return {
response: "",
possible: false,
};
}
}
);
function checkAvailabilityWithStartEnd(
reservation: { arrivalDate: string; departureDate: string },
startDate: string,
endDate: string,
calendar: { date: string; isAvailable: number }[]
): { response: string; possible: boolean } {
const reservationStart = new Date(reservation.arrivalDate);
const reservationEnd = new Date(reservation.departureDate);
const requestedStart = new Date(startDate);
const requestedEnd = new Date(endDate);
let intervalsToCheck: [Date, Date][] = [];
if (
reservationStart.getTime() === requestedStart.getTime() &&
reservationEnd.getTime() === requestedEnd.getTime()
) {
return {
response: `The requested dates of ${startDate} to ${endDate} are available.`,
possible: true,
};
}
// Define intervals to check based on conditions
if (reservationStart.getTime() === requestedStart.getTime()) {
intervalsToCheck.push([reservationEnd, requestedEnd]);
} else if (reservationEnd.getTime() === requestedEnd.getTime()) {
intervalsToCheck.push([requestedStart, reservationStart]);
} else if (requestedStart < reservationStart) {
if (requestedEnd < reservationStart) {
intervalsToCheck.push([requestedStart, requestedEnd]);
} else {
intervalsToCheck.push([requestedStart, reservationStart]);
if (requestedEnd >= reservationEnd) {
intervalsToCheck.push([reservationEnd, requestedEnd]);
}
}
} else {
if (requestedEnd < reservationEnd) {
return {
response: `The requested dates of ${startDate} to ${endDate} are available.`,
possible: true,
};
} else {
intervalsToCheck.push([reservationEnd, requestedEnd]);
}
}
// Check intervals
const isUnavailable = intervalsToCheck.some(([start, end]) =>
calendar.some(
(item) =>
new Date(item.date) >= start &&
new Date(item.date) < end &&
item.isAvailable === 0
)
);
if (isUnavailable) {
console.log(
`The requested dates of ${startDate} to ${endDate} are not available.`
);
return {
response: `The requested dates of ${startDate} to ${endDate} are not available.`,
possible: false,
};
}
console.log(
`The requested dates of ${startDate} to ${endDate} are available.`
);
return {
response: `The requested dates of ${startDate} to ${endDate} are available.`,
possible: true,
};
}
function checkSingleDateAvailability(
date: string | null,
calendar: { date: string; isAvailable: number }[],
request?: string
): { response: string; possible: boolean } {
if (!date) return { response: "No date provided.", possible: true };
const targetTime = new Date(date).getTime();
const isUnavailable = calendar.some(
(item) =>
new Date(item.date).getTime() === targetTime &&
item.isAvailable === 0
);
if (isUnavailable) {
console.log(`The requested date of ${date} is not available.`);
return {
response: request
? `The requested date of ${date} is not available for ${request}.`
: `The requested date of ${date} is not available.`,
possible: false,
};
}
console.log(`The requested date of ${date} is available.`);
return {
response: request
? `The requested date of ${date} is available for ${request}.`
: `The requested date of ${date} is available. No adjacent date was provided.`,
possible: true,
};
}
Editor is loading...
Leave a Comment