Skip to content
SeongWoo Shin edited this page Aug 6, 2023 · 11 revisions

📌 주요 기능

✔ 등록된 숙소 조회

  • 숙소 종류 필터링 (게스트 하우스, 호텔, 모텔, 펜션)
  • 개별 숙소 클릭 시
    • 숙소 삭제 - REST API - delete요청
    • 숙소 디테일 페이지 이동, 숙소 수정 페이지 이동 - react-router-dom의 useNavigate hook을 통한 페이지 이동

Code

AccommodationFilter.tsx
export default function AccommodationFilter({ setAccommodationList, setRoomList }: AccommodationFilterProps) {
const originAccommodationList = useRecoilValue(originalAccommodationListState);
const [selectedAccommodationId, setSelectedAccommodationId] = useRecoilState(selectedAccommodationIdState);
const [roomReservationStatus, setRoomReservationStatus] = useRecoilState(roomReservationStatusState);
const [roomListTotalPage, setRoomListTotalPage] = useRecoilState(roomListTotalPageState);
const [roomListTotalElement, setRoomListTotalElement] = useRecoilState(roomListTotalElementState);
const [originalRoomList, setOriginalRoomList] = useRecoilState(originalRoomListState);

const [activeButton, setActiveButton] = useState('ALL');

const handleButtonClick = (accommodationType: string) => {
// 숙소별 예약 내역 조회를 위한 초기화 과정
  setSelectedAccommodationId(0);
  setRoomList([]);
  setOriginalRoomList([]);
  setRoomReservationStatus('USING');
  setRoomListTotalPage(0);
  setRoomListTotalElement(0);

// 본 기능
  if (activeButton === accommodationType) {
    setActiveButton('ALL');
    setAccommodationList(originAccommodationList);
  } else {
    setActiveButton(accommodationType);

    if (originAccommodationList.length > 0) {
      const newAccommodationList = [...originAccommodationList];
      const filteredNewAccommodationList = newAccommodationList.filter(
        accommodation => accommodation.houseType === accommodationType,
      );
      setAccommodationList(filteredNewAccommodationList);
    }
  }
};

return (
  <StyledFilterContainer>
    <StyledFilterButton active={activeButton === 'GUEST'} onClick={() => handleButtonClick('GUEST')}>
      {AccommodationIconMap.GUEST}
      <p>게스트하우스</p>
    </StyledFilterButton>
    <StyledFilterButton active={activeButton === 'HOTEL'} onClick={() => handleButtonClick('HOTEL')}>
      {AccommodationIconMap.HOTEL}
      <p>호텔</p>
    </StyledFilterButton>
    <StyledFilterButton active={activeButton === 'MOTEL'} onClick={() => handleButtonClick('MOTEL')}>
      {AccommodationIconMap.MOTEL}
      <p>모텔</p>
    </StyledFilterButton>
    <StyledFilterButton active={activeButton === 'PENSION'} onClick={() => handleButtonClick('PENSION')}>
      {AccommodationIconMap.PENSION}
      <p>펜션</p>
    </StyledFilterButton>
  </StyledFilterContainer>
);
}
AccommodationItem.tsx
// 숙소 삭제 시
const deleteAccommodation = async () => {
  if (originalRoomList.length > 0) {
    alert('예약내역이 존재하기때문에 삭제하실 수 없습니다.');
    setOpenModal(false);
    return;
  }

  try {
    await sendRequest({ url: `/user/houses/${data.id}`, method: 'DELETE' });
  } catch (err) {
    console.log(err);
  }
};

useEffect(() => {
  if (!responseData) return;
  const fetchData = async () => {
    try {
      await accommodationSendRequest({ url: ACCOMMODATION_API });
    } catch (err) {
      console.log(err);
    }
  };

  if (responseData.isSuccess) {
    alert('숙소 삭제가 완료되었습니다.');
    fetchData();
    setOpenModal(false);
  } else {
    alert('숙소 삭제가 완료되지않았습니다.');
    setOpenModal(false);
  }
}, [responseData]);
AccommodationOption.tsx
interface AccommodationOptionProps {
houseId: number;
onClick: () => void;
}

export default function AccommodationOption({ houseId, onClick }: AccommodationOptionProps) {
const navigate = useNavigate();

const moveDetailPage = (e: React.MouseEvent) => {
  e.stopPropagation();
  navigate(`/detail/${houseId}`);
};

const moveManagePage = (e: React.MouseEvent) => {
  e.stopPropagation();
  navigate(`/hosting/${houseId}`);
};

return (
  <StyledOptionContainer>
    <StyledOption onClick={moveDetailPage}>숙소 새창열기</StyledOption>
    <StyledOption onClick={moveManagePage}>숙소수정</StyledOption>
    <StyledOption onClick={onClick}>숙소삭제</StyledOption>
  </StyledOptionContainer>
);
}

✔ 숙소 등록 버튼

  • react-router-dom 라이브러리의 useNavigate hook을 통한 페이지 이동
  • recoil의 useResetRecoilState으로 전역 상태 값을 default 값으로 초기화 (전역 상태로 관리하고 있던 숙소 관련 데이터 초기화)
  • useMediaQuery 커스텀 훅을 사용하여 반응형 버튼 구현

