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 #625

Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
1e8e548
Refactor: 재사용하지 않는 컴포넌트 파일 삭제 및 합침
cindycho0423 Jun 4, 2024
70d5c7b
Remove: 사용하지 않는 리셋 css 삭제
cindycho0423 Jun 4, 2024
2bcf067
Refactor: 이미지 파일들 위치 변경 및 네이밍 변경
cindycho0423 Jun 4, 2024
2f49f38
Refactor: 날짜 포맷 변경 함수 utils로 분리
cindycho0423 Jun 4, 2024
6a79bba
Refactor: type 파일 생성 및 페이지별로 변경
cindycho0423 Jun 4, 2024
80f4b4f
Refactor: 조언 관련 수정
cindycho0423 Jun 4, 2024
b04a38c
Refactor: 함수 형태 변경
cindycho0423 Jun 4, 2024
20621c9
Chore: 이미지 파일들 추가
cindycho0423 Jun 6, 2024
b0857bf
Chore: 불필요한 파일 정리 및 설정 파일 정리
cindycho0423 Jun 7, 2024
535f1ed
Feat: api파일 생성
cindycho0423 Jun 7, 2024
ada1db1
Chore: 폴더 위치 변경으로 인한 임포트문 수정
cindycho0423 Jun 7, 2024
7e71b57
Feat: 게시글 상세 페이지 구현 및 컴포넌트 분리
cindycho0423 Jun 7, 2024
365c1d0
Feat: 랜딩페이지 및 푸터 가져옴
cindycho0423 Jun 7, 2024
a7f2f18
Feat: addboard 페이지 구현
cindycho0423 Jun 7, 2024
be0311d
Feat: 로그인 회원가입 페이지 생성
cindycho0423 Jun 7, 2024
9d9a3cd
Feat: 로그인 되었을 때 안되었을 때 네비게이션 변경
cindycho0423 Jun 7, 2024
c67e075
Feat: boards 페이지 폴더 생성 및 변경
cindycho0423 Jun 7, 2024
3b1f2a6
Feat: 페이지네이션 구현
cindycho0423 Jun 8, 2024
f31f1b1
Feat: 댓글창 무한 스크롤 구현
cindycho0423 Jun 8, 2024
67cd0b8
Remove: 불필요한 코드 삭제
cindycho0423 Jun 8, 2024
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
32 changes: 32 additions & 0 deletions components/article-preview.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import getFormatDate from '@/lib/utils/formatDate';
import Image from 'next/image';
import icHeart from '@/public/images/icons/ic_heart.png';
import icProfile from '@/public/images/icons/ic_profile.png';
import { ArticleProps } from '@/types';

export default function ArticlePreview({ createdAt, likeCount, image, title, writer }: ArticleProps) {
const createdDate = getFormatDate(createdAt);
return (
<div className='flex flex-col gap-4'>
<div className='flex justify-between gap-2 font-semibold text-cool-gray800'>
<p className='min-h-[48px] min-w-[263px] sm: w-[263px] md:w-[616px] lg:w-[1120px] leading-5'>{title}</p>
{image && (
<div className='flex justify-center items-center bg-white border border-solid rounded-md border-cool-gray200 w-[72px] h-[72px]'>
<Image src={image} alt='게시글 이미지' width={48} height={45} />
</div>
)}
</div>
<div className='flex justify-between pb-6 border-b border-solid text-cool-gray400 border-cool-gray200'>
<div className='flex items-center gap-2'>
<Image src={icProfile} alt='프로필 이미지' width={24} />
<span className='text-cool-gray600'>{writer.nickname}</span>
<time>{createdDate}</time>
</div>
<div className='flex items-center gap-1'>
<Image src={icHeart} alt='좋아요 하트' width={16}></Image>
<span>{likeCount}+</span>
</div>
</div>
</div>
);
}
63 changes: 26 additions & 37 deletions components/articles.tsx
Original file line number Diff line number Diff line change
@@ -1,52 +1,67 @@
import Image from 'next/image';
import Link from 'next/link';
import icSearch from '@/public/images/icons/ic_search.png';
import icHeart from '@/public/images/icons/ic_heart.png';
import icProfile from '@/public/images/icons/ic_profile.png';
import SelectBox from './select-box';
import { getArticles } from '@/lib/api/getArticles';
import { ArticleProps } from '@/types';
import getFormatDate from '@/lib/utils/formatDate';
import { ChangeEvent, useState, useEffect } from 'react';
import useDebounce from '@/hooks/useDebounce';
import { constants, SEARCH_TIME } from '../lib/constants';
import ArticlePreview from './article-preview';
import Pagination from './pagination';

