Untitled

mail@pastecode.io avatar
unknown
plain_text
22 days ago
11 kB
0
Indexable
Never
import { Response } from "express";
import { v4 as uuid } from "uuid";
import redis from "../../controllers/redis";
import { find, findOne, remove, update } from "../../db/operations";
import secondsToDhms from "../../utils/secondsToDhms";
import { BookingPayload, Req } from "../../utils/types";
import Booking from "./model";

const CREATE_ALLOWED = new Set([
  "name",
  "phone",
  "email",
  "numberOfPerson",
  "totalPrice",
  "paid",
  "startDate",
  "endDate",
  "note",
  "rooms",
  "status",
  "trackingId"
]);

const ALLOWED_QUERY = new Set([
  "name",
  "phone",
  "email",
  "startDate",
  "endDate",
  "status",
  "page",
  "limit",
  "paginate",
  "sortBy",
  "trackingId",
  "$in"
]);

const populate = { path: "rooms approvedBy", populate: { path: "room", select: "name" } };

export const createBooking = async (req: Req, res: Response) => {
  try {
    const isValid = Object.keys(req.body).every(k => CREATE_ALLOWED.has(k));
    if (!isValid) return res.status(400).send({ status: 400, messae: "Invalid parameters provided" });
    req.body.trackingId = uuid();
    let data = req.body as BookingPayload;
    const { startDate, endDate, rooms } = data;
    const start = new Date(startDate);
    const end = new Date(endDate);
    const today = new Date().setHours(0, 0, 0, 0);
    if (isNaN(start.getTime()) || isNaN(end.getTime())) {
      return res.status(400).send({ status: 400, message: "Invalid date format" });
    }
    if (start.getTime() < today) {
      return res.status(400).send({ message: "Start date cannot be in the past date" });
    }
    if (end < start) {
      return res.status(400).send({ message: "End date cannot be before start date" });
    }
    const overlappingBooking = await Booking.find({
      startDate: { $lte: end },
      endDate: { $gte: start }
    })
      .populate({ path: "rooms", populate: { path: "room", select: "name" } })
      .lean();
    // console.log(JSON.stringify());
    return res.status(200).send("OK");

    if (overlappingBooking.length > 0) {
      const unavailableRooms = overlappingBooking.flatMap(booking => booking.rooms).map(room => room);
      return res.status(400).send({ status: 400, message: "Some rooms are not available in the given date range", unavailableRooms });
    }
    const roomDocs = await find({
      table: Room,
      key: { query: { $in: rooms.map(room => room.room), paginate: "false" }, allowedQuery: ALLOWED_QUERY }
    });
    data.totalPrice = calculatePrice(roomDocs, data.rooms);
    data.numberOfPerson = data.rooms.reduce((total, room) => room.guests + total, 0);
    const booking = new Booking(data);
    await booking.save();
    await booking.populate({ path: "rooms", populate: { path: "room" } });
    if (!booking) return res.status(503).send({ status: 503, message: "Failed to save booking, please try again later" });
    return res.status(201).send({
      status: 201,
      message: "Booking created successfully",
      data: { booking, trackingUrl: `${process.env.TRACKING_URL!}?trackingId=${booking.trackingId}` }
    });
  } catch (err) {
    console.error(err);
    return res.status(500).send({ status: 500, message: "Internal Server Error" });
  }
};

export const updateBooking = async (req: Req, res: Response) => {
  try {
    console.log(req.body);
    const isValid = Object.keys(req.body).every(k => CREATE_ALLOWED.has(k));
    if (!isValid) return res.status(400).send({ status: 400, message: "Invalid Parameters provided" });
    const { id } = req.params;
    if (id === "undefined" || !id || id.trim().length === 0) return res.status(400).send({ status: 400, message: "ID is required" });
    let booking = await findOne({ table: Booking, key: { id } });
    if (!booking) return res.status(404).send({ status: 404, message: "Booking not found" });
    req.body.approvedBy = req?.user?.id;
    booking = await update({ table: Booking, key: { id, body: req.body, populate } });
    if (!booking) return res.status(503).send({ status: 503, message: "Failed to update booking,please try again later." });
    return res.status(200).send({ status: 200, message: "Booking updated successfully", data: booking });
  } catch (err) {
    console.error(err);
    return res.status(500).send({ status: 500, message: "Internal Server Error" });
  }
};

export const getOneBooking = async (req: Req, res: Response) => {
  try {
    const { id } = req.params;
    if (id === "undefined" || !id || id.trim().length === 0) return res.status(400).send({ status: 400, message: "ID is required" });
    const booking = await findOne({ table: Booking, key: { id, populate } });
    if (!booking) return res.status(404).send({ status: 404, message: "Booking not found" });
    return res.status(200).send({ status: 200, message: "Booking retrieved successfully", data: booking });
  } catch (err) {
    console.error(err);
    return res.status(500).send({ status: 500, message: "Internal Server Error" });
  }
};

export const getAllBookings = async (req: Req, res: Response) => {
  try {
    const isValidQuery = Object.keys(req.query).every(k => ALLOWED_QUERY.has(k));
    if (!isValidQuery) return res.status(400).send({ status: 400, message: "Query Validation Failed" });
    const bookings = await find({ table: Booking, key: { query: req.query, allowedQuery: ALLOWED_QUERY, populate } });
    return res.status(200).send({ status: 200, message: "Bookings retrieved successfully", data: bookings });
  } catch (err) {
    console.error(err);
    return res.status(500).send({ status: 500, message: "Internal Server Error" });
  }
};

