Untitled
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