Code

AccommodationRegistrationButton.tsx
export default function AccommodationRegistrationButton({ children }: AccommodationRegistrationButtonProps) {
const changePoint = useMediaQuery('(min-width: 780px)');
const navigate = useNavigate();
const resetStepState = useResetRecoilState(stepState);
const resetAccommodationState = useResetRecoilState(accommodationState);
const resetDisabledState = useResetRecoilState(disabledState);
const resetAddressCheckState = useResetRecoilState(addressCheckState);
const resetErrorModalState = useResetRecoilState(errorModalState);
const resetImageDataState = useResetRecoilState(imageDataState);
const resetImageListState = useResetRecoilState(imageListState);
const resetRoomImageListState = useResetRecoilState(roomImageListState);

const moveAccommodationPage = () => {
  resetStepState();
  resetAccommodationState();
  resetDisabledState();
  resetAddressCheckState();
  resetErrorModalState();
  resetImageDataState();
  resetImageListState();
  resetRoomImageListState();
  navigate(ACCOMMODATION_PAGE);
};

return (
  <StyledButton changePoint={changePoint} onClick={moveAccommodationPage}>
    <AiOutlinePlus size={20} />
    {changePoint && <div>{children}</div>}
  </StyledButton>
);
}
useMediaQuery.ts
import { useEffect, useState } from 'react';

/**@param query  CSS 미디어쿼리 ex) "(min-width: 780px)" */

export function useMediaQuery(query: string): boolean {
const [matches, setMatches] = useState(false);

useEffect(() => {
  const mediaQuery = window.matchMedia(query);

  const updateMatches = () => {
    setMatches(mediaQuery.matches);
  };

  updateMatches();
  mediaQuery.addEventListener('change', updateMatches);

  return () => {
    mediaQuery.removeEventListener('change', updateMatches);
  };
}, [query]);

return matches;
}

✔ 숙소별 예약 객실 현황 조회

  • 예약 상태 필터링 (예약 완료, 사용 중, 이용 완료)
  • 예약 내역 리스트 (테이블)
  • Pagination (페이지네이션) - 쿼리스트링(페이지수, 예약상태)을 활용한 API 통신

Code

RoomFilter.tsx
interface RoomFilterProps {
setRoomList: Dispatch<SetStateAction<RoomDataType[]>>;
}

export default function RoomFilter({ setRoomList }: RoomFilterProps) {
const originalRoomList = useRecoilValue(originalRoomListState);
const selectedHouseId = useRecoilValue(selectedAccommodationIdState);
const [roomListTotalPage, setRoomListTotalPage] = useRecoilState(roomListTotalPageState);
const [roomListTotalElement, setRoomListTotalElement] = useRecoilState(roomListTotalElementState);
const originalRoomListTotalPage = useRecoilValue(originalRoomListTotalPageState);

const { responseData, sendRequest } = useAuthorizedRequest<any>({});

const [roomListPageNumber, setRoomListPageNumber] = useRecoilState(roomListPageState);
const [roomReservationStatus, setRoomReservationStatus] = useRecoilState(roomReservationStatusState);

const handleBadgeClick = async (status: string) => {
  if (roomReservationStatus === status) {
    setRoomList(originalRoomList);
    setRoomReservationStatus('USING');
    setRoomListTotalPage(originalRoomListTotalPage);
    setRoomListPageNumber(0);
  } else {
    setRoomReservationStatus(status);
    setRoomListPageNumber(0);
    try {
      await sendRequest({
        url: `/user/reservations/houses/${selectedHouseId}?page=0&reservationStatus=${status}`,
      });
    } catch (err) {
      console.log(err);
    }
  }
};

useEffect(() => {
  if (!responseData) {
    return setRoomList([]);
  }
  if (responseData.isSuccess) {
    setRoomList(responseData.result.reservationList);
    setRoomListTotalPage(responseData.result.totalPage);
    setRoomListTotalElement(responseData.result.totalElements);
  }
}, [responseData]);

return (
  <StyledFilterContainer>
    <StyledBadgeContainer active={roomReservationStatus === 'COMPLETE'} onClick={() => handleBadgeClick('COMPLETE')}>
      {roomReservationStatus === 'COMPLETE' && <CheckMark />}
      <StatusBadge status="COMPLETE"></StatusBadge>
    </StyledBadgeContainer>
    <StyledBadgeContainer active={roomReservationStatus === 'USING'} onClick={() => handleBadgeClick('USING')}>
      {roomReservationStatus === 'USING' && <CheckMark />}
      <StatusBadge status="USING"></StatusBadge>
    </StyledBadgeContainer>
    <StyledBadgeContainer active={roomReservationStatus === 'FINISHED'} onClick={() => handleBadgeClick('FINISHED')}>
      {roomReservationStatus === 'FINISHED' && <CheckMark />}
      <StatusBadge status="FINISHED"></StatusBadge>
    </StyledBadgeContainer>
  </StyledFilterContainer>
);
}
RoomList.tsx
interface RoomListProps {
data: RoomDataType[];
}

