Skip to content

Commit b5fdeab

Browse files
authored
[Feat] 예금 상세 페이지 구현 (#44)
* feat: 최고한도 포맷팅 함수 * feat: 적금 상세 페이지 라우팅 설정 * feat: 적금 상세 페이지 수정 * feat: 예금 상세 페이지 구현 * feat: 띄어쓰기 수정 * feat: css 수정 * feat: css 수정
1 parent 594d417 commit b5fdeab

File tree

7 files changed

+290
-22
lines changed

7 files changed

+290
-22
lines changed
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
import { vars } from '../../styles/theme.css';
2+
import { style } from '@vanilla-extract/css';
3+
4+
export const detailContainer = style({
5+
display: 'flex',
6+
flexDirection: 'column',
7+
width: '70rem',
8+
gap: '1rem',
9+
border: `1px solid ${vars.color.pink300}`,
10+
borderRadius: '10px',
11+
padding: '2rem 3rem',
12+
marginBottom: '2rem',
13+
marginTop: '9rem',
14+
});
15+
16+
export const bank = style({
17+
fontSize: vars.size.md,
18+
fontWeight: vars.weight.bold,
19+
color: vars.color.pink300,
20+
});
21+
22+
export const product = style({
23+
fontSize: vars.size.xl,
24+
fontWeight: vars.weight.bold,
25+
color: vars.color.pink300,
26+
paddingBottom: '1rem',
27+
});
28+
29+
export const detailTextContainer = style({
30+
display: 'flex',
31+
flexDirection: 'column',
32+
gap: '1.1rem',
33+
paddingBottom: '1.5rem',
34+
});
35+
36+
export const textContainer = style({
37+
display: 'flex',
38+
gap: '0.5rem',
39+
});
40+
41+
export const textColContainer = style({
42+
display: 'flex',
43+
flexDirection: 'column',
44+
gap: '0.7rem',
45+
});
46+
47+
export const textTitle = style({
48+
fontSize: vars.size.sm,
49+
fontWeight: vars.weight.medium,
50+
color: vars.color.pink300,
51+
});
52+
53+
export const textValue = style({
54+
fontSize: vars.size.sm,
55+
fontWeight: vars.weight.regular,
56+
color: vars.color.gray800,
57+
});
58+
59+
export const pre = style({
60+
whiteSpace: 'pre-wrap',
61+
fontFamily: 'inherit',
62+
fontSize: vars.size.xs,
63+
color: vars.color.gray800,
64+
lineHeight: '2rem',
65+
});
66+
67+
export const optionContainer = style({
68+
display: 'flex',
69+
flexDirection: 'column',
70+
borderTop: `1.2px solid ${vars.color.pink300}`,
71+
paddingTop: '1.5rem',
72+
gap: '0.7rem',
73+
});
74+
75+
export const optionText = style({
76+
fontSize: vars.size.md,
77+
fontWeight: vars.weight.bold,
78+
color: vars.color.pink300,
79+
paddingBottom: '0.3rem',
80+
});
81+
82+
export const optionTextContainer = style({
83+
display: 'flex',
84+
gap: '0.5rem',
85+
});
86+
87+
export const optionTitle = style({
88+
fontSize: vars.size.ms,
89+
fontWeight: vars.weight.medium,
90+
color: vars.color.pink300,
91+
});
92+
93+
export const optionValue = style({
94+
fontSize: vars.size.ms,
95+
fontWeight: vars.weight.regular,
96+
color: vars.color.gray800,
97+
});
98+
99+
export const similarProductsContainer = style({
100+
display: 'flex',
101+
flexDirection: 'column',
102+
});
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
import { useEffect, useState } from 'react';
2+
import { useParams } from 'react-router-dom';
3+
import { getDepositsDetail } from '../../shared/api/products';
4+
import { formatLimit } from '../../shared/utils/format';
5+
import * as styles from './deposit-detail-page.css';
6+
import Header from '../../shared/components/header/header';
7+
8+
type Option = {
9+
interestType: string;
10+
saveTerm: number;
11+
baseRate: number;
12+
maxRate: number;
13+
};
14+
15+
type ProductDetail = {
16+
productId: number;
17+
optionId: number;
18+
bankName: string;
19+
productName: string;
20+
joinDeny: string;
21+
joinMember: string;
22+
joinWay: string;
23+
specialCondition: string;
24+
etcNote: string;
25+
maxLimit: string;
26+
maturityInterestInfo: string;
27+
options: Option[];
28+
};
29+
30+
const DepositDetailPage = () => {
31+
const { productId, optionId } = useParams();
32+
33+
// TODO:API 연결 후 mockdata 삭제
34+
const mockDetail: ProductDetail = {
35+
productId: 34,
36+
optionId: 293,
37+
bankName: '주식회사 케이뱅크',
38+
productName: '코드K 정기예금',
39+
joinDeny: '제한없음',
40+
joinMember: '만 17세 이상 실명의 개인 및 개인사업자',
41+
joinWay: '스마트폰',
42+
specialCondition: '우대조건 없음',
43+
etcNote: '가입금액 : 1백만원 이상\n가입기간 : 1개월~36개월',
44+
maxLimit: '제한 없음',
45+
maturityInterestInfo:
46+
'만기 후 \n- 1개월 이내 : 만기시점 기본금리 X 50%\n- 1개월 초과~6개월 이내 : 만기시점 기본금리 X 30%\n- 6개월 초과 : 연 0.20%',
47+
options: [
48+
{
49+
interestType: '단리',
50+
saveTerm: 6,
51+
baseRate: 2.86,
52+
maxRate: 2.86,
53+
},
54+
],
55+
};
56+
57+
// TODO: API 연결 후 주석 삭제
58+
//const [detail, setDetail] = useState<ProductDetail | null>(null);
59+
const [detail, setDetail] = useState<ProductDetail | null>(mockDetail);
60+
61+
useEffect(() => {
62+
if (!productId || !optionId) return;
63+
64+
getDepositsDetail(Number(productId), Number(optionId)).then((res) => {
65+
setDetail(res.result);
66+
});
67+
}, [productId, optionId]);
68+
69+
if (!detail) return <div>Loading...</div>;
70+
71+
const option = detail.options[0];
72+
73+
return (
74+
<>
75+
<Header />
76+
<div className={styles.detailContainer}>
77+
<h2 className={styles.bank}>{detail.bankName}</h2>
78+
<h1 className={styles.product}>{detail.productName}</h1>
79+
<div className={styles.detailTextContainer}>
80+
<div className={styles.textContainer}>
81+
<h3 className={styles.textTitle}>가입 대상:</h3>
82+
<p className={styles.textValue}>{detail.joinMember}</p>
83+
</div>
84+
<div className={styles.textContainer}>
85+
<h3 className={styles.textTitle}>가입 방법:</h3>
86+
<p className={styles.textValue}>{detail.joinWay}</p>
87+
</div>
88+
<div className={styles.textContainer}>
89+
<h3 className={styles.textTitle}>가입 제한:</h3>
90+
<p className={styles.textValue}>{detail.joinDeny}</p>
91+
</div>
92+
<div className={styles.textContainer}>
93+
<h3 className={styles.textTitle}>최고 한도:</h3>
94+
<p className={styles.textValue}>{formatLimit(detail.maxLimit)}</p>
95+
</div>
96+
<div className={styles.textColContainer}>
97+
<h3 className={styles.textTitle}>우대 조건</h3>
98+
<pre className={styles.pre}>{detail.specialCondition}</pre>
99+
</div>
100+
<div className={styles.textColContainer}>
101+
<h3 className={styles.textTitle}>만기 후 이자율</h3>
102+
<pre className={styles.pre}>{detail.maturityInterestInfo}</pre>
103+
</div>
104+
<div className={styles.textColContainer}>
105+
<h3 className={styles.textTitle}>기타 유의사항</h3>
106+
<pre className={styles.pre}>{detail.etcNote}</pre>
107+
</div>
108+
</div>
109+
110+
{option && (
111+
<div className={styles.optionContainer}>
112+
<h3 className={styles.optionText}>상품 옵션 ⭐</h3>
113+
<div className={styles.optionTextContainer}>
114+
<h4 className={styles.optionTitle}>이자 유형:</h4>
115+
<p className={styles.optionValue}>{option.interestType}</p>
116+
</div>
117+
<div className={styles.optionTextContainer}>
118+
<h4 className={styles.optionTitle}>저축 기간:</h4>
119+
<p className={styles.optionValue}>{option.saveTerm}개월</p>
120+
</div>
121+
<div className={styles.optionTextContainer}>
122+
<h4 className={styles.optionTitle}>기본 금리:</h4>
123+
<p className={styles.optionValue}>{option.baseRate}%</p>
124+
</div>
125+
<div className={styles.optionTextContainer}>
126+
<h4 className={styles.optionTitle}>최대 금리:</h4>
127+
<p className={styles.optionValue}>{option.maxRate}%</p>
128+
</div>
129+
</div>
130+
)}
131+
</div>
132+
133+
{/* TODO: 유사 상품 리스트 API 연결 후 구현 */}
134+
<div className={styles.similarProductsContainer}>
135+
<div>유사 상품 리스트</div>
136+
<div>유사 상품 리스트</div>
137+
</div>
138+
</>
139+
);
140+
};
141+
142+
export default DepositDetailPage;

src/pages/savings-detail/savings-detail-page.css.ts

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ export const product = style({
2929
export const detailTextContainer = style({
3030
display: 'flex',
3131
flexDirection: 'column',
32-
gap: '1rem',
32+
gap: '1.1rem',
3333
paddingBottom: '1.5rem',
3434
});
3535

@@ -38,6 +38,12 @@ export const textContainer = style({
3838
gap: '0.5rem',
3939
});
4040

41+
export const textColContainer = style({
42+
display: 'flex',
43+
flexDirection: 'column',
44+
gap: '0.7rem',
45+
});
46+
4147
export const textTitle = style({
4248
fontSize: vars.size.sm,
4349
fontWeight: vars.weight.medium,
@@ -54,7 +60,7 @@ export const pre = style({
5460
whiteSpace: 'pre-wrap',
5561
fontFamily: 'inherit',
5662
fontSize: vars.size.xs,
57-
color: vars.color.gray700,
63+
color: vars.color.gray800,
5864
lineHeight: '2rem',
5965
});
6066

@@ -63,18 +69,19 @@ export const optionContainer = style({
6369
flexDirection: 'column',
6470
borderTop: `1.2px solid ${vars.color.blue300}`,
6571
paddingTop: '1.5rem',
66-
});
67-
68-
export const optionTextContainer = style({
69-
display: 'flex',
70-
gap: '0.5rem',
71-
paddingTop: '0.7rem',
72+
gap: '0.7rem',
7273
});
7374

7475
export const optionText = style({
7576
fontSize: vars.size.md,
7677
fontWeight: vars.weight.bold,
7778
color: vars.color.blue300,
79+
paddingBottom: '0.3rem',
80+
});
81+
82+
export const optionTextContainer = style({
83+
display: 'flex',
84+
gap: '0.5rem',
7885
});
7986

8087
export const optionTitle = style({

src/pages/savings-detail/savings-detail-page.tsx

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { useEffect, useState } from 'react';
22
import { useParams } from 'react-router-dom';
33
import { getSavingsDetail } from '../../shared/api/products';
4+
import { formatLimit } from '../../shared/utils/format';
45
import * as styles from './savings-detail-page.css';
56
import Header from '../../shared/components/header/header';
67

@@ -14,6 +15,7 @@ type Option = {
1415

1516
type ProductDetail = {
1617
productId: number;
18+
optionId: number;
1719
bankName: string;
1820
productName: string;
1921
joinDeny: string;
@@ -32,6 +34,7 @@ const SavingsDetailPage = () => {
3234
// TODO:API 연결 후 mockdata 삭제
3335
const mockDetail: ProductDetail = {
3436
productId: 50,
37+
optionId: 167,
3538
bankName: '수협은행',
3639
productName: 'Sh해양플라스틱Zero!적금',
3740
joinDeny: '제한없음',
@@ -78,35 +81,42 @@ const SavingsDetailPage = () => {
7881
<h1 className={styles.product}>{detail.productName}</h1>
7982
<div className={styles.detailTextContainer}>
8083
<div className={styles.textContainer}>
81-
<h3 className={styles.textTitle}>가입대상:</h3>
84+
<h3 className={styles.textTitle}>가입 대상:</h3>
8285
<p className={styles.textValue}>{detail.joinMember}</p>
8386
</div>
8487
<div className={styles.textContainer}>
85-
<h3 className={styles.textTitle}>가입방법:</h3>
88+
<h3 className={styles.textTitle}>가입 방법:</h3>
8689
<p className={styles.textValue}>{detail.joinWay}</p>
8790
</div>
8891
<div className={styles.textContainer}>
89-
<h3 className={styles.textTitle}>가입제한:</h3>
92+
<h3 className={styles.textTitle}>가입 제한:</h3>
9093
<p className={styles.textValue}>{detail.joinDeny}</p>
9194
</div>
9295
<div className={styles.textContainer}>
93-
<h3 className={styles.textTitle}>최고한도:</h3>
94-
<p className={styles.textValue}>{detail.maxLimit}</p>
96+
<h3 className={styles.textTitle}>최고 한도:</h3>
97+
<p className={styles.textValue}>{formatLimit(detail.maxLimit)}</p>
98+
</div>
99+
<div className={styles.textColContainer}>
100+
<h3 className={styles.textTitle}>우대 조건</h3>
101+
<pre className={styles.pre}>{detail.specialCondition}</pre>
102+
</div>
103+
<div className={styles.textColContainer}>
104+
<h3 className={styles.textTitle}>만기 후 이자율</h3>
105+
<pre className={styles.pre}>{detail.maturityInterestInfo}</pre>
106+
</div>
107+
<div className={styles.textColContainer}>
108+
<h3 className={styles.textTitle}>기타 유의사항</h3>
109+
<pre className={styles.pre}>{detail.etcNote}</pre>
95110
</div>
96-
<h3 className={styles.textTitle}>우대 조건</h3>
97-
<pre className={styles.pre}>{detail.specialCondition}</pre>
98-
99-
<h3 className={styles.textTitle}>만기 후 이자율</h3>
100-
<pre className={styles.pre}>{detail.maturityInterestInfo}</pre>
101-
102-
<h3 className={styles.textTitle}>기타 유의사항</h3>
103-
<pre className={styles.pre}>{detail.etcNote}</pre>
104111
</div>
105112

106113
{option && (
107114
<div className={styles.optionContainer}>
108115
<h3 className={styles.optionText}>상품 옵션 ⭐</h3>
109-
116+
<div className={styles.optionTextContainer}>
117+
<h4 className={styles.optionTitle}>이자 유형:</h4>
118+
<p className={styles.optionValue}>{option.interestType}</p>
119+
</div>
110120
<div className={styles.optionTextContainer}>
111121
<h4 className={styles.optionTitle}>적립 방식:</h4>
112122
<p className={styles.optionValue}>{option.reserveType}</p>

src/shared/router/index.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,14 @@ import HomePage from '../../pages/home/home-page';
44
import LoginPage from '../../pages/login/login-page';
55
import SignupPage from '../../pages/signup/signup-page';
66
import SavingsDetailPage from '../../pages/savings-detail/savings-detail-page';
7+
import DepositDetailPage from '../../pages/deposit-detail/deposit-detail-page';
78
import ErrorPage from '../../pages/error/error-page';
89

910
export const router = createBrowserRouter([
1011
{ path: PATH.HOME, element: <HomePage /> },
1112
{ path: PATH.LOGIN, element: <LoginPage /> },
1213
{ path: PATH.SIGNUP, element: <SignupPage /> },
1314
{ path: PATH.SAVINGS_DETAIL, element: <SavingsDetailPage /> },
15+
{ path: PATH.DEPOSIT_DETAIL, element: <DepositDetailPage /> },
1416
{ path: PATH.ERROR, element: <ErrorPage /> },
1517
]);

0 commit comments

Comments
 (0)