Untitled
unknown
plain_text
a year ago
20 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 [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