This repository has been archived by the owner on Jul 29, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* test: 전체글 조회 mock 추가 * feat: 전체글 조회 api 구현 * chore: 사용하지 않는 파일 삭제 * feat: `Pagination` 공통 컴포넌트 구현 * feat: `home-border` 아이콘 추가 * refactor: 전체 글 api 스펙 수정으로 인한 수정 * fix: `WritingTable` 발행 시간 -> 생성 날짜로 수정 * feat: `HomePage` 페이지 구현 * feat: `HomePage` 레이아웃과 라우터 연결 * fix: API 명세 수정으로 인한 타입 수정 --------- Co-authored-by: Youngjin Park <[email protected]>
- Loading branch information
1 parent
0b8a48a
commit c4dbe7e
Showing
17 changed files
with
458 additions
and
18 deletions.
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
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
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
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
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 styled from 'styled-components'; | ||
|
||
type Props = { | ||
pageLength: number; | ||
activePage: number; | ||
changeActivePage: (pageIndex: number) => void; | ||
}; | ||
|
||
const Pagination = ({ pageLength, activePage, changeActivePage }: Props) => { | ||
const isShowPrevious = activePage > 1; | ||
const isShowNext = activePage < pageLength; | ||
|
||
// 시작 페이지와 끝 페이지의 범위를 설정해주는 함수 | ||
const setPageRange = () => { | ||
const isBetween1and4 = activePage < 5; | ||
const isNearLast4Pages = activePage >= pageLength - 4; | ||
|
||
if (isBetween1and4) return [1, Math.min(9, pageLength)]; | ||
|
||
if (isNearLast4Pages) return [Math.max(pageLength - 8, 1), pageLength]; | ||
|
||
return [activePage - 4, activePage + 4]; | ||
}; | ||
|
||
const [startPage, endPage] = setPageRange(); | ||
|
||
return ( | ||
<S.Container> | ||
<S.PaginationButton $isShow={isShowPrevious} onClick={() => changeActivePage(activePage - 1)}> | ||
{'<'} | ||
</S.PaginationButton> | ||
{Array.from({ length: endPage - startPage + 1 }, (_, index) => { | ||
const pageIndex = startPage + index; | ||
|
||
return ( | ||
<li key={pageIndex}> | ||
<S.PageButton | ||
$isActive={pageIndex === activePage} | ||
onClick={() => changeActivePage(pageIndex)} | ||
> | ||
{pageIndex} | ||
</S.PageButton> | ||
</li> | ||
); | ||
})} | ||
<S.PaginationButton $isShow={isShowNext} onClick={() => changeActivePage(activePage + 1)}> | ||
{'>'} | ||
</S.PaginationButton> | ||
</S.Container> | ||
); | ||
}; | ||
|
||
export default Pagination; | ||
|
||
const S = { | ||
Container: styled.div` | ||
display: flex; | ||
gap: 16px; | ||
`, | ||
|
||
PaginationButton: styled.button<{ $isShow: boolean }>` | ||
width: 2.8rem; | ||
height: 2.8rem; | ||
border-radius: 4px; | ||
font-size: 2rem; | ||
opacity: ${({ $isShow }) => ($isShow ? 1 : 0)}; | ||
pointer-events: ${({ $isShow }) => ($isShow ? 'auto' : 'none')}; | ||
&:hover { | ||
background-color: ${({ theme }) => theme.color.gray3}; | ||
} | ||
`, | ||
|
||
PageButton: styled.button<{ $isActive: boolean }>` | ||
width: 2.8rem; | ||
height: 2.8rem; | ||
background-color: ${({ theme, $isActive }) => ($isActive ? theme.color.gray3 : 'inherit')}; | ||
border-radius: 4px; | ||
font-size: 1.6rem; | ||
font-weight: ${({ $isActive }) => ($isActive ? 600 : 400)}; | ||
&:hover { | ||
background-color: ${({ theme }) => theme.color.gray3}; | ||
} | ||
`, | ||
}; |
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,43 @@ | ||
import { HomeBorderIcon } from 'assets/icons'; | ||
import { NAVIGATE_PATH } from 'constants/path'; | ||
import { usePageNavigate } from 'hooks/usePageNavigate'; | ||
import { useLocation } from 'react-router-dom'; | ||
import styled from 'styled-components'; | ||
|
||
const HomeButton = () => { | ||
const location = useLocation(); | ||
const { goSpacePage } = usePageNavigate(); | ||
|
||
return ( | ||
<S.Button onClick={goSpacePage} $isClicked={location.pathname === NAVIGATE_PATH.spacePage}> | ||
<HomeBorderIcon /> | ||
<S.Title>전체 글</S.Title> | ||
</S.Button> | ||
); | ||
}; | ||
|
||
export default HomeButton; | ||
|
||
const S = { | ||
Button: styled.button<{ $isClicked: boolean }>` | ||
display: flex; | ||
align-items: center; | ||
gap: 8px; | ||
width: 100%; | ||
height: 3.6rem; | ||
padding: 8px; | ||
background-color: ${({ theme, $isClicked }) => $isClicked && theme.color.gray4}; | ||
border-radius: 4px; | ||
text-align: start; | ||
&:hover { | ||
background-color: ${({ theme }) => theme.color.gray3}; | ||
} | ||
`, | ||
|
||
Title: styled.p` | ||
font-size: 1.4rem; | ||
font-weight: 500; | ||
`, | ||
}; |
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,130 @@ | ||
import { Fragment } from 'react'; | ||
import styled from 'styled-components'; | ||
import { usePageNavigate } from 'hooks/usePageNavigate'; | ||
import { dateFormatter } from 'utils/date'; | ||
import { blogIcon } from 'components/WritingTable/WritingTable'; | ||
import Pagination from 'components/@common/Pagination/Pagination'; | ||
import { useHomeTable } from './useHomeTable'; | ||
|
||
type Props = { | ||
initialPageIndex?: number; | ||
}; | ||
|
||
const HomeTable = ({ initialPageIndex = 1 }: Props) => { | ||
const { content, totalPages, rowRef, activePage, changeActivePage } = | ||
useHomeTable(initialPageIndex); | ||
const { goWritingPage } = usePageNavigate(); | ||
|
||
if (!content || !totalPages) return null; | ||
|
||
return ( | ||
<S.Container> | ||
<S.HomeTable summary='카테고리 내부 글 목록을 나타낸다'> | ||
<colgroup> | ||
<col width='20%' /> | ||
<col width='40%' /> | ||
<col width='20%' /> | ||
<col width='20%' /> | ||
</colgroup> | ||
<thead> | ||
<tr ref={rowRef} tabIndex={0}> | ||
<th>카테고리</th> | ||
<th>글 제목</th> | ||
<th>생성 날짜</th> | ||
<th>발행한 블로그 플랫폼</th> | ||
</tr> | ||
</thead> | ||
<tbody> | ||
{content.map( | ||
({ | ||
id, | ||
title, | ||
publishedDetails, | ||
createdAt, | ||
category: { id: categoryId, categoryName }, | ||
}) => ( | ||
<tr | ||
key={id} | ||
onClick={() => goWritingPage({ categoryId, writingId: id })} | ||
role='button' | ||
tabIndex={0} | ||
> | ||
<td>{categoryName}</td> | ||
<td>{title}</td> | ||
<td>{dateFormatter(createdAt, 'YYYY.MM.DD.')}</td> | ||
<td> | ||
<S.PublishedToIconContainer> | ||
{publishedDetails.map(({ blogName }, index) => ( | ||
<Fragment key={index}>{blogIcon[blogName]}</Fragment> | ||
))} | ||
</S.PublishedToIconContainer> | ||
</td> | ||
</tr> | ||
), | ||
)} | ||
</tbody> | ||
</S.HomeTable> | ||
<Pagination | ||
pageLength={totalPages} | ||
activePage={activePage} | ||
changeActivePage={changeActivePage} | ||
/> | ||
</S.Container> | ||
); | ||
}; | ||
|
||
export default HomeTable; | ||
|
||
const S = { | ||
Container: styled.div` | ||
display: flex; | ||
flex-direction: column; | ||
justify-content: center; | ||
align-items: center; | ||
gap: 50px; | ||
`, | ||
|
||
HomeTable: styled.table` | ||
width: 100%; | ||
text-align: left; | ||
font-size: 1.4rem; | ||
th { | ||
color: ${({ theme }) => theme.color.gray8}; | ||
} | ||
td { | ||
.publishedTo { | ||
display: flex; | ||
gap: 0.8rem; | ||
} | ||
} | ||
th, | ||
td { | ||
padding: 1.1rem; | ||
border: 1px solid ${({ theme }) => theme.color.gray5}; | ||
} | ||
th:first-child, | ||
td:first-child { | ||
border-left: none; | ||
} | ||
th:last-child, | ||
td:last-child { | ||
border-right: none; | ||
} | ||
tbody tr:hover { | ||
cursor: pointer; | ||
transform: scale(1.01); | ||
transition: all 300ms; | ||
} | ||
`, | ||
|
||
PublishedToIconContainer: styled.div` | ||
display: flex; | ||
gap: 0.8rem; | ||
`, | ||
}; |
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,40 @@ | ||
import { useEffect, useRef, useState } from 'react'; | ||
import { useQuery } from '@tanstack/react-query'; | ||
|
||
import { getHomeWritings } from 'apis/writings'; | ||
import { GetHomeWritingsResponse } from 'types/apis/writings'; | ||
|
||
export const useHomeTable = (initialPageIndex: number) => { | ||
const [activePage, setActivePage] = useState(initialPageIndex); | ||
const [fetchOption, setFetchOption] = useState(`?page=${activePage}&sort=createAt,desc`); | ||
const { data } = useQuery<GetHomeWritingsResponse>(['homeWritings', fetchOption], () => | ||
getHomeWritings(fetchOption), | ||
); | ||
const rowRef = useRef<HTMLTableRowElement>(null); | ||
|
||
const changeActivePage = (pageIndex: number) => { | ||
setFetchOption(`?page=${pageIndex}&sort=createAt,desc`); | ||
setActivePage(pageIndex); | ||
}; | ||
|
||
useEffect(() => { | ||
rowRef.current?.focus(); | ||
}, []); | ||
|
||
return { | ||
content: data?.content, | ||
pageable: data?.pageable, | ||
totalPages: data?.totalPages, | ||
totalElements: data?.totalElements, | ||
last: data?.last, | ||
size: data?.size, | ||
number: data?.number, | ||
sort: data?.sort, | ||
numberOfElements: data?.numberOfElements, | ||
first: data?.first, | ||
empty: data?.empty, | ||
rowRef, | ||
activePage, | ||
changeActivePage, | ||
}; | ||
}; |
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
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
Oops, something went wrong.