Untitled
unknown
plain_text
a year ago
23 kB
6
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