diff --git a/src/assets/images/gray_back.svg b/src/assets/images/gray_back.svg new file mode 100644 index 0000000..e4119eb --- /dev/null +++ b/src/assets/images/gray_back.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/images/gray_hamburger.svg b/src/assets/images/gray_hamburger.svg new file mode 100644 index 0000000..0ac7818 --- /dev/null +++ b/src/assets/images/gray_hamburger.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/assets/images/mypage_menu_delete.svg b/src/assets/images/mypage_menu_delete.svg new file mode 100644 index 0000000..f22d436 --- /dev/null +++ b/src/assets/images/mypage_menu_delete.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/assets/images/mypage_menu_review.svg b/src/assets/images/mypage_menu_review.svg new file mode 100644 index 0000000..e6fd198 --- /dev/null +++ b/src/assets/images/mypage_menu_review.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/assets/images/mypage_menu_userInfo.svg b/src/assets/images/mypage_menu_userInfo.svg new file mode 100644 index 0000000..03a4434 --- /dev/null +++ b/src/assets/images/mypage_menu_userInfo.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/components/commons/MobileNavigationBar/MobileNavigationBar.module.scss b/src/components/commons/MobileNavigationBar/MobileNavigationBar.module.scss index 2c62793..efccce2 100644 --- a/src/components/commons/MobileNavigationBar/MobileNavigationBar.module.scss +++ b/src/components/commons/MobileNavigationBar/MobileNavigationBar.module.scss @@ -6,11 +6,12 @@ padding-left: 3rem; padding-right: 3rem; - &-logo { - width: 4rem; + &__back { + width: 3rem; + cursor: pointer; } - &-btn { + &__btn { @include text-style(1.5, 700, $black30); button { cursor: pointer; @@ -20,9 +21,5 @@ @include responsive("M") { padding-left: 2rem; padding-right: 2rem; - - &-logo { - width: 3rem; - } } } diff --git a/src/components/commons/MobileNavigationBar/index.tsx b/src/components/commons/MobileNavigationBar/index.tsx index 0cbc074..57f087d 100644 --- a/src/components/commons/MobileNavigationBar/index.tsx +++ b/src/components/commons/MobileNavigationBar/index.tsx @@ -1,7 +1,10 @@ import { IMAGES } from "@/constants/images"; import { PAGE_PATH } from "@/constants/pagePath"; +import { sidebarState } from "@/recoil/sidebarAtom"; +import { isMyPage } from "@/utils/pageHelpers"; import classNames from "classnames/bind"; -import { Link, useNavigate } from "react-router-dom"; +import { useLocation, useNavigate } from "react-router-dom"; +import { useSetRecoilState } from "recoil"; import Image from "../Image"; import styles from "./MobileNavigationBar.module.scss"; @@ -9,16 +12,27 @@ const cx = classNames.bind(styles); const MobileNavigationBar = ({ isAuth }: { isAuth: boolean | null }) => { const navigate = useNavigate(); - const { signIn } = PAGE_PATH; + const { pathname } = useLocation(); + const { signIn, myPage } = PAGE_PATH; + const setSidebarState = useSetRecoilState(sidebarState); return (
- - - -
- {/* {isAuth ? : } */} - {!isAuth && } + +
+ {isAuth ? ( + isMyPage(pathname) ? ( + + ) : ( + + ) + ) : ( + + )}
); diff --git a/src/components/commons/Modal/Modal.module.scss b/src/components/commons/Modal/Modal.module.scss index 58fb281..80098d9 100644 --- a/src/components/commons/Modal/Modal.module.scss +++ b/src/components/commons/Modal/Modal.module.scss @@ -1,4 +1,5 @@ .container { + @include flexbox(); position: fixed; left: 0; top: 0; @@ -8,10 +9,14 @@ outline: none; z-index: $modal; animation: fadeIn 0.2s; - @include flexbox(); + &.backdrop { background-color: rgba(0, 0, 0, 0.4); } + + &.sidebar { + @include flexbox(end, start); + } } @keyframes fadeIn { diff --git a/src/components/commons/Modal/index.tsx b/src/components/commons/Modal/index.tsx index ad34a12..464f59d 100644 --- a/src/components/commons/Modal/index.tsx +++ b/src/components/commons/Modal/index.tsx @@ -10,6 +10,7 @@ type ModalProps = { isOpen?: boolean; disableScrollLock?: boolean; hideBackdrop?: boolean; + isSidebar?: boolean; onBackdropClick?: MouseEventHandler; onKeyDown?: KeyboardEventHandler; onConfirmClick?: MouseEventHandler; @@ -19,6 +20,7 @@ const Modal = ({ children, isOpen = false, hideBackdrop = false, + isSidebar = false, onBackdropClick, onKeyDown, onConfirmClick, @@ -76,7 +78,7 @@ const Modal = ({ return (
diff --git a/src/components/commons/NavigationBar/RightButtons.tsx b/src/components/commons/NavigationBar/RightButtons.tsx index 4303de0..0d767a5 100644 --- a/src/components/commons/NavigationBar/RightButtons.tsx +++ b/src/components/commons/NavigationBar/RightButtons.tsx @@ -13,7 +13,7 @@ const cx = classNames.bind(styles); const RightButtons = ({ isAuth }: { isAuth: boolean | null }) => { const navigate = useNavigate(); const { logout, isModalOpen, setIsModalOpen } = useLogoutMutation(); - const { signIn, signUp } = PAGE_PATH; + const { signIn, signUp, myPage } = PAGE_PATH; const handleKeyDown = (e: KeyboardEvent) => { if (e.key === "Escape") { @@ -28,9 +28,9 @@ const RightButtons = ({ isAuth }: { isAuth: boolean | null }) => { <> {isAuth ? (
- {/* */} + diff --git a/src/components/domains/myPage/contents/Delete/index.tsx b/src/components/domains/myPage/contents/Delete/index.tsx new file mode 100644 index 0000000..9cca9b1 --- /dev/null +++ b/src/components/domains/myPage/contents/Delete/index.tsx @@ -0,0 +1,8 @@ +const Delete = () => { + return ( + <> +

Delete Component

+ + ); +}; +export default Delete; diff --git a/src/components/domains/myPage/contents/Level/index.tsx b/src/components/domains/myPage/contents/Level/index.tsx new file mode 100644 index 0000000..9aefa77 --- /dev/null +++ b/src/components/domains/myPage/contents/Level/index.tsx @@ -0,0 +1,8 @@ +const Level = () => { + return ( + <> +

Level Component

+ + ); +}; +export default Level; diff --git a/src/components/domains/myPage/contents/Likes/index.tsx b/src/components/domains/myPage/contents/Likes/index.tsx new file mode 100644 index 0000000..c55a9d0 --- /dev/null +++ b/src/components/domains/myPage/contents/Likes/index.tsx @@ -0,0 +1,8 @@ +const Likes = () => { + return ( + <> +

Likes Component

+ + ); +}; +export default Likes; diff --git a/src/components/domains/myPage/contents/Reviews/index.tsx b/src/components/domains/myPage/contents/Reviews/index.tsx new file mode 100644 index 0000000..b3fcdaf --- /dev/null +++ b/src/components/domains/myPage/contents/Reviews/index.tsx @@ -0,0 +1,8 @@ +const Reviews = () => { + return ( + <> +

Reviews Component

+ + ); +}; +export default Reviews; diff --git a/src/components/domains/myPage/contents/Trips/index.tsx b/src/components/domains/myPage/contents/Trips/index.tsx new file mode 100644 index 0000000..3112f99 --- /dev/null +++ b/src/components/domains/myPage/contents/Trips/index.tsx @@ -0,0 +1,8 @@ +const Trips = () => { + return ( + <> +

Trips Component

+ + ); +}; +export default Trips; diff --git a/src/components/domains/myPage/contents/UpdateUserInfo/index.tsx b/src/components/domains/myPage/contents/UpdateUserInfo/index.tsx new file mode 100644 index 0000000..9dfca7f --- /dev/null +++ b/src/components/domains/myPage/contents/UpdateUserInfo/index.tsx @@ -0,0 +1,8 @@ +const UpdateUserInfo = () => { + return ( + <> +

UpdateUserInfo Component

+ + ); +}; +export default UpdateUserInfo; diff --git a/src/components/domains/myPage/layout/NavBar.module.scss b/src/components/domains/myPage/layout/NavBar.module.scss new file mode 100644 index 0000000..d7976cb --- /dev/null +++ b/src/components/domains/myPage/layout/NavBar.module.scss @@ -0,0 +1,32 @@ +.nav { + background-color: white; + padding: 3.6rem 2.3rem 4rem; + border: solid 0.1rem $gray20; + border-radius: 1rem; + + @include responsive("T") { + padding: 2.6rem 1.7rem 2.8rem; + border: none; + } + + &__list { + @include column-flexbox(center, start); + gap: 2.4rem; + + @include responsive("T") { + gap: 1.7rem; + } + + &-item { + @include text-style(2, 400, $black30); + + @include responsive("T") { + font-size: 1.5rem; + } + + & &--active { + font-weight: 700; + } + } + } +} diff --git a/src/components/domains/myPage/layout/NavBar.tsx b/src/components/domains/myPage/layout/NavBar.tsx new file mode 100644 index 0000000..4d72048 --- /dev/null +++ b/src/components/domains/myPage/layout/NavBar.tsx @@ -0,0 +1,44 @@ +import { MYPAGE_MENU_LIST } from "@/constants/mypageMenuList"; +import { NavLink } from "react-router-dom"; + +import { sidebarState } from "@/recoil/sidebarAtom"; +import classNames from "classnames/bind"; +import { useSetRecoilState } from "recoil"; +import { useWindowSize } from "usehooks-ts"; +import styles from "./NavBar.module.scss"; + +const cx = classNames.bind(styles); + +const NavBar = () => { + const setSidebar = useSetRecoilState(sidebarState); + const { width: windowWidth } = useWindowSize(); + const isMobile = windowWidth < 1200; + + return ( +
+
    + {MYPAGE_MENU_LIST.map(({ id, name, navigate }) => { + return ( +
  • + + cx({ + "nav__list-item--active": isActive, + }) + } + onClick={() => { + if (isMobile) { + setSidebar((prev) => !prev); + } + }}> + {name} + +
  • + ); + })} +
+
+ ); +}; +export default NavBar; diff --git a/src/components/domains/myPage/layout/Sidebar.module.scss b/src/components/domains/myPage/layout/Sidebar.module.scss new file mode 100644 index 0000000..50c46d5 --- /dev/null +++ b/src/components/domains/myPage/layout/Sidebar.module.scss @@ -0,0 +1,40 @@ +.sidebar { + @include responsive("T") { + width: 21rem; + height: 100%; + background-color: white; + } + + @include responsive("M") { + width: 16rem; + } + + &__top-bar { + display: none; + + @include responsive("T") { + @include flexbox(end, center); + height: 8rem; + padding-right: 3rem; + } + + @include responsive("M") { + height: 6rem; + padding-right: 2rem; + } + + &--close-btn { + @include responsive("T") { + width: 2.4rem; + height: 2.4rem; + display: block; + cursor: pointer; + } + } + } + + &__menu { + @include column-flexbox(center, stretch); + gap: 1.4rem; + } +} diff --git a/src/components/domains/myPage/layout/Sidebar.tsx b/src/components/domains/myPage/layout/Sidebar.tsx new file mode 100644 index 0000000..b90ff08 --- /dev/null +++ b/src/components/domains/myPage/layout/Sidebar.tsx @@ -0,0 +1,29 @@ +import Image from "@/components/commons/Image"; +import { IMAGES } from "@/constants/images"; +import { sidebarState } from "@/recoil/sidebarAtom"; +import { useSetRecoilState } from "recoil"; +import NavBar from "./NavBar"; +import UserInfo from "./UserInfo"; + +import classNames from "classnames/bind"; +import styles from "./Sidebar.module.scss"; + +const cx = classNames.bind(styles); + +export default function Sidebar() { + const setSidebar = useSetRecoilState(sidebarState); + + return ( +
+
+ +
+
+ + +
+
+ ); +} diff --git a/src/components/domains/myPage/layout/UserInfo.module.scss b/src/components/domains/myPage/layout/UserInfo.module.scss new file mode 100644 index 0000000..c0954c7 --- /dev/null +++ b/src/components/domains/myPage/layout/UserInfo.module.scss @@ -0,0 +1,78 @@ +.user-info { + background-color: white; + display: grid; + grid-template-columns: auto 1fr; + grid-template-areas: + "nickname nickname" + "profileImg role" + "point point"; + gap: 1rem 0.7rem; + border: solid 0.1rem $gray20; + border-radius: 1rem; + padding: 2.6rem 2.2rem; + + @include responsive("T") { + gap: 1rem 0.5rem; + padding: 1.9rem 1.6rem; + border: none; + } + + &__nickname { + @include text-style(2.5, 700, $black30); + grid-area: nickname; + + @include responsive("T") { + font-size: 1.8rem; + } + + &-suffix { + @include text-style(2, 500, $black30); + + @include responsive("T") { + font-size: 1.5rem; + } + } + } + + &__profileImg { + grid-area: profileImg; + background-color: $gray20; + border-radius: 50%; + width: 4rem; + height: 4rem; + padding: 0.5rem; + + @include responsive("T") { + width: 3rem; + height: 3rem; + } + } + + &__role { + @include text-style(1.4, 500, $black30); + align-self: center; + + @include responsive("T") { + font-size: 1.1rem; + } + } + + &__point { + @include text-style(1.8, 700, $black30); + grid-area: point; + padding-top: 2.9rem; + + @include responsive("T") { + font-size: 1.6rem; + padding-top: 1.5rem; + } + + &-suffix { + @include text-style(1.6, 500, $black30); + + @include responsive("T") { + font-size: 1.4rem; + } + } + } +} diff --git a/src/components/domains/myPage/layout/UserInfo.tsx b/src/components/domains/myPage/layout/UserInfo.tsx new file mode 100644 index 0000000..cf5f8a1 --- /dev/null +++ b/src/components/domains/myPage/layout/UserInfo.tsx @@ -0,0 +1,47 @@ +import Image from "@/components/commons/Image"; +import { IMAGES } from "@/constants/images"; +import { authState } from "@/recoil/authAtom"; +import { useRecoilState } from "recoil"; + +import classNames from "classnames/bind"; +import styles from "./UserInfo.module.scss"; + +const cx = classNames.bind(styles); + +const UserInfo = () => { + const [auth] = useRecoilState(authState); + const tempPoint = 1000; + + if (!auth) { + return ( +
+

사용자 정보를 불러올 수 없습니다.

+ +
+ ); + } + + const { nickname, profileImgUrl, role } = auth; // fix: 백엔드에 point 값 요청하기 + + return ( +
+
+ {nickname} + +
+
+ {profileImgUrl ? ( + + ) : ( + + )} +
+
{role}
+
+ {tempPoint} + point +
+
+ ); +}; +export default UserInfo; diff --git a/src/constants/images.ts b/src/constants/images.ts index 65d177b..95e234c 100644 --- a/src/constants/images.ts +++ b/src/constants/images.ts @@ -17,10 +17,10 @@ import BlueFavoriteIcon from "@/assets/images/blue_favorite.svg"; import BlueKaKaoIcon from "@/assets/images/blue_kakao.svg"; import BlueLinkIcon from "@/assets/images/blue_link.svg"; import blueStar from "@/assets/images/blue_star.svg"; -import cancel from "@/assets/images/cancel.svg"; import calendarIcon from "@/assets/images/calendar.svg"; import calendarGreyIcon from "@/assets/images/calendar_grey.svg"; import calendarWhiteIcon from "@/assets/images/calendar_white.svg"; +import cancel from "@/assets/images/cancel.svg"; import check from "@/assets/images/check.svg"; import ChildIcon from "@/assets/images/child.svg"; import ColumnLine from "@/assets/images/column_line.svg"; @@ -36,10 +36,12 @@ import destinationPin from "@/assets/images/destinationPin.svg"; import destinationPinStress from "@/assets/images/destinationPin_stress.svg"; import PetIcon from "@/assets/images/dog.svg"; import Down_arrow from "@/assets/images/down_arrow.svg"; +import grayBackIcon from "@/assets/images/gray_back.svg"; import GrayBookmarkIcon from "@/assets/images/gray_bookmark.svg"; import grayBus from "@/assets/images/gray_bus.svg"; import grayCar from "@/assets/images/gray_car.svg"; import GrayFavoriteIcon from "@/assets/images/gray_favorite.svg"; +import grayHamburgerIcon from "@/assets/images/gray_hamburger.svg"; import GrayKaKaoIcon from "@/assets/images/gray_kakao.svg"; import GrayLinkIcon from "@/assets/images/gray_link.svg"; import grayLocation from "@/assets/images/gray_location.svg"; @@ -64,9 +66,12 @@ import Four from "@/assets/images/main/4.svg"; import memberIcon from "@/assets/images/member.svg"; import memberWhiteIcon from "@/assets/images/member_white.svg"; import modalClose from "@/assets/images/modal_close.svg"; +import mypageMenuDelete from "@/assets/images/mypage_menu_delete.svg"; import mypageMenuHeart from "@/assets/images/mypage_menu_heart.svg"; import mypageMenuLevel from "@/assets/images/mypage_menu_level.svg"; +import mypageMenuReview from "@/assets/images/mypage_menu_review.svg"; import mypageMenuTravel from "@/assets/images/mypage_menu_travel.svg"; +import mypageMenuUpdateUserInfo from "@/assets/images/mypage_menu_userInfo.svg"; import PartyIcon from "@/assets/images/party.svg"; import markerInMap from "@/assets/images/pin_on_map.png"; import plus from "@/assets/images/plus.svg"; @@ -433,16 +438,36 @@ export const IMAGES = { src: apiDataLogo, alt: "한국관광공사 로고", }, - mypageMenuHeart: { - src: mypageMenuHeart, - alt: "마이페이지 찜 아이콘", + mypageMenuUpdateUserInfo: { + src: mypageMenuUpdateUserInfo, + alt: "마이페이지 회원 정보 변경 아이콘", }, mypageMenuLevel: { src: mypageMenuLevel, - alt: "마이페이지 나의 등급 아이콘", + alt: "마이페이지 내 등급/뱃지 아이콘", }, mypageMenuTravel: { src: mypageMenuTravel, alt: "마이페이지 내가 만든 여행 아이콘", }, + mypageMenuHeart: { + src: mypageMenuHeart, + alt: "마이페이지 찜 아이콘", + }, + mypageMenuReview: { + src: mypageMenuReview, + alt: "마이페이지 내가 쓴 리뷰 아이콘", + }, + mypageMenuDelete: { + src: mypageMenuDelete, + alt: "마이페이지 회원 탈퇴 아이콘", + }, + grayHamburgerIcon: { + src: grayHamburgerIcon, + alt: "햄버거 메뉴 아이콘", + }, + grayBackIcon: { + src: grayBackIcon, + alt: "뒤로가기 아이콘", + }, }; diff --git a/src/constants/mypageMenuList.ts b/src/constants/mypageMenuList.ts index 0582a36..12f7dd0 100644 --- a/src/constants/mypageMenuList.ts +++ b/src/constants/mypageMenuList.ts @@ -1,8 +1,23 @@ +import Delete from "@/components/domains/myPage/contents/Delete"; +import Level from "@/components/domains/myPage/contents/Level"; +import Likes from "@/components/domains/myPage/contents/Likes"; +import Reviews from "@/components/domains/myPage/contents/Reviews"; +import Trips from "@/components/domains/myPage/contents/Trips"; +import UpdateUserInfo from "@/components/domains/myPage/contents/UpdateUserInfo"; import { IMAGES } from "./images"; import { PAGE_PATH } from "./pagePath"; export const MYPAGE_MENU_LIST = [ - { id: 1, icon: IMAGES.mypageMenuHeart, name: "찜", navigate: PAGE_PATH.myPageLikes }, - { id: 2, icon: IMAGES.mypageMenuLevel, name: "나의 등급", navigate: PAGE_PATH.myPageRank }, - { id: 3, icon: IMAGES.mypageMenuTravel, name: "내가 만든 여행", navigate: PAGE_PATH.myPageTrips }, + { + id: 1, + icon: IMAGES.mypageMenuUpdateUserInfo, + name: "회원 정보 변경", + navigate: PAGE_PATH.myPageUpdateUserInfo, + content: UpdateUserInfo, + }, + { id: 2, icon: IMAGES.mypageMenuLevel, name: "내 등급/뱃지", navigate: PAGE_PATH.myPageRank, content: Level }, + { id: 3, icon: IMAGES.mypageMenuTravel, name: "내가 만든 여행", navigate: PAGE_PATH.myPageTrips, content: Trips }, + { id: 4, icon: IMAGES.mypageMenuHeart, name: "찜", navigate: PAGE_PATH.myPageLikes, content: Likes }, + { id: 5, icon: IMAGES.mypageMenuReview, name: "내가 쓴 리뷰", navigate: PAGE_PATH.myPageReview, content: Reviews }, + { id: 6, icon: IMAGES.mypageMenuDelete, name: "회원 탈퇴", navigate: PAGE_PATH.myPageDelete, content: Delete }, ]; diff --git a/src/constants/pagePath.ts b/src/constants/pagePath.ts index 27b800d..64e7dcc 100644 --- a/src/constants/pagePath.ts +++ b/src/constants/pagePath.ts @@ -10,9 +10,12 @@ export const PAGE_PATH = { courseEdit: "/course/:id/edit", // 코스 수정하기 destinationEdit: "/destination/:id/edit", // 여행지 수정하기 myPage: "/mypage", // 마이페이지 - myPageLikes: "/mypage/likes", // 마이페이지 찜 - myPageRank: "/mypage/rank", // 마이페이지 나의 등급 + myPageUpdateUserInfo: "/mypage/userInfo", // 마이페이지 회원 정보 변경 + myPageRank: "/mypage/rank", // 마이페이지 나의 등급/뱃지 myPageTrips: "/mypage/trips", // 마이페이지 내가 만든 여행 + myPageLikes: "/mypage/likes", // 마이페이지 찜 + myPageReview: "/mypage/review", // 마이페이지 내가 쓴 리뷰 + myPageDelete: "/mypage/delete", // 마이페이지 회원 탈퇴 authKakao: "/auth/kakao/callback", }; @@ -21,3 +24,4 @@ const { signIn, signUp, courseRegister, destinationRegister } = PAGE_PATH; export const authPages = [signIn, signUp]; export const registerPages = [courseRegister, destinationRegister]; export const dynamicPages = [/\/course\/[^/]+\/edit/, /\/destination\/[^/]+\/edit/]; +export const myPages = /^\/mypage/; diff --git a/src/hooks/useAuth.ts b/src/hooks/useAuth.ts index 74c6921..5ec5d1f 100644 --- a/src/hooks/useAuth.ts +++ b/src/hooks/useAuth.ts @@ -2,7 +2,7 @@ import { getBasicInfo } from "@/api/my"; import { PAGE_PATH } from "@/constants/pagePath"; import { authState } from "@/recoil/authAtom"; import { getAccessToken } from "@/utils/manageTokenInfo"; -import { isRegisterPage, isSignPage } from "@/utils/pageHelpers"; +import { isMyPage, isRegisterPage, isSignPage } from "@/utils/pageHelpers"; import { useQuery } from "@tanstack/react-query"; import { useEffect } from "react"; import { useLocation, useNavigate } from "react-router-dom"; @@ -10,7 +10,7 @@ import { useRecoilState } from "recoil"; const useAuth = () => { const [auth, setAuth] = useRecoilState(authState); - const location = useLocation(); + const { pathname } = useLocation(); const navigate = useNavigate(); const accessToken = getAccessToken(); @@ -33,12 +33,12 @@ const useAuth = () => { setAuth(userInfo); } - if (accessToken && isSignPage(location.pathname)) { + if (accessToken && isSignPage(pathname)) { navigate("/"); - } else if (!accessToken && isRegisterPage(location.pathname)) { + } else if (!accessToken && (isRegisterPage(pathname) || isMyPage(pathname))) { navigate(PAGE_PATH.signIn, { replace: true }); } - }, [accessToken, isSuccess, userInfo, location.pathname, navigate, setAuth]); + }, [accessToken, isSuccess, userInfo, pathname, navigate, setAuth]); return { auth, isLoading }; }; diff --git a/src/layout/MyPageLayout/MyPageLayout.module.scss b/src/layout/MyPageLayout/MyPageLayout.module.scss new file mode 100644 index 0000000..f62e92c --- /dev/null +++ b/src/layout/MyPageLayout/MyPageLayout.module.scss @@ -0,0 +1,49 @@ +.my-page-layout { + display: grid; + grid-template-columns: 21.5rem auto; + grid-template-areas: + "heading heading" + "sidebar main"; + gap: 2.5rem 12.5rem; + + @include responsive("T") { + display: block; + } + + &__heading { + @include text-style(3, 600, $black30); + grid-area: heading; + padding-left: 1.5rem; + + @include responsive("T") { + display: none; + } + + &-selectedMenu { + @include text-style(2.5, 500, $black30); + + @include responsive("T") { + font-size: 1.8rem; + } + } + } + + &__sidebar { + grid-area: sidebar; + display: flex; + flex-direction: column; + gap: 1.4rem; + + @include responsive("T") { + display: none; + } + + &--mobile { + height: 100%; + } + } + + &__main { + grid-area: main; + } +} diff --git a/src/layout/MyPageLayout/index.tsx b/src/layout/MyPageLayout/index.tsx new file mode 100644 index 0000000..d9019cd --- /dev/null +++ b/src/layout/MyPageLayout/index.tsx @@ -0,0 +1,43 @@ +import { PropsWithChildren } from "react"; + +import Modal from "@/components/commons/Modal"; +import Sidebar from "@/components/domains/myPage/layout/Sidebar"; +import { sidebarState } from "@/recoil/sidebarAtom"; +import { useRecoilState } from "recoil"; + +import classNames from "classnames/bind"; +import { useWindowSize } from "usehooks-ts"; +import styles from "./MyPageLayout.module.scss"; + +const cx = classNames.bind(styles); + +interface MyPageLayoutProps { + selectedMenu: string; +} + +const MyPageLayout = ({ selectedMenu, children }: PropsWithChildren) => { + const [sidebar, setSidebar] = useRecoilState(sidebarState); + const { width: windowWidth } = useWindowSize(); + const isMobile = windowWidth < 1200; + + return ( +
+

+ MYPAGE / {selectedMenu} +

+ +
{children}
+ + {isMobile && ( + setSidebar((prev) => !prev)} isSidebar={true}> + + + )} +
+ ); +}; +export default MyPageLayout; diff --git a/src/pages/MyPage/MyPageLikes.tsx b/src/pages/MyPage/MyPageLikes.tsx deleted file mode 100644 index b6e9eaa..0000000 --- a/src/pages/MyPage/MyPageLikes.tsx +++ /dev/null @@ -1,8 +0,0 @@ -const MyPageLikes = () => { - return ( - <> -

MyPageLikes Component

- - ); -}; -export default MyPageLikes; diff --git a/src/pages/MyPage/MyPageRank.tsx b/src/pages/MyPage/MyPageRank.tsx deleted file mode 100644 index 99cc358..0000000 --- a/src/pages/MyPage/MyPageRank.tsx +++ /dev/null @@ -1,8 +0,0 @@ -const MyPageRank = () => { - return ( - <> -

MyPageRank Component

- - ); -}; -export default MyPageRank; diff --git a/src/pages/MyPage/MyPageSub.tsx b/src/pages/MyPage/MyPageSub.tsx new file mode 100644 index 0000000..f21924c --- /dev/null +++ b/src/pages/MyPage/MyPageSub.tsx @@ -0,0 +1,16 @@ +import MyPageLayout from "@/layout/MyPageLayout"; +import { ReactNode } from "react"; + +interface MyPageSubProps { + selectedMenu: string; + content: ReactNode; +} + +const MyPageSub = ({ selectedMenu, content }: MyPageSubProps) => { + return ( + <> + {content} + + ); +}; +export default MyPageSub; diff --git a/src/pages/MyPage/MyPageTrips.tsx b/src/pages/MyPage/MyPageTrips.tsx deleted file mode 100644 index d67efa8..0000000 --- a/src/pages/MyPage/MyPageTrips.tsx +++ /dev/null @@ -1,8 +0,0 @@ -const MyPageTrips = () => { - return ( - <> -

MyPageTrips Component

- - ); -}; -export default MyPageTrips; diff --git a/src/pages/PageRouter.tsx b/src/pages/PageRouter.tsx index 3806d28..cf693f6 100644 --- a/src/pages/PageRouter.tsx +++ b/src/pages/PageRouter.tsx @@ -1,4 +1,5 @@ import AuthListener from "@/components/commons/AuthListener"; +import { MYPAGE_MENU_LIST } from "@/constants/mypageMenuList"; import { PAGE_PATH } from "@/constants/pagePath"; import { lazy } from "react"; import { Route, Routes } from "react-router-dom"; @@ -15,9 +16,7 @@ const CourseEditPage = lazy(() => import("./CourseEditPage")); const DestinationRegisterPage = lazy(() => import("./DestinationRegisterPage")); const DestinationEditPage = lazy(() => import("./DestinationEditPage")); const MyPage = lazy(() => import("./MyPage")); -const MyPageLikes = lazy(() => import("./MyPage/MyPageLikes")); -const MyPageRank = lazy(() => import("./MyPage/MyPageRank")); -const MyPageTrips = lazy(() => import("./MyPage/MyPageTrips")); +const MyPageSub = lazy(() => import("./MyPage/MyPageSub")); const { search, @@ -30,9 +29,6 @@ const { destinationRegister, destinationEdit, myPage, - myPageLikes, - myPageRank, - myPageTrips, authKakao, } = PAGE_PATH; @@ -51,9 +47,9 @@ function PageRouter() { } /> } /> } /> - } /> - } /> - } /> + {MYPAGE_MENU_LIST.map(({ id, name, navigate, content: ContentComponent }) => ( + } />} /> + ))} } /> diff --git a/src/recoil/sidebarAtom.ts b/src/recoil/sidebarAtom.ts new file mode 100644 index 0000000..28c049a --- /dev/null +++ b/src/recoil/sidebarAtom.ts @@ -0,0 +1,6 @@ +import { atom } from "recoil"; + +export const sidebarState = atom({ + key: "sidebarState", + default: false, +}); diff --git a/src/utils/pageHelpers.ts b/src/utils/pageHelpers.ts index dfa91c1..a1bca87 100644 --- a/src/utils/pageHelpers.ts +++ b/src/utils/pageHelpers.ts @@ -1,5 +1,6 @@ -import { authPages, dynamicPages, registerPages } from "@/constants/pagePath"; +import { authPages, dynamicPages, myPages, registerPages } from "@/constants/pagePath"; export const isSignPage = (path: string) => authPages.includes(path); export const isRegisterPage = (path: string) => registerPages.includes(path) || dynamicPages.some((regex) => regex.test(path)); +export const isMyPage = (path: string) => myPages.test(path);