Untitled
unknown
plain_text
a year ago
20 kB
10
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 [userInformation, setUserInformation] = useState<{
name: string;
phone: string;
}>({
name: "",
phone: "",
});
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
);
console.log(isSelected, "isSelected");
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 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]
);
function handleNextButton({ index, id }: { index: number; id: string }) {
setActiveRoom(index);
}
const handlePreviousButton = useCallback(() => {
setActiveRoom((prev) => prev - 1);
}, []);
async function handleFinalSubmitButton({
index,
id,
}: {
index: number;
id: number;
}) {
// setActiveRoom(index);
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 ({
action,
index,
id,
}: {
action: string;
index?: number;
id?: string | number;
guests?: number;
}) => {
switch (action) {
case "SUBMIT":
if (!id) {
toast.success("select the room?");
return;
}
break;
case "NEXT":
if (index !== undefined) {
setActiveRoom(index);
} else {
setActiveRoom((prev) => prev + 1);
}
break;
case "PREVIOUS":
setActiveRoom((prev) => prev - 1);
break;
case "FINAL_SUBMIT":
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);
} catch (error) {
toast.error("Error creating booking");
}
break;
default:
break;
}
};
const inputOnChange = useCallback((e: ChangeEvent<HTMLInputElement>) => {
const { name, value } = e.target;
setUserInformation((prev) => ({ ...prev, [name]: value }));
}, []);
const roomsToRender = availableRoom || data;
return (
<>
<DateSection
pickerDate={pickerDate}
handleDateChange={handleDateChange}
/>
<div>
<p className="px-1 flex justify-center lg:justify-start pb-4 text-xs font-normal capitalize">
Click to select or deselect rooms.
</p>
<div className="mb-5 flex justify-center lg:justify-start gap-4 mx-2 lg:mx-2">
<div className="flex gap-3 items-center">
<div className="h-5 w-5 rounded-sm border bg-[#F9FAFA]"></div>
<span className="text-[10px] lg:font-medium lg:text-xs">
Available Room
</span>
</div>
<div className="flex gap-3 items-center">
<div className="h-5 w-5 rounded-sm border bg-[#C7C8CA]"></div>
<span className="text-[10px] lg:font-medium lg:text-xs">
Booked Room
</span>
</div>
<div className="flex gap-3 items-center">
<div className="h-5 w-5 rounded-sm border bg-[#98C157]"></div>
<span className="text-[10px] lg:font-medium lg:text-xs">
Selected Room
</span>
</div>
</div>
</div>
<div className="grid grid-cols-1 lg:grid-cols-7 gap-4 content-center m-2 lg:m-0 sm:mx-24">
<div className="border-2 border-gray-300 col-span-7 lg:col-span-2 w-full h-full flex justify-center items-center">
<div className="grid grid-cols-2 gap-6 py-5 lg:py-0 ">
{roomsToRender.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 xl:h-44 md:h-36 md:w-24 xl: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 xl:h-44 md:h-36 md:w-24 xl:w-32 my-2"
onClick={() =>
handleImageClick(room.id, index, numberOfGuest)
}
/>
</Tooltip>
) : room.hold ? (
<CustomImage
src={roomImages.booked[index]}
alt="booked room"
className="cursor-not-allowed h-28 w-24 sm:h-36 xl:h-44 md:h-36 md:w-24 xl: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 xl:h-44 md:h-36 md:w-24 xl:w-32 my-2"
onClick={() =>
handleImageClick(room.id, index, numberOfGuest)
}
/>
</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 xl:h-44 md:h-36 md:w-24 xl:w-32 my-2"
onClick={() =>
handleImageClick(room.id, index, numberOfGuest)
}
/>
</Tooltip>
)}
</div>
))}
</div>
</div>
<div className="col-span-7 mt-5 md:mt-0 lg:col-span-5 w-full h-full flex flex-col justify-center items-end ">
<div className="md:w-[72%] w-[100%] lg:-mt-[51px]">
<div className="w-full flex gap-3 justify-center flex-wrap mb-3 ">
{/* {userFinalRoomData?.map((room: any, index: number) => (
<div key={index} onClick={() => setActiveRoom(index)}>
<CustomButton
customClass={`w-[100px] rounded-sm text-white py-2 text-sm me-2 ${
activeRoom === index
? "bg-[#1DC5CE]"
: "bg-[#1DC5CE] opacity-60"
}`}
text={room.name}
/>
</div>
))} */}
{userFinalRoomData && userFinalRoomData.length > 0 ? (
userFinalRoomData.map((room: any, index: number) => (
<div key={index} onClick={() => setActiveRoom(index)}>
<CustomButton
customClass={`w-[86px] px-0 sm:w-[100px] rounded-sm text-white py-2 text-xs lg:text-sm me-2 ${
activeRoom === index
? "bg-[#1DC5CE]"
: "bg-[#1DC5CE] opacity-60"
}`}
text={room.name}
/>
</div>
))
) : (
<div className="w-full py-[18px] me-2 ">
{/* You can add a message or leave it empty */}
</div>
)}
</div>
</div>
<div className="w-[100%] lg: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="grid grid-cols-1 gap-y-7 pb-3 m-6 md:mt-5 xl:mt-10">
<GuestCounter
number={numberOfGuest}
setNumber={setNumberOfGuest}
maxCapacity={
(userFinalRoomData &&
userFinalRoomData[activeRoom]?.maxCapacity) ??
2
}
roomId={userFinalRoomData && userFinalRoomData[activeRoom]?.id}
setUserSubmitRoomData={setUserSubmitRoomData}
/>
{activeRoom === userFinalRoomData?.length - 1 && (
<div className="flex w-full gap-5 items-center">
<div className="w-full">
<input
onChange={inputOnChange}
name="name"
type="text"
value={userInformation.name}
className="w-full border-b border-[#E3DBB7] py-2 text-sm font-light capitalize tracking-wide outline-none lg:text-base"
placeholder="Your Name"
/>
{!userInformation.name && (
<div className="md:text-xs text-[10px] font-light text-red-500 mt-2">
Name field is required
</div>
)}
</div>
<div className="w-full">
<input
onChange={inputOnChange}
name="phone"
type="text"
value={userInformation.phone}
className="w-full border-b border-[#E3DBB7] py-2 text-sm font-light capitalize tracking-wide outline-none"
placeholder="Phone Number"
/>
{!userInformation.phone && (
<div className="md:text-xs text-[10px] font-light text-red-500 mt-2 ">
Phone number field is required
</div>
)}
</div>
</div>
)}
<div>
<p className="mb-2 text-sm md:text-base font-light capitalize tracking-wider lg:text-md">
from ৳
{userFinalRoomData.length > 0 ? (
<strong className="text-base font-semibold lg:text-lg mx-2">
{userFinalRoomData &&
Math.ceil(
userFinalRoomData[activeRoom]?.price[
numberOfGuest - 1
]?.price /
userFinalRoomData[activeRoom]?.price[
numberOfGuest - 1
]?.numberOfPerson
)}
</strong>
) : (
<strong className="text-sm md:text-base font-semibold lg:text-lg mx-2">
0
</strong>
)}
per person
</p>
<p className="mb-1 text-sm md:text-base font-light capitalize tracking-wider">
sub total ৳
{userFinalRoomData.length > 0 ? (
<strong className="text-sm md:text-base font-semibold lg:text-xl mx-2">
{userFinalRoomData &&
userFinalRoomData[activeRoom]?.price[numberOfGuest - 1]
?.price}
</strong>
) : (
<strong className="text-sm md:text-base font-semibold lg:text-lg mx-2">
0
</strong>
)}
for this room
</p>
</div>
<div className="flex gap-3">
{activeRoom > 0 && (
<CustomButton
onClick={() => handleSubmit({ action: "PREVIOUS" })}
customClass="w-[80px] !px-0 font-normal lg:font-medium lg:px-2 lg:w-[100px] text-white bg-[#1DC5CE] py-[11px] !text-xs rounded-sm"
text="PREVIOUS"
/>
)}
{activeRoom < userFinalRoomData?.length - 1 ? (
<CustomButton
onClick={() =>
handleSubmit({
action: "NEXT",
index: activeRoom + 1,
id: userFinalRoomData[activeRoom]?.id,
})
}
customClass="w-[80px] !px-0 font-normal lg:font-medium lg:px-2 lg:w-[100px] text-white bg-[#1DC5CE] py-[11px] !text-xs rounded-sm"
text="NEXT"
/>
) : (
<CustomButton
onClick={() =>
handleSubmit({
action: "FINAL_SUBMIT",
index: activeRoom + 1,
id: userFinalRoomData[activeRoom]?.id,
})
}
customClass="w-[80px] !px-0 lg:px-2 font-normal lg:font-medium lg:w-[100px] text-white bg-[#1DC5CE] py-[11px] !text-xs rounded-sm"
text="CONFIRM"
/>
)}
</div>
</div>
</div>
</div>
</div>
</>
);
}
Editor is loading...
Leave a Comment