Web Project/booking

Select all rooms (Frontend)

hmmmmmmmmmmmm 2024. 11. 15. 19:35

 

 

 

· 기능 개요

객실 목록 조회, 필터링, 페이징

 

 

ApiFunctions.js

// 객실 전체 조회
export async function getAllRooms() {
    
    try {
        const result = await api.get("/rooms/all-rooms")
        return result.data;
    }
    catch(error) {
        throw new Error("전체 객실 정보를 가져오는데 실패했습니다")
    }
}

 

 

 

ExistingRooms.jsx

// 기존 객실 목록

import { useState } from "react"
import { deleteRoom, getAllRooms } from "../utils/ApiFunctions";
import { useEffect } from "react";
import RoomFilter from "../common/RoomFilter";
import RoomPaginator from "../common/RoomPaginator";
import { FaEdit, FaEye, FaTrashAlt } from "react-icons/fa";
import { Link } from "react-router-dom";

export default function ExistingRooms() {
    
    // 전체 객실 목록
    const [rooms, setRooms] = useState([]);
    // 현재 페이지 번호
    const [currentPage, setCurrentPage] = useState(1);
    // 페이지당 표시할 객실 수 (8개, 고정 값)
    const [roomsPerPage] = useState(8);
    const [isLoading, setIsLoading] = useState(false);
    // 필터링된 객실 목록
    const [filteredRooms, setFilteredRooms] = useState([]);
    const [successMessage, setSuccessMessage] = useState("");
    const [errorMessage, setErrorMessage] = useState("");

    useEffect(() => {
        fetchRooms();
    }, [])

    // 객실 전체 조회 api 호출
    const fetchRooms = async() => {
        setIsLoading(true)

        try {
            const result = await getAllRooms()
            setRooms(result)
            setFilteredRooms(result)
            setIsLoading(false)
        }
        catch(error) {
            console.error(error)
        }
    }

    // 객실 개별 삭제 api 호출
    const handleDelete = async(roomId) => {
        try {
            const result = await deleteRoom(roomId)
            if (result === "") {
                setSuccessMessage(`객실 번호 ${roomId} 삭제 성공`)
                fetchRooms()
            }
            else {
                console.error(`객실 번호 ${roomId} 삭제 실패`)
            }
        }
        catch(error) {
            setErrorMessage(error.message)
        }
        
        setTimeout(() => {
            setSuccessMessage("")
            setErrorMessage("")
        }, 3000)
    }


    // 페이지 번호 클릭
    const handlePaginationClick = (pageNumber) => {
        setCurrentPage(pageNumber)
    }


    // 총 페이지 계산
    const calculateTotalPages = (filteredRooms, roomsPerPage, rooms) => {
        const totalRooms = filteredRooms.length > 0 ? filteredRooms.length : rooms.length
        // 총 객실 수를 페이지당 객실 수로 나누어 올림
        return Math.ceil(totalRooms / roomsPerPage)
    }
    
    // 현재 페이지에서 첫 번째로 보여줄 객실의 인덱스(순서 번호)
    const indexOfFirstRoom = (currentPage - 1) * roomsPerPage
    // 현재 페이지에서 마지막으로 보여줄 객실의 인덱스
    const indexOfLastRoom = currentPage * roomsPerPage
    // 현재 페이지에 해당하는 객실 목록(최대 8개)
    const currentRooms = filteredRooms.slice(indexOfFirstRoom, indexOfLastRoom)


    return (
        <>
            {successMessage && (
                <div className="fixed top-4 left-1/2 transform -translate-x-1/2 bg-green-500 text-white py-2 px-4 rounded shadow-lg text-center">
                    {successMessage}
                </div>
            )}
            {isLoading ? (
                <p className="text-center text-gray-600">Loading...</p>
            ) : (
                <section className="container mx-auto p-5">
                    <div className="text-center mb-8">
                        <h2 className="text-4xl font-bold text-gray-800">기존 객실 목록</h2>
                    </div>

                    {/* 객실 타입 필터링 */}
                    <div className="flex justify-center mb-6">
                        <div className="w-full">
                            <RoomFilter
                                data={rooms}
                                setFilteredData={setFilteredRooms}
                            />
                        </div>
                    </div>

                    {/* 객실 테이블 */}
                    <div className="overflow-x-auto">
                        <table className="min-w-full bg-white border rounded-lg shadow-md">
                            <thead className="bg-gray-100">
                                <tr>
                                    <th className="px-6 py-3 text-gray-700 font-semibold text-sm">ID</th>
                                    <th className="px-6 py-3 text-gray-700 font-semibold text-sm">Room Type</th>
                                    <th className="px-6 py-3 text-gray-700 font-semibold text-sm">Room Price</th>
                                    <th className="px-6 py-3 text-gray-700 font-semibold text-sm">Actions</th>
                                </tr>
                            </thead>
                            <tbody>
                                {currentRooms.map((room) => (
                                    <tr key={room.id} className="border-t text-center">
                                        <td className="px-6 py-4">{room.id}</td>
                                        <td className="px-6 py-4">{room.roomType}</td>
                                        <td className="px-6 py-4">{room.roomPrice}</td>
                                        <td className="px-6 py-4 flex justify-center items-center gap-4">
                                            <Link
                                                to={`/view-room/${room.id}`}
                                                className="text-blue-500 hover:text-blue-700"
                                            >
                                                <FaEye />
                                            </Link>
                                            <Link
                                                to={`/edit-room/${room.id}`}
                                                className="text-yellow-500 hover:text-yellow-700"
                                            >
                                                <FaEdit />
                                            </Link>
                                            <button
                                                className="text-red-500 hover:text-red-700"
                                                onClick={() => handleDelete(room.id)}
                                            >
                                                <FaTrashAlt />
                                            </button>
                                        </td>
                                    </tr>
                                ))}
                            </tbody>
                        </table>
                    </div>

                    {/* 객실 추가 버튼 */}
                    <div className="flex justify-end mt-4">
                        <Link 
                            to="/add-room"
                            className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"
                        >
                            객실 추가
                        </Link>
                    </div>

                    {/* 객실 목록 페이징 */}
                    <div className="flex justify-center mt-8 left-0 right-0 bg-white">
                        <RoomPaginator 
                            currentPage={currentPage}
                            totalPages={calculateTotalPages(filteredRooms, roomsPerPage, rooms)}
                            onPageChange={handlePaginationClick}
                        />
                    </div>
                </section>
            )}
        </>
    );
}

 

 

 

