Untitled

 avatar
unknown
plain_text
a year ago
23 kB
3
Indexable
"use client";
import CustomImage from "@/app/_components/ui/CustomImage";
import {
  useCreateBookingMutation,
  useGetTempBookingMutationMutation,
  useGetTempBookingQuery,
} from "@/app/features/booking/bookingApi";
import {
  useGetRoomsByDateMutationMutation,
  useGetRoomsByDateQuery,
  useGetRoomsQuery,
} from "@/app/features/room/roomApi";
import { ChangeEvent, useCallback, useEffect, useMemo, useState } from "react";
import toast from "react-hot-toast";
import { Tooltip } from "rizzui";
import CustomButton from "../../_components/CustomButton";
import { useBookingCtx } from "../context/BookingProvider";
import DateSection from "./DateSection";
import GuestCounter from "./GuestCounter";
import PakageImageSlider from "./PakageImageSlider";

const roomImages = {
  notBooked: [
    "/room/not-booked/Room1.png",
    "/room/not-booked/Room2.png",
    "/room/not-booked/Room3.png",
    "/room/not-booked/Room4.png",
    "/room/not-booked/Room5.png",
    "/room/not-booked/Room6.png",
  ],
  selected: [
    "/room/selected/Room1.png",
    "/room/selected/Room2.png",
    "/room/selected/Room3.png",
    "/room/selected/Room4.png",
    "/room/selected/Room5.png",
    "/room/selected/Room6.png",
  ],
  booked: [
    "/room/booked/Room1.png",
    "/room/booked/Room2.png",
    "/room/booked/Room3.png",
    "/room/booked/Room4.png",
    "/room/booked/Room5.png",
    "/room/booked/Room6.png",
  ],
};

interface RoomData {
  id: string;
  name: string;
  price: number;
  maxCapacity: number;
  status: string;
  discount: number;
  images: string[];
  videos: string[];
  selected?: boolean;
  selectedRoomId?: number;
  confirmed?: string;
  image?: string;
  booked?: boolean;
  hold?: boolean;
  holdByCurrentUser?: any[];
}

interface BookingData {
  ownHold?: RoomData[];
  totalHolds?: string[];
}

interface BookingHomeProps {
  data: any;
  resource: any;
}

enum ActionType {
  SUBMIT = "SUBMIT",
  NEXT = "NEXT",
  PREVIOUS = "PREVIOUS",
}

interface IRoom {
  id: string;
  guests: number;
}

