Skip to content

Commit

Permalink
[Feat] 유저 페이지 UI(뷰) 구현 (#65)
Browse files Browse the repository at this point in the history
* feat: layout main no padding 예외 추가

* chore: nivo 차트 패키지 설치

* style: lint 미적용 파일들 lint 적용

* design: noPadding style 하단 패딩 추가

* feat: PieChart 컴포넌트 구현 (shared)

* feat: 유저 프로필 ui 컴포넌트 구현

* feat: 유저 페이지 컨텐츠 탭 메뉴 ui 구현 + 뷰 로직

* feat: 유저 컨텐츠 섹션 ui 구현

* feat: 유저 페이지 ui 구현 & 라우터 설정
  • Loading branch information
csk6314 authored Nov 30, 2024
1 parent cd526fd commit 456ebad
Show file tree
Hide file tree
Showing 22 changed files with 759 additions and 16 deletions.
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
3 changes: 2 additions & 1 deletion src/app/appRouter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
WriteGatheringPage,
PortfolioListPage,
WriteArchivePage,
UserPage,
RegisterPage,
SearchPage,
} from '@/pages';
Expand Down Expand Up @@ -51,7 +52,7 @@ const AppRouter = () => {
},
{
path: '/user',
element: <>{/** userPage */}</>,
element: <UserPage />,
},
{
path: '/register',
Expand Down
1 change: 1 addition & 0 deletions src/features/user/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './ui';
92 changes: 92 additions & 0 deletions src/features/user/ui/UserProfileInfo.module.scss
Original file line number Diff line number Diff line change
@@ -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;
}
}
}
}
45 changes: 45 additions & 0 deletions src/features/user/ui/UserProfileInfo.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<div className={styles.userProfileWrapper}>
<div className={styles.profileImage}>
<img
alt='profile-image'
src='https://api.surfit.io/v1/directory/avatar/350599784?t=1732628475'
/>
</div>
<div className={styles.userInfo}>
<div className={styles.infoHeader}>
<strong>채승규</strong>
<FontAwesomeIcon icon={faGear} />
</div>
<div className={styles.jobInfos}>
<span>프론트엔드 개발자</span>
<span>@Naver</span>
</div>
<a className={styles.portfolioLink}>https://notefolio.net/nomonomonomonomonomonomo</a>
<div className={styles.introInfoWrapper}>
<span className={styles.infoTitle}>소개</span>
<p>
디자인이 정말 좋아서 하고 있는 9년차 UIUX 독립 디자이너의 노모노모 디자인
스튜디오입니다. UIUX 디자인을 기반으로 활동하지만, 다양한 디자인 분야를 끊임없이
탐구하고 연구하고 있습니다.
</p>
</div>

<div className={styles.userLinks}>
<a className={styles.userLink}>
<FontAwesomeIcon icon={faLink} />
</a>
<a className={styles.userLink}>
<FontAwesomeIcon icon={faLink} />
</a>
</div>
</div>
</div>
);
};
1 change: 1 addition & 0 deletions src/features/user/ui/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './UserProfileInfo';
44 changes: 44 additions & 0 deletions src/pages/UserPage/UserPage.module.scss
Original file line number Diff line number Diff line change
@@ -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;
}
21 changes: 21 additions & 0 deletions src/pages/UserPage/UserPage.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<div className={styles.container}>
{/** 배너 컴포넌트 분리 예정 */}
<div className={cn(styles.userBanner, styles['green'])}></div>
<div className={styles.userSectionWrapper}>
<div className={styles.userProfileContainer}>
<UserProfileInfo />
</div>
<UserContents />
</div>
</div>
);
};
2 changes: 1 addition & 1 deletion src/pages/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
57 changes: 57 additions & 0 deletions src/shared/ui/Chart/PieChart.tsx
Original file line number Diff line number Diff line change
@@ -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) => (
<div style={{ width: '100%', height: '400px' }}>
<ResponsivePie
activeOuterRadiusOffset={8}
arcLabelsSkipAngle={10}
arcLabelsTextColor={{
from: 'color',
modifiers: [['darker', 2.5]],
}}
arcLinkLabelsColor={{ from: 'color' }}
colors={({ data }) => 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',
},
},
}}
/>
</div>
);
22 changes: 13 additions & 9 deletions src/shared/ui/TripleDot/TripleDot.module.scss
Original file line number Diff line number Diff line change
@@ -1,22 +1,32 @@
.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);
}
}

.container {
display: flex;
gap: 0.94rem;
align-items: center;
justify-content: center;
margin: auto;
gap: 0.94rem;

&:hover {
.dot {
Expand All @@ -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;
}
3 changes: 3 additions & 0 deletions src/widgets/Layout/Layout.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.noPadding {
padding: 0 0 $header-height;
}
2 changes: 2 additions & 0 deletions src/widgets/Layout/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,5 @@ export const NAV_LINKS = [
title: '게더링',
},
];

export const NO_PAD_ROUTES: readonly string[] = ['/user'];
12 changes: 10 additions & 2 deletions src/widgets/Layout/index.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,20 @@
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';

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({
Expand All @@ -19,7 +27,7 @@ export const Layout = () => {
return (
<>
<Header />
<main>
<main className={cn({ [styles.noPadding]: isNoPadHeader })}>
<Outlet />
</main>
<Footer />
Expand Down
Loading

1 comment on commit 456ebad

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚡ Lighthouse report for http://localhost:3000/

Category Score
🔴 Performance 21
🟢 Accessibility 95
🟢 Best Practices 100
🟠 SEO 83

Detailed Metrics

Metric Value
🔴 First Contentful Paint 40.3 s
🔴 Largest Contentful Paint 68.9 s
🔴 Total Blocking Time 1,210 ms
🟢 Cumulative Layout Shift 0
🔴 Speed Index 53.4 s

Please sign in to comment.