Skip to content

Commit

Permalink
Merge pull request #36 from Andrevile/feature/#28-PersonalProfile
Browse files Browse the repository at this point in the history
Feature/#28 personal profile
Andrevile authored Apr 27, 2022

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
2 parents 39b293a + dc6ea36 commit 38f2405
Showing 34 changed files with 644 additions and 2 deletions.
13 changes: 13 additions & 0 deletions components/Layout/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import React from 'react';
import * as S from './styles';
import Header from 'components/header/Header';
const Layout: React.FC = ({ children }) => {
return (
<>
<Header />
<S.Wrapper>{children}</S.Wrapper>
</>
);
};

export default Layout;
7 changes: 7 additions & 0 deletions components/Layout/styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import styled from 'styled-components';

export const Wrapper = styled.div`
max-width: ${(props) => props.theme.desktop};
padding: 0 30px;
margin: 0 auto;
`;
21 changes: 21 additions & 0 deletions components/Profile/Banner/AddImage/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import React from 'react';

import ImageUploadWrapper from 'components/common/ImageUploadWrapper';
import { AddImageWrapper, AddImageSvg, AddImageText } from 'components/common/Atomic/AddItem';

interface Props {
text: string;
}

const AddImage: React.FC<Props> = ({ text }) => {
return (
<ImageUploadWrapper name="banner">
<AddImageWrapper>
<AddImageSvg width={80} height={80} />
<AddImageText>{text}</AddImageText>
</AddImageWrapper>
</ImageUploadWrapper>
);
};

export default AddImage;
16 changes: 16 additions & 0 deletions components/Profile/Banner/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import React, { useState } from 'react';
import { useRecoilValue } from 'recoil';

import * as S from './styles';

import AddImage from './AddImage';

import { userRegisterInfoState, UserRegisterInfoType } from 'recoil/auth';

const Banner: React.FC = () => {
const [banner, setBanner] = useState<string>();

return <S.BannerWrapper url={banner}>{!banner && <AddImage text="프로필 배너를 추가 해주세요." />}</S.BannerWrapper>;
};

export default Banner;
15 changes: 15 additions & 0 deletions components/Profile/Banner/styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import styled from 'styled-components';
import { styledProps } from './types';

export const BannerWrapper = styled.div<styledProps>`
width: 100%;
height: 280px;
background-color: ${(props) => props.theme.color.backgroundGray};
background-image: ${(props) => props.url && `url(${props.url})`};
background-repeat: no-repeat;
background-position: center;
background-size: cover;
display: flex;
justify-content: center;
align-items: center;
`;
6 changes: 6 additions & 0 deletions components/Profile/Banner/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { DefaultTheme } from 'styled-components';

export interface styledProps {
url?: string | ArrayBuffer | null;
theme: DefaultTheme;
}
23 changes: 23 additions & 0 deletions components/Profile/ItemList/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import React, { useEffect } from 'react';
import * as S from './styles';
import AddItemCard from 'components/common/AddItemCard';
import Thumbnail from 'components/common/Thumbnail';
interface Props {
itemList: string[]; //데이터 형식에따라 타입 변환할 것
}
const ItemList: React.FC<Props> = ({ itemList }) => {
useEffect(() => {
console.log(itemList);
}, [itemList]);
return (
<S.ItemListWrapper>
{itemList.length ? (
itemList.map((item, i) => <Thumbnail key={i} item={item}></Thumbnail>)
) : (
<AddItemCard></AddItemCard>
)}
</S.ItemListWrapper>
);
};

export default ItemList;
9 changes: 9 additions & 0 deletions components/Profile/ItemList/styles.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import styled from 'styled-components';

export const ItemListWrapper = styled.div`
display: grid;
grid-template-columns: repeat(auto-fill, minmax(363px, 1fr));
row-gap: 25px;
column-gap: 24px;
`;
89 changes: 89 additions & 0 deletions components/Profile/UserInfo/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import React, { useState } from 'react';
import * as S from './styles';
import Image from 'next/image';
import {
ProfileEditButton,
UploadProductButton,
FollowButton,
MessageButton,
} from 'components/common/Atomic/Tabs/Button';
import { Keyword } from 'components/common/Atomic/Tabs/Keyword';
import { numberWithCommas } from 'utils/numberWithCommas';

