Skip to content

Commit afefc18

Browse files
authored
Merge pull request #5 from Snowgent/feature/#3-onboarding
[FEATURE] 온보딩 페이지 구현 #3
2 parents 2a71f1e + c655ab2 commit afefc18

File tree

8 files changed

+220
-2
lines changed

8 files changed

+220
-2
lines changed

src/components/Navigation.tsx

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
const Navigation = () => {
2+
return (
3+
<>
4+
<div className="flex h-16 w-full items-center px-4 shadow-md">
5+
<h1 className="text-2xl font-bold text-[#0D2D84]">Snowgent</h1>
6+
</div>
7+
</>
8+
);
9+
};
10+
11+
export default Navigation;
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
const FormButton = ({ type, onClick }: { type: string; onClick: () => void }) => {
2+
const buttonStyle =
3+
'bg-[#1d4ed8] w-full text-white py-3 rounded-lg text-center font-medium cursor-pointer';
4+
5+
const grayButtonStyle =
6+
'bg-gray-300 w-full text-gray-700 py-3 rounded-lg text-center font-medium cursor-pointer';
7+
8+
const handleClick = () => {
9+
onClick();
10+
};
11+
12+
switch (type) {
13+
case 'next':
14+
return (
15+
<div className={buttonStyle} onClick={handleClick}>
16+
다음
17+
</div>
18+
);
19+
20+
case 'prev':
21+
return (
22+
<div className={grayButtonStyle} onClick={handleClick}>
23+
이전
24+
</div>
25+
);
26+
27+
case 'submit':
28+
return (
29+
<div className={buttonStyle} onClick={handleClick}>
30+
완료
31+
</div>
32+
);
33+
34+
default:
35+
break;
36+
}
37+
};
38+
39+
export default FormButton;
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
const MultiSelect = ({ title }: { title: string }) => {
2+
return (
3+
<div className="flex flex-col gap-6">
4+
{/* 제목 */}
5+
<p className="text-[24px] font-semibold">{title}을 선택하세요</p>
6+
{/* 입력칸 */}
7+
<input type="text" className="rounded-xl border border-gray-200 px-2 py-4 text-[20px]" />
8+
</div>
9+
);
10+
};
11+
12+
export default MultiSelect;
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { useState } from 'react';
2+
3+
const PriceInput = ({ title, placeholder }: { title: string; placeholder?: string }) => {
4+
const [value, setValue] = useState('');
5+
6+
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
7+
// 숫자만 입력 가능하도록
8+
const numericValue = e.target.value.replace(/[^0-9]/g, '');
9+
setValue(numericValue);
10+
};
11+
12+
return (
13+
<div className="flex flex-col gap-6">
14+
{/* 제목 */}
15+
<p className="text-[24px] font-semibold">{title}을 입력해주세요</p>
16+
17+
{/* 입력칸 */}
18+
<div className="relative flex items-center rounded-xl border border-gray-200">
19+
<input
20+
type="text"
21+
value={value}
22+
onChange={handleChange}
23+
placeholder={placeholder || '0'}
24+
className="flex-1 rounded-xl px-4 py-4 text-[20px] outline-none"
25+
/>
26+
<span className="pointer-events-none pr-4 text-[20px] text-gray-500">만원</span>
27+
</div>
28+
</div>
29+
);
30+
};
31+
32+
export default PriceInput;
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
const defaultOptions = [
2+
{ id: 1, name: 'option1' },
3+
{ id: 2, name: 'option2' },
4+
{ id: 3, name: 'option3' },
5+
{ id: 4, name: 'option4' },
6+
{ id: 5, name: 'option5' },
7+
{ id: 6, name: 'option6' },
8+
{ id: 7, name: 'option7' },
9+
];
10+
11+
const SingleSelect = ({ title }: { title: string }) => {
12+
return (
13+
<div className="flex flex-col gap-6">
14+
{/* 제목 */}
15+
<p className="text-[24px] font-semibold">{title} 선택하세요</p>
16+
{/* 단일 선택 버튼 */}
17+
<select className="rounded-xl border border-gray-200 px-2 py-4 text-[20px] outline-none focus:border-gray-200">
18+
<option className="" value="">
19+
선택하세요
20+
</option>
21+
{defaultOptions.map((option) => (
22+
<option key={option.id} value={option.id}>
23+
{option.name}
24+
</option>
25+
))}
26+
</select>
27+
</div>
28+
);
29+
};
30+
31+
export default SingleSelect;
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
const TextInput = ({ title, placeholder }: { title: string; placeholder?: string }) => {
2+
return (
3+
<div className="flex flex-col gap-6">
4+
{/* 제목 */}
5+
<p className="text-[24px] font-semibold">{title} 입력해주세요</p>
6+
{/* 입력칸 */}
7+
<input
8+
type="text"
9+
placeholder={placeholder}
10+
className="rounded-xl border border-gray-200 px-2 py-4 text-[20px] outline-none focus:border-gray-200"
11+
/>
12+
</div>
13+
);
14+
};
15+
16+
export default TextInput;

