Skip to content
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

[김희진] sprint10 #125

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@
const nextConfig = {
reactStrictMode: true,
images: {
domains: ['sprint-fe-project.s3.ap-northeast-2.amazonaws.com'],
domains: [
'sprint-fe-project.s3.ap-northeast-2.amazonaws.com',
'example.com',
'via.placeholder.com',
'flexible.img.hani.co.kr',
],
},
};

Expand Down
9 changes: 9 additions & 0 deletions pages/boards/[id].tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { useRouter } from 'next/router';
import styles from './[id].module.css';

export default function BoardDetail() {
const router = useRouter();
const { id } = router.query;

return <div>{id}</div>;
}
13 changes: 9 additions & 4 deletions pages/boards/components/BestBoardCard.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import Image from 'next/image';
import styles from './BestBoardCard.module.css';
import medalSvg from '@/src/assets/ic_medal.svg';
import heardSvg from '@/src/assets/ic_heart.svg';
import { Board } from '@/src/apis/boardTypes';
import heartSvg from '@/src/assets/ic_heart.svg';
import type { Board } from '@/src/apis/boardTypes';

interface BestBoardCardProps
extends Pick<
Expand All @@ -26,13 +26,18 @@ export default function BestBoardCard({
<div className={styles.contentContainer}>
<h4 className={styles.contentTitle}>{title}</h4>
<div className={styles.contentImgWrapper}>
<Image src={image} alt="medal" fill style={{ objectFit: 'cover' }} />
<Image
src={image}
alt="게시판 첨부이미지"
fill
style={{ objectFit: 'cover' }}
/>
</div>
</div>
<div className={styles.additionalInfo}>
<span>{writer.nickname}</span>
<div className={styles.likeCountWrapper}>
<Image src={heardSvg} alt="heardIcon" width={16} height={16} />
<Image src={heartSvg} alt="heartIcon" width={16} height={16} />
<span>{likeCount}</span>
</div>
<span>{new Date(createdAt).toLocaleDateString()}</span>
Expand Down
2 changes: 1 addition & 1 deletion pages/boards/components/BestBoards.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Board } from '@/src/apis/boardTypes';
import type { Board } from '@/src/apis/boardTypes';
import BestBoardCard from './BestBoardCard';
import styles from './BestBoards.module.css';

Expand Down
74 changes: 74 additions & 0 deletions pages/boards/components/BoardCard.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
.boardCard {
background: #fcfcfc;
padding: 20px;
position: relative;
display: flex;
flex-direction: column;
gap: 16px;
}

.boardCard::after {
content: '';
position: absolute;
left: 0;
bottom: 0;
width: 100%;
height: 1px;
background-color: var(--Secondary-200);
}

.contentContainer {
display: flex;
gap: 8px;
min-height: 72px;
}

.title {
color: var(--Secondary-800);
font-size: 20px;
font-weight: 600;

flex: 1;
}

.imageWrapper {
position: relative;
width: 72px;
height: 72px;

border-radius: 6px;
border: 1px solid var(--Secondary-200, #e5e7eb);
background: #fff;
}

.additionalInfo {
display: flex;
justify-content: space-between;
}

.infoWrapper {
display: flex;
align-items: center;
gap: 8px;
}

.nickname {
color: var(--Secondary-600, #4b5563);
font-size: 14px;
font-weight: 400;
}

.date {
color: var(--Secondary-400, #9ca3af);
font-size: 14px;
font-weight: 400;
}

.likeCountWrapper {
display: flex;
align-items: center;
gap: 8px;
color: var(--Secondary-500, #6b7280);
font-size: 16px;
font-weight: 400;
}
44 changes: 44 additions & 0 deletions pages/boards/components/BoardCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import Image from 'next/image';
import styles from './BoardCard.module.css';
import type { Board } from '@/src/apis/boardTypes';
import heartSvg from '@/src/assets/ic_heart.svg';
import avatarSvg from '@/src/assets/avatar.svg';

export default function BoardCard({
title,
image,
writer,
createdAt,
likeCount,
}: Board) {
return (
<div className={styles.boardCard}>
<div className={styles.contentContainer}>
<p className={styles.title}>{title}</p>
{image && (
<div className={styles.imageWrapper}>
<Image
src={image}
alt="게시판 첨부이미지"
fill
style={{ objectFit: 'cover' }}
/>
</div>
)}
</div>
<div className={styles.additionalInfo}>
<div className={styles.infoWrapper}>
<Image src={avatarSvg} alt="avatar" width={24} height={24} />
<span className={styles.nickname}>{writer.nickname}</span>
<span className={styles.date}>
{new Date(createdAt).toLocaleDateString()}
</span>
</div>
<div className={styles.likeCountWrapper}>
<Image src={heartSvg} alt="heardIcon" width={16} height={16} />
<span>{likeCount}</span>
</div>
</div>
</div>
);
}
27 changes: 27 additions & 0 deletions pages/boards/components/Boards.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,16 @@
font-weight: 700;
}

.filter {
display: flex;
gap: 20px;
}

.searchBar {
padding: 9px 20px 9px 16px;
border-radius: 12px;
background: var(--Secondary-100, #f3f4f6);
flex: 1;

display: flex;
align-items: center;
Expand All @@ -31,6 +37,27 @@
color: var(--Secondary-400, #9ca3af);
}

.options {
height: 100%;
padding: 12px 20px;
padding-right: 50px;

border-radius: 12px;
border: 1px solid var(--Secondary-200, #e5e7eb);

appearance: none;
background: url('../../../src/assets/ic_arrow_down.svg') no-repeat right 10px
center;
}

.boardsContainer {
margin-top: 24px;
display: flex;
flex-direction: column;
justify-content: center;
gap: 24px;
}

/* tablet */
@media screen and (max-width: 1199px) {
.boardsHeader {
Expand Down
42 changes: 38 additions & 4 deletions pages/boards/components/Boards.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,59 @@ import Image from 'next/image';
import Button from '@/src/components/Button';
import styles from './Boards.module.css';
import searchSvg from '@/src/assets/ic_search.svg';
import { useState } from 'react';
import BoardCard from './BoardCard';
import Link from 'next/link';
import { useBoards } from '@/src/hooks/useBoards';

export default function Boards() {
const [orderBy, setOrderBy] = useState('recent');
const { boards, isLoading, error, observerRef, resetBoards } =
useBoards(orderBy);

const handleOptionChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
const newOrder = e.target.value;
setOrderBy(newOrder);
resetBoards();
};

return (
<div>
<div className={styles.boardsHeader}>
<section className={styles.boardsHeader}>
<h3 className={styles.title}>게시글</h3>
<div>
<Button>글쓰기</Button>
</div>
</div>
<div className={styles.filter}>
</section>
<section className={styles.filter}>
<div className={styles.searchBar}>
<Image src={searchSvg} alt="searchIcon" width={24} height={24} />
<input
className={styles.searchData}
placeholder="검색할 상품을 입력해주세요"
/>
</div>
</div>
<div>
<select
className={styles.options}
id="options"
onChange={handleOptionChange}
>
<option value="recent">최신순</option>
<option value="like">좋아요순</option>
</select>
</div>
</section>
<section className={styles.boardsContainer}>
{boards.map((board) => (
<Link key={board.id} href={`/boards/${board.id}`}>
<BoardCard {...board} />
</Link>
))}
{isLoading && <div>Loading...</div>}
{error && <div>{error}</div>}
</section>
<div ref={observerRef} style={{ height: '1px' }}></div>
Copy link
Collaborator

Choose a reason for hiding this comment

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

P3:
해당 태그가 보이면 데이터를 추가로 가지고 오게 되니 이러한 요소는 마지막 요소보다 마지막에서 조금 더 위의 요소,
예를 들면 10개의 아이템중 마지막이 아닌 7번째 요소에 이벤트를 걸어두는 것이 더 좋습니다.
그러면 유저의 스크롤이 다 되기전에 미리 데이터를 불러오게 되어 UX 측면에서 장점이 있습니다.

</div>
);
}
4 changes: 2 additions & 2 deletions pages/boards/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { GetStaticProps } from 'next';
import BestBoards from './components/BestBoards';
import { Board } from '@/src/apis/boardTypes';
import type { Board } from '@/src/apis/boardTypes';
import { getBoards } from '@/src/apis/boardsApi';
import Boards from './components/Boards';

Expand Down Expand Up @@ -28,6 +28,6 @@ export const getStaticProps: GetStaticProps = async () => {
props: {
boards: list || [],
},
revalidate: 600, // Re-generate the page every 600 seconds (ISR)
revalidate: 600,
};
};
2 changes: 1 addition & 1 deletion src/apis/boardTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,6 @@ export interface GetBoardsResponse {
export interface GetBoardsRequestParams {
page?: number;
pageSize?: number;
orderBy?: 'recent' | 'like';
orderBy?: string;
keyword?: string;
}
2 changes: 1 addition & 1 deletion src/apis/boardsApi.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Board, GetBoardsResponse, GetBoardsRequestParams } from './boardTypes';
import type { GetBoardsResponse, GetBoardsRequestParams } from './boardTypes';

const BASE_URL = 'https://panda-market-api.vercel.app';

Expand Down
5 changes: 5 additions & 0 deletions src/assets/ic_arrow_down.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 1 addition & 2 deletions src/components/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,11 @@ import styles from './Button.module.css';

interface PrimaryButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
children: React.ReactNode;
className?: string;
}

export default function Button({
children,
className,
className = '',
...props
}: PrimaryButtonProps) {
return (
Expand Down
19 changes: 19 additions & 0 deletions src/components/GlobalLayout.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,22 @@
margin-top: 75px;
width: 100%;
}

.maxContainer {
max-width: var(--size-max-width);
margin: auto;
}

/* tablet */
@media screen and (max-width: 1199px) {
.maxContainer {
max-width: 760px;
}
}

/* mobile */
@media screen and (max-width: 767px) {
.maxContainer {
max-width: 360px;
}
}
2 changes: 1 addition & 1 deletion src/components/GlobalLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export default function GlobalLayout({ children }: { children: ReactNode }) {
<>
<Header />
<main className={styles.main}>
<div className={'max-container'}>{children}</div>
<div className={styles.maxContainer}>{children}</div>
</main>
</>
);
Expand Down
4 changes: 4 additions & 0 deletions src/components/Header.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@
border-bottom: 1px solid #dfdfdf;
}

.active {
color: var(--Primary-100);
}

.headerContainer {
padding: 10px 0;
display: flex;
Expand Down
Loading
Loading