interface Props {
articlesServer: ArticleProps[];
totalCount: number;
}

export default function Articles({ articlesServer }: Props) {
export default function Articles({ articlesServer, totalCount: initialTotalCount }: Props) {
const [orderBy, setOrderby] = useState('recent');
const [keyword, setKeyword] = useState('');
const [articles, setArticles] = useState<ArticleProps[]>(articlesServer);
const [pageNum, setPageNum] = useState(constants.PAGE_NUM);
cindycho0423 marked this conversation as resolved.
Show resolved Hide resolved
const [totalCount, setTotalCount] = useState(initialTotalCount);
const debouncedValue = useDebounce(keyword, SEARCH_TIME);

const handleOrderClick = async (sortType: string): Promise<void> => {
setOrderby(sortType);
setPageNum(1);
try {
const sortData = await getArticles(constants.PAGE_NUM, constants.PAGE_SIZE, sortType, keyword);
const { list: sortData, totalCount: updatedTotalCount } = await getArticles(
1,
constants.PAGE_SIZE,
sortType,
keyword
);
setArticles(sortData);
setTotalCount(updatedTotalCount);
} catch (error) {
console.error(error);
}
};

const handleChange = async (e: ChangeEvent<HTMLInputElement>): Promise<void> => {
const handleChange = (e: ChangeEvent<HTMLInputElement>): void => {
setKeyword(e.target.value);
setPageNum(1);
};

useEffect(() => {
const fetchArticles = async () => {
try {
const sortData = await getArticles(constants.PAGE_NUM, constants.PAGE_SIZE, orderBy, debouncedValue);
const { list: sortData, totalCount: updatedTotalCount } = await getArticles(
pageNum,
constants.PAGE_SIZE,
orderBy,
debouncedValue
);
setArticles(sortData);
setTotalCount(updatedTotalCount);
} catch (error) {
console.error(error);
}
};

fetchArticles();
}, [debouncedValue, orderBy]);
}, [orderBy, pageNum, debouncedValue]);

return (
<div>
Expand All @@ -61,40 +76,14 @@ export default function Articles({ articlesServer }: Props) {
/>
<SelectBox handleOrder={handleOrderClick} />
</div>
<div className='flex flex-col gap-6'>
<div className='flex flex-col gap-6 min-h-[350px]'>
{articles.map(article => (
<Link key={article.id} href={`/boards/${article.id}`}>
<ArticlePreview {...article} />
</Link>
))}
</div>
</div>
);
}

function ArticlePreview({ createdAt, likeCount, image, title, writer }: ArticleProps) {
const createdDate = getFormatDate(createdAt);
return (
<div className='flex flex-col gap-4'>
<div className='flex justify-between gap-2 font-semibold text-cool-gray800'>
<p className='min-h-[48px] min-w-[263px] sm: w-[263px] md:w-[616px] lg:w-[1120px] leading-5'>{title}</p>
{image && (
<div className='flex justify-center items-center bg-white border border-solid rounded-md border-cool-gray200 w-[72px] h-[72px]'>
<Image src={image} alt='게시글 이미지' width={48} height={45} />
</div>
)}
</div>
<div className='flex justify-between pb-6 border-b border-solid text-cool-gray400 border-cool-gray200'>
<div className='flex items-center gap-2'>
<Image src={icProfile} alt='프로필 이미지' width={24} />
<span className='text-cool-gray600'>{writer.nickname}</span>
<time>{createdDate}</time>
</div>
<div className='flex items-center gap-1'>
<Image src={icHeart} alt='좋아요 하트' width={16}></Image>
<span>{likeCount}+</span>
</div>
</div>
<Pagination pageNum={pageNum} setPageNum={setPageNum} totalCount={totalCount} />
</div>
);
}
63 changes: 63 additions & 0 deletions components/pagination.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { Dispatch, SetStateAction } from 'react';
import { constants } from '../lib/constants';

type Props = {
totalCount: number;
pageNum: number;
setPageNum: Dispatch<SetStateAction<number>>;
};

export default function Pagination({ pageNum, setPageNum, totalCount }: Props) {
const totalPage = Math.ceil(totalCount / constants.PAGE_SIZE);
const pageGroup = Math.ceil(pageNum / 5);
const startPage = (pageGroup - 1) * 5 + 1;
let lastPage = pageGroup * 5;
if (lastPage > totalPage) lastPage = totalPage;

const handlePrevious = () => {
if (pageNum > 1) {
setPageNum(pageNum - 1);
}
};

const handleNext = () => {
if (pageNum < totalPage) {
setPageNum(pageNum + 1);
}
};

const handlePage = (pageIndex: number) => {
setPageNum(pageIndex);
};

return (
<div className='flex items-center justify-center gap-1 my-10'>
<button
onClick={handlePrevious}
disabled={pageNum === 1}
className='bg-white border border-gray-200 rounded-[50%] min-w-9 min-h-9 active:border-0 active:text-white active:bg-blue-500'>
&lt;
</button>
<div className='flex justify-center'>
{Array.from({ length: lastPage - startPage + 1 }, (_, i) => startPage + i).map(pageIndex => (
<button
key={pageIndex}
onClick={() => handlePage(pageIndex)}
className={`min-w-9 min-h-9 mx-1 rounded-full border ${
pageNum === pageIndex
? 'bg-blue-500 text-white'
: 'bg-white border-gray-200 active:border-0 active:text-white active:bg-blue-500'
}`}>
{pageIndex}
</button>
))}
</div>
<button
onClick={handleNext}
disabled={pageNum === totalPage}
className='rounded-[50%] min-w-9 min-h-9 bg-white border border-gray-200 active:border-0 active:text-white active:bg-blue-500'>
&gt;
</button>
</div>
);
}
6 changes: 3 additions & 3 deletions lib/api/getArticles.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import { ArticleProps } from '@/types';
import { ArticleProps, ArticleList } from '@/types';
import axiosInstance from './axios';

export const getArticles = async (
page: number,
pageSize: number,
orderBy: string[] | string,
keyword?: string[] | string
): Promise<ArticleProps[]> => {
): Promise<ArticleList> => {
const query = `page=${page}&pageSize=${pageSize}&orderBy=${orderBy}&keyword=${keyword}`;
try {
const response = await axiosInstance.get(`/articles?${query}`);
return response.data.list ?? [];
return response.data;
} catch (error) {
console.error(`Failed to fetch items: ${error}`);
throw new Error('정보를 불러오는데 실패했습니다');
Expand Down
6 changes: 6 additions & 0 deletions next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ const nextConfig = {
port: '',
pathname: '/...',
},
{
protocol: 'https',
hostname: 'example.com',
port: '',
pathname: '/',
},
],
},
};
Expand Down
13 changes: 9 additions & 4 deletions pages/boards/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,33 +2,38 @@ import Head from 'next/head';
import BestArticles from '@/components/best-articles';
import Articles from '@/components/articles';
import axiosInstance from '@/lib/api/axios';
import { ArticleProps } from '@/types';
import { ArticleProps, ArticleList } from '@/types';
import Link from 'next/link';