export const deleteBooking = async (req: Req, res: Response) => {
  try {
    const { id } = req.params;
    if (id === "undefined" || !id || id.trim().length === 0) return res.status(400).send({ status: 400, message: "ID is required" });
    const booking = await findOne({ table: Booking, key: { id } });
    if (!booking) return res.status(404).send({ status: 404, message: "Booking not found" });
    await remove({ table: Booking, key: { id } });
    return res.status(200).send({ status: 200, message: "Booking deleted successfully", data: booking });
  } catch (err) {
    console.error(err);
    return res.status(500).send({ status: 500, message: "Internal Server Error" });
  }
};

export const trackBooking = async (req: Req, res: Response) => {
  try {
    const { trackingId } = req.params;
    if (!trackingId || trackingId === "") return res.status(400).send({ status: 400, message: "Tracking Id is required" });
    const booking = await findOne({ table: Booking, key: { trackingId, populate } });
    if (!booking) return res.status(404).send({ status: 404, message: "Booking not found" });
    return res.status(200).send({ status: 200, message: "Booking retrieved successfully", data: booking });
  } catch (err) {
    console.error(err);
    return res.status(500).send({ status: 500, message: "Internal Server Error" });
  }
};

export const tempHold = async (req: Req, res: Response) => {
  try {
    if (!req?.body?.date) return res.status(400).send({ status: 400, message: "Date is required" });
    const key = req.session;
    if (Object.keys(req.body).length === 0) return res.status(400).send({ status: 400, message: "Body is empty" });
    let rooms = req.body.rooms;
    if (rooms.length === 0) return res.status(400).send({ status: 400, message: "No room id provided" });
    const dateKey = new Date(new Date(req.body.date as string).setUTCHours(0, 0, 0, 0)).toISOString();
    let roomsByDate = await redis.getValue(dateKey);
    if (roomsByDate && JSON.parse(roomsByDate).length > 0) {
      roomsByDate = Array.from(new Set([...JSON.parse(roomsByDate), ...rooms]));
      await redis.setValue(dateKey, JSON.stringify(roomsByDate), 24 * 60 * 60);
    } else await redis.setValue(dateKey, JSON.stringify(rooms), 24 * 60 * 60);
    let holded = await redis.getValue(key!);
    if (holded && JSON.parse(holded).length > 0) {
      holded = JSON.parse(holded);
      holded.forEach(async (h: string, index: number) => {
        if (rooms.includes(h)) {
          await redis.removeValue(h);
          rooms = [];
          holded.splice(index, 1);
        }
      });
      if (rooms.length === 0 && holded.length === 0)
        return res.status(200).send({ status: 200, message: "Holded room updated successfully", data: [] });
      const holdExpirations = await Promise.all(
        holded.map(async (hold: string) => ({
          id: hold,
          expiration: await redis.getExpirationTime(hold)
        }))
      );
      holded = holdExpirations.filter(h => h.expiration > 0).map(h => h.id);
      holded = Array.from(new Set([...holded, ...rooms]));
    } else holded = [...rooms];
    await redis.setValue(key!, JSON.stringify(holded));
    await Promise.all(rooms.map(async (room: string) => await redis.setValue(room, room, 15 * 60)));
    const holdedWithTime = await Promise.all(
      holded.map(async (hold: string) => ({ id: hold, time: secondsToDhms(await redis.getExpirationTime(hold)) }))
    );
    return res.status(200).send({ status: 200, message: "Holded room updated successfully", data: holdedWithTime });
  } catch (err) {
    console.error(err);
    return res.status(500).send({ status: 500, message: "Internal Server Error" });
  }
};

export const getHoldRooms = async (req: Req, res: Response) => {
  try {
    if (!req.query.date) return res.status(400).send({ status: 400, message: "Date is required" });
    const session = req.session;
    const dateKey = new Date(new Date(req.query.date as string).setUTCHours(0, 0, 0, 0)).toISOString();
    let roomsByDate = await redis.getValue(dateKey);
    if (roomsByDate) roomsByDate = JSON.parse(roomsByDate);
    else roomsByDate = [];
    const allSessions = JSON.parse(await redis.getValue("sessions"));
    let holds = await Promise.all(allSessions.map(async (s: string) => await redis.getValue(s)));
    holds = holds.filter(h => h !== null);
    holds = holds.map(h => JSON.parse(h)).flat();
    holds = Array.from(new Set([...holds.map(h => h)]));
    const holdExpirations = await Promise.all(
      holds.map(async (hold: string) => ({
        id: hold,
        expiration: await redis.getExpirationTime(hold)
      }))
    );
    holds = holdExpirations.filter(h => h.expiration > 0).map(h => h.id);
    holds = holds.filter(h => roomsByDate.includes(h));
    let ownHold = await redis.getValue(session!);
    if (ownHold) {
      ownHold = JSON.parse(ownHold);
      ownHold = ownHold.filter((hold: string) => new Set(holds).has(hold));
      ownHold = await Promise.all(ownHold.map(async (h: string) => ({ id: h, time: secondsToDhms(await redis.getExpirationTime(h)) })));
      ownHold = ownHold.filter((hold: any) => roomsByDate.includes(hold.id));
    } else ownHold = [];
    return res.status(200).send({ status: 200, message: "Holds data retrieved", data: { totalHold: holds, holdByCurrentUser: ownHold } });
  } catch (err) {
    console.error(err);
    return res.status(500).send({ status: 500, message: "Internal Server Error" });
  }
};
Leave a Comment