Untitled

mail@pastecode.io avatar
unknown
plain_text
7 months ago
14 kB
2
Indexable
Never
"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,
    };
}
Leave a Comment