From e34b364e0b8c55ca1781910255535ec44fd5fbcc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B3=A0=EA=B0=80=ED=98=95?= <101045330+aazkgh@users.noreply.github.com> Date: Mon, 16 Sep 2024 23:54:41 +0900 Subject: [PATCH] =?UTF-8?q?Feat:=20=ED=9A=8C=EC=9B=90=20=EA=B0=80=EC=9E=85?= =?UTF-8?q?=20=ED=94=8C=EB=A1=9C=EC=9A=B0=20=ED=8D=BC=EB=B8=94=EB=A6=AC?= =?UTF-8?q?=EC=8B=B1=EC=9D=84=20=ED=86=B5=ED=95=9C=20=EA=B3=B5=ED=86=B5=20?= =?UTF-8?q?=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EC=83=9D=EC=84=B1=20?= =?UTF-8?q?=EB=B0=8F=20=EC=88=98=EC=A0=95=20(#35)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 회원 가입 라우팅 설정 * feat: 공통 컴포넌트 반영 * feat:바텀 버튼 공통 컴포넌트 구현 * refactor: 마이페이지에 공통 버튼 컴포넌트 적용 * feat: Header 공통 컴포넌트 생성 * feat: 회원 가입 플로우에 헤더 적용 * chore: 오타 수정 * refactor: 선택지 컴포넌트 분리 * chore: 변수명 변경 * refactor: 마이페이지 공통 컴포넌트 적용 및 상태 리팩토링 * refactor: 공통 컴포넌트 지역 선택 드롭다운 리팩토링 * feat:사용자의 클릭 영억에 따른 기능 추가 * refacotr:중복되는 코드 단축 * feat:네비게이션 설정 * feat:여행자 유형 설정 상태 관리 * feat: 회원가입 플로우 여행자 유형 설정 * chore: 빼먹은 코드 추가 및 불필요한 코드 삭제 * design: 바텀 버튼 레이아웃 수정 * design: 디자인 요소 수정 * feat:context api 생성 * feat: 지역 상태 저장 * feat: 여행자 유형 상태 관리 * fix: 여행자 유형 상태 관리 수정 * feat: 지역 입력 추상화 --- src/Router.tsx | 2 + src/components/BottomButton.tsx | 44 +++++ src/components/Header.tsx | 55 ++++++ src/components/SelectRegion.tsx | 198 +++++++++++++------ src/components/SelectTravelerType.tsx | 157 +++------------ src/components/TravelerType.tsx | 63 ++++++ src/views/Login/components/Region.tsx | 60 ++++++ src/views/Login/components/SignUpContext.tsx | 40 ++++ src/views/Login/components/UserType.tsx | 65 ++++++ src/views/Login/pages/SignUpPage.tsx | 48 +++++ src/views/Mypage/components/Favorite.tsx | 15 +- src/views/Mypage/components/Main.tsx | 34 +--- src/views/Mypage/components/MypageHeader.tsx | 50 ----- src/views/Mypage/components/PersonalInfo.tsx | 46 +---- src/views/Mypage/constants/text.ts | 5 + src/views/Mypage/pages/Mypage.tsx | 76 +++---- 16 files changed, 611 insertions(+), 347 deletions(-) create mode 100644 src/components/BottomButton.tsx create mode 100644 src/components/Header.tsx create mode 100644 src/components/TravelerType.tsx create mode 100644 src/views/Login/components/Region.tsx create mode 100644 src/views/Login/components/SignUpContext.tsx create mode 100644 src/views/Login/components/UserType.tsx create mode 100644 src/views/Login/pages/SignUpPage.tsx delete mode 100644 src/views/Mypage/components/MypageHeader.tsx create mode 100644 src/views/Mypage/constants/text.ts diff --git a/src/Router.tsx b/src/Router.tsx index 4bd2ca4..5bf95e0 100644 --- a/src/Router.tsx +++ b/src/Router.tsx @@ -4,6 +4,7 @@ import Settings from './components/Settings'; import DetailPage from './views/Detail/pages/DetailPage'; import ErrorReportPage from './views/ErrorReport/pages/ErrorReportPage'; import LoginCallBack from './views/Login/components/LoginCallBack'; +import SignUpPage from './views/Login/pages/SignUpPage'; import MainPage from './views/Main/pages/MainPage'; import Mypage from './views/Mypage/pages/Mypage'; import SearchPage from './views/Search/pages/SearchPage'; @@ -16,6 +17,7 @@ const router = createBrowserRouter([ { path: '/', element: }, { path: '/auth/callback', element: }, { path: '/detail', element: }, + { path: '/sign-up', element: }, ], }, { path: '/detail', element: }, diff --git a/src/components/BottomButton.tsx b/src/components/BottomButton.tsx new file mode 100644 index 0000000..cf7d7bc --- /dev/null +++ b/src/components/BottomButton.tsx @@ -0,0 +1,44 @@ +import { css } from '@emotion/react'; + +import { COLORS, FONTS } from '@/styles/constants'; + +interface BottomButtonProps { + text: string; + clickedFn: () => void; + disabled?: boolean; +} + +const BottomButton = ({ text, clickedFn, disabled }: BottomButtonProps) => { + return ( +
+ +
+ ); +}; + +export default BottomButton; + +const bottomBtnLayout = css` + position: fixed; + bottom: 0; + + width: 100%; + padding: 1.2rem 2rem; +`; + +const bottomBtn = (disabled?: boolean) => css` + width: 100%; + padding: 1.7rem 0; + border-radius: 1.2rem; + + background-color: ${disabled ? COLORS.gray1 : COLORS.brand1}; + + color: ${disabled ? COLORS.gray4 : COLORS.white}; + ${FONTS.Body2}; +`; diff --git a/src/components/Header.tsx b/src/components/Header.tsx new file mode 100644 index 0000000..26f1239 --- /dev/null +++ b/src/components/Header.tsx @@ -0,0 +1,55 @@ +import { css } from '@emotion/react'; + +import { COLORS, FONTS } from '@/styles/constants'; + +interface HeaderProps { + title?: string; + leftIcon?: React.FunctionComponent< + React.ComponentProps<'svg'> & { title?: string } + >; + leftFn?: () => void; + rightIcon?: React.FunctionComponent< + React.ComponentProps<'svg'> & { title?: string } + >; + rightFn?: () => void; +} + +const Header = ({ + title, + leftIcon: LeftIcon, + leftFn, + rightIcon: RightIcon, + rightFn, +}: HeaderProps) => { + return ( +
+
+ {LeftIcon && } + {title} +
+ {RightIcon && } +
+ ); +}; + +export default Header; + +const header = css` + display: flex; + justify-content: space-between; + align-items: center; + + width: 100%; + height: 5rem; + padding: 1.2rem 2rem; +`; + +const leftSide = css` + display: flex; + gap: 1rem; + align-items: center; + + color: ${COLORS.brand1}; + + ${FONTS.H4}; +`; diff --git a/src/components/SelectRegion.tsx b/src/components/SelectRegion.tsx index bfe8e04..c2f6dae 100644 --- a/src/components/SelectRegion.tsx +++ b/src/components/SelectRegion.tsx @@ -1,74 +1,125 @@ import { css } from '@emotion/react'; -import { useState } from 'react'; +import { useEffect, useRef, useState } from 'react'; +import { ArrowToggleClosed, ArrowToggleOpen } from '@/assets/icon'; import { REGION_LIST } from '@/constants/REGION_LIST'; import { COLORS, FONTS } from '@/styles/constants'; -const SelectRegion = () => { +export type Region = { + city: string; + town: string; +}; + +interface SelectRegionProps { + region: Region; + setRegion: React.Dispatch>; +} + +const SelectRegion = ({ region, setRegion }: SelectRegionProps) => { const [locationList, setLocationList] = useState([]); const [inputState, setInputState] = useState({ city: false, town: false }); - const onChangeRegion = (selected: string) => { - const town = REGION_LIST.find((item) => item.city === selected)?.town || []; - setLocationList(town); - }; - - const onChangeInput = (input: string) => { - if (input === 'city') { - setInputState((prev) => { + const cityRef = useRef(null); + const townRef = useRef(null); + + useEffect(() => { + const handleFocus = (e: MouseEvent) => { + if (cityRef.current && !cityRef.current.contains(e.target as Node)) + setInputState((prev) => { + return { city: false, town: prev.town }; + }); + if (townRef.current && !townRef.current.contains(e.target as Node)) + setInputState((prev) => { + return { city: prev.city, town: false }; + }); + }; + + document.addEventListener('mouseup', handleFocus); + + return () => { + document.removeEventListener('mouseup', handleFocus); + }; + }, []); + + const onClickDropDown = (inputType: 'city' | 'town', regionName: string) => { + if (inputType === 'city') { + setRegion(() => { return { - ...prev, - city: true, + city: regionName, + town: '', }; }); - } else if (input === 'town') { - setInputState((prev) => { + + const town = + REGION_LIST.find((item) => item.city === regionName)?.town || []; + setLocationList(town); + } else if (inputType === 'town') { + setRegion((prev) => { return { ...prev, - town: true, + town: regionName, }; }); } + setInputState(() => { + return { city: false, town: false }; + }); }; + const renderDropdown = (items: string[], onClick: (item: string) => void) => ( +
+
    + {items.map((item) => ( +
  • onClick(item)}> + +
  • + ))} +
+
+ ); + return (
  • 지역*
    -
    - + + {inputState.city ? : } +
    + {inputState.city && + renderDropdown( + REGION_LIST.map((item) => item.city), + (item) => onClickDropDown('city', item), + )}
    - -
    - +
    +
    { + setInputState((prev) => { + return { city: false, town: !prev.town }; + }); + }}> + + {region.city && inputState.town ? ( + + ) : ( + + )} +
    + {region.city && + inputState.town && + renderDropdown(locationList, (item) => + onClickDropDown('town', item), + )}
  • @@ -79,12 +130,20 @@ export default SelectRegion; const formItem = css` display: flex; + flex: 1; + gap: 1.2rem; flex-direction: column; width: 100%; + overflow-y: hidden; + ${FONTS.Body2}; + + & input { + border: none; + } `; const title = css` @@ -93,29 +152,50 @@ const title = css` const multiInputSection = css` display: flex; + gap: 1.2rem; justify-content: space-between; + + overflow-y: hidden; + + & > div { + display: flex; + gap: 0.8rem; + flex-direction: column; + + flex: 1; + } `; -const inputDefault = css` +const inputBox = (initial: boolean) => css` + display: flex; + justify-content: space-between; + align-items: center; + padding: 1.6rem; border: 1px solid ${COLORS.gray3}; border-radius: 1rem; - color: ${COLORS.gray9}; + color: ${initial ? COLORS.gray4 : COLORS.gray9}; `; -const region = css` - ${inputDefault}; - width: 48%; -`; +const scrollBox = css` + border: 1px solid ${COLORS.gray3}; + border-radius: 1rem; + flex: 1; -const selectTab = css` - width: 100%; - border: none; - outline: none; + overflow-y: scroll; `; -const beforeSelectTab = css` - ${selectTab}; - color: ${COLORS.gray4}; +const dropDownBox = css` + display: flex; + gap: 0.8rem; + flex-direction: column; + + padding: 1.6rem; + + color: ${COLORS.gray9}; + + & > li { + padding: 0.6rem; + } `; diff --git a/src/components/SelectTravelerType.tsx b/src/components/SelectTravelerType.tsx index f3185e6..2e69a7c 100644 --- a/src/components/SelectTravelerType.tsx +++ b/src/components/SelectTravelerType.tsx @@ -1,5 +1,4 @@ import { css } from '@emotion/react'; -import { useState } from 'react'; import { BlindTypeIcon, @@ -7,34 +6,30 @@ import { CheckFilledIcon, HearingTypeIcon, InfantTypeIcon, - MapMonoGrayIcon, NoneTypeIcon, PhysicalTypeIcon, } from '@/assets/icon'; import { COLORS, FONTS } from '@/styles/constants'; -import MypageHeader from '@/views/Mypage/components/MypageHeader'; -import { currentTabType } from '@/views/Mypage/pages/Mypage'; interface SelectTravelerTypeProps { - handleSetCurrentTab?: (clicked: currentTabType) => void; - page: string; - children: string; + currentTravelerType: string[]; + setTravelerType: React.Dispatch>; } -const TYPE_LIST = [ - { icon: , text: '지체장애인' }, - { icon: , text: '시각장애인' }, - { icon: , text: '청각장애인' }, - { icon: , text: '영유아가족' }, - { icon: , text: '해당되지 않아요' }, -]; - -const SelectTravelerType = (props: SelectTravelerTypeProps) => { - const { handleSetCurrentTab, page, children } = props; - const [seletedType, setSelectedType] = useState([]); +const SelectTravelerType = ({ + currentTravelerType, + setTravelerType, +}: SelectTravelerTypeProps) => { + const TYPE_LIST = [ + { icon: , text: '지체장애인' }, + { icon: , text: '시각장애인' }, + { icon: , text: '청각장애인' }, + { icon: , text: '영유아가족' }, + { icon: , text: '해당되지 않아요' }, + ]; const handleSetSelectedType = (text: string) => { - setSelectedType((prev) => { + setTravelerType((prev) => { // 해당되지 않아요 선택 시, 다른 옵션 해제 or 재선택 시, 해당되지 않아요 또한 선택 해제 if (text === '해당되지 않아요') { return prev.includes(text) ? [] : [text]; @@ -53,91 +48,30 @@ const SelectTravelerType = (props: SelectTravelerTypeProps) => { }; return ( - <> - {page === 'mypage' ? ( - - ) : null} - ; -
    -
    -

    - 여행자 유형 - 을
    - 모두 선택해주세요 -

    -

    다중선택 가능

    - -
      - {TYPE_LIST.map((item) => ( -
    • handleSetSelectedType(item.text)}> -
      - {item.icon} - {item.text} -
      - - {seletedType.includes(item.text) ? ( - - ) : ( - - )} -
    • - ))} -
    -
    - -
    -
    - -

    여행자 유형은 장소 추천과 리뷰 필터링에 반영돼요!

    +
      + {TYPE_LIST.map((item) => ( +
    • handleSetSelectedType(item.text)}> +
      + {item.icon} + {item.text}
      - -
    -
    - + {currentTravelerType.includes(item.text) ? ( + + ) : ( + + )} + + ))} + ); }; export default SelectTravelerType; -const contentContainer = css` - display: flex; - justify-content: space-between; - flex-direction: column; - - height: calc(100dvh - 4.8rem); -`; - -const mainText = css` - margin-top: 1.2rem; - - color: ${COLORS.brand1}; - - ${FONTS.H2}; -`; - -const highlight = css` - display: inline; - - box-shadow: inset 0 -1.5rem 0 ${COLORS.brand2}; -`; - -const subText = css` - padding: 0.9rem 0; - - color: ${COLORS.gray4}; - text-align: end; - ${FONTS.Body3}; -`; - const list = css` display: flex; flex-direction: column; @@ -159,32 +93,3 @@ const itemText = css` display: flex; gap: 1.2rem; `; - -const explaination = css` - display: flex; - gap: 0.8rem; - align-items: center; - - width: calc(100% + 4rem); - padding: 1.2rem 2rem; - margin-left: -2rem; - - background-color: ${COLORS.gray0}; - - color: ${COLORS.gray9}; - ${FONTS.Body3}; - font-weight: 400; -`; - -const submitBtn = css` - width: 100%; - padding: 1.7rem 15.35rem; - margin: 1.2rem 0; - border-radius: 1.2rem; - - background-color: ${COLORS.brand1}; - - color: ${COLORS.white}; - - ${FONTS.Body2}; -`; diff --git a/src/components/TravelerType.tsx b/src/components/TravelerType.tsx new file mode 100644 index 0000000..a86dfab --- /dev/null +++ b/src/components/TravelerType.tsx @@ -0,0 +1,63 @@ +import { css } from '@emotion/react'; + +import { MapMonoGrayIcon } from '@/assets/icon'; +import SelectTravelerType from '@/components/SelectTravelerType'; +import { COLORS, FONTS } from '@/styles/constants'; + +interface TravelerTypeProps { + travelerType: string[]; + setTravelerType: React.Dispatch>; +} + +const TravelerType = ({ travelerType, setTravelerType }: TravelerTypeProps) => { + return ( +
    +
    +

    다중선택 가능

    + +
    + +
    + +

    여행자 유형은 장소 추천과 리뷰 필터링에 반영돼요!

    +
    +
    + ); +}; + +export default TravelerType; + +const contentContainer = css` + display: flex; + justify-content: space-between; + flex-direction: column; + + flex: 1; +`; + +const subText = css` + padding: 0.9rem 0; + + color: ${COLORS.gray4}; + text-align: end; + ${FONTS.Body3}; +`; + +const explanation = css` + display: flex; + gap: 0.8rem; + align-items: center; + + width: calc(100% + 4rem); + padding: 1.2rem 2rem; + margin-left: -2rem; + + background-color: ${COLORS.gray0}; + + color: ${COLORS.gray9}; + ${FONTS.Body3}; + font-weight: 400; +`; diff --git a/src/views/Login/components/Region.tsx b/src/views/Login/components/Region.tsx new file mode 100644 index 0000000..e51073c --- /dev/null +++ b/src/views/Login/components/Region.tsx @@ -0,0 +1,60 @@ +import { css } from '@emotion/react'; + +import BottomButton from '@/components/BottomButton'; +import SelectRegion from '@/components/SelectRegion'; +import { COLORS, FONTS } from '@/styles/constants'; + +import { useSignUpContext } from './SignUpContext'; + +interface RegionProps { + setStep: React.Dispatch>; +} + +const Region = ({ setStep }: RegionProps) => { + const { region, setRegion } = useSignUpContext(); + + const moveNext = () => { + setStep('여행자 유형 설정'); + }; + + return ( + <> +
    +

    + 지역을 +
    + 선택해주세요 +

    + +
    + + + ); +}; + +export default Region; + +const regionLayout = css` + display: flex; + gap: 3.2rem; + justify-content: space-between; + flex-direction: column; + + height: 100%; + padding: 2rem; +`; + +const mainText = css` + height: fit-content; + margin-top: 1.2rem; + + color: ${COLORS.brand1}; + + ${FONTS.H2}; +`; + +const highlight = css` + display: inline; + + box-shadow: inset 0 -1.5rem 0 ${COLORS.brand2}; +`; diff --git a/src/views/Login/components/SignUpContext.tsx b/src/views/Login/components/SignUpContext.tsx new file mode 100644 index 0000000..45675fe --- /dev/null +++ b/src/views/Login/components/SignUpContext.tsx @@ -0,0 +1,40 @@ +import React, { createContext, useContext, useState } from 'react'; + +import { Region } from '@/components/SelectRegion'; + +// Context의 타입 정의 +interface SignUpContextType { + region: Region; + setRegion: React.Dispatch>; + travelerType: string[]; + setTravelerType: React.Dispatch>; +} + +// 기본값 정의 +const defaultContextValue: SignUpContextType = { + region: { city: '', town: '' }, + setRegion: () => {}, + travelerType: [], + setTravelerType: () => {}, +}; + +// Context 생성 +const SignUpContext = createContext(defaultContextValue); + +// Context Provider 컴포넌트 +export const SignUpProvider = ({ children }: React.PropsWithChildren) => { + const [region, setRegion] = useState({ city: '', town: '' }); + const [travelerType, setTravelerType] = useState([]); + + const values = { region, setRegion, travelerType, setTravelerType }; + + return ( + {children} + ); +}; + +// Context를 사용하는 훅 +export const useSignUpContext = () => { + const context = SignUpContext && useContext(SignUpContext); + return context; +}; diff --git a/src/views/Login/components/UserType.tsx b/src/views/Login/components/UserType.tsx new file mode 100644 index 0000000..6032a9e --- /dev/null +++ b/src/views/Login/components/UserType.tsx @@ -0,0 +1,65 @@ +import { css } from '@emotion/react'; +import { useNavigate } from 'react-router-dom'; + +import BottomButton from '@/components/BottomButton'; +import TravelerType from '@/components/TravelerType'; +import { COLORS, FONTS } from '@/styles/constants'; + +import { useSignUpContext } from './SignUpContext'; + +const UserType = () => { + const navigate = useNavigate(); + + const { travelerType, setTravelerType } = useSignUpContext(); + + const moveNext = () => { + navigate(`/`); + }; + + return ( + <> +
    +

    + 여행자 유형을 +
    + 모두 선택해주세요 +

    + +
    + + + + ); +}; + +export default UserType; + +const userTypeLayout = css` + display: flex; + flex-direction: column; + + height: 100%; + padding: 2rem; +`; + +const mainText = css` + height: fit-content; + margin-top: 1.2rem; + + color: ${COLORS.brand1}; + + ${FONTS.H2}; +`; + +const highlight = css` + display: inline; + + box-shadow: inset 0 -1.5rem 0 ${COLORS.brand2}; +`; diff --git a/src/views/Login/pages/SignUpPage.tsx b/src/views/Login/pages/SignUpPage.tsx new file mode 100644 index 0000000..1810843 --- /dev/null +++ b/src/views/Login/pages/SignUpPage.tsx @@ -0,0 +1,48 @@ +import { css } from '@emotion/react'; +import { useState } from 'react'; +import { useNavigate } from 'react-router-dom'; + +import { HeaderBackIcon } from '@/assets/icon'; +import Header from '@/components/Header'; + +import Region from '../components/Region'; +import { SignUpProvider } from '../components/SignUpContext'; +import UserType from '../components/UserType'; + +const SignUpPage = () => { + const [step, setStep] = useState('지역 설정'); + + const navigate = useNavigate(); + + const moveBack = () => { + if (step === '지역 설정') { + navigate(`/`); + } + if (step === '여행자 유형 설정') { + setStep('지역 설정'); + } + }; + + const renderItem = () => { + if (step === '지역 설정') { + return ; + } + if (step === '여행자 유형 설정') { + return ; + } + }; + + return ( + +
    +
    {renderItem()}
    + + ); +}; + +export default SignUpPage; + +const SignUpPageLayout = css` + width: 100%; + height: calc(100dvh - 8rem - 4.8rem); +`; diff --git a/src/views/Mypage/components/Favorite.tsx b/src/views/Mypage/components/Favorite.tsx index 653258a..f237c75 100644 --- a/src/views/Mypage/components/Favorite.tsx +++ b/src/views/Mypage/components/Favorite.tsx @@ -4,26 +4,13 @@ import { Link } from 'react-router-dom'; import { MypageHeartIcon } from '@/assets/icon'; import { COLORS, FONTS } from '@/styles/constants'; -import { currentTabType } from '../pages/Mypage'; import FavoritePlaceList from './FavoritePlaceList'; -import MypageHeader from './MypageHeader'; const favoriteList = []; -interface FavoriteProps { - handleSetCurrentTab: (clicked: currentTabType) => void; -} - -const Favorite = (props: FavoriteProps) => { - const { handleSetCurrentTab } = props; - +const Favorite = () => { return ( <> - - {favoriteList.length === 0 ? (
    diff --git a/src/views/Mypage/components/Main.tsx b/src/views/Mypage/components/Main.tsx index a243d74..b272367 100644 --- a/src/views/Mypage/components/Main.tsx +++ b/src/views/Mypage/components/Main.tsx @@ -4,39 +4,28 @@ import { ArrowRightIcon } from '@/assets/icon'; import { ProfileImg } from '@/assets/image'; import { COLORS, FONTS } from '@/styles/constants'; -import { currentTabType } from '../pages/Mypage'; - -interface TabItemType { - name: string; - tab: currentTabType; -} +import { MYPAGE_TAB_CONTENTS } from '../constants/text'; interface MainProps { - handleSetCurrentTab: (clicked: currentTabType) => void; + handleSetCurrentTab: (clicked: string) => void; } -const MYPAGE_TAB_ITEM: TabItemType[] = [ - { name: '개인정보 조회 및 변경', tab: 'personalInfo' }, - { name: '저장한 여행지 목록', tab: 'favoritePlace' }, - { name: '여행자 유형 설정', tab: 'travelerType' }, -]; - function Main(props: MainProps) { const { handleSetCurrentTab } = props; + return (
    -
    마이페이지
    프로필이미지_사진 서아람
      - {MYPAGE_TAB_ITEM.map((item) => ( + {Object.entries(MYPAGE_TAB_CONTENTS).map(([key, name]) => (
    • handleSetCurrentTab(item.tab)}> -

      {item.name}

      + key={key} + onClick={() => handleSetCurrentTab(name)}> +

      {name}

    • ))} @@ -50,15 +39,6 @@ function Main(props: MainProps) { export default Main; -const header = css` - width: 100%; - padding: 1rem; - - color: ${COLORS.brand1}; - - ${FONTS.H4}; -`; - const profileSection = css` display: flex; align-items: center; diff --git a/src/views/Mypage/components/MypageHeader.tsx b/src/views/Mypage/components/MypageHeader.tsx deleted file mode 100644 index e601841..0000000 --- a/src/views/Mypage/components/MypageHeader.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import { css } from '@emotion/react'; - -import { HeaderBackIcon } from '@/assets/icon'; -import { COLORS, FONTS } from '@/styles/constants'; - -import { currentTabType } from '../pages/Mypage'; - -interface HeaderProps { - handleSetCurrentTab: ((clicked: currentTabType) => void) | undefined; - state: string; -} - -const HEADER_CONTENTS = [ - { state: 'personalInfo', content: '개인정보 조회 및 변경' }, - { state: 'favorite', content: '저장한 여행지 목록' }, - { state: 'travelerType', content: '' }, -]; - -const MypageHeader = (props: HeaderProps) => { - const { handleSetCurrentTab, state } = props; - - const content = HEADER_CONTENTS.find((item) => item.state === state)?.content; - - return ( -
      - { - handleSetCurrentTab && handleSetCurrentTab('main'); - }} - /> - {content} -
      - ); -}; - -export default MypageHeader; - -const header = css` - display: flex; - gap: 1rem; - align-items: center; - - width: 100%; - height: 5rem; - padding: 1.2rem 0; - - color: ${COLORS.brand1}; - - ${FONTS.H4}; -`; diff --git a/src/views/Mypage/components/PersonalInfo.tsx b/src/views/Mypage/components/PersonalInfo.tsx index ad9af52..413c1fd 100644 --- a/src/views/Mypage/components/PersonalInfo.tsx +++ b/src/views/Mypage/components/PersonalInfo.tsx @@ -1,25 +1,14 @@ import { css } from '@emotion/react'; +import { useState } from 'react'; -import SelectRegion from '@/components/SelectRegion'; +import SelectRegion, { Region } from '@/components/SelectRegion'; import { COLORS, FONTS } from '@/styles/constants'; -import { currentTabType } from '../pages/Mypage'; -import MypageHeader from './MypageHeader'; - -interface PersonalInfoProps { - handleSetCurrentTab: (clicked: currentTabType) => void; -} - -const PersonalInfo = (props: PersonalInfoProps) => { - const { handleSetCurrentTab } = props; +const PersonalInfo = () => { + const [region, setRegion] = useState({ city: '', town: '' }); return ( <> - -
      • @@ -38,16 +27,8 @@ const PersonalInfo = (props: PersonalInfoProps) => {
    -
  • - 성별 - -
  • - - + - ); @@ -63,6 +44,7 @@ const PersonalInfoContainter = css` width: 100%; height: calc(100dvh - 6.2rem); + overflow-y: hidden; `; const itemList = css` @@ -72,6 +54,9 @@ const itemList = css` flex-direction: column; width: 100%; + height: 100%; + overflow-y: hidden; + padding-top: 2.7rem; `; @@ -114,16 +99,3 @@ const birth = (variant: string) => css` ${inputDefault}; width: ${variant === 'year' ? '38%' : '28%'}; `; - -const submitBtn = css` - width: 100%; - padding: 1.7rem 0; - border-radius: 1.2rem; - - background-color: ${COLORS.brand1}; - - color: ${COLORS.white}; - text-align: center; - - ${FONTS.Body2}; -`; diff --git a/src/views/Mypage/constants/text.ts b/src/views/Mypage/constants/text.ts new file mode 100644 index 0000000..2ce4647 --- /dev/null +++ b/src/views/Mypage/constants/text.ts @@ -0,0 +1,5 @@ +export const MYPAGE_TAB_CONTENTS = { + PERSONAL_INFO: '개인정보 조회 및 변경', + FAVORITE_TRAVEL_LIST: '찜한 여행지 목록', + TRAVELER_TYPE: '여행자 유형 설정', +}; diff --git a/src/views/Mypage/pages/Mypage.tsx b/src/views/Mypage/pages/Mypage.tsx index a42f171..972f04e 100644 --- a/src/views/Mypage/pages/Mypage.tsx +++ b/src/views/Mypage/pages/Mypage.tsx @@ -1,41 +1,45 @@ import { css } from '@emotion/react'; import { useState } from 'react'; +import { HeaderBackIcon } from '@/assets/icon'; +import BottomButton from '@/components/BottomButton'; +import Header from '@/components/Header'; import MenuBar from '@/components/MenuBar'; -import SelectTravelerType from '@/components/SelectTravelerType'; +import TravelerType from '@/components/TravelerType'; import Favorite from '../components/Favorite'; import Main from '../components/Main'; import PersonalInfo from '../components/PersonalInfo'; - -export type currentTabType = - | 'main' - | 'personalInfo' - | 'favoritePlace' - | 'travelerType'; +import { MYPAGE_TAB_CONTENTS } from '../constants/text'; const Mypage = () => { - const [currentTab, setCurrentTab] = useState('main'); + const [currentTab, setCurrentTab] = useState('main'); + const [travelerTypes, setTravelerTypes] = useState([]); + + const backToMainTab = () => { + setCurrentTab('main'); + }; - const handleSetCurrentTab = (clicked: currentTabType) => { - setCurrentTab(clicked); + const handleSetCurrentTab = (clickedTab: string) => { + setCurrentTab(clickedTab); }; - const renderComponent = (state: currentTabType) => { + const handleData = () => {}; + + const renderComponent = (state: string) => { switch (state) { case 'main': return
    ; - case 'personalInfo': - return ; - case 'favoritePlace': - return ; - case 'travelerType': + case MYPAGE_TAB_CONTENTS.PERSONAL_INFO: + return ; + case MYPAGE_TAB_CONTENTS.FAVORITE_TRAVEL_LIST: + return ; + case MYPAGE_TAB_CONTENTS.TRAVELER_TYPE: return ( - - 저장 - + ); default: return null; @@ -43,15 +47,23 @@ const Mypage = () => { }; return ( -
    - {renderComponent(currentTab)} - - {currentTab === 'main' && ( -
    - -
    + <> + {currentTab === 'main' ? ( +
    + ) : ( +
    + )} +
    {renderComponent(currentTab)}
    + {currentTab === 'main' ? ( + + ) : ( + )} -
    + ); }; @@ -62,12 +74,8 @@ const mypageContainer = css` flex-direction: column; width: 100dvw; - height: 100dvh; + height: calc(100dvh - 8rem - 4.8rem); padding: 0 2rem; background-color: white; `; - -const footer = css` - margin-left: -2rem; -`;