Skip to content

Commit b45b4f5

Browse files
authored
Merge pull request #60 from Soohyuniii/feature/setting-info-페이지-개발
feat: select info 페이지 개발 작업
2 parents c07908b + 1807a16 commit b45b4f5

File tree

6 files changed

+310
-15
lines changed

6 files changed

+310
-15
lines changed

public/icon/arrow_left.svg

Lines changed: 3 additions & 0 deletions
Loading

src/components/Header.tsx

Lines changed: 38 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,44 @@
1-
const Header = () => {
1+
import { useNavigate } from 'react-router-dom';
2+
3+
type HeaderProps = {
4+
title: string;
5+
showPreviousIcon?: boolean;
6+
showShareIcon?: boolean;
7+
};
8+
9+
const Header = ({ title, showPreviousIcon = true, showShareIcon = true }: HeaderProps) => {
10+
const navigate = useNavigate();
11+
12+
const handleGoBack = () => {
13+
navigate(-1);
14+
};
15+
216
return (
3-
<div className="flex flex-row bg-white border border-gray-100 items-center justify-center w-[500px] h-[56px] relative">
17+
<div className="flex flex-row bg-white border border-gray-100 items-center justify-center w-full h-[56px] relative">
18+
{showPreviousIcon && (
19+
<img
20+
src="/public/icon/arrow_left.svg"
21+
alt="Go To Back"
22+
className="absolute left-[18.77px] cursor-pointer"
23+
width={9.87}
24+
height={16}
25+
onClick={handleGoBack}
26+
/>
27+
)}
28+
429
<h1 className="text-[18px] font-bold text-gray-900 absolute left-1/2 transform -translate-x-1/2">
5-
상대방 정보선택
30+
{title}
631
</h1>
7-
<img
8-
src="/public/icon/share.svg"
9-
alt="Share"
10-
className="absolute right-[20px] cursor-pointer"
11-
width={16}
12-
height={16}
13-
/>
32+
33+
{showShareIcon && (
34+
<img
35+
src="/public/icon/share.svg"
36+
alt="Share"
37+
className="absolute right-[20px] cursor-pointer"
38+
width={16}
39+
height={16}
40+
/>
41+
)}
1442
</div>
1543
);
1644
};

src/components/button/FormButton.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ const FormButton = ({
1616
const baseStyles = "flex justify-center items-center rounded-lg transition";
1717

1818
const sizeStyles =
19-
size === "sm" ? "min-w-[68px] w-auto h-[40px]" : "w-[72px] h-[72px]";
19+
size === "sm" ? "min-w-[68px] w-auto h-[40px]" : "w-[70px] h-[72px]";
2020

2121
const fontSize =
2222
size === "md"

src/index.css

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ button {
3737
}
3838

3939
@keyframes pulse-custom {
40-
4140
0%,
4241
100% {
4342
transform: scale(1);
@@ -69,4 +68,4 @@ button {
6968
* {
7069
@apply transition-transform duration-200 ease-in-out;
7170
}
72-
}
71+
}

src/pages/Home.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ const Home = () => {
1010
const navigate = useNavigate();
1111

1212
const handleNavigate = () => {
13-
navigate("/setting-info");
13+
const mode = "fastFriend";
14+
navigate("/select-info", { state: mode });
1415
};
1516

1617
return (

src/pages/SelectInfo.tsx

Lines changed: 265 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,269 @@
1+
import { useState } from "react";
2+
import { useLocation } from "react-router-dom";
3+
import FormButton from "@/components/button/FormButton";
4+
import Header from "@/components/Header";
5+
16
const SelectInfo = () => {
2-
return <div>빠른 대화 설정 및 친구 저장 페이지 </div>;
7+
const location = useLocation();
8+
const mode = location.state; // mode: fastFriend, virtualFriend 두 종류 존재
9+
const isNameRequired = mode === "virtualFriend";
10+
const headerTitle = mode === 'fastFriend' ? "상대방 정보선택" : "친구 저장하기";
11+
12+
const [selectedMBTI, setSelectedMBTI] = useState<{
13+
[key: string]: string | null;
14+
}>({
15+
E: null,
16+
N: null,
17+
F: null,
18+
P: null
19+
});
20+
const [name, setName] = useState<string>("");
21+
const [age, setAge] = useState<string | null>(null);
22+
const [gender, setGender] = useState<string | null>(null);
23+
const [relationship, setRelationship] = useState<string | null>(null);
24+
const [interest, setInterest] = useState<string[]>([]);
25+
26+
const mbtiOptions = ["E", "N", "F", "P", "I", "S", "T", "J"];
27+
const ageOptions = ["10대", "20대", "30대 이상"];
28+
const genderOptions = ["여자", "남자"];
29+
const relationshipOptions = [
30+
"부모",
31+
"자녀",
32+
"친구",
33+
"짝사랑",
34+
"이별",
35+
"연인",
36+
"선생님",
37+
"직장동료"
38+
];
39+
const interestOptions = [
40+
"연애",
41+
"결혼",
42+
"취미",
43+
"사회생활",
44+
"여행",
45+
"운동",
46+
"심리",
47+
"뷰티/패션",
48+
"음식",
49+
"인간관계"
50+
];
51+
52+
const handleMBTISelect = (option: string) => {
53+
const group = getMBTIgroup(option);
54+
setSelectedMBTI((prevState) => ({
55+
...prevState,
56+
[group]: prevState[group] === option ? null : option
57+
}));
58+
};
59+
60+
const getMBTIgroup = (option: string) => {
61+
if (["E", "I"].includes(option)) return "E";
62+
if (["N", "S"].includes(option)) return "N";
63+
if (["F", "T"].includes(option)) return "F";
64+
if (["P", "J"].includes(option)) return "P";
65+
return "";
66+
};
67+
68+
const isMBTISelected = (option: string) => {
69+
const group = getMBTIgroup(option);
70+
return selectedMBTI[group] === option;
71+
};
72+
73+
const handleInterestSelect = (option: string) => {
74+
if (interest.includes(option)) {
75+
setInterest((prevInterests) =>
76+
prevInterests.filter((item) => item !== option)
77+
);
78+
} else {
79+
setInterest((prevInterests) => [...prevInterests, option]);
80+
}
81+
};
82+
83+
const isInterestSelected = (option: string) => {
84+
return interest.includes(option);
85+
};
86+
87+
const handleNameChange = (e: React.ChangeEvent<HTMLInputElement>) => {
88+
const value = e.target.value;
89+
if (value.length > 6) {
90+
const truncatedValue = value.substring(0, 6);
91+
setName(truncatedValue);
92+
} else {
93+
setName(value);
94+
}
95+
};
96+
97+
const handleButtonClick = (value: string, setter: (val: string | null) => void, state: string | null) => {
98+
setter(state === value ? null : value);
99+
};
100+
101+
const handleStartChat = () => {
102+
const isEGroupSelected = selectedMBTI.E !== null;
103+
const isNGroupSelected = selectedMBTI.N !== null;
104+
const isFGroupSelected = selectedMBTI.F !== null;
105+
const isPGroupSelected = selectedMBTI.P !== null;
106+
107+
// 선택한 MBTI값이 하나라도 부재할 경우
108+
if (
109+
!isEGroupSelected ||
110+
!isNGroupSelected ||
111+
!isFGroupSelected ||
112+
!isPGroupSelected
113+
) {
114+
return alert("각 MBTI 그룹에서 하나의 값을 선택해주세요."); // TODO: Toast popup UI 완료 시 반영 예정
115+
}
116+
117+
// 이름 필수 && 이름이 입력되지 않았을 경우
118+
if (isNameRequired && !name) {
119+
return alert("이름을 입력해주세요."); // TODO: Toast popup UI 완료 시 반영 예정
120+
}
121+
122+
alert("대화 시작 API 연결하기."); // TODO: API 연동 예정
123+
};
124+
125+
return (
126+
<div className="flex w-[360px] flex-col bg-white md:w-[375px] lg:w-[500px]">
127+
<Header title={headerTitle} />
128+
129+
<div className="w-[320px] mx-auto">
130+
{/* MBTI 선택 */}
131+
<div className="mb-[40px] pt-[48px]">
132+
<p className="font-bold text-[20px] leading-[30px] tracking-[-0.01em]">
133+
상대방의 MBTI를 선택하면 <br />
134+
대화를 시뮬레이션 해볼 수 있어요
135+
</p>
136+
137+
<div className="pt-[24px] grid grid-cols-4 gap-[24px_13px]">
138+
{mbtiOptions.map((option) => (
139+
<FormButton
140+
key={option}
141+
size="md"
142+
selected={isMBTISelected(option)}
143+
onClick={() => handleMBTISelect(option)}
144+
>
145+
{option}
146+
</FormButton>
147+
))}
148+
</div>
149+
</div>
150+
</div>
151+
152+
<div className="w-full h-[8px] bg-[#EEF0F3]" />
153+
154+
<div className="w-[320px] mx-auto">
155+
<div className="pt-[40px]">
156+
<p className="font-bold text-[20px] leading-[30px] tracking-[-0.01em]">
157+
정보 추가 입력
158+
</p>
159+
160+
{/* 이름 입력 */}
161+
<div className="pt-[32px] flex flex-col gap-2">
162+
<label
163+
htmlFor="name"
164+
className="font-bold text-2lg leading-[24px] tracking-[0em] text-gray-600"
165+
>
166+
이름
167+
{isNameRequired && <span className="text-red-500">*</span>}
168+
</label>
169+
<input
170+
id="name"
171+
type="text"
172+
value={name}
173+
onChange={handleNameChange}
174+
className="w-full h-[56px] px-4 border border-gray-200 rounded-lg focus:outline-none focus:ring-primary-light focus:border-primary-light"
175+
placeholder="이름"
176+
maxLength={6}
177+
/>
178+
</div>
179+
180+
{/* 나이 선택 */}
181+
<div className="pt-[20px] pb-[12px]">
182+
<p className="font-bold text-2lg leading-[24px] tracking-[0em] text-gray-600">
183+
나이
184+
</p>
185+
<div className="pt-[16px] flex gap-[16px]">
186+
{ageOptions.map((option) => (
187+
<FormButton
188+
key={option}
189+
size="sm"
190+
selected={age === option}
191+
onClick={() => handleButtonClick(option, setAge, age)}
192+
>
193+
{option}
194+
</FormButton>
195+
))}
196+
</div>
197+
</div>
198+
199+
{/* 성별 선택 */}
200+
<div className="pt-[20px] pb-[12px]">
201+
<p className="font-bold text-2lg leading-[24px] tracking-[0em] text-gray-600">
202+
성별
203+
</p>
204+
<div className="pt-[16px] flex gap-[16px]">
205+
{genderOptions.map((option) => (
206+
<FormButton
207+
key={option}
208+
size="sm"
209+
selected={gender === option}
210+
onClick={() => handleButtonClick(option, setGender, gender)}
211+
>
212+
{option}
213+
</FormButton>
214+
))}
215+
</div>
216+
</div>
217+
218+
{/* 관계 선택 */}
219+
<div className="pt-[20px] pb-[20px]">
220+
<p className="font-bold text-2lg leading-[24px] tracking-[0em] text-gray-600">
221+
상대방과 나의 관계
222+
</p>
223+
<div className="pt-[16px] grid grid-cols-4 gap-[16px]">
224+
{relationshipOptions.map((option) => (
225+
<FormButton
226+
key={option}
227+
size="sm"
228+
selected={relationship === option}
229+
onClick={() => handleButtonClick(option, setRelationship, relationship)}
230+
>
231+
{option}
232+
</FormButton>
233+
))}
234+
</div>
235+
</div>
236+
237+
{/* 관심사 선택 */}
238+
<div className="pt-[20px] pb-[26px]">
239+
<p className="font-bold text-2lg leading-[24px] tracking-[0em] text-gray-600">
240+
관심사
241+
</p>
242+
<div className="pt-[16px] grid grid-cols-4 gap-[16px]">
243+
{interestOptions.map((option) => (
244+
<FormButton
245+
key={option}
246+
size="sm"
247+
selected={isInterestSelected(option)}
248+
onClick={() => handleInterestSelect(option)}
249+
>
250+
{option}
251+
</FormButton>
252+
))}
253+
</div>
254+
</div>
255+
</div>
256+
257+
{/* 대화 시작 버튼 */}
258+
<button
259+
className="w-full my-[22px] h-[60px] bg-primary-normal text-white rounded-[8px] font-bold"
260+
onClick={handleStartChat}
261+
>
262+
대화 시작하기
263+
</button>
264+
</div>
265+
</div>
266+
);
3267
};
4268

5269
export default SelectInfo;

0 commit comments

Comments
 (0)