RoomFilter.jsx

// 객실 타입 필터링

import { useState } from "react";

export default function RoomFilter({ data, setFilteredData }) {
    
    // 사용자가 선택한 필터
    const [selectedType, setSelectedType] = useState("");

    // 필터 적용
    const handleSelect = (type) => {
        setSelectedType(type);

        // 필터 All 선택 --> 전체 데이터 부모에 전달
        if (type === "") {
            setFilteredData(data); 
        } 
        else {
            // 선택한 타입과 일치하는 객실만 필터링
            const filteredRooms = data.filter((room) => room.roomType.toLowerCase().includes(type.toLowerCase()));
            // 필터링된 데이터를 부모에 전달
            setFilteredData(filteredRooms);
        }
    };

    
    // 객실 타입 목록
    const roomTypes = [
        // All을 위한 빈 문자열
        "", 
        // 중복 없는(Set) 객실 타입 목록
        ...new Set(data.map((room) => room.roomType))
    ];



    return (
        <div className="mt-4">
            <div className="flex space-x-2 justify-center mx-auto">
                {roomTypes.map((type, index) => (
                    <label
                        key={index}
                        className={`px-4 py-2 border rounded-md cursor-pointer ${
                            selectedType === type
                                ? "bg-blue-600 text-white"
                                : "bg-white text-gray-700 border-gray-300"
                        }`}
                        onClick={() => handleSelect(type)}
                    >
                        <input
                            type="radio"
                            name="roomType"
                            value={type}
                            checked={selectedType === type}
                            // 변경 시 필터 적용
                            onChange={() => handleSelect(type)}
                            className="hidden"
                        />
                        {/* 버튼 텍스트 --> 빈 문자열이면 All로 표시 */}
                        {type || "All"}
                    </label>
                ))}
            </div>
        </div>
    );
}

 

 

 

RoomPaginator.jsx

// 객실 목록 페이징

export default function RoomPaginator({ currentPage, totalPages, onPageChange }) {
    
    // 총 페이지 수를 기반으로 페이지 번호 배열 생성 --> toalPages가 5면, pageNumbers는 [1, 2, 3, 4, 5]
    const pageNumbers = Array.from({ length: totalPages }, (_, i) => i + 1);

    return (
        <nav className="flex justify-center mt-8">
            <ul className="flex space-x-2">
                {/* 페이지 번호 목록 버튼 */}
                {pageNumbers.map((pageNumber) => (
                    <li key={pageNumber}>
                        <button
                            onClick={() => onPageChange(pageNumber)}
                            className={`px-4 py-2 rounded-md text-sm font-semibold focus:outline-none 
                                ${currentPage === pageNumber 
                                    ? "bg-blue-500 text-white" 
                                    : "bg-gray-200 text-gray-700 hover:bg-gray-300"
                                }`}
                        >
                            {pageNumber}
                        </button>
                    </li>
                ))}
            </ul>
        </nav>
    );
}

'Web Project > booking' 카테고리의 다른 글

Delete a single room (Frontend)  (0) 2024.11.18
Delete a single room (Backend)  (0) 2024.11.17
Select all rooms (Backend)  (0) 2024.11.09
Add new room (Frontend)  (0) 2024.11.08
Select room types (Backend)  (0) 2024.11.08