interface Props {
articlesServer: ArticleProps[];
totalCount: number;
}
export async function getServerSideProps() {
try {
const res = await axiosInstance.get(`/articles`);
const articlesServer: ArticleProps[] = res.data.list ?? [];
const getArticlesServer: ArticleList = res.data ?? {};
const articlesServer: ArticleProps[] = getArticlesServer.list ?? [];
const totalCount = getArticlesServer.totalCount;

return {
props: {
articlesServer,
totalCount,
},
};
} catch (error) {
console.error('Failed to fetch article:', error);
return {
props: {
articlesServer: [],
totalCount: 0,
},
};
}
}

export default function Boards({ articlesServer }: Props) {
export default function Boards({ articlesServer, totalCount }: Props) {
return (
<>
<Head>
Expand All @@ -45,7 +50,7 @@ export default function Boards({ articlesServer }: Props) {
<button className='bg-brand-blue rounded-lg text-white w-[88px] h-[42px] font-semibold'>글쓰기</button>
</Link>
</div>
<Articles articlesServer={articlesServer} />
<Articles articlesServer={articlesServer} totalCount={totalCount} />
</div>
</>
);
Expand Down
5 changes: 5 additions & 0 deletions types.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
export type ArticleList = {
totalCount: number;
list: ArticleProps[];
};

export type ArticleProps = {
updatedAt: string;
createdAt: string;
Expand Down