-
Notifications
You must be signed in to change notification settings - Fork 51
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
[박경서] week13 #493
The head ref may contain hidden characters: "part3-\uBC15\uACBD\uC11C-week13"
[박경서] week13 #493
Changes from all commits
93a0e93
41129ae
43a3c5a
f9fd509
1bbc6e6
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 |
---|---|---|
@@ -0,0 +1,4 @@ | ||
{ | ||
"presets": ["next/babel"], | ||
"plugins": [["styled-components", { "ssr": true }]] | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,3 @@ | ||
{ | ||
"extends": "next/core-web-vitals" | ||
"extends": ["next/babel", "next/core-web-vitals"] | ||
} |
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. 머지한 후, 브랜치 자동 삭제해주는 기능이군요 👍🏻 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. 오호랏 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. 오 좋은 기능이네요 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. 오.. 그런 기능이었군요..! |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
name: delete branch on close pr | ||
|
||
on: | ||
pull_request: | ||
types: [closed] | ||
|
||
permissions: | ||
pull-requests: write | ||
|
||
jobs: | ||
delete-branch: | ||
runs-on: ubuntu-latest | ||
steps: | ||
- name: delete branch | ||
uses: SvanBoxel/delete-merged-branch@main |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
import { dateCalculator } from './dateCalculator'; | ||
import * as C from './styled-component/CardStyledComponent'; | ||
import { ItemState } from '../pages/sharedPage'; | ||
import styled from 'styled-components'; | ||
import Image from 'next/image'; | ||
|
||
interface CardProps { | ||
item: ItemState; | ||
} | ||
|
||
const Div = styled.div` | ||
position: relative; | ||
width: 340px; | ||
height: 234px; | ||
`; | ||
Comment on lines
+11
to
+15
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.
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. 전에 한번 styled-components로 바꾼 뒤에 추가적으로 Image 컴포넌트 때문에 필요한 거라 여기에 그냥 적어뒀습니다! |
||
|
||
export default function Card({ item }: CardProps) { | ||
const apiDate = new Date(item.createdAt); | ||
const elapsedTime = dateCalculator(apiDate); | ||
const year = apiDate.getFullYear(); | ||
const month = apiDate.getMonth() + 1; | ||
const days = apiDate.getDate(); | ||
|
||
if (item.imageSource === undefined) { | ||
item.imageSource = '/no-image.svg'; | ||
} | ||
|
||
return ( | ||
<C.CardWrapperLink href={item.url} target="_blank"> | ||
<C.CardBox> | ||
<C.CardImgDiv> | ||
<Div> | ||
<Image | ||
className="card-img" | ||
fill | ||
sizes="100%" | ||
src={item.imageSource} | ||
alt={item.title} | ||
style={{ objectFit: 'cover' }} | ||
priority | ||
/> | ||
</Div> | ||
</C.CardImgDiv> | ||
<C.CardText> | ||
<C.CardTimeAgo>{elapsedTime}</C.CardTimeAgo> | ||
<C.TextDescription>{item.description}</C.TextDescription> | ||
<C.CardYear>{`${year}. ${month}. ${days}`}</C.CardYear> | ||
</C.CardText> | ||
</C.CardBox> | ||
</C.CardWrapperLink> | ||
); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
import styled from 'styled-components'; | ||
import * as F from './styled-component/FooterStyledComponent'; | ||
import Image from 'next/image'; | ||
import Link from 'next/link'; | ||
|
||
const Div = styled.div` | ||
position: relative; | ||
width: 20px; | ||
height: 20px; | ||
`; | ||
export default function Footer() { | ||
return ( | ||
<F.FooterContainer> | ||
<F.FoterLeft>©codeit - 2023</F.FoterLeft> | ||
<F.FooterMidle> | ||
<F.PolicyNFaqA href="./policy.html">Privacy Policy</F.PolicyNFaqA> | ||
<F.PolicyNFaqA href="./faq.html">FAQ</F.PolicyNFaqA> | ||
</F.FooterMidle> | ||
<F.FooterRight> | ||
<Link href="https://www.facebook.com"> | ||
<Div> | ||
<Image src="/facebook.svg" fill alt="facebook" /> | ||
</Div> | ||
</Link> | ||
<Link href="https://www.twitter.com"> | ||
<Div> | ||
<Image src="/twitter.svg" fill alt="twitter" /> | ||
</Div> | ||
</Link> | ||
<Link href="https://www.youtube.com"> | ||
<Div> | ||
<Image src="/youtube.svg" fill alt="youtube" /> | ||
</Div> | ||
</Link> | ||
<Link href="https://www.instagram.com"> | ||
<Div> | ||
<Image src="/insta.svg" fill alt="instagram" /> | ||
</Div> | ||
</Link> | ||
</F.FooterRight> | ||
</F.FooterContainer> | ||
); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
import Image from 'next/image'; | ||
import { ChangeEvent, useState } from 'react'; | ||
import eyeOff from 'assets/icon/eye-off.svg'; | ||
import eyeOn from 'assets/icon/eye-on.svg'; | ||
import styled, { css } from 'styled-components'; | ||
|
||
const Div = styled.div` | ||
position: relative; | ||
display: flex; | ||
align-items: center; | ||
width: 350px; | ||
`; | ||
|
||
const StyledInput = styled.input<{ isError: boolean }>` | ||
display: flex; | ||
width: 100%; | ||
padding: 18px 15px; | ||
justify-content: center; | ||
align-items: center; | ||
border-radius: 8px; | ||
color: #373740; | ||
border: 1px solid #ccd5e3; | ||
outline: none; | ||
&:focus { | ||
border-color: #6d6afe; | ||
} | ||
|
||
${({ isError }) => | ||
isError && | ||
css` | ||
border-color: #ff5b56; | ||
|
||
&:focus { | ||
border-color: #ff5b56; | ||
} | ||
`} | ||
`; | ||
|
||
const ErrorText = styled.h4` | ||
color: #ff5b56; | ||
font-size: 14px; | ||
font-style: normal; | ||
font-weight: 400; | ||
line-height: normal; | ||
margin-top: 6px; | ||
`; | ||
|
||
const StyledButton = styled.button` | ||
position: absolute; | ||
border: none; | ||
cursor: pointer; | ||
right: 15px; | ||
background-color: #fff; | ||
`; | ||
|
||
function Input() { | ||
const [isError, setIsError] = useState(false); | ||
const [isVisible, setIsVisible] = useState(false); | ||
const [value, setValue] = useState(''); | ||
|
||
const handleVisibility = () => setIsVisible(!isVisible); | ||
const handleChange = (e: ChangeEvent<HTMLInputElement>) => { | ||
Comment on lines
+57
to
+62
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. setter함수 내에 state를 그대로 쓰는 게 안티패턴이라고 본 것이 기억나네요 https://sangcho.tistory.com/entry/Commons-Mistakes-with-React-useState 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. 오 감사합니다! |
||
setIsError(false); | ||
setValue(e.target.value); | ||
}; | ||
|
||
function blurHandler() { | ||
if (value === '') { | ||
setIsError(true); | ||
} | ||
} | ||
|
||
return ( | ||
<div style={{ backgroundColor: '#f0f6ff' }}> | ||
<Div> | ||
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. 이거는 모든 팀원들한테 해당하는 건데 저희 Styled~ 라고 네이밍 했으니까 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. 바꾸겠습니다! |
||
<StyledInput | ||
placeholder="내용 입력" | ||
isError={isError} | ||
type={isVisible ? 'text' : 'password'} | ||
value={value} | ||
onChange={handleChange} | ||
onBlur={blurHandler} | ||
/> | ||
<StyledButton onClick={handleVisibility}> | ||
<Image | ||
src={isVisible ? eyeOn : eyeOff} | ||
width={16} | ||
height={16} | ||
alt="비밀번호 숨기기 버튼" | ||
/> | ||
</StyledButton> | ||
</Div> | ||
{isError && <ErrorText>내용을 다시 작성해주세요</ErrorText>} | ||
</div> | ||
); | ||
} | ||
|
||
export default Input; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
import { useState, useEffect } from 'react'; | ||
import { getData } from '../pages/api/api'; | ||
import Profile from './Profile'; | ||
import * as N from './styled-component/NavStyledComponent'; | ||
import Image from 'next/image'; | ||
import { useRouter } from 'next/router'; | ||
import Link from 'next/link'; | ||
|
||
export interface LoginState { | ||
id: number; | ||
name: string; | ||
email: string; | ||
profileImageSource: string; | ||
created_at: string; | ||
image_source: string; | ||
auth_id: string; | ||
} | ||
|
||
export default function Nav() { | ||
const [login, setLogin] = useState<LoginState>(); | ||
const router = useRouter(); | ||
|
||
const url = router.pathname; | ||
|
||
const handleLoad = async () => { | ||
if (url === '/shared') { | ||
const data = await getData('sample/user'); | ||
setLogin(data); | ||
} else if (url === '/folder') { | ||
const { data } = await getData('users/1'); | ||
setLogin(data); | ||
Comment on lines
+29
to
+31
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. DEFAULT USER ID와 같이 공통으로 쓰일 것 같은 상수는 따로 관리해서 import 받는 것도 좋을 것 같아요 :) 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. 상수화 해보겠습니다! |
||
} | ||
}; | ||
|
||
useEffect(() => { | ||
handleLoad(); | ||
}, []); | ||
return ( | ||
<N.NavContainer> | ||
<N.NavWrapper> | ||
<Link href="/"> | ||
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. 지금은 잠깐 만들어 놓은 /페이지로 이동하지만 시간이 나면 첫 주차 때 만든 홈페이지로 이동하게 끔 수정하겠습니다. |
||
<N.NavLibraryImg> | ||
<Image src="/linkbrary.svg" fill priority alt="libraryLogo" /> | ||
</N.NavLibraryImg> | ||
</Link> | ||
{login?.id ? ( | ||
<Profile item={login} /> | ||
) : ( | ||
<N.NavLoginButton onClick={handleLoad}>로그인</N.NavLoginButton> | ||
)} | ||
</N.NavWrapper> | ||
</N.NavContainer> | ||
); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
import * as N from './styled-component/NavStyledComponent'; | ||
import { LoginState } from './Nav'; | ||
|
||
interface ProfileProps { | ||
item: LoginState; | ||
} | ||
|
||
export default function Profile({ item }: ProfileProps) { | ||
return ( | ||
<N.ProfileDiv> | ||
<N.ProfileDivLogo> | ||
{item.id ? ( | ||
<N.ProfileHumanImg src={item.profileImageSource} alt="profileImg" /> | ||
) : ( | ||
<N.ProfileHumanImg src={item.image_source} alt="profileImg" /> | ||
)} | ||
</N.ProfileDivLogo> | ||
{item.id ? ( | ||
<N.ProfileDivMail>{item.email}</N.ProfileDivMail> | ||
) : ( | ||
<N.ProfileDivMail>{item.email}</N.ProfileDivMail> | ||
)} | ||
</N.ProfileDiv> | ||
); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
import { getData } from '../pages/api/api'; | ||
import { useCallback, useState, useEffect } from 'react'; | ||
import * as N from './styled-component/NavStyledComponent'; | ||
import Image from 'next/image'; | ||
import styled from 'styled-components'; | ||
|
||
interface UserState { | ||
id: number; | ||
name: string; | ||
profileImageSource: string; | ||
} | ||
|
||
const Div = styled.div` | ||
position: relative; | ||
width: 188px; | ||
height: 48px; | ||
`; | ||
|
||
const SmileDiv = styled.div` | ||
position: relative; | ||
width: 60; | ||
height: 60px; | ||
`; | ||
|
||
export default function SharedHeader() { | ||
const [user, setUser] = useState<UserState>({ | ||
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. 저도 하나의 state로 객체로 관리하는 것을 연습해봐야겠네요 👍 |
||
id: 1, | ||
name: '', | ||
profileImageSource: '', | ||
}); | ||
|
||
const handleLoad = useCallback(async () => { | ||
const { folder } = await getData('sample/folder'); | ||
setUser(folder.owner); | ||
}, []); | ||
useEffect(() => { | ||
handleLoad(); | ||
}, [handleLoad]); | ||
|
||
return ( | ||
<N.NavContainer> | ||
<N.SharedNavWrapper> | ||
<N.SharedNavLogo> | ||
<SmileDiv> | ||
<Image | ||
src={user.profileImageSource} | ||
fill | ||
sizes="100%" | ||
alt="profileImg" | ||
/> | ||
</SmileDiv> | ||
<N.SharedNavDiv className="nav2-div">{user?.name}</N.SharedNavDiv> | ||
</N.SharedNavLogo> | ||
<Div> | ||
<Image | ||
src="/favorites.svg" | ||
fill | ||
sizes="100%" | ||
alt="starImg" | ||
priority | ||
/> | ||
</Div> | ||
</N.SharedNavWrapper> | ||
</N.NavContainer> | ||
); | ||
} |
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.
종민님한테도 코드 리뷰 했던 거지만 next 버전이 올라가면서 이런 방법도 생겼다는 걸 알려드리고 싶었습니다!
현재 코드에서 방법 1 사용하셨는데, 방법 2 로도 가능합니다!
링크
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.
최고!
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.
오 신기하네요. 감사합니다!