src/pages/HomePage.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ const HomePage = () => {
66
return (
77
<div className="flex h-full flex-col items-center justify-center gap-8">
88
<img src={logo} alt="Vite logo" className="h-40" />
9-
<h1 className="text-6xl text-[#0D2D84]">Snowgent</h1>
9+
<h1 className="text-6xl font-bold text-[#0D2D84]">Snowgent</h1>
1010
<button
1111
onClick={() => navigate('/onboarding')}
1212
className="cursor-pointer rounded-lg bg-blue-50 px-10 py-5 text-xl font-semibold text-[#0D2D84] hover:bg-blue-100"
Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,82 @@
1+
import { useState } from 'react';
2+
import Navigation from '../../components/Navigation';
3+
import FormButton from '../../components/onboarding/FormButton';
4+
import { useNavigate } from 'react-router-dom';
5+
import TextInput from '../../components/onboarding/TextInput';
6+
import PriceInput from '../../components/onboarding/PriceInput';
7+
import SingleSelect from '../../components/onboarding/SingleSelect';
8+
import MultiSelect from '../../components/onboarding/MultiSelect';
9+
110
const Onboarding = () => {
2-
return <div>Onboarding</div>;
11+
const navigate = useNavigate();
12+
const [currentStep, setCurrentStep] = useState(1);
13+
const totalSteps = 5;
14+
15+
const handlePrev = () => {
16+
if (currentStep > 1) {
17+
setCurrentStep(currentStep - 1);
18+
}
19+
};
20+
21+
const handleNext = () => {
22+
if (currentStep < totalSteps) {
23+
setCurrentStep(currentStep + 1);
24+
}
25+
};
26+
27+
const handleComplete = () => {
28+
navigate('/chat');
29+
};
30+
31+
const renderStepContent = () => {
32+
switch (currentStep) {
33+
case 1:
34+
return <SingleSelect key="step1" title="업종을" />;
35+
case 2:
36+
return <TextInput key="step2" title="업체명을" placeholder="" />;
37+
case 3:
38+
return <TextInput key="step3" title="위치(시군구)를" placeholder="" />;
39+
case 4:
40+
return <PriceInput key="step4" title="평균 월매출" placeholder="숫자" />;
41+
case 5:
42+
return <PriceInput key="step5" title="목표 월매출" placeholder="숫자" />;
43+
default:
44+
return <MultiSelect key="default" title="원하는 타겟층" />;
45+
}
46+
};
47+
48+
return (
49+
<div className="flex h-full flex-col">
50+
<Navigation />
51+
<div className="flex flex-1 flex-col p-5">
52+
<div className="mb-6">
53+
<div className="mb-2 flex justify-between text-sm text-gray-600">
54+
<span>단계 {currentStep}</span>
55+
<span>
56+
{currentStep} / {totalSteps}
57+
</span>
58+
</div>
59+
<div className="h-2 w-full rounded-full bg-gray-200">
60+
<div
61+
className="h-2 rounded-full bg-blue-500 transition-all duration-300"
62+
style={{ width: `${(currentStep / totalSteps) * 100}%` }}
63+
/>
64+
</div>
65+
</div>
66+
67+
<div className="flex-1 overflow-y-auto">{renderStepContent()}</div>
68+
69+
<div className="mt-6 flex gap-3">
70+
{currentStep > 1 && <FormButton type="prev" onClick={handlePrev} />}
71+
{currentStep === totalSteps ? (
72+
<FormButton type="submit" onClick={handleComplete} />
73+
) : (
74+
<FormButton type="next" onClick={handleNext} />
75+
)}
76+
</div>
77+
</div>
78+
</div>
79+
);
380
};
481

582
export default Onboarding;

0 commit comments

Comments
 (0)