Skip to content

Commit

Permalink
[FE] feat #68: 결제 기능 - 카드 (#82)
Browse files Browse the repository at this point in the history
* [#68] chore: msw핸들러 추가

* [#68] chore: 로고 파일 추가

* [#68] design: 로딩인디케이터, 결제 실패사유 ui

* [#68] feat: 카드 결제 진행시 번호 입력할 인풋 렌더링

* [#68] feat: 카드 결제 진행 - 결제 실패 상황 구현

* [#44] fix: 모달창을 닫으면 시간이 멈추던 장바구니 타이머 버그 수정

- 모달창을 닫으면 시간이 초기화된 후 다시 타이머가 흐른다

* [#68] feat: timer컴포넌트 분리

* [#68] comment: 주석추가
  • Loading branch information
gunoc authored Jun 29, 2023
1 parent 3a93564 commit ef8e26a
Show file tree
Hide file tree
Showing 17 changed files with 335 additions and 171 deletions.
Binary file added fe/public/assets/logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
84 changes: 3 additions & 81 deletions fe/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,75 +7,11 @@ import { TabMenu } from './components/Tab/TabMenu';
import { OrderData } from './utils/types';

function App() {
const [loading, setLoading] = useState(false);
const [activeTab, setActiveTab] = useState(0);
const [menuList, setMenuList] = useState([]);
const [productList, setProductList] = useState([]);
const [loading, setLoading] = useState(false);

const [orderList, setOrderList] = useState<OrderData[]>([]);
const [isAddMenuModalOpen, setIsAddMenuModalOpen] = useState(false);
const [addMenuModalContent, setAddMenuModalContent] = useState(null);
const [isPaymentModalOpen, setIsPaymentModalOpen] = useState(false);
const [paymentModalContent, setPaymentModalContent] = useState(null);

useEffect(() => {
// useEffect로 상태 변경 체크는 됨
if (isAddMenuModalOpen) {
console.log('모달 열!');
console.log(isAddMenuModalOpen);
}
if (!isAddMenuModalOpen) {
console.log('모달 닫!');
console.log(isAddMenuModalOpen);
}
}, [isAddMenuModalOpen]);

function addPaymentModalOpenHandler(content: any) {
if (isPaymentModalOpen) {
return;
}
setIsPaymentModalOpen(true);
setPaymentModalContent(content);
}

function addPaymentModalCloseHandler() {
if (!isPaymentModalOpen) {
return;
}

setIsPaymentModalOpen(false);
setPaymentModalContent(null);
}

function addMenuModalOpenHandler(content: any) {
if (isAddMenuModalOpen) {
return;
}
console.log(isAddMenuModalOpen + '가 false여야함');
setIsAddMenuModalOpen(true);

setAddMenuModalContent(content);
console.log(content);
}

function addMenuModalCloseHandler() {
console.log('되고있나요');
console.log(isAddMenuModalOpen + '가 true여야함');
setIsAddMenuModalOpen(false);
setAddMenuModalContent(null);
// 이 위로 변경

if (!isAddMenuModalOpen) {
return;
}

// 여전히 이건 안찍힘 true로 바뀐 상태가 아닌듯?? 비동기때문인지?? 근데 왜 useEffect에는 감지가 되는가..? 이유는 모름
console.log('되고있나요아악');
console.log(isAddMenuModalOpen + '가 true여야함 아아악');
// 이 위치에 있었는데
// setIsAddMenuModalOpen(false);
// setAddMenuModalContent(null);
}

useEffect(() => {
setLoading(true);
Expand All @@ -100,22 +36,8 @@ function App() {
return (
<div className={classes.kiosk}>
<TabMenu menuList={menuList} activeTab={activeTab} setActiveTab={setActiveTab} />
<MainArea
productList={productList}
setOrderList={setOrderList}
modalContent={addMenuModalContent}
isModalOpen={isAddMenuModalOpen}
addModalOpenHandler={addMenuModalOpenHandler}
addModalCloseHandler={addMenuModalCloseHandler}
/>
<Cart
orderList={orderList}
setOrderList={setOrderList}
modalContent={paymentModalContent}
isModalOpen={isPaymentModalOpen}
addModalOpenHandler={addPaymentModalOpenHandler}
addModalCloseHandler={addPaymentModalCloseHandler}
/>
<MainArea productList={productList} setOrderList={setOrderList} />
<Cart orderList={orderList} setOrderList={setOrderList} />
</div>
);
}
Expand Down
3 changes: 1 addition & 2 deletions fe/src/components/Cart/Cart.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
.timer > span {
font: var(--font-body-regular);
}

.second {
font: var(--font-body-large);
}
Expand Down Expand Up @@ -97,9 +98,7 @@
cursor: pointer;
}


.active {
background-color: var(--color-red);
border: 1px solid var(--color-red);
}

59 changes: 21 additions & 38 deletions fe/src/components/Cart/Cart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,54 +4,32 @@ import { Payment } from '../Modal/Payment';
import classes from './Cart.module.css';
import { CartItem } from './CartItem';
import { useEffect, useState } from 'react';
import { Timer } from './Timer';

export function Cart({
orderList,
setOrderList,
modalContent,
isModalOpen,
addModalOpenHandler,
addModalCloseHandler,
}: {
orderList: OrderData[];
setOrderList: React.Dispatch<React.SetStateAction<OrderData[]>>;
modalContent: any;
isModalOpen: boolean;
addModalOpenHandler: (content: any) => void;
addModalCloseHandler: () => void;
}) {
const [totalPrice, setTotalPrice] = useState(0);
const [seconds, setSeconds] = useState(500);
const [isPayProcessing, setIsPayProcessing] = useState(false);
const [isPaymentModalOpen, setIsPaymentModalOpen] = useState(false);

function modalOpenHandler() {
setIsPaymentModalOpen(true);
}

function modalCloseHandler() {
setIsPaymentModalOpen(false);
setIsPayProcessing(false);
}

useEffect(() => {
calculateTotalPrice();
}, [orderList]);

useEffect(() => {
if (orderList.length === 0) {
setSeconds(500);
} else {
const timer = setTimeout(() => {
setSeconds((prevSeconds) => prevSeconds - 1);
}, 1000);

if (isPayProcessing) {
clearInterval(timer);
setIsPayProcessing(false);
}

if (seconds === 0) {
clearInterval(timer);
setOrderList([]);
}

return () => {
clearTimeout(timer);
};
}
}, [orderList, seconds]);

function calculateTotalPrice() {
const totalPrice = orderList.reduce((acc, cur) => acc + cur.price * cur.quantity, 0);
setTotalPrice(totalPrice);
Expand All @@ -61,12 +39,12 @@ export function Cart({
setOrderList([]);
}

function payBtnClickHandler(content: React.ReactNode) {
function payBtnClickHandler() {
if (totalPrice === 0) {
return;
}
setIsPayProcessing(true);
addModalOpenHandler(content);
modalOpenHandler();
}

const isPayBtnActive = totalPrice > 0;
Expand All @@ -84,7 +62,8 @@ export function Cart({
<div className={classes.right}>
<div className={classes.timer}>
<span>
<span className={classes.second}>{seconds}</span> 초 후 주문이 취소됩니다.
<Timer orderList={orderList} setOrderList={setOrderList} isPayProcessing={isPayProcessing} />초 후 주문이
취소됩니다.
</span>
</div>
<div className={classes.info}>
Expand All @@ -100,15 +79,19 @@ export function Cart({
<button
className={`${classes.payBtn} ${isPayBtnActive ? classes.active : ''}`}
onClick={() => {
payBtnClickHandler(<Payment addModalOpenHandler={addModalOpenHandler} />);
payBtnClickHandler();
}}
>
결제하기
</button>
</div>
</div>
</div>
{isModalOpen && <Modal addModalCloseHandler={addModalCloseHandler}>{modalContent}</Modal>}
{isPaymentModalOpen && (
<Modal modalCloseHandler={modalCloseHandler}>
<Payment orderList={orderList} modalCloseHandler={modalCloseHandler} />
</Modal>
)}
</>
);
}
3 changes: 3 additions & 0 deletions fe/src/components/Cart/Timer.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.seconds {
font: var(--font-body-large);
}
46 changes: 46 additions & 0 deletions fe/src/components/Cart/Timer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { OrderData } from '../../utils/types';
import { useEffect, useState } from 'react';
import classes from './Timer.module.css';

export function Timer({
orderList,
setOrderList,
isPayProcessing,
}: {
orderList: OrderData[];
setOrderList: React.Dispatch<React.SetStateAction<OrderData[]>>;
isPayProcessing: boolean;
}) {
const [seconds, setSeconds] = useState(500);

useEffect(() => {
if (orderList.length === 0 || isPayProcessing) {
setSeconds(500);
} else {
const timer = setTimeout(() => {
setSeconds((prevSeconds) => prevSeconds - 1);
}, 1000);

if (isPayProcessing) {
clearTimeout(timer);
}

if (seconds === 0) {
clearTimeout(timer);
setOrderList([]);
}

return () => {
clearTimeout(timer);
};
}
}, [orderList, seconds, isPayProcessing]);
// orderList의존성빼기
useEffect(() => {
if (!isPayProcessing) {
setSeconds(500);
}
}, [isPayProcessing]);

return <span className={classes.seconds}>{seconds}</span>;
}
42 changes: 42 additions & 0 deletions fe/src/components/Loading/Loading.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
.loading {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
border-radius: 5px;
background-color: var(--color-white);
z-index: 2;
}

.wrapper {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
gap: 4.2rem;
width: 100%;
height: 100%;
}

.logo {
animation: flip 2s ease-in-out infinite;
}

@keyframes flip {
0% {
transform: perspective(800px) rotateY(0deg);
}
50% {
transform: perspective(800px) rotateY(180deg);
}
100% {
transform: perspective(800px) rotateY(360deg);
}
}

.text {
font: var(--font-body-large);
font-size: 2rem;
color: var(--color-black);
}
12 changes: 12 additions & 0 deletions fe/src/components/Loading/Loading.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import classes from './Loading.module.css';

export function Loading() {
return (
<div className={classes.loading}>
<div className={classes.wrapper}>
<img className={classes.logo} src="/assets/logo.png" alt="logo" />
<p className={classes.text}>결제 진행중...</p>
</div>
</div>
);
}
42 changes: 42 additions & 0 deletions fe/src/components/Loading/PaymentResult.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
.cause {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
border-radius: 5px;
background-color: var(--color-white);
z-index: 2;
padding: 0 70px;
}

.wrapper {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
gap: 5rem;
width: 100%;
height: 100%;
}

.causeTitle {
font: var(--font-body-large);
font-size: 3.7rem;
color: var(--color-black);
}

.causeText {
font: var(--font-body-large);
font-size: 2rem;
color: var(--color-black);
}

.btn {
width: 100%;
padding: 2rem;
border-radius: 5px;
font: var(--font-body-large);
color: var(--color-white);
background: var(--color-red);
}
Loading

0 comments on commit ef8e26a

Please sign in to comment.