-
Notifications
You must be signed in to change notification settings - Fork 79
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #579 from YoungUnKim/Sprint9-김영운
Sprint9 김영운
- Loading branch information
Showing
39 changed files
with
1,363 additions
and
470 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
import axios from '@/utils/axio'; | ||
import { useEffect, useState } from 'react'; | ||
import style from '@/styles/bestPost.module.css'; | ||
import bestBadge from '@/public/icon/img_badge.svg'; | ||
import heartImg from '@/public/icon/ic_heart.svg'; | ||
import Image from 'next/image'; | ||
import timeString from '@/utils/timeString'; | ||
import { useMediaQuery } from 'react-responsive'; | ||
import Link from 'next/link'; | ||
|
||
interface List { | ||
id: number; | ||
title: string; | ||
content: string; | ||
image: null | string; | ||
likeCount: number; | ||
createdAt: string; | ||
updatedAt: string; | ||
writer: Writer; | ||
} | ||
|
||
interface Writer { | ||
id: number; | ||
nickname: string; | ||
} | ||
|
||
export default function BestPost() { | ||
const [bestPosts, setBestPosts] = useState<List[]>([]); | ||
|
||
const isTablet = useMediaQuery({ | ||
query: `(max-width: 1024px)`, | ||
}); | ||
|
||
const isMobile = useMediaQuery({ | ||
query: `(max-width: 768px)`, | ||
}); | ||
|
||
function pageSizePerSize() { | ||
if (isMobile) return 1; | ||
else if (isTablet) return 2; | ||
else return 3; | ||
} | ||
|
||
async function getBest() { | ||
const pageSize = pageSizePerSize(); | ||
const res = await axios.get(`/articles?page=1&pageSize=${pageSize}&orderBy=like`); | ||
const posts = res.data.list ?? []; | ||
setBestPosts(posts); | ||
} | ||
|
||
useEffect(() => { | ||
try { | ||
getBest(); | ||
} catch (error) { | ||
console.log(error); | ||
} | ||
// eslint-disable-next-line react-hooks/exhaustive-deps | ||
}, [isTablet, isMobile]); | ||
|
||
return ( | ||
<> | ||
{bestPosts.map(item => ( | ||
<Link key={item.id} href={`/boards/${item.id}`}> | ||
<div className={style.BestContainer}> | ||
<div className={style.BestBadge}> | ||
<Image width={102} height={30} src={bestBadge} alt="베스트뱃지" className={style.bestBadge} /> | ||
</div> | ||
<div className={style.BestTitle}> | ||
<span className={style.BestTitleText}>{item.title}</span> | ||
{item.image && <Image width={72} height={72} alt="이미지" src={item.image} />} | ||
</div> | ||
<div className={style.BestInfo}> | ||
<div className={style.BestInfoFirst}> | ||
<span className={style.Writer}>{item.writer.nickname}</span> | ||
<div className={style.BestInfoHeart}> | ||
<Image width={16} height={16} alt="하트" src={heartImg} /> | ||
<span className={style.LikeCount}>{item.likeCount}</span> | ||
</div> | ||
</div> | ||
<div> | ||
<span className={style.times}>{timeString(item.createdAt)}</span> | ||
</div> | ||
</div> | ||
</div> | ||
</Link> | ||
))} | ||
</> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
import Image from 'next/image'; | ||
import { useEffect, useRef, useState } from 'react'; | ||
import arrow from '@/public/icon/ic_arrow_down.svg'; | ||
import sort from '@/public/icon/ic_sort.svg'; | ||
import style from '@/styles/dropdown.module.css'; | ||
import { useMediaQuery } from 'react-responsive'; | ||
|
||
interface DropdownProps { | ||
onChange: (order: 'recent' | 'like') => void; | ||
} | ||
|
||
export default function Dropdown({ onChange }: DropdownProps) { | ||
const [isDropdownOpen, setDropdownOpen] = useState(false); | ||
const [order, setOrder] = useState<'recent' | 'like'>('recent'); | ||
|
||
const ORDER_KR = { | ||
recent: '최신순', | ||
like: '좋아요순', | ||
}; | ||
|
||
const isMobile = useMediaQuery({ | ||
query: '(max-width: 768px)', | ||
}); | ||
|
||
const handleMobileChange = () => { | ||
if (isMobile) return <Image src={sort} alt="arrow-icon" />; | ||
else | ||
return ( | ||
<> | ||
<span>{order === 'recent' ? '최신순' : '좋아요순'}</span> | ||
<Image src={arrow} alt="arrow-icon" /> | ||
</> | ||
); | ||
}; | ||
|
||
const dropdownContainerRef = useRef<HTMLDivElement>(null); | ||
|
||
const handleOrderChange = (order: 'recent' | 'like') => { | ||
setOrder(order); | ||
onChange(order as 'recent' | 'like'); | ||
setDropdownOpen(false); | ||
}; | ||
|
||
const handleDropdownClick = () => { | ||
setDropdownOpen(prev => !prev); | ||
}; | ||
|
||
useEffect(() => { | ||
const handleClickOutside = (e: MouseEvent) => { | ||
if (dropdownContainerRef.current && !dropdownContainerRef.current.contains(e.target as Node)) { | ||
setDropdownOpen(false); | ||
} | ||
}; | ||
|
||
document.addEventListener('mousedown', handleClickOutside); | ||
return () => { | ||
document.removeEventListener('mousedown', handleClickOutside); | ||
}; | ||
}, []); | ||
|
||
useEffect(() => { | ||
setDropdownOpen(false); | ||
}, [order]); | ||
|
||
return ( | ||
<div className={style.dropdown_container} ref={dropdownContainerRef}> | ||
<div className={`${style.dropdown} ${isDropdownOpen ? style.open : ''}`} onClick={handleDropdownClick}> | ||
<div className={style.dropdown_button}> | ||
<div className={style.select}>{handleMobileChange()}</div> | ||
{isDropdownOpen && ( | ||
<ul className={style.dropdown_contents}> | ||
<li | ||
className={`${style.dropdown_li} ${order === 'recent' ? style.selected : ''}`} | ||
onClick={() => handleOrderChange('recent')} | ||
> | ||
최신순 | ||
</li> | ||
<li | ||
className={`${style.dropdown_li} ${order === 'like' ? style.selected : ''}`} | ||
onClick={() => handleOrderChange('like')} | ||
> | ||
좋아요순 | ||
</li> | ||
</ul> | ||
)} | ||
</div> | ||
</div> | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
import React from 'react'; | ||
import MainLogo from '@/public/icon/main_logo.svg'; | ||
import SmallMainLogo from '@/public/icon/main_logo_small.svg'; | ||
import Link from 'next/link'; | ||
import { useRouter } from 'next/router'; | ||
import Image from 'next/image'; | ||
import styles from '@/styles/Header.module.css'; | ||
import user_icon from '@/public/icon/user_icon.svg'; | ||
|
||
const NavBar: React.FC = () => { | ||
const router = useRouter(); | ||
|
||
return ( | ||
<nav className={styles.navvar}> | ||
<Link href="/"> | ||
<Image width={153} height={51} className={styles.mainlogo} src={MainLogo} alt="로고" /> | ||
<Image width={81} height={27} className={styles.mainlogo} src={SmallMainLogo} alt="로고" /> | ||
</Link> | ||
<div className={styles.menus}> | ||
<Link href="/boards" className={router.pathname === '/boards' ? styles.focus : ''}> | ||
<span>자유게시판</span> | ||
</Link> | ||
<Link href="/items" className={router.pathname === '/items' ? styles.focus : ''}> | ||
<span>중고마켓</span> | ||
</Link> | ||
</div> | ||
<Link href="/mypage"> | ||
<Image width={40} height={40} alt="user-icon" src={user_icon} /> | ||
</Link> | ||
</nav> | ||
); | ||
}; | ||
|
||
export default NavBar; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
import styles from '@/styles/posts.module.css'; | ||
import Image from 'next/image'; | ||
import heartImg from '@/public/icon/ic_heart.svg'; | ||
import timeString from '@/utils/timeString'; | ||
import user_icon from '@/public/icon/user_icon.svg'; | ||
import Link from 'next/link'; | ||
|
||
interface List { | ||
id: number; | ||
title: string; | ||
content: string; | ||
image: null | string; | ||
likeCount: number; | ||
createdAt: string; | ||
updatedAt: string; | ||
writer: Writer; | ||
} | ||
interface Writer { | ||
id: number; | ||
nickname: string; | ||
} | ||
|
||
interface PostsProps { | ||
posts: List; | ||
} | ||
|
||
export default function Posts({ posts }: PostsProps) { | ||
return ( | ||
<Link href={`/borads/${posts.id}`}> | ||
<div key={posts.id} className={styles.postContainer}> | ||
<div className={styles.postsTop}> | ||
<span className={styles.postsTitle}>{posts.title}</span> | ||
{posts.image && <Image width={72} height={72} alt="이미지" src={posts.image} />} | ||
</div> | ||
<div className={styles.postsBottom}> | ||
<div className={styles.BestInfoFirst}> | ||
<Image width={24} height={24} alt="프로필사진" src={user_icon} /> | ||
<span className={styles.Writer}>{posts.writer.nickname}</span> | ||
<span className={styles.times}>{timeString(posts.createdAt)}</span> | ||
</div> | ||
<div> | ||
<div className={styles.BestInfoHeart}> | ||
<Image width={16} height={16} alt="하트" src={heartImg} /> | ||
<span className={styles.LikeCount}>{posts.likeCount}</span> | ||
</div> | ||
</div> | ||
</div> | ||
</div> | ||
</Link> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
import styles from '@/styles/Board.module.css'; | ||
import searchIC from '@/public/icon/ic_Search.svg'; | ||
import Image from 'next/image'; | ||
|
||
interface SearchInputProps { | ||
value: string; | ||
onChange: (event: React.ChangeEvent<HTMLInputElement>) => void; | ||
} | ||
|
||
export default function SearchInput({ value, onChange }: SearchInputProps) { | ||
return ( | ||
<div className={styles.SearchContainer}> | ||
<Image width={15} height={15} src={searchIC} alt="검색 아이콘" className={styles.SearchIcon} /> | ||
<input | ||
type="text" | ||
placeholder="검색할 상품을 입력해주세요" | ||
value={value} | ||
onChange={onChange} | ||
className={styles.SearchInput} | ||
/> | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,16 @@ | ||
/** @type {import('next').NextConfig} */ | ||
const nextConfig = { | ||
reactStrictMode: true, | ||
} | ||
images: { | ||
remotePatterns: [ | ||
{ | ||
protocol: 'https', | ||
hostname: 'sprint-fe-project.s3.ap-northeast-2.amazonaws.com', | ||
port: '', | ||
pathname: '/Sprint_Mission/**', | ||
}, | ||
], | ||
}, | ||
}; | ||
|
||
module.exports = nextConfig | ||
module.exports = nextConfig; |
Oops, something went wrong.