From b5bb219fae6ac41b9cfd8992977355cc68f7bffa Mon Sep 17 00:00:00 2001 From: Savien/Woo Jun Han <49388937+MrMirror21@users.noreply.github.com> Date: Sat, 19 Oct 2024 03:07:08 +0900 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20feat:=20funnel=20=ED=8C=A8=ED=84=B4?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80=20=EC=A0=95=EB=B3=B4=20=EC=9E=85=EB=A0=A5?= =?UTF-8?q?=20step=201=20=EA=B5=AC=ED=98=84=20#5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Information/InformationStep.tsx | 220 +++++++++++++++--- src/pages/Information/InformationPage.tsx | 14 +- src/utils/information.ts | 2 +- 3 files changed, 195 insertions(+), 41 deletions(-) diff --git a/src/components/Information/InformationStep.tsx b/src/components/Information/InformationStep.tsx index d7f598e1..a1df46da 100644 --- a/src/components/Information/InformationStep.tsx +++ b/src/components/Information/InformationStep.tsx @@ -1,8 +1,15 @@ import Input from '../Common/Input'; import { initialUserInfo, UserInfoRequestBody } from '@/types/api/users'; import { UserInfo } from '../../types/api/users'; -import { useState } from 'react'; -import { isValidFirstName } from '@/utils/information'; +import { useEffect, useState } from 'react'; +import { + formatPhoneNumber, + isValidName, + isValidPhoneNumber, +} from '@/utils/information'; +import Dropdown from '../Common/Dropdown'; +import { country, gender, phone, visa } from '@/constants/information'; +import RadioButton from './RadioButton'; const InformationStep = ({ userInfo, @@ -11,46 +18,189 @@ const InformationStep = ({ userInfo: UserInfoRequestBody; onNext: (newInfo: UserInfoRequestBody) => void; }) => { + // 현재 step내에서 입력받는 정보를 따로 관리할 state, 추후 다음 step으로 넘어갈 때 funnel 관리 페이지의 state로 통합된다. const [newUserInfo, setNewUserInfo] = useState(initialUserInfo); - const [invalidList, setInvalidList] = useState({ - firstName: false, - lastName: false, + // 버튼 활성화 여부를 위한 플래그 + const [isInvalid, setIsInvalid] = useState(true); + // 세 부분으로 나누어 입력받는 방식을 위해 전화번호만 별도의 state로 분리, 추후 유효성 검사 단에서 통합 + const [phoneNum, setPhoneNum] = useState({ + start: '', + middle: '', + end: '', }); - const checkFirstNameInvalid = () => { - if ( - newUserInfo.first_name === undefined || - newUserInfo.first_name == '' || - !isValidFirstName(newUserInfo.first_name) - ) { - setInvalidList({ ...invalidList, firstName: true }); + /* 정보 입력 시마다 유효성을 검사해 모든 값이 유효하면 버튼이 활성화 */ + useEffect(() => { + const { first_name, last_name, birth, nationality, visa } = newUserInfo; + + const isFormValid = + isValidName(String(first_name)) && + isValidName(String(last_name)) && + birth !== '' && + nationality !== '' && + visa !== '' && + isValidPhoneNumber(phoneNum); + + if (isFormValid) { + setNewUserInfo((prevInfo) => ({ + ...prevInfo, + phone_number: formatPhoneNumber(phoneNum), + })); } - }; - const checkInvalidAll = () => { - checkFirstNameInvalid(); - if (Object.values(invalidList).every((value) => value === true)) - onNext({ ...userInfo, user_info: newUserInfo }); - }; + setIsInvalid(!isFormValid); + }, [newUserInfo, phoneNum]); + return ( -
-
-
First Name
- - setInvalidList({ ...invalidList, firstName: false }) - } - onChange={(value) => - setNewUserInfo({ ...newUserInfo, first_name: value }) - } - canDelete={false} - isInvalid={invalidList.firstName} - /> +
+
+ {/* 이름 작성 */} +
+
+ First Name +
+ + setNewUserInfo({ ...newUserInfo, first_name: value }) + } + canDelete={false} + /> +
+ {/* 성 작성 */} +
+
+ Last Name +
+ + setNewUserInfo({ ...newUserInfo, last_name: value }) + } + canDelete={false} + /> +
+
+
+ Gender +
+
+ {gender.map((gender) => ( + + setNewUserInfo({ + ...newUserInfo, + gender: value.toUpperCase(), + }) + } + isOn={gender.toUpperCase() === newUserInfo.gender} + /> + ))} +
+
+ {/* 생년월일 선택 */} +
+
+ Date of birth +
+ + setNewUserInfo({ ...newUserInfo, birth: value }) + } + /> +
+ {/* 국적 선택 */} +
+
+ Nationality +
+ + setNewUserInfo({ ...newUserInfo, nationality: value }) + } + /> +
+ {/* 비자 선택 */} +
+
+ Visa Status +
+ + setNewUserInfo({ ...newUserInfo, visa: value }) + } + /> +
+ {/* 전화번호 선택, dropdown으로 앞 번호를, 중간 번호와 뒷 번호는 각각 input으로 입력 받음 */} +
+
+ Telephone No. +
+
+
+ setPhoneNum({ ...phoneNum, start: value })} + /> +
+ setPhoneNum({ ...phoneNum, middle: value })} + canDelete={false} + /> + setPhoneNum({ ...phoneNum, end: value })} + canDelete={false} + /> +
+
+
+ {/* 정보 입력 시마다 유효성을 검사해 모든 값이 유효하면 버튼이 활성화 */} +
+
+ {/* TODO : 버튼 컴포넌트 들어오면 변경 */} + {isInvalid ? ( + + ) : ( + + )} +
-
); }; diff --git a/src/pages/Information/InformationPage.tsx b/src/pages/Information/InformationPage.tsx index 0e7782f6..87b8be7e 100644 --- a/src/pages/Information/InformationPage.tsx +++ b/src/pages/Information/InformationPage.tsx @@ -6,19 +6,21 @@ import { } from '@/types/api/users'; import { useState } from 'react'; +// funnel 패턴으로 구현한 추가정보 입력 페이지. 총 3 step으로 구성 const InformationPage = () => { const [currentStep, setCurrentStep] = useState(1); const [userInfo, setUserInfo] = useState( initialUserInfoRequestBody, ); + // 다음 step으로 넘어갈 때 호출되며, 각 step에서 입력한 정보를 userInfo에 저장, 다음 step으로 이동한다. const handleNext = (newInfo: UserInfoRequestBody) => { setUserInfo(newInfo); setCurrentStep((prev) => prev + 1); }; return ( -
-
+
+
setCurrentStep(currentStep + 1)} @@ -27,9 +29,11 @@ const InformationPage = () => {
- {currentStep === 1 && ( - - )} +
+ {currentStep === 1 && ( + + )} +
); }; diff --git a/src/utils/information.ts b/src/utils/information.ts index 57360c32..85da8ba7 100644 --- a/src/utils/information.ts +++ b/src/utils/information.ts @@ -35,4 +35,4 @@ export const formatPhoneNumber = (phone: { start: string; middle: string; end: string; -}) => `${phone.start}-${phone.middle}-${phone.end}`; \ No newline at end of file +}) => `${phone.start}-${phone.middle}-${phone.end}`;