-
Notifications
You must be signed in to change notification settings - Fork 3
Host
SeongWoo Shin edited this page Aug 6, 2023
·
11 revisions
- 숙소 종류 필터링 (게스트 하우스, 호텔, 모텔, 펜션)
- 개별 숙소 클릭 시
- 숙소 삭제 - REST API - delete요청
- 숙소 디테일 페이지 이동, 숙소 수정 페이지 이동 - react-router-dom의 useNavigate hook을 통한 페이지 이동
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 커스텀 훅을 사용하여 반응형 버튼 구현
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 통신
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>
);
}