export default function RoomList({ data }: RoomListProps) {
return (
  <StyledRoomContainer>
    <StyledThead>
      <StyledTr>
        <th>객실이름</th>
        <th>사진</th>
        <th>상태</th>
        <th>예약자성함</th>
        <th>인원</th>
        <th>전화번호</th>
        <th>체크인시간</th>
        <th>체크아웃시간</th>
      </StyledTr>
    </StyledThead>

    {data.length < 1 ? (
      <tbody>
        <StyledBlankTr>
          <StyledBlankTd colSpan={8}>객실 예약 내역이 없습니다.</StyledBlankTd>
        </StyledBlankTr>
      </tbody>
    ) : (
      <tbody>
        {data.map((room, idx) => {
          return <RoomItem key={`room${idx}`} data={room} />;
        })}
      </tbody>
    )}
  </StyledRoomContainer>
);
}
RoomItem.tsx
interface RoomItemProps {
data: RoomDataType;
}

export default function RoomItem({ data }: RoomItemProps) {
return (
  <StyledItemContainer>
    <StyledTd>{data.roomName}</StyledTd>
    <StyledTd>
      <StyledImg src={data.roomImageUrl} />
    </StyledTd>
    <StyledTd>
      <StatusBadge status={data.reservationStatus} />
    </StyledTd>
    <StyledTd>{data.personName}</StyledTd>
    <StyledTd>{data.peopleCount}</StyledTd>
    <StyledTd>{data.phoneNumber}</StyledTd>
    <StyledTd>{data.checkInDate}</StyledTd>
    <StyledTd>{data.checkOutDate}</StyledTd>
  </StyledItemContainer>
);
}
RoomListPagination.tsx
export default function RoomListPagination({ setRoomList }: RoomListPaginationProps) {
const selectedHouseId = useRecoilValue(selectedAccommodationIdState);
const roomReservationStatus = useRecoilValue(roomReservationStatusState);

const { responseData, sendRequest } = useAuthorizedRequest<any>({});

const [roomListTotalPage, setRoomListTotalPage] = useRecoilState(roomListTotalPageState);

const [roomListPageNumber, setRoomListPageNumber] = useRecoilState(roomListPageState);
const [currentPageRange, setCurrentPageRange] = useState<number[]>([]);

const pageArr = Array.from({ length: roomListTotalPage }).map((_, idx) => idx + 1);

const getPageRange = (start: number, end: number, arr: number[]) => {
  return arr.slice(start, end);
};

const getPrevPageNumber = async () => {
  if (roomListPageNumber === 0) return;
  if (roomListPageNumber % 10 === 0) {
    setCurrentPageRange(getPageRange(roomListPageNumber - 10, roomListPageNumber, pageArr));
  }
  setRoomListPageNumber(preState => preState - 1);

  try {
    await sendRequest({
      url: `/user/reservations/houses/${selectedHouseId}?page=${
        roomListPageNumber - 1
      }&reservationStatus=${roomReservationStatus}`,
    });
  } catch (err) {
    console.log(err);
  }
};

const getCurrentPageNumber = (value: number) => async () => {
  setRoomListPageNumber(value);
  try {
    await sendRequest({
      url: `/user/reservations/houses/${selectedHouseId}?page=${value}&reservationStatus=${roomReservationStatus}`,
    });
  } catch (err) {
    console.log(err);
  }
};

const getNextPageNumber = async () => {
  if (roomListPageNumber === roomListTotalPage - 1) return;
  if ((roomListPageNumber + 1) % 10 === 0) {
    setCurrentPageRange(getPageRange(roomListPageNumber + 1, roomListPageNumber + 10, pageArr));
  }
  setRoomListPageNumber(preState => preState + 1);
  try {
    await sendRequest({
      url: `/user/reservations/houses/${selectedHouseId}?page=${
        roomListPageNumber + 1
      }&reservationStatus=${roomReservationStatus}`,
    });
  } catch (err) {
    console.log(err);
  }
};

useEffect(() => {
  setCurrentPageRange(getPageRange(0, 10, pageArr));
}, [roomListTotalPage]);

useEffect(() => {
  if (!responseData) return;
  if (responseData?.isSuccess) {
    setRoomListTotalPage(responseData.result.totalPage);
    return setRoomList(responseData.result.reservationList);
  }
}, [roomListPageNumber, responseData, setRoomList]);

return (
  <StyledPaginationContainer>
    <StyledButton onClick={getPrevPageNumber}>{'<'}</StyledButton>

    {currentPageRange.map((value, idx) => {
      return (
        <StyledPageNumber onClick={getCurrentPageNumber(idx)} isSelected={roomListPageNumber === idx} key={idx}>
          {value}
        </StyledPageNumber>
      );
    })}
    <StyledButton onClick={getNextPageNumber}>{'>'}</StyledButton>
  </StyledPaginationContainer>
);
}
Clone this wiki locally