-
Notifications
You must be signed in to change notification settings - Fork 46
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[김법균] Week14 #458
The head ref may contain hidden characters: "part3-\uAE40\uBC95\uADE0"
[김법균] Week14 #458
Changes from all commits
4ff3eac
afc85a4
51cd32b
4b13e67
80657d1
5a20445
71fc42e
c06e1ef
7450edd
d494e1c
6524c10
2e998f1
df4176b
9543365
d85cd76
54a6d86
38f02df
0ef3b30
abf2956
235a8a2
9c7af94
0a80ad0
c661db3
25948dd
572c3a1
ea1481f
ff659cd
be585da
7adf8d7
bb1eb95
318d23e
ae1190f
ebec153
92f5aea
c55266b
a21a6b4
006447c
13d1c7a
8ca6d8d
db72aea
1702d39
bbc6fc3
70edc32
24f16bb
4e99ba4
facb247
12e3cb5
84fe27d
43417d3
fd4f1bb
50cef44
4df5259
1cdb459
1d56499
23340a5
4df9304
b740fc5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,3 @@ | ||
{ | ||
"extends": "next/core-web-vitals" | ||
"extends": "next" | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
.account { | ||
display: flex; | ||
align-items: center; | ||
gap: 6px; | ||
} | ||
.userProfileImg { | ||
width: 28px; | ||
aspect-ratio: 1/1; | ||
border-radius: 50%; | ||
} | ||
|
||
.userEmail { | ||
width: fit-content; | ||
font-size: 14px; | ||
@media (max-width: 767px) { | ||
position: absolute; | ||
width: 1px; | ||
height: 1px; | ||
clip: rect(0 0 0 0); | ||
overflow: hidden; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
import styles from './Account.module.scss'; | ||
|
||
interface AccountProps { | ||
profileImgSource?: string; | ||
userEmail: string; | ||
} | ||
|
||
const Account = ({ profileImgSource, userEmail }: AccountProps) => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. account라기 보단 user-profile이라는 콤포넌트가 더 적합한 이름으로 보이는데 어떻게 생각하시나요? |
||
return ( | ||
<div className={styles.account}> | ||
<img | ||
src={profileImgSource} | ||
alt='User Profile' | ||
className={styles.userProfileImg} | ||
/> | ||
<span className={styles.userEmail}>{userEmail}</span> | ||
</div> | ||
); | ||
}; | ||
export default Account; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
.folderToolBarContainer { | ||
display: flex; | ||
flex-direction: column; | ||
gap: 24px; | ||
width: 1060px; | ||
|
||
.folderToolButtons { | ||
display: flex; | ||
width: 100%; | ||
justify-content: space-between; | ||
align-items: center; | ||
|
||
.folderNameButtons { | ||
display: flex; | ||
flex-wrap: wrap; | ||
width: 80%; | ||
gap: 8px; | ||
} | ||
} | ||
|
||
.folderNameDisplay { | ||
font-size: 24px; | ||
letter-spacing: -0.2px; | ||
font-weight: 600; | ||
} | ||
|
||
.folderNameBar { | ||
display: flex; | ||
justify-content: space-between; | ||
align-items: center; | ||
|
||
.utilButtons { | ||
display: flex; | ||
gap: 12px; | ||
} | ||
} | ||
|
||
@media (max-width: 1124px) { | ||
width: 706px; | ||
} | ||
|
||
@media (max-width: 767px) { | ||
width: 325px; | ||
|
||
.folderNameBar { | ||
flex-direction: column; | ||
align-items: flex-start; | ||
gap: 12px; | ||
} | ||
} | ||
} | ||
|
||
.folderNameButton { | ||
background-color: #fff; | ||
border: 1px solid #6d6afe; | ||
padding: 8px 12px; | ||
border-radius: 5px; | ||
height: 36px; | ||
|
||
&:hover { | ||
background-color: #e7effb; | ||
} | ||
} | ||
|
||
.focusedButton { | ||
background-color: #6d6afe; | ||
color: #fff; | ||
&:hover { | ||
background-color: #6d6afe; | ||
} | ||
} | ||
|
||
.utilButton { | ||
position: relative; | ||
display: flex; | ||
gap: 4px; | ||
color: #9fa6b2; | ||
font-size: 14px; | ||
font-weight: 600; | ||
} | ||
.utilButtonIcon { | ||
position: relative; | ||
width: 18px; | ||
height: 18px; | ||
} | ||
|
||
.folderAddButton { | ||
display: flex; | ||
font-weight: 500; | ||
gap: 4px; | ||
color: #6d6afe; | ||
|
||
.addIconWhite { | ||
display: none; | ||
} | ||
|
||
@media (max-width: 767px) { | ||
position: fixed; | ||
bottom: 101px; | ||
padding: 8px 24px; | ||
left: 50%; | ||
transform: translateX(-50%); | ||
border-radius: 20px; | ||
background-color: #6d6afe; | ||
color: #fff; | ||
z-index: 1; | ||
|
||
.addIcon { | ||
display: none; | ||
} | ||
|
||
.addIconWhite { | ||
display: block; | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
import FolderToolBarButton from './FolderToolBarButton'; | ||
import styles from './FolderToolBar.module.scss'; | ||
import UtilButton from './UtilButton'; | ||
import { UTIL_BUTTONS_PROPS } from './constants'; | ||
import { FolderObj } from '@/utils/interfaces'; | ||
|
||
const addIcon = '/assets/images/add_icon.svg'; | ||
const addIconWhite = '/assets/images/add_icon_white.svg'; | ||
interface FolderToolBarProps { | ||
folders?: FolderObj[]; | ||
currentFolderId?: number; | ||
folderNameOnClick: (id: number) => void; | ||
onFolderNameChangeClick: () => void; | ||
onFolderAddClick: () => void; | ||
onFolderDeleteClick: () => void; | ||
onShare: () => void; | ||
} | ||
|
||
export default function FolderToolBar({ | ||
folders, | ||
currentFolderId, | ||
folderNameOnClick, | ||
onFolderAddClick, | ||
onFolderNameChangeClick, | ||
onFolderDeleteClick, | ||
onShare, | ||
}: FolderToolBarProps) { | ||
const currentFolder = folders?.find( | ||
(folder) => folder.id === currentFolderId | ||
); | ||
const currentFolderName = currentFolder?.name; | ||
UTIL_BUTTONS_PROPS.share.onClick = onShare; | ||
UTIL_BUTTONS_PROPS.changeName.onClick = onFolderNameChangeClick; | ||
UTIL_BUTTONS_PROPS.delete.onClick = onFolderDeleteClick; | ||
|
||
return ( | ||
<div className={styles.folderToolBarContainer}> | ||
<div className={styles.folderToolButtons}> | ||
<ul className={styles.folderNameButtons}> | ||
{folders?.map((item) => ( | ||
<li key={item.id}> | ||
<FolderToolBarButton | ||
onClick={folderNameOnClick} | ||
id={item.id} | ||
isFocused={item.id === currentFolderId} | ||
> | ||
{item.name} | ||
</FolderToolBarButton> | ||
</li> | ||
))} | ||
</ul> | ||
|
||
<button className={styles.folderAddButton} onClick={onFolderAddClick}> | ||
<span>폴더 추가</span> | ||
<img | ||
src={addIcon} | ||
alt='폴더 추가 아이콘' | ||
className={styles.addIcon} | ||
/> | ||
<img | ||
src={addIconWhite} | ||
alt='폴더 추가 아이콘' | ||
className={styles.addIconWhite} | ||
/> | ||
</button> | ||
</div> | ||
<div className={styles.folderNameBar}> | ||
<span className={styles.folderNameDisplay}>{currentFolderName}</span> | ||
<ul className={styles.utilButtons}> | ||
{Object.entries(UTIL_BUTTONS_PROPS).map(([key, btn]) => ( | ||
<li key={btn.id}> | ||
{currentFolderId !== -1 && ( | ||
<UtilButton | ||
imgSrc={btn.imgSrc} | ||
alt={btn.alt} | ||
onClick={btn.onClick} | ||
> | ||
{btn.btnText} | ||
</UtilButton> | ||
)} | ||
</li> | ||
))} | ||
</ul> | ||
</div> | ||
</div> | ||
); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import styles from './FolderToolBar.module.scss'; | ||
|
||
interface FolderToolBarButtonProps { | ||
id: number; | ||
children: React.ReactNode; | ||
onClick: (id: number) => void; | ||
isFocused: boolean; | ||
} | ||
|
||
export default function FolderToolBarButton({ | ||
id, | ||
children, | ||
onClick, | ||
isFocused, | ||
}: FolderToolBarButtonProps) { | ||
const handleClick = () => { | ||
onClick(id); | ||
}; | ||
return ( | ||
<button | ||
onClick={handleClick} | ||
className={`${styles.folderNameButton} ${ | ||
isFocused && styles.focusedButton | ||
}`} | ||
> | ||
{children} | ||
</button> | ||
); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
import styles from './FolderToolBar.module.scss'; | ||
import Image from 'next/image'; | ||
|
||
interface UtilButtonProps { | ||
imgSrc: string; | ||
children: React.ReactNode; | ||
onClick: () => void; | ||
alt: string; | ||
} | ||
|
||
export default function UtilButton({ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이 버튼의 존재 의의가 무엇인가요? |
||
imgSrc, | ||
children, | ||
onClick, | ||
alt, | ||
}: UtilButtonProps) { | ||
return ( | ||
<button className={styles.utilButton} onClick={onClick}> | ||
<div className={styles.utilButtonIcon}> | ||
<Image src={imgSrc} alt={alt} fill /> | ||
</div> | ||
{children} | ||
</button> | ||
); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
const shareIcon = '/assets/images/share_icon.svg'; | ||
const penIcon = '/assets/images/pen_icon.svg'; | ||
const deleteIcon = '/assets/images/delete_icon.svg'; | ||
|
||
interface UtilButtonProp { | ||
imgSrc: string; | ||
btnText: string; | ||
alt: string; | ||
id: number; | ||
onClick: () => void; | ||
} | ||
|
||
const noop = () => {}; | ||
|
||
export const UTIL_BUTTONS_PROPS: { [key: string]: UtilButtonProp } = { | ||
share: { | ||
imgSrc: shareIcon, | ||
btnText: '공유', | ||
alt: '공유 아이콘', | ||
id: 1, | ||
onClick: noop, | ||
}, | ||
changeName: { | ||
imgSrc: penIcon, | ||
btnText: '이름 변경', | ||
alt: '이름 변경 아이콘', | ||
id: 2, | ||
onClick: noop, | ||
}, | ||
delete: { | ||
imgSrc: deleteIcon, | ||
btnText: '삭제', | ||
alt: '삭제 아이콘', | ||
id: 3, | ||
onClick: noop, | ||
}, | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,17 +2,48 @@ import Button from '../Button/Button'; | |
import Image from 'next/image'; | ||
import styles from './Header.module.css'; | ||
import Link from 'next/link'; | ||
|
||
import Account from '@/components/Account/Account'; | ||
import { useUserInfo } from '@/contexts/UserInfoContext'; | ||
import { axiosInstance } from '@/utils/axiosInstance'; | ||
import { useEffect } from 'react'; | ||
export default function Header() { | ||
const { userInfo, setUserInfo } = useUserInfo(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. useUserInfo는 그냥 우리 어플리케이션에 저장된 사용자 전부에 대한 관리를 하는 모듈로 보입니다. useAuth에서 로그인 된 사용자 정보를 관리하고 있게 한다면, 이 사용자 정보를 변경할 수 있는 setUserInfo를 제공해주는건 되게 좋지 않아보입니다. useAuth에서 제공해야 하는 요소들은
만 제공해주어야 할 거구요 |
||
const loadUser = async () => { | ||
const accessToken = localStorage.getItem('accessToken'); | ||
if (accessToken) { | ||
const response = await axiosInstance.get('/users', { | ||
headers: { | ||
Authorization: `Bearer ${accessToken}`, | ||
}, | ||
}); | ||
setUserInfo(response.data.data[0]); | ||
} | ||
}; | ||
Comment on lines
+12
to
+22
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이 로직도 header에서 처리되기보단 로그인 된 사용자 정보를 관리하는 곳에서 처리가 되어야 하겠죠 |
||
|
||
useEffect(() => { | ||
loadUser(); | ||
}, []); | ||
|
||
return ( | ||
<> | ||
<header className={styles.headerContainer}> | ||
<div className={styles.headerBar}> | ||
<Link className={styles.logoImageContainer} href={`/`}> | ||
<Image src='/assets/images/logo.svg' alt='Logo' fill /> | ||
</Link> | ||
<Link href={'/signin'}> | ||
<Button className={styles.signInButton}>로그인</Button> | ||
</Link> | ||
|
||
{userInfo ? ( | ||
<Account | ||
profileImgSource={userInfo.image_source} | ||
userEmail={userInfo.email} | ||
/> | ||
) : ( | ||
<Link href={'/signin'}> | ||
<Button className={styles.signInButton}>로그인</Button> | ||
</Link> | ||
)} | ||
|
||
</div> | ||
</header> | ||
</> | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@bk-git-hub
prop으로 전달받는 요소의 경우, 우리 프로젝트에서 전반적으로 사용되는 유저 데이터가 존재하나요?
그렇다면 유저 데이터에 대한 타입을 선언해서 그냥 유저 정보를 다 받아오도록 하는게 더 좋지 않을까 생각이 되어요.