Untitled
unknown
plain_text
a year ago
29 kB
7
Indexable
// ======= ===v2===========optimize 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 { useRouter } from "next/navigation"; 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"; import RoomSelector from "./RoomSelector "; // Define room images 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" ] }; // Define interfaces for room and booking data 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[]; } // Enum for different actions enum ActionType { SUBMIT = "SUBMIT", NEXT = "NEXT", PREVIOUS = "PREVIOUS", FINAL_SUBMIT = "FINAL_SUBMIT" } // Props for BookingHome component interface BookingHomeProps { data: any; // Replace with specific type if possible resource: any; // Replace with specific type if possible } // Main component export default function BookingHome({ data, resource }: BookingHomeProps) { const { availableRoom, setAvailableRoom, pickerDate, setPickerDate, holdData, setHoldData, handleImageClick, userSubmitRoomData, setUserSubmitRoomData }: any = useBookingCtx(); const router = useRouter(); const [createBooking] = useCreateBookingMutation({}); 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, isLoading: tempLoading } = useGetTempBookingQuery({ date: pickerDate.startDate }); const [getTempBookingMutation] = useGetTempBookingMutationMutation({}); // Function to update rooms data based on booking and availability information const getBookings = useCallback( async (data: BookingData, available: any) => { if (!rooms?.data) return; const ownHoldIds = new Set(data?.ownHold?.map((hold: any) => hold.id)); setHoldData(ownHoldIds); const updatedRooms = rooms.data.map((room: any, 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); }, // eslint-disable-next-line react-hooks/exhaustive-deps [rooms?.data, setAvailableRoom] ); useEffect(() => { if (rooms?.data || availableRooms || tempBook) { getBookings(tempBook?.data, availableRooms); } if (rooms?.data || availableRooms || tempBook) { const filteredRoomsWithGuests = availableRoom ?.filter((room: RoomData) => holdData?.has(room.id)) .map((room: RoomData) => ({ guests: 2, room: room.id })); setUserSubmitRoomData(filteredRoomsWithGuests); } // eslint-disable-next-line react-hooks/exhaustive-deps }, [rooms?.data, availableRooms, tempBook, pickerDate.startDate, getBookings]); // Handle date change 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] ); // State for active room and final room data 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]); // Get matched objects between user submitted rooms and final rooms 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] ); // Handle input change for user information const inputOnChange = useCallback((e: ChangeEvent<HTMLInputElement>) => { const { name, value } = e.target; setUserInformation(prev => ({ ...prev, [name]: value })); }, []); // Handle form submission const handleSubmit = async ({ action, index, id }: { action: string; index?: number; id?: string | number; guests?: number }) => { switch (action) { case ActionType.SUBMIT: if (!id) { toast.success("Select a room first"); return; } break; case ActionType.NEXT: if (index !== undefined) { setActiveRoom(index); } else { setActiveRoom(prev => prev + 1); } break; case ActionType.PREVIOUS: setActiveRoom(prev => prev - 1); break; case ActionType.FINAL_SUBMIT: if (!userInformation.name || !userInformation.phone) { toast.error("Please fill in name and 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); router.push("/success"); } catch (error) { toast.error("Error creating booking"); } break; default: break; } }; // ==========v2===========optimize return ( <div className=''> <div className=''> <DateSection pickerDate={pickerDate} handleDateChange={handleDateChange} /> </div> <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-8 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</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</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</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'> {/* booking room section */} <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 '> {avRoomIsLoading || !availableRoom ? roomImages.notBooked.map((room: string, index: number) => ( <div key={index} className='relative overflow-hidden'> <CustomImage src={roomImages.notBooked[index]} alt='available room' className='h-28 w-24 sm:h-36 md:h-44 md:w-32 my-2' /> <div className='absolute top-0 left-0 z-10'> <span className='relative flex h-[20rem] w-[20rem]'> <span className='animate-pulse custom-animation absolute inline-flex rounded-none h-full w-full bg-[#7e7777af] opacity-70 '></span> </span> </div> </div> )) : 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 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, 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 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, 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 xl:h-44 md:h-36 md:w-24 xl:w-32 my-2' onClick={() => handleImageClick(room.id, index, numberOfGuest, resource?.data?.booking)} /> </Tooltip> )} </div> ))} </div> </div> {/* booking room section */} <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]'> <RoomSelector userFinalRoomData={userFinalRoomData} activeRoom={activeRoom} setActiveRoom={setActiveRoom} /> </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} holdByCurrentUser={availableRoom && availableRoom[0].holdByCurrentUser} finalData={res || []} /> {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='my-5'> {activeRoom > 0 && ( <CustomButton onClick={() => handleSubmit({ action: "PREVIOUS" })} customClass='w-[150px] text-white bg-[#1DC5CE] py-2 text-2xl me-2 rounded-none' text='Previous' /> )} {activeRoom < userFinalRoomData?.length - 1 ? ( <CustomButton onClick={() => handleSubmit({ action: "NEXT", 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={() => handleSubmit({ action: "FINAL_SUBMIT", 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 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> </div> ); } // "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"; // import RoomSelector from "./RoomSelector "; // 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, // setHoldData, // 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: "" // }); // 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, isLoading: tempLoading } = useGetTempBookingQuery({ // date: pickerDate.startDate // }); // const [getTempBookingMutation] = useGetTempBookingMutationMutation({}); // const getBookings = useCallback( // async (data: BookingData, available: any) => { // if (!rooms?.data) return; // const ownHoldIds = new Set(data?.ownHold?.map((hold: any) => hold.id)); // setHoldData(ownHoldIds); // const updatedRooms = rooms.data.map((room: any, 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); // }, // // eslint-disable-next-line react-hooks/exhaustive-deps // [rooms?.data, setAvailableRoom] // ); // useEffect(() => { // if (rooms?.data || availableRooms || tempBook) { // getBookings(tempBook?.data, availableRooms); // } // if (rooms?.data || availableRooms || tempBook) { // const filteredRoomsWithGuests = availableRoom // ?.filter((room: RoomData) => holdData?.has(room.id)) // .map((room: RoomData) => ({ // guests: 2, // room: room.id // })); // setUserSubmitRoomData(filteredRoomsWithGuests); // } // // eslint-disable-next-line react-hooks/exhaustive-deps // }, [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))); // } // // eslint-disable-next-line react-hooks/exhaustive-deps // }, [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 inputOnChange = useCallback((e: ChangeEvent<HTMLInputElement>) => { // const { name, value } = e.target; // setUserInformation(prev => ({ ...prev, [name]: value })); // setRoomInfo(prevRoomInfo => prevRoomInfo.map(room => ({ ...room, [name]: value }))); // }, []); // 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; // } // };
Editor is loading...
Leave a Comment