Skip to content

Commit 9ab196e

Browse files
authored
Merge pull request #14 from HSU-Deulbull/design/aneey-booking#8
[Design] 사전 예매 UI 완성
2 parents 9887040 + 2a026df commit 9ab196e

35 files changed

Lines changed: 1409 additions & 2 deletions

package-lock.json

Lines changed: 70 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,14 @@
1414
"react": "^19.0.0",
1515
"react-dom": "^19.0.0",
1616
"react-router-dom": "^7.9.4",
17+
"react-scroll": "^1.9.3",
1718
"styled-components": "^6.1.19"
1819
},
1920
"devDependencies": {
2021
"@eslint/js": "^9.18.0",
2122
"@types/react": "^19.0.8",
2223
"@types/react-dom": "^19.0.3",
24+
"@types/react-scroll": "^1.8.10",
2325
"@vite-pwa/assets-generator": "^0.2.6",
2426
"@vitejs/plugin-react": "^4.2.1",
2527
"eslint": "^9.18.0",

src/assets/booking/check.svg

Lines changed: 10 additions & 0 deletions
Loading

src/assets/booking/checked.svg

Lines changed: 10 additions & 0 deletions
Loading

src/assets/booking/close.svg

Lines changed: 17 additions & 0 deletions
Loading

src/assets/booking/copy.svg

Lines changed: 3 additions & 0 deletions
Loading

src/assets/booking/poster.svg

Lines changed: 15 additions & 0 deletions
Loading

src/datas/BookingLinks.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
export const LINKS = {
2+
QUESTION_CHAT: 'https://www.kakaocorp.com/page/service/service/openchat',
3+
KAKAO_PAY: 'https://qr.kakaopay.com/Fbqn2HMS089292',
4+
NAVER_PAY: 'https://smartstore.naver.com/example',
5+
};
6+
7+
export const ACCOUNTS = {
8+
DEFAULT: 'KB국민은행 93770201535579 이진현',
9+
};

src/hooks/UseBookingForm.tsx

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import { useMemo, useState } from 'react';
2+
3+
const UseBookingForm = () => {
4+
const [name, setName] = useState('');
5+
const [phone, setPhone] = useState('');
6+
const [member, setMember] = useState('');
7+
8+
const [checked, setChecked] = useState({
9+
info: false,
10+
price: false,
11+
confirm: false,
12+
});
13+
14+
// 위의 세 타입 중 하나만 받도록 설정
15+
const toggleCheck = (key: keyof typeof checked) => {
16+
setChecked((prev) => ({ ...prev, [key]: !prev[key] }));
17+
};
18+
19+
const isNameValid = name.trim().length > 0;
20+
const isPhoneValid = /^010-\d{3,4}-\d{4}$/.test(phone);
21+
const isMemberValid = Number(member) > 0;
22+
const isAllChecked = Object.values(checked).every(Boolean);
23+
24+
const isAllValid =
25+
isNameValid && isPhoneValid && isMemberValid && isAllChecked;
26+
27+
const errorMsgs = useMemo(() => {
28+
const msgs: string[] = [];
29+
if (!isNameValid || !isPhoneValid || !isMemberValid)
30+
msgs.push('*예매 정보 입력 항목을 다시 확인해 주세요.');
31+
if (!checked.price)
32+
msgs.push('*가격 안내 및 입금 항목을 다시 확인해 주세요.');
33+
if (!checked.confirm)
34+
msgs.push('*최종 확인 및 제출 항목을 다시 확인해 주세요.');
35+
return msgs;
36+
}, [isNameValid, isPhoneValid, isMemberValid, checked]);
37+
38+
return {
39+
name,
40+
phone,
41+
member,
42+
checked,
43+
isAllValid,
44+
errorMsgs,
45+
setName,
46+
setPhone,
47+
setMember,
48+
toggleCheck,
49+
};
50+
};
51+
52+
export default UseBookingForm;
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
import * as S from './styles/BookingPage.style';
2+
3+
import { Element } from 'react-scroll';
4+
5+
import SectionHeader from './components/sectionHeader/SectionHeader';
6+
import UseBookingForm from '../../hooks/UseBookingForm';
7+
import InfoSection from './components/infoSection/InfoSection';
8+
import InputSection from './components/inputSection/InputSection';
9+
import PriceSection from './components/priceSection/PriceSection';
10+
import ConfirmSection from './components/confirmSection/ConfirmSection';
11+
import { useState } from 'react';
12+
import SubmitModal from './components/submitModal/SubmitModal';
13+
import { LINKS } from '../../datas/BookingLinks';
14+
15+
const BookingPage = () => {
16+
const form = UseBookingForm();
17+
const { isAllValid, errorMsgs } = form;
18+
19+
const [isOpen, setIsOpen] = useState(false);
20+
const [questionLink, setQuestionLink] = useState<string>(LINKS.QUESTION_CHAT);
21+
22+
const handleOpen = () => {
23+
setIsOpen((prev) => !prev);
24+
};
25+
26+
return (
27+
<>
28+
<S.BookingContainer id="bookingScroll">
29+
{/* 상단 사전 예매 제목 */}
30+
<SectionHeader
31+
title="사전예매"
32+
questionText="예매 관련 문의하기"
33+
questionLink={questionLink}
34+
subtitle="더 싼 가격으로 미리 하는 사전예매 ~ 12/12(마감일)"
35+
/>
36+
37+
{/* 공연 기본 정보 확인 */}
38+
<InfoSection form={form} />
39+
<S.Line />
40+
41+
<Element name="info">
42+
{/* 예매 정보 입력 */}
43+
<InputSection form={form} />
44+
</Element>
45+
46+
<S.Line />
47+
48+
<Element name="price">
49+
{/* 가격 안내 및 입금 */}
50+
<PriceSection form={form} />
51+
</Element>
52+
53+
<S.Line />
54+
55+
<Element name="confirm">
56+
{/* 최종 확인 및 제출 */}
57+
<ConfirmSection form={form} />
58+
</Element>
59+
60+
<Element name="end">
61+
{errorMsgs.length > 0 && (
62+
<S.ErrorMsgWrapper>
63+
{errorMsgs.map((msg, idx) => (
64+
<S.ErrorMsg key={idx}>{msg}</S.ErrorMsg>
65+
))}
66+
</S.ErrorMsgWrapper>
67+
)}
68+
69+
<S.EndButton
70+
$active={!!isAllValid}
71+
onClick={() => {
72+
if (isAllValid) handleOpen();
73+
}}
74+
>
75+
<S.ButtonText>최종 제출</S.ButtonText>
76+
</S.EndButton>
77+
</Element>
78+
</S.BookingContainer>
79+
80+
{isOpen && (
81+
<SubmitModal
82+
form={form}
83+
onClose={handleOpen}
84+
questionLink={questionLink}
85+
/>
86+
)}
87+
</>
88+
);
89+
};
90+
91+
export default BookingPage;

0 commit comments

Comments
 (0)