Skip to content

Commit

Permalink
Feat: 페이지 이동 시 저장 확인 모달 구현 (#183)
Browse files Browse the repository at this point in the history
  • Loading branch information
gahyuun authored Jan 23, 2024
1 parent 3b45fc9 commit d78c33a
Show file tree
Hide file tree
Showing 14 changed files with 174 additions and 46 deletions.
7 changes: 5 additions & 2 deletions src/components/coupon/coupon-header/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,18 @@ import {
} from '@/constants/coupon';
import 'moment/locale/ko';
import locale from 'antd/es/locale/ko_KR';
import { isCouponModifiedState } from '@stores/coupon/atom';
import { useRecoilValue } from 'recoil';
export const CouponHeader = ({
expiry,
handleSelectStatus,
handleDeleteButton,
isModified,
handleChangeDate,
handleEditButton,
handleModalOpen,
}: CouponHeaderProps) => {
const isCouponModified = useRecoilValue(isCouponModifiedState);

const couponStatusOption = [
{ value: COUPON_STATUS_ENABLE.value, label: COUPON_STATUS_ENABLE.label },
{ value: COUPON_STATUS_DISABLE.value, label: COUPON_STATUS_DISABLE.label },
Expand All @@ -43,7 +46,7 @@ export const CouponHeader = ({
</TextBox>
<StyledSaveButton
type="primary"
disabled={!isModified()}
disabled={!isCouponModified}
onClick={handleEditButton}
data-testid="save-button"
>
Expand Down
1 change: 0 additions & 1 deletion src/components/coupon/coupon-header/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ export type CouponHeaderProps = {
expiry: string;
handleSelectStatus: (value: string) => void;
handleDeleteButton: VoidFunction;
isModified: () => boolean;
handleChangeDate: (date: string) => void;
handleEditButton: VoidFunction;
handleModalOpen: VoidFunction;
Expand Down
2 changes: 1 addition & 1 deletion src/components/coupon/table/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ export const CouponTable = ({
columns={columns}
dataSource={couponTableData}
pagination={false}
scroll={{ y: 510 }}
scroll={{ y: 'calc(88vh - 242px)' }}
/>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ const StyledCarouselWrapper = styled.div`
.ant-carousel .slick-next {
top: 60%;
transform: translateY(-60%);
z-index: 2;
z-index: 1;
}
.ant-carousel .slick-prev {
Expand Down
4 changes: 2 additions & 2 deletions src/components/layout/init-layout/InitLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ const StyledLayout = styled(Layout)`
const StyledHeader = styled(Layout.Header)`
position: fixed;
top: 0;
z-index: 10;
z-index: 2;
width: 100%;
background-color: ${colors.black100};
Expand Down Expand Up @@ -118,7 +118,7 @@ const StyledHeadContentCotainer = styled.div`
background-color: ${colors.white};
position: fixed;
z-index: 10;
z-index: 2;
width: 100%;
margin-top: 64px;
Expand Down
39 changes: 36 additions & 3 deletions src/components/layout/side-bar/accommodation-list/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { TextBox } from '@components/text-box';
import { Button } from 'antd';
import { Button, Modal } from 'antd';
import styled from 'styled-components';
import { colors } from '@/constants/colors';
import { useState } from 'react';
Expand All @@ -9,6 +9,8 @@ import { Accommodation } from '@api/accommodation/type';
import { useLocation, useNavigate } from 'react-router-dom';
import { ROUTES } from '@/constants/routes';
import { setCookie } from '@hooks/sign-in/useSignIn';
import { useRecoilValue } from 'recoil';
import { isCouponModifiedState } from '@stores/coupon/atom';

export const AccommodationList = ({
accommodationListData,
Expand All @@ -18,12 +20,30 @@ export const AccommodationList = ({
const { accommodations } = accommodationListData || { accommodations: [] };
const navigate = useNavigate();
const location = useLocation();
const isCouponModified = useRecoilValue(isCouponModifiedState);

const handleSelectBox = () => {
if (accommodations.length <= 1) return;
setClickedSelectBox(!clickedSelectBox);
};

const checkModified = (item: Accommodation, idx: number) => {
if (isCouponModified)
Modal.confirm({
title: '수정사항이 저장되지 않았습니다.',
content: '페이지를 나가겠습니까?',
cancelText: '나가기',
okText: '취소',
className: 'confirm-modal',
onCancel: () => {
handleNavigate(item, idx);
},
});
else {
handleNavigate(item, idx);
}
};

const handleNavigate = (item: Accommodation, idx: number) => {
const accommodationId = item.id;
const currentPath = location.pathname;
Expand All @@ -46,7 +66,20 @@ export const AccommodationList = ({
};

const navigateToAccommodationAddPage = () => {
navigate(ROUTES.INIT_ACCOMMODATION_REGISTRATION);
if (isCouponModified)
Modal.confirm({
title: '수정사항이 저장되지 않았습니다.',
content: '페이지를 나가겠습니까?',
cancelText: '나가기',
okText: '취소',
className: 'confirm-modal',
onCancel: () => {
navigate(ROUTES.INIT_ACCOMMODATION_REGISTRATION);
},
});
else {
navigate(ROUTES.INIT_ACCOMMODATION_REGISTRATION);
}
};

return (
Expand All @@ -67,7 +100,7 @@ export const AccommodationList = ({
{accommodations?.map((item, idx) => (
<StyledAccommodationItem
key={item.id}
onClick={() => handleNavigate(item, idx)}
onClick={() => checkModified(item, idx)}
>
<StyledFlex>
<TextBox typography="body3" fontWeight="bold">
Expand Down
31 changes: 28 additions & 3 deletions src/components/layout/side-bar/navigation/index.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,41 @@
import { colors } from '@/constants/colors';
import { getNavigationMap } from '@/constants/navigation';
import { TextBox } from '@components/text-box';
import { Link } from 'react-router-dom';
import { isCouponModifiedState } from '@stores/coupon/atom';
import { Modal } from 'antd';
import { useNavigate } from 'react-router-dom';
import { useRecoilValue } from 'recoil';
import styled from 'styled-components';

export const Navigation = () => {
const isCouponModified = useRecoilValue(isCouponModifiedState);
const navigate = useNavigate();
const handleNavigate = (link: string) => {
if (isCouponModified) {
Modal.confirm({
title: '수정사항이 저장되지 않았습니다.',
content: '페이지를 나가겠습니까?',
cancelText: '나가기',
okText: '취소',
className: 'confirm-modal',
onCancel: () => {
navigate(link);
},
});
} else {
navigate(link);
}
};
return (
<nav>
<StyledNavWrap>
{Object.entries(getNavigationMap()).map(([key, { label, link }]) => (
<StyledNavItem key={key}>
<Link to={link}>
<div
onClick={() => {
handleNavigate(link);
}}
>
<TextBox
typography="body2"
color="black900"
Expand All @@ -19,7 +44,7 @@ export const Navigation = () => {
>
{label}
</TextBox>
</Link>
</div>
</StyledNavItem>
))}
</StyledNavWrap>
Expand Down
50 changes: 40 additions & 10 deletions src/hooks/coupon/useCoupon.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import {
import { calculatedCouponPoints } from '@/utils/discountCoupon';
import { useParams } from 'react-router-dom';
import { RESPONSE_CODE } from '@/constants/api';
import { useRecoilState } from 'recoil';
import { isCouponModifiedState } from '@stores/coupon/atom';
/**
* @description 쿠폰 관리 페이지 로직을 다루는 hook
*
Expand Down Expand Up @@ -53,6 +55,9 @@ export const useCoupon = () => {
const [isModalOpen, setIsModalOpen] = useState(false);
const { accommodationId } = useParams();
const [isPointModalOpen, setIsPointModalOpen] = useState(false);
const [isCouponModified, setIsCouponModified] = useRecoilState(
isCouponModifiedState,
);

const {
data,
Expand Down Expand Up @@ -125,6 +130,39 @@ export const useCoupon = () => {
processPurchaseData();
}, [isModalOpen]);

useEffect(() => {
setSelectedStatus('');
setSelectedRowKeys([]);
setCouponData({
expiry: '',
coupons: [],
});
setPurchaseData({
batchValue: 0,
isAppliedBatchEdit: false,
totalPoints: 0,
rooms: [],
});
originCouponTableData.current = {
expiry: '',
coupons: [],
};
getCouponRemove();
}, [accommodationId]);

useEffect(() => {
setIsCouponModified(
JSON.stringify(originCouponTableData.current) !==
JSON.stringify(couponData),
);
}, [couponData]);

useEffect(() => {
return () => {
setIsCouponModified(false);
};
}, []);

const processCouponTableData = (data: coupons) => {
const couponTableData = [];
const originData = [];
Expand Down Expand Up @@ -270,13 +308,6 @@ export const useCoupon = () => {
setCouponData({ expiry: date, coupons });
};

const isModified = () => {
return (
JSON.stringify(originCouponTableData.current) !==
JSON.stringify(couponData)
);
};

const isSelectedRow = () => {
return selectedRowKeys.length !== 0;
};
Expand Down Expand Up @@ -356,7 +387,7 @@ export const useCoupon = () => {
};

const handleDeleteButton = () => {
if (isModified()) {
if (isCouponModified) {
message.warning('수정 중인 내용을 먼저 저장하세요');
return;
}
Expand Down Expand Up @@ -404,7 +435,7 @@ export const useCoupon = () => {
};

const handleModalOpen = () => {
if (isModified()) {
if (isCouponModified) {
message.warning('수정 중인 내용을 먼저 저장하세요');
return;
}
Expand Down Expand Up @@ -543,7 +574,6 @@ export const useCoupon = () => {
handleSelectCouponType,
handleChangeDayLimit,
handleDeleteButton,
isModified,
handleChangeDate,
handleEditButton,
handleModalOpen,
Expand Down
32 changes: 24 additions & 8 deletions src/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { SideBar } from '@components/layout/side-bar';
import { Layout } from 'antd';
import { Layout, Modal } from 'antd';
import { Outlet, useLocation, useNavigate } from 'react-router-dom';
import { ROUTES } from './constants/routes';
import { colors } from '@/constants/colors';
Expand All @@ -8,10 +8,11 @@ import { TextBox } from '@components/text-box';
import logoImg from '@assets/image/logo.png';
import { StyledLayoutProps, StyledSiderProps } from './types/layout';
import { FaBars } from 'react-icons/fa6';
import { useRecoilState } from 'recoil';
import { useRecoilState, useRecoilValue } from 'recoil';
import { isSideBarOpenState } from '@stores/layout';
import { getCookie } from '@hooks/sign-in/useSignIn';
import { mobileBreakPoint } from './constants/mobile';
import { isCouponModifiedState } from '@stores/coupon/atom';

export const RootLayout = () => {
const navigate = useNavigate();
Expand All @@ -22,14 +23,29 @@ export const RootLayout = () => {
);
const isRoomUpdateRoute = currentRoute.includes(ROUTES.ROOM_UPDATE);
const [isOpenSideBar, setIsOpenSideBar] = useRecoilState(isSideBarOpenState);
const isCouponModified = useRecoilValue(isCouponModifiedState);

const shouldApplyGrayBackground =
isRoomRegistrationRoute || isRoomUpdateRoute;

const moveToMain = () => {
const accommodationId = getCookie('accommodationId');
const updatedMainPath = `/${accommodationId}${ROUTES.MAIN}`;
navigate(updatedMainPath);

if (isCouponModified)
Modal.confirm({
title: '수정사항이 저장되지 않았습니다.',
content: '페이지를 나가겠습니까?',
cancelText: '나가기',
okText: '취소',
className: 'confirm-modal',
onCancel: () => {
navigate(updatedMainPath);
},
});
else {
navigate(updatedMainPath);
}
};

const openSideBar = () => {
Expand Down Expand Up @@ -81,9 +97,9 @@ const StyledHeader = styled(Layout.Header)`
background-color: ${colors.black100};
box-shadow: 0px 1px 5px 0px #0000001a;
padding: 0 24px;
z-index: 1003;
z-index: 7;
@media (max-width: ${mobileBreakPoint}) {
z-index: 1000;
z-index: 4;
}
`;

Expand All @@ -93,13 +109,13 @@ const StyledSider = styled(Layout.Sider)<StyledSiderProps>`
height: calc(100vh - 56px);
background-color: ${colors.white};
box-shadow: 0 0 5px rgba(0, 0, 0, 0.1);
z-index: 1002;
z-index: 6;
@media (max-width: ${mobileBreakPoint}) {
transform: ${(props) =>
props.isOpenSideBar ? 'translateX(0%)' : 'translateX(-100%)'};
top: 0;
position: fixed;
z-index: 2001;
z-index: 8;
height: 100%;
}
`;
Expand Down Expand Up @@ -150,7 +166,7 @@ const StyledDim = styled.div`
width: 100%;
height: 100vh;
background-color: rgba(0, 0, 0, 0.3);
z-index: 1001;
z-index: 5;
display: none;
@media (max-width: ${mobileBreakPoint}) {
display: block;
Expand Down
Loading

0 comments on commit d78c33a

Please sign in to comment.