diff --git a/package.json b/package.json index 7282b85..71a5373 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,8 @@ "@fortawesome/free-solid-svg-icons": "^6.7.1", "@fortawesome/react-fontawesome": "^0.2.2", "@hookform/resolvers": "^3.9.1", + "@nivo/core": "^0.88.0", + "@nivo/pie": "^0.88.0", "@tanstack/react-query": "^5.60.6", "@tanstack/react-query-devtools": "^5.60.6", "@types/react-datepicker": "^7.0.0", diff --git a/src/app/appRouter.tsx b/src/app/appRouter.tsx index 0ad4c90..7ae26bb 100644 --- a/src/app/appRouter.tsx +++ b/src/app/appRouter.tsx @@ -7,6 +7,7 @@ import { WriteGatheringPage, PortfolioListPage, WriteArchivePage, + UserPage, RegisterPage, SearchPage, } from '@/pages'; @@ -51,7 +52,7 @@ const AppRouter = () => { }, { path: '/user', - element: <>{/** userPage */}, + element: , }, { path: '/register', diff --git a/src/features/user/index.ts b/src/features/user/index.ts new file mode 100644 index 0000000..5ecdd1f --- /dev/null +++ b/src/features/user/index.ts @@ -0,0 +1 @@ +export * from './ui'; diff --git a/src/features/user/ui/UserProfileInfo.module.scss b/src/features/user/ui/UserProfileInfo.module.scss new file mode 100644 index 0000000..96b30e9 --- /dev/null +++ b/src/features/user/ui/UserProfileInfo.module.scss @@ -0,0 +1,92 @@ +.userProfileWrapper { + display: flex; + flex-direction: column; + row-gap: 1rem; + align-items: center; + width: 100%; + padding: 0 2rem; + color: $primary-color; + border-radius: 1rem; + + & .profileImage { + width: 16rem; + height: 16rem; + overflow: hidden; + border-radius: 1rem; + box-shadow: 0 0 4px 1px rgba(54, 54, 54, 10%); + + & > img { + width: 100%; + height: 100%; + } + } + + & .userInfo { + display: flex; + flex-direction: column; + row-gap: 0.25rem; + width: 100%; + + & .infoHeader { + display: flex; + align-items: center; + justify-content: space-between; + width: 100%; + + & > strong { + font-size: 1.5rem; + font-weight: 600; + } + } + + & .portfolioLink { + color: $third-color; + text-decoration: underline; + word-break: break-all; + + &:hover { + color: $active-color; + cursor: pointer; + } + } + + & .jobInfos { + display: flex; + column-gap: 0.5rem; + font-weight: 500; + + & > span:last-of-type { + font-weight: 600; + } + } + + & .introInfoWrapper { + padding-top: 1rem; + + & > .infoTitle { + font-weight: 600; + } + } + + & .userLinks { + display: flex; + column-gap: 0.5rem; + align-items: center; + width: 100%; + padding-top: 1rem; + + & .userLink { + display: flex; + align-items: center; + justify-content: center; + width: 2rem; + height: 2rem; + font-size: 0.875rem; + color: $secondary-color; + cursor: pointer; + background-color: $primary-color; + border-radius: 0.5rem; + } + } + } +} diff --git a/src/features/user/ui/UserProfileInfo.tsx b/src/features/user/ui/UserProfileInfo.tsx new file mode 100644 index 0000000..742b2ea --- /dev/null +++ b/src/features/user/ui/UserProfileInfo.tsx @@ -0,0 +1,45 @@ +import { faGear, faLink } from '@fortawesome/free-solid-svg-icons'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; + +import styles from './UserProfileInfo.module.scss'; + +export const UserProfileInfo = () => { + return ( +
+
+ profile-image +
+
+
+ 채승규 + +
+
+ 프론트엔드 개발자 + @Naver +
+ https://notefolio.net/nomonomonomonomonomonomo +
+ 소개 +

+ 디자인이 정말 좋아서 하고 있는 9년차 UIUX 독립 디자이너의 노모노모 디자인 + 스튜디오입니다. UIUX 디자인을 기반으로 활동하지만, 다양한 디자인 분야를 끊임없이 + 탐구하고 연구하고 있습니다. +

+
+ +
+ + + + + + +
+
+
+ ); +}; diff --git a/src/features/user/ui/index.ts b/src/features/user/ui/index.ts new file mode 100644 index 0000000..f2aadf6 --- /dev/null +++ b/src/features/user/ui/index.ts @@ -0,0 +1 @@ +export * from './UserProfileInfo'; diff --git a/src/pages/UserPage/UserPage.module.scss b/src/pages/UserPage/UserPage.module.scss new file mode 100644 index 0000000..6c8bdad --- /dev/null +++ b/src/pages/UserPage/UserPage.module.scss @@ -0,0 +1,44 @@ +$colors: ( + 'red': $red, + 'yellow': $yellow, + 'green': $green, + 'blue': $blue, + 'purple': $purple, + 'default': $third-color, +); + +@each $name, $value in $colors { + .#{$name} { + background-color: $value; + } +} + +.container { + width: 100%; +} + +.userBanner { + width: 100%; + height: 20rem; +} + +.userProfileContainer { + position: relative; + width: 20rem; + min-width: 20rem; + + & > div { + position: absolute; + top: 0; + transform: translateY(-8rem); + } +} + +.userSectionWrapper { + display: flex; + column-gap: 4rem; + width: 100%; + padding: 0 4rem; + + // border: 1px solid blue; +} diff --git a/src/pages/UserPage/UserPage.tsx b/src/pages/UserPage/UserPage.tsx new file mode 100644 index 0000000..080803f --- /dev/null +++ b/src/pages/UserPage/UserPage.tsx @@ -0,0 +1,21 @@ +import cn from 'classnames'; + +import styles from './UserPage.module.scss'; + +import { UserProfileInfo } from '@/features/user'; +import { UserContents } from '@/widgets'; + +export const UserPage = () => { + return ( +
+ {/** 배너 컴포넌트 분리 예정 */} +
+
+
+ +
+ +
+
+ ); +}; diff --git a/src/pages/index.ts b/src/pages/index.ts index 8ca00d6..15c3260 100644 --- a/src/pages/index.ts +++ b/src/pages/index.ts @@ -6,4 +6,4 @@ export { RegisterPage } from './RegisterPage/RegisterPage'; export { SearchPage } from './SearchPage/SearchPage'; export { WriteArchivePage } from './WriteArchivePage/WriteArchivePage'; export { WriteGatheringPage } from './WriteGatheringPage/WriteGatheringPage'; - +export { UserPage } from './UserPage/UserPage'; diff --git a/src/shared/ui/Chart/PieChart.tsx b/src/shared/ui/Chart/PieChart.tsx new file mode 100644 index 0000000..c4ade9a --- /dev/null +++ b/src/shared/ui/Chart/PieChart.tsx @@ -0,0 +1,57 @@ +import { ResponsivePie } from '@nivo/pie'; + +interface dataType { + id: string; + label: string; + value: number; + color: string; +} + +interface Props { + data: dataType[]; +} + +export const PieChart = ({ data /* see data tab */ }: Props) => ( +
+ data.color as string} + cornerRadius={3} + data={data} + enableArcLinkLabels={false} + innerRadius={0.5} + legends={[ + { + anchor: 'bottom', + direction: 'row', + justify: false, + translateX: 0, + translateY: 56, + itemsSpacing: 0, + itemWidth: 100, + itemHeight: 18, + itemTextColor: '#333533', + itemDirection: 'left-to-right', + itemOpacity: 1, + symbolSize: 18, + symbolShape: 'circle', + }, + ]} + margin={{ top: 40, right: 80, bottom: 80, left: 80 }} + padAngle={0.7} + theme={{ + tooltip: { + container: { + color: '#333533', + }, + }, + }} + /> +
+); diff --git a/src/shared/ui/TripleDot/TripleDot.module.scss b/src/shared/ui/TripleDot/TripleDot.module.scss index ccb8028..cec4491 100644 --- a/src/shared/ui/TripleDot/TripleDot.module.scss +++ b/src/shared/ui/TripleDot/TripleDot.module.scss @@ -1,11 +1,21 @@ +.dot { + width: 0.325rem; + height: 0.325rem; + background-color: $primary-color; + border: 1px solid $primary-color; + border-radius: 50%; +} + @keyframes shake { 0%, 100% { transform: translateX(0); } + 25% { transform: translateX(-2px); } + 75% { transform: translateX(2px); } @@ -13,10 +23,10 @@ .container { display: flex; + gap: 0.94rem; align-items: center; justify-content: center; margin: auto; - gap: 0.94rem; &:hover { .dot { @@ -26,20 +36,14 @@ &:nth-child(1) { animation-delay: 0s; } + &:nth-child(2) { animation-delay: 0.1s; } + &:nth-child(3) { animation-delay: 0.2s; } } } } - -.dot { - width: 0.325rem; - height: 0.325rem; - border: 1px solid $primary-color; - border-radius: 50%; - background-color: $primary-color; -} diff --git a/src/widgets/Layout/Layout.module.scss b/src/widgets/Layout/Layout.module.scss new file mode 100644 index 0000000..e77482b --- /dev/null +++ b/src/widgets/Layout/Layout.module.scss @@ -0,0 +1,3 @@ +.noPadding { + padding: 0 0 $header-height; +} diff --git a/src/widgets/Layout/constants.ts b/src/widgets/Layout/constants.ts index 8c63077..fa0cff5 100644 --- a/src/widgets/Layout/constants.ts +++ b/src/widgets/Layout/constants.ts @@ -12,3 +12,5 @@ export const NAV_LINKS = [ title: '게더링', }, ]; + +export const NO_PAD_ROUTES: readonly string[] = ['/user']; diff --git a/src/widgets/Layout/index.tsx b/src/widgets/Layout/index.tsx index 983396b..d36780f 100644 --- a/src/widgets/Layout/index.tsx +++ b/src/widgets/Layout/index.tsx @@ -1,5 +1,11 @@ -import { Outlet } from 'react-router-dom'; +import cn from 'classnames'; +import { Outlet, useLocation } from 'react-router-dom'; +//constant +import { NO_PAD_ROUTES } from './constants'; +//style +import styles from './Layout.module.scss'; +//component import { Footer } from './ui/Footer/Footer'; import { Header } from './ui/Header/Header'; @@ -7,6 +13,8 @@ import { useArchiveStore } from '@/features'; import { usePageLifecycle } from '@/shared/hook'; export const Layout = () => { + const { pathname } = useLocation(); + const isNoPadHeader = NO_PAD_ROUTES.includes(pathname); const { resetArchiveData, clearStorage } = useArchiveStore(); usePageLifecycle({ @@ -19,7 +27,7 @@ export const Layout = () => { return ( <>
-
+