export default function BookingHome({ data, resource }: BookingHomeProps) {
  const [createBooking] = useCreateBookingMutation({});
  const {
    availableRoom,
    setAvailableRoom,
    pickerDate,
    setPickerDate,
    toISOString,
    holdData,
    handleImageClick,
    tempHoldRooms,
    setSelectedRoomIndex,
    selectedRoomIndex,
    userSubmitRoomData,
    setUserSubmitRoomData,
  }: any = useBookingCtx();

  const [roomInfo, setRoomInfo] = useState<RoomData[]>([]);
  const [guests, setGuests] = useState<number>(1);
  const [userInformation, setUserInformation] = useState<{
    name: string;
    phone: string;
  }>({
    name: "",
    phone: "",
  });
  console.log("availableRoom", availableRoom);
  const { data: availableRooms, isLoading: avRoomIsLoading } =
    useGetRoomsByDateQuery({
      paginate: false,
      startDate: pickerDate.startDate,
    });

  const { data: rooms, isLoading } = useGetRoomsQuery<any>({ paginate: false });
  const [getRoomsByDateMutation] = useGetRoomsByDateMutationMutation({});
  const { data: tempBook } = useGetTempBookingQuery({
    date: pickerDate.startDate,
  });
  const [getTempBookingMutation] = useGetTempBookingMutationMutation({});

  const getBookings = useCallback(
    async (data: BookingData, available: any) => {
      if (!rooms?.data) return;

      const updatedRooms = rooms.data.map((room: RoomData, index: number) => {
        const isSelected = data?.ownHold?.some(
          (r: RoomData) => r.id === room.id
        );
        return {
          ...room,
          selected: isSelected,
          selectedRoomId: index + 1,
          confirmed: "start",
          image: roomImages.notBooked[index],
          booked: !available?.data.some((avRoom: any) => avRoom.id === room.id),
          hold: data?.totalHolds?.includes(room.id),
          holdByCurrentUser: data?.ownHold,
        };
      });

      setAvailableRoom(updatedRooms);
    },
    [rooms?.data, setAvailableRoom]
  );

  useEffect(() => {
    if (rooms?.data || availableRooms || tempBook) {
      getBookings(tempBook?.data, availableRooms);
    }
  }, [
    rooms?.data,
    availableRooms,
    tempBook,
    pickerDate.startDate,
    getBookings,
  ]);

  const handleGuestChange = useCallback(
    (delta: number) => {
      if (selectedRoomIndex !== null) {
        setRoomInfo((prevRoomInfo) =>
          prevRoomInfo.map((room: any, i: number) =>
            i === selectedRoomIndex
              ? {
                  ...room,
                  guests: Math.min(4, Math.max(1, room.guests + delta)),
                }
              : room
          )
        );
      }
    },
    [selectedRoomIndex]
  );

  const handleDateChange = useCallback(
    (date: string) => {
      const mainDate = new Date(
        new Date(date).setUTCHours(0, 0, 0, 0)
      ).toISOString();
      const endDate = new Date(
        new Date(mainDate).getTime() + 24 * 60 * 60 * 1000
      ).toISOString();
      setPickerDate({ startDate: mainDate, endDate });
    },
    [setPickerDate]
  );

  const [activeRoom, setActiveRoom] = useState<number>(0);
  const [userFinalRoomData, setUserFinalRoomData] = useState<any[]>([]);
  const [numberOfGuest, setNumberOfGuest] = useState<number>(2);

  useEffect(() => {
    if (availableRoom) {
      setUserFinalRoomData(
        availableRoom.filter((room: RoomData) => holdData?.has(room.id))
      );
    }
  }, [availableRoom, holdData]);

  const getMatchedObjects = useCallback(
    (submitData: any[], finalData: any[]) => {
      return submitData.filter((obj2) =>
        finalData.some((obj1) => obj1.id === obj2.room)
      );
    },
    []
  );

  const res = useMemo(
    () => getMatchedObjects(userSubmitRoomData, userFinalRoomData),
    [userSubmitRoomData, userFinalRoomData, getMatchedObjects]
  );

  const sendData = {
    rooms: res,
    name: userInformation.name,
    phone: userInformation.phone,
    startDate: pickerDate.startDate,
    endDate: pickerDate.endDate,
  };
  function handleNextButton({ index, id }: { index: number; id: string }) {
    setActiveRoom(index);

    setUserSubmitRoomData((prevData: any) => {
      const exists = prevData.find((item: any) => item.room === id);
      if (exists) {
        return prevData.map((item: any) =>
          item.room === id ? { ...item, guests: numberOfGuest } : item
        );
      } else {
        return [...prevData, { guests: numberOfGuest, room: id }];
      }
    });
  }

  const handlePreviousButton = useCallback(() => {
    setActiveRoom((prev) => prev - 1);
  }, []);

  async function handleFinalSubmitButton({
    index,
    id,
  }: {
    index: number;
    id: number;
  }) {
    // setActiveRoom(index);

    setUserSubmitRoomData((prevData: any) => {
      const exists = prevData.find((item: any) => item.room === id);
      if (exists) {
        return prevData.map((item: any) =>
          item.room === id ? { ...item, guests: numberOfGuest } : item
        );
      } else {
        return [...prevData, { guests: numberOfGuest, room: id }];
      }
    });

    if (!userInformation.name || !userInformation.phone) {
      toast.error("Fill name or phone number");
      return;
    }

    const sendData = {
      rooms: res,
      name: userInformation.name,
      phone: userInformation.phone,
      startDate: pickerDate.startDate,
      endDate: pickerDate.endDate,
    };

    try {
      await createBooking(sendData);
      toast.success("Booking created successfully");
      setActiveRoom(0);
      setUserFinalRoomData([]);
      setAvailableRoom([]);
    } catch (error) {
      toast.error("Error creating booking");
    }
  }

  const handleSubmit = async (
    id?: string,
    guests?: number,
    action?: string
  ) => {
    if (action === ActionType.SUBMIT && !id) {
      // submit the data with necessary information
      return;
    }
    if (action === ActionType.NEXT) setActiveRoom((prev) => prev + 1);
    else setActiveRoom((prev) => prev - 1);
    if (guests && !isNaN(guests))
      setUserFinalRoomData((prev) =>
        prev.map((d) => (d.room === id ? { ...d, guests } : d))
      );
  };

  const inputOnChange = useCallback((e: ChangeEvent<HTMLInputElement>) => {
    const { name, value } = e.target;
    setUserInformation((prev) => ({ ...prev, [name]: value }));
    setRoomInfo((prevRoomInfo) =>
      prevRoomInfo.map((room) => ({ ...room, [name]: value }))
    );
  }, []);

  return (
    <div className="">
      <div className="">
        <DateSection
          pickerDate={pickerDate}
          handleDateChange={handleDateChange}
        />
      </div>
      <div className="">
        <div className="">
          <p className="px-1 pb-4 text-xs font-normal capitalize">
            Click to select or deselect rooms.
          </p>
          <div className="mb-5 flex gap-4">
            <div className="flex gap-3">
              <div className="h-5 w-5 rounded-sm border bg-[#F9FAFA]"></div>
              <span className="text-[10px] font-medium lg:text-xs">
                Available Room
              </span>
            </div>
            <div className="flex gap-3">
              <div className="h-5 w-5 rounded-sm border bg-[#C7C8CA]"></div>
              <span className="text-[10px] font-medium lg:text-xs">
                Booked Room
              </span>
            </div>
            <div className="flex gap-3">
              <div className="h-5 w-5 rounded-sm border bg-[#98C157]"></div>
              <span className="text-[10px] font-medium lg:text-xs">
                Selected Room
              </span>
            </div>
          </div>
        </div>
      </div>
      <div className="grid grid-cols-1 lg:grid-cols-7 gap-8 content-center m-2 lg:m-0">
        <div className="border border-gray-500 col-span-1 lg:col-span-2 w-full h-full flex justify-center items-center">
          <div className="grid grid-cols-2 gap-3 my-2">
            {availableRoom
              ? availableRoom.map((room: RoomData, index: number) => (
                  <div key={room.id}>
                    {room.booked ? (
                      <CustomImage
                        src={roomImages.booked[index]}
                        alt="booked room"
                        className="cursor-not-allowed h-28 w-24 sm:h-36 md:h-44 md:w-32"
                      />
                    ) : room.selected && room.hold ? (
                      <Tooltip
                        placement="top"
                        color="invert"
                        className="bg-white"
                        content={room.name}
                      >
                        <CustomImage
                          src={roomImages.selected[index]}
                          alt="selected room"
                          className="h-28 w-24 sm:h-36 md:h-44 md:w-32 my-2"
                          onClick={() =>
                            handleImageClick(
                              room.id,
                              index,
                              numberOfGuest,
                              resource?.data?.booking
                            )
                          }
                        />
                      </Tooltip>
                    ) : room.hold ? (
                      <CustomImage
                        src={roomImages.booked[index]}
                        alt="booked room"
                        className="cursor-not-allowed h-28 w-24 sm:h-36 md:h-44 md:w-32"
                      />
                    ) : room.selected ? (
                      <Tooltip
                        placement="top"
                        color="invert"
                        className="bg-white"
                        content={room.name}
                      >
                        <CustomImage
                          src={roomImages.selected[index]}
                          alt="selected room"
                          className="h-28 w-24 sm:h-36 md:h-44 md:w-32 my-2"
                          onClick={() =>
                            handleImageClick(
                              room.id,
                              index,
                              numberOfGuest,
                              resource?.data?.booking
                            )
                          }
                        />
                      </Tooltip>
                    ) : (
                      <Tooltip
                        placement="top"
                        color="invert"
                        className="bg-white"
                        content={room.name}
                      >
                        <CustomImage
                          src={roomImages.notBooked[index]}
                          alt="available room"
                          className="h-28 w-24 sm:h-36 md:h-44 md:w-32 my-2"
                          onClick={() =>
                            handleImageClick(
                              room.id,
                              index,
                              numberOfGuest,
                              resource?.data?.booking
                            )
                          }
                        />
                      </Tooltip>
                    )}
                  </div>
                ))
              : data?.map((room: RoomData, index: number) => (
                  <div key={room.id}>
                    {room.booked ? (
                      <CustomImage
                        src={roomImages.booked[index]}
                        alt="booked room"
                        className="cursor-not-allowed h-28 w-24 sm:h-36 md:h-44 md:w-32"
                      />
                    ) : room.selected && room.hold ? (
                      <Tooltip
                        placement="top"
                        color="invert"
                        className="bg-white"
                        content={room.name}
                      >
                        <CustomImage
                          src={roomImages.selected[index]}
                          alt="selected room"
                          className="h-28 w-24 sm:h-36 md:h-44 md:w-32 my-2"
                          onClick={() =>
                            handleImageClick(
                              room.id,
                              index,
                              numberOfGuest,
                              resource?.data?.booking
                            )
                          }
                        />
                      </Tooltip>
                    ) : room.hold ? (
                      <CustomImage
                        src={roomImages.booked[index]}
                        alt="booked room"
                        className="cursor-not-allowed h-28 w-24 sm:h-36 md:h-44 md:w-32"
                      />
                    ) : room.selected ? (
                      <Tooltip
                        placement="top"
                        color="invert"
                        className="bg-white"
                        content={room.name}
                      >
                        <CustomImage
                          src={roomImages.selected[index]}
                          alt="selected room"
                          className="h-28 w-24 sm:h-36 md:h-44 md:w-32 my-2"
                          onClick={() =>
                            handleImageClick(
                              room.id,
                              index,
                              numberOfGuest,
                              resource?.data?.booking
                            )
                          }
                        />
                      </Tooltip>
                    ) : (
                      <Tooltip
                        placement="top"
                        color="invert"
                        className="bg-white"
                        content={room.name}
                      >
                        <CustomImage
                          src={roomImages.notBooked[index]}
                          alt="available room"
                          className="h-28 w-24 sm:h-36 md:h-44 md:w-32 my-2"
                          onClick={() =>
                            handleImageClick(
                              room.id,
                              index,
                              numberOfGuest,
                              resource?.data?.booking
                            )
                          }
                        />
                      </Tooltip>
                    )}
                  </div>
                ))}
          </div>
        </div>
        <div className="col-span-1 lg:col-span-5 w-full h-full flex flex-col justify-center items-end">
          <div className=" flex gap-2 flex-wrap mb-2">
            {userFinalRoomData?.map((room: any, index: number) => (
              <div key={index} onClick={() => setActiveRoom(index)}>
                <CustomButton
                  customClass={`w-[100px] rounded-none text-white py-2 text-2xl me-2 ${
                    activeRoom === index
                      ? "bg-[#1DC5CE]"
                      : "bg-[#1DC5CE] opacity-60"
                  }`}
                  text={room.name}
                />
              </div>
            ))}
          </div>
          <div className="w-[72%] max-h-[100%] bg-white shadow-lg rounded-2xl overflow-hidden">
            <div className="w-full">
              <PakageImageSlider
                images={
                  userFinalRoomData && userFinalRoomData[activeRoom]?.images
                }
              />
            </div>
            <div className="m-5 mt-10">
              <GuestCounter
                number={numberOfGuest}
                setNumber={setNumberOfGuest}
                maxCapacity={
                  (userFinalRoomData &&
                    userFinalRoomData[activeRoom]?.maxCapacity) ??
                  2
                }
              />
              <div className="flex gap-5 pb-3 pt-2">
                <div>
                  <input
                    onChange={inputOnChange}
                    name="name"
                    type="text"
                    value={userInformation.name}
                    className="w-full border-b border-[#E3DBB7] px-1 py-2 text-sm font-light capitalize tracking-wide outline-none "
                    placeholder="Name"
                  />
                  {!userInformation.name && (
                    <div className="text-xs font-light text-red-500">
                      Name field is required
                    </div>
                  )}
                </div>
                <div>
                  <input
                    onChange={inputOnChange}
                    name="phone"
                    type="text"
                    value={userInformation.phone}
                    className="w-full border-b border-[#E3DBB7] px-1 py-2 text-sm font-light capitalize tracking-wide outline-none"
                    placeholder="Phone Number"
                  />
                  {!userInformation.phone && (
                    <div className="text-xs font-light text-red-500">
                      Phone number field is required
                    </div>
                  )}
                </div>
              </div>
              <div>
                <p className="mb-2 text-base font-light capitalize tracking-wider lg:text-md">
                  from ৳{" "}
                  {userFinalRoomData.length > 0 && (
                    <strong className="text-base font-semibold lg:text-lg">
                      {userFinalRoomData &&
                        Math.ceil(
                          userFinalRoomData[activeRoom]?.price[
                            numberOfGuest - 1
                          ]?.price /
                            userFinalRoomData[activeRoom]?.price[
                              numberOfGuest - 1
                            ]?.numberOfPerson
                        )}
                    </strong>
                  )}{" "}
                  per person
                </p>
                <p className="mb-1 text-base font-light capitalize tracking-wider">
                  sub total ৳{" "}
                  <strong className="text-base font-semibold lg:text-xl">
                    {userFinalRoomData &&
                      userFinalRoomData[activeRoom]?.price[numberOfGuest - 1]
                        ?.price}
                  </strong>{" "}
                  for this room
                </p>
              </div>
              <div className="my-5">
                {activeRoom > 0 && (
                  <CustomButton
                    onClick={handlePreviousButton}
                    customClass="w-[150px] text-white bg-[#1DC5CE] py-2 text-2xl me-2 rounded-none"
                    text="Previous"
                  />
                )}
                {activeRoom < userFinalRoomData?.length - 1 ? (
                  <CustomButton
                    onClick={() =>
                      handleNextButton({
                        index: activeRoom + 1,
                        id: userFinalRoomData[activeRoom]?.id,
                      })
                    }
                    customClass="w-[150px] text-white bg-[#1DC5CE] py-2 text-2xl me-2 rounded-none"
                    text="Next"
                  />
                ) : (
                  <CustomButton
                    onClick={() =>
                      handleFinalSubmitButton({
                        index: activeRoom + 1,
                        id: userFinalRoomData[activeRoom]?.id,
                      })
                    }
                    customClass="w-[150px] text-white bg-[#1DC5CE] py-2 me-2 text-2xl rounded-none"
                    text="Final Submit"
                  />
                )}
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
  );
}
Editor is loading...
Leave a Comment