const UserInfo = () => {
const [userName, setUserName] = useState<string>('Andre');
const [abilties, setAbiliies] = useState<string[]>([
'일러스트레이션',
'그래픽 디자인',
'일러스트레이션',
'그래픽 디자인',
'일러스트레이션',
'그래픽 디자인',
'일러스트레이션',
'그래픽 디자인',
'일러스트레이션',
'그래픽',
'마케팅',
]);
const [followers, setFollowers] = useState<number>(10214);
const [followings, setFollowings] = useState<number>(35150);
const [currentUser, setCurrentUser] = useState<boolean>(false);
const [intro, setIntro] = useState<string>(
'사용자 소개입니다.사용자 소개입니다.사용자 소개입니다.사용자 소개입니다.사용자 소개입니다.사용자 소개입니다.사용자 소개입니다.사용자 소개입니다.사용자 소개입니다.사용자 소개입니다.사용자 소개입니다.사용자 소개입니다.사용자 소개입니다.'
);
const handler = () => {
setCurrentUser(!currentUser);
};
return (
<S.InfoWrapper>
<S.ProfileImg>
<Image src="/images/profile_off.svg" onClick={handler} width={120} height={120} />
</S.ProfileImg>
<S.InfoSection>
<h1>{userName}</h1>
<S.InfoDescription>
<div style={{ width: '400px' }}>
{abilties.map((ability, i) => (
<Keyword key={i}>{ability}</Keyword>
))}
</div>
<S.FollowInfo>
<span>팔로워</span>
<span>{numberWithCommas(followers)}</span>
<span>팔로잉</span>
<span>{numberWithCommas(followings)}</span>
</S.FollowInfo>
<p>{intro}</p>
</S.InfoDescription>
</S.InfoSection>
<S.InfoAside>
{currentUser ? (
<>
{' '}
<ProfileEditButton>
<Image src="/images/profile-edit.svg" width={24} height={24} />
<span>프로필 수정</span>
</ProfileEditButton>
<UploadProductButton bgColor>
<Image src="/images/profile-edit-write2.svg" width={24} height={24} />
<span>작품 업로드</span>
</UploadProductButton>
</>
) : (
<>
<FollowButton bgColor>
<Image src="/images/icon-add-round.svg" width={24} height={24} />
<span>팔로우</span>
</FollowButton>
<MessageButton>
<Image src="/images/icon-message.svg" width={24} height={24} />
<span>메시지</span>
</MessageButton>
</>
)}
</S.InfoAside>
</S.InfoWrapper>
);
};

export default UserInfo;
57 changes: 57 additions & 0 deletions components/Profile/UserInfo/styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import styled from 'styled-components';
import Image from 'next/image';
export const InfoWrapper = styled.div`
padding: 24px;
position: relative;
margin-bottom: 80px;
display: flex;
`;

export const ProfileImg = styled.div``;

export const InfoSection = styled.div`
margin-left: 24px;
width: 610px;
& > h1{
font-size: 20px;
line-height: 1.3;
font-weight : ${({ theme }) => theme.fontWeight.bold}
color: ${({ theme }) => theme.color.profileNameBlack};
margin-bottom: 16px;
}
`;

export const InfoDescription = styled.div`
display: flex;
flex-direction: column;
flex-wrap: wrap;
p {
color: ${({ theme }) => theme.color.gray_700};
font-weight: ${({ theme }) => theme.fontWeight.medium};
font-size: 12px;
line-height: 1.416666;
}
`;

export const InfoAside = styled.div`
position: absolute;
right: 24px;
`;

export const FollowInfo = styled.div`
margin-bottom: 8px;
span {
font-weight: 400;
font-size: 12px;
line-height: 1.833333;
letter-spacing: -0.01em;
color: ${({ theme }) => theme.color.gray_700};
margin-right: 16px;
}
span:nth-child(2n-1) {
margin-right: 4px;
}
`;
31 changes: 31 additions & 0 deletions components/common/AddItemCard/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import React from 'react';
import styled from 'styled-components';
import { AddImageWrapper, AddImageSvg } from '../Atomic/AddItem';
const AddItem = () => {
return (
<AddItemCard>
<AddImageWrapper>
<AddImageText>게시물을 추가 해주세요.</AddImageText>
<AddImageSvg width={80} height={80} />
</AddImageWrapper>
</AddItemCard>
);
};

export default AddItem;

const AddItemCard = styled.div`
width: 363px;
height: 280px;
background-color: ${({ theme }) => theme.color.gray_100};
display: flex;
align-items: center;
justify-content: center;
border-radius: 10px;
`;

const AddImageText = styled.span`
color: ${(props) => props.theme.color.addTextGray};
font-size: 14px;
margin-bottom: 16px;
`;
24 changes: 24 additions & 0 deletions components/common/Atomic/AddItem/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import styled, { DefaultTheme } from 'styled-components';
import Image from 'next/image';

export const AddImageWrapper = styled.div`
cursor: pointer;
position: relative;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
`;

export const AddImageSvg = styled(Image).attrs((props) => ({
src: '/images/add_project_default.svg',
}))`
display: block;
border-radius: 50%;
`;

export const AddImageText = styled.span<{ theme: DefaultTheme }>`
color: ${(props) => props.theme.color.addTextGray};
font-size: 14px;
margin-top: 16px;
`;
50 changes: 50 additions & 0 deletions components/common/Atomic/Tabs/Button/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import styled, { css } from 'styled-components';

export const DefaultButton = styled.button<{ bgColor?: boolean }>`
height: 40px;
border-radius: 30px;
display: inline-flex;
align-items: center;
justify-content: center;
${({ bgColor, theme }) => {
const color = bgColor ? theme.color.DefaultPrimaryGreen : theme.color.backgroundGray;
const pressed = bgColor ? theme.color.PressedPrimaryGreen : theme.color.backgroundHoverGray;
return css`
background-color: ${color};
&: hover {
background-color: ${pressed};
}
&: active {
background-color: ${pressed};
}
`;
}}
& + & {
margin-left: 16px;
}
& > span {
font-size: 16px;
font-weight: ${({ theme }) => theme.fontWeight.medium};
line-height: 1.448125;
margin-left: 4px;
}
`;

export const ProfileEditButton = styled(DefaultButton)`
padding: 10px 24px 10px 20px;
`;

export const UploadProductButton = styled(DefaultButton)`
padding: 10px 24px 10px 20px;
`;

export const FollowButton = styled(DefaultButton)`
padding: 10px 16px;
`;

export const MessageButton = styled(DefaultButton)`
padding: 10px 16px;
`;
16 changes: 16 additions & 0 deletions components/common/Atomic/Tabs/Keyword/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import styled from 'styled-components';

export const Keyword = styled.span`
display: inline-block;
height: 24px;
padding: 4px 8px;
border-radius: 12px;
font-size: 12px;
font-weight: ${({ theme }) => theme.fontWeight.medium};
line-height: 1.4166666;
background-color: ${({ theme }) => theme.color.backgroundGray};
margin-right: 8px;
margin-bottom: 8px;
`;
31 changes: 31 additions & 0 deletions components/common/Atomic/Tabs/TabButton/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import styled, { css, DefaultTheme } from 'styled-components';

export const TabButton = styled.button<{ active: boolean; theme: DefaultTheme }>`
display: inline-block;
height: 31px;
font-size: 16px;
line-height: 1.448125;
padding: 4px 12px;
border-radius: 24px;
margin-left: 24px;
font-weight: ${({ theme }) => theme.fontWeight.medium};
background-color: ${({ theme }) => theme.color.white};
border: 1px solid ${({ theme }) => theme.color.gray_300};
color: ${({ theme }) => theme.color.black};
span {
margin-left: 4px;
color: ${({ theme }) => theme.color.gray_400};
}
${({ active }) => {
return (
active &&
css`
border: none;
background-color: ${({ theme }) => theme.color.black};
color: ${({ theme }) => theme.color.white};
span {
}
`
);
}};
`;
70 changes: 70 additions & 0 deletions components/common/Thumbnail/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import React from 'react';
import styled, { css } from 'styled-components';
import Image from 'next/image';

interface Props {
item: string; //작품 정보에 대한 타입을 정의해줘야함
}
const Thumbnail: React.FC<Props> = ({ item }) => {
return (
<ItemCard>
<ImageWrapper>
<Image src="/images/icon-folder-add.svg" width={24} height={24} />
</ImageWrapper>
{item.length > 0 && (
<ItemInfo>
<p>{item}</p>
</ItemInfo>
)}
</ItemCard>
);
};

export default Thumbnail;

const ItemCard = styled.div`
position: relative;
width: 363px;
height: 280px;
border-radius: 10px;
background-color: ${({ theme }) => theme.color.gray_500};
overflow: hidden;
`;

const ItemInfo = styled.div`
position: absolute;
bottom: 0;
background: linear-gradient(180deg, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0.3) 70.31%);
width: 100%;
height: 100px;
padding: 0 16px;
p {
position: absolute;
color: ${({ theme }) => theme.color.white};
font-weght: ${({ theme }) => theme.fontWeight.medium};
font-size: 16px;
line-height: 1.4375;
letter-spacing: 0.02em;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
width: 332px;
height: 23px;
bottom: 16px;
}
`;

const ImageWrapper = styled.button`
position: absolute;
display: flex;
align-items: center;
justify-content: center;
z-index: 1;
width: 32px;
height: 32px;
top: 16px;
right: 16px;
background: rgba(0, 0, 0, 0.5);
border-radius: 5px;
`;
13 changes: 13 additions & 0 deletions db.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"user": [
{
"id": 1,
"userProfile": null,
"email": "abg3000@naver.com",
"nickname": "장종오",
"description": "안녕하세요",
"mainCategory": [],
"banner": null
}
]
}
4 changes: 4 additions & 0 deletions pages/_document.tsx
Original file line number Diff line number Diff line change
@@ -38,6 +38,10 @@ class MyDocument extends Document {
<meta property="og:url" content="//" />
<meta name="description" content="" />
<meta name="keywords" content="" />
<link
href="https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@400;500;700;900&display=swap"
rel="stylesheet"
/>
</Head>
<body id="body">
<Main />
53 changes: 53 additions & 0 deletions pages/profile.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import React, { useCallback, useState } from 'react';
import Layout from 'components/Layout';
import Banner from 'components/Profile/Banner';
import UserInfo from 'components/Profile/UserInfo';
import ItemList from 'components/Profile/ItemList';
import { TabButton } from 'components/common/Atomic/Tabs/TabButton';
const Profile: React.FC = () => {
const [user, getUserQuery] = useState('serre'); // useQuery로 유저정보 받아옴.

//Suspense를 사용하게 된다면, useQuery를 여러개 선언하는것은 사용할 수 없으므로, useQueries를 사용해야함
const Items = {
items: [], //['아이템1', '아이템2'],
scrabs: [
'',
'스크랩1 제목입니다.스크랩1 제목입니다.스크랩1 제목입니다.스크랩1 제목입니다.스크랩1 제목입니다.',
'스크랩2',
'스크랩3',
'스크랩4',
'스크랩5',
'스크랩6',
'스크랩7',
'스크랩8',
],
};
const [currentTab, setCurrentTab] = useState(0);
const tabMenuArr = [
['작업물', 'items'],
['스크랩', 'scrabs'],
];
const selectTab = useCallback(
(index: number) => () => {
setCurrentTab(index);
},
[currentTab, setCurrentTab]
);
return (
<Layout>
<Banner />
<UserInfo />
<div style={{ marginBottom: '40px' }}>
{tabMenuArr.map((tab, i) => (
<TabButton active={currentTab === i} key={i} onClick={selectTab(i)}>
{tab[0]}
<span>{Items[tabMenuArr[i][1]].length}</span>
</TabButton>
))}
</div>
<ItemList itemList={Items[tabMenuArr[currentTab][1]]} />
</Layout>
);
};

export default Profile;
4 changes: 4 additions & 0 deletions public/images/Message.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions public/images/add_project_default.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions public/images/add_project_pressed.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions public/images/icon-add-round.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions public/images/icon-folder-add.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions public/images/icon-message.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/images/profile-edit-write2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions public/images/profile-edit-write2.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions public/images/profile-edit.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
13 changes: 13 additions & 0 deletions public/images/profile_off.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 4 additions & 1 deletion styles/globalStyle.ts
Original file line number Diff line number Diff line change
@@ -16,11 +16,14 @@ export const GlobalStyle = createGlobalStyle`
html{
font-size: 16px;
-webkit-text-size-adjust: none;
font-family: -apple-system,BlinkMacSystemFont,helvetica,Apple SD Gothic Neo,sans-serif;
font-family: 'Noto Sans KR',sans-serif;
font-display: fallback;
-ms-overflow-style: none;
scrollbar-width: none;
}
button,span,input{
font-family: 'Noto Sans KR',sans-serif;
}
button {
background: none;
padding: 0;
18 changes: 18 additions & 0 deletions styles/style.d.ts
Original file line number Diff line number Diff line change
@@ -3,12 +3,30 @@ import 'styled-components';
declare module 'styled-components' {
export interface DefaultTheme {
color: {
white: '#ffffff';
black: '#000000';
purple: '#8661de';
blue: '#00bac7';
gray: '#f6f6f6';
green: '#07b495';
DefaultPrimaryGreen: '#bdF486';
PressedPrimaryGreen: '#abf066';
lightGreen: '#99ecdd';
darkGray: '#54595d';
backgroundGray: '#eeeeee';
backgroundHoverGray: '#e0e0e0';
addTextGray: '#9e9e9e';
gray_100: '#f5f5f5';
gray_300: '#e5e5e5';
gray_400: '#bdbdbd';
gray_500: '#757575';
gray_700: '#616161';
profileNameBlack: '#212121';
};
fontWeight: {
bold: 700;
medium: 500;
};
desktop: '1200px';
}
}
18 changes: 18 additions & 0 deletions styles/theme.ts
Original file line number Diff line number Diff line change
@@ -2,11 +2,29 @@ import { DefaultTheme } from 'styled-components';

export const theme: DefaultTheme = {
color: {
white: '#ffffff',
black: '#000000',
purple: '#8661de',
blue: '#00bac7',
gray: '#f6f6f6',
green: '#07b495',
DefaultPrimaryGreen: '#bdF486',
PressedPrimaryGreen: '#abf066',
lightGreen: '#99ecdd',
darkGray: '#54595d',
backgroundGray: '#eeeeee', //색깔 변수 이름 소통 필요
backgroundHoverGray: '#e0e0e0',
addTextGray: '#9e9e9e',
gray_100: '#f5f5f5',
gray_300: '#e5e5e5',
gray_400: '#bdbdbd',
gray_500: '#757575',
gray_700: '#616161',
profileNameBlack: '#212121',
},
fontWeight: {
bold: 700,
medium: 500,
},
desktop: '1200px',
};
3 changes: 2 additions & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
@@ -14,7 +14,8 @@
"isolatedModules": true,
"jsx": "preserve",
"rootDir": ".",
"incremental": true
"incremental": true,
"baseUrl": "."
},
"include": [
"next-env.d.ts",
7 changes: 7 additions & 0 deletions utils/numberWithCommas.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export const numberWithCommas = (num: number): string => {
let parts: number | string[] = num;
parts = parts.toString().split('.');
return parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ',') + (parts[1] ? '.' + parts[1] : '');
};

//숫자를 넣으면 3자리마다 , 찍히고 소수점 2자리 까지 표현

0 comments on commit 38f2405

Please sign in to comment.