Skip to content

Commit 08678e2

Browse files
authored
1 parent b02b626 commit 08678e2

File tree

4 files changed

+126
-11
lines changed

4 files changed

+126
-11
lines changed

frontend/src/App.jsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ function App() {
4646
<Route path=':ideaId' element={<IdeaPage />} />
4747
<Route path=':ideaId/edit' element={<IdeaEditPage />} />
4848
<Route path='add' element={<IdeaAddPage />} />
49+
<Route path='page/:page' element={<IdeasPage />} />
4950
</Route>
5051
</Routes>
5152
<Footer />

frontend/src/components/IdeaFormSection.jsx

Lines changed: 62 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,83 @@
1-
import { useEffect } from 'react';
2-
import { useApi } from '../hooks/useApi';
3-
import { Link } from 'react-router';
1+
import { useCallback, useEffect, useMemo, useState } from 'react';
2+
import { Link, NavLink } from 'react-router';
3+
44
import UpvoteImg from '../assets/Upvote.svg';
55

6-
const IdeaFormSection = ({ count, sort = null }) => {
6+
import { useApi } from '../hooks/useApi';
7+
import { Pagination } from './Pagination';
8+
9+
const IdeaFormSection = ({
10+
count,
11+
sort = null,
12+
page = 0,
13+
paginate = false,
14+
}) => {
15+
const [showPages, setShowPages] = useState(false);
16+
const [totalPages, setTotalPages] = useState(0);
17+
const [entries, setEntries] = useState([]);
718
const { isLoading, error, data, fetchFromApi } = useApi({
819
loadingInitially: true,
920
});
1021

11-
const sorting = sort ? `&sort=${sort}` : '';
22+
const getApiUrl = useCallback(
23+
(page = 0) => {
24+
const sorting = sort ? `&sort=${sort}` : '';
25+
const skip = page > 0 ? `&skip=${page * count}` : '';
26+
return `/ideas/?limit=${count}${sorting}${skip}`;
27+
},
28+
[count, sort]
29+
);
30+
31+
const getPageUrl = page => `/ideas/page/${page + 1}`;
1232

1333
useEffect(() => {
14-
fetchFromApi(`/ideas/?limit=${count}${sorting}`);
15-
}, [count, fetchFromApi, sorting]);
34+
fetchFromApi(getApiUrl(page));
35+
}, [fetchFromApi, getApiUrl, page]);
36+
37+
useEffect(() => {
38+
if (data?.data?.length > 0) {
39+
setEntries(data?.data);
40+
}
41+
}, [data]);
42+
43+
useEffect(() => {
44+
if (data?.count > 0) {
45+
const pages = Math.ceil(data.count / count);
46+
setTotalPages(pages);
47+
if (pages > 1) {
48+
setShowPages(true);
49+
}
50+
}
51+
}, [data, count]);
52+
53+
const pagination = useMemo(
54+
() => (
55+
<Pagination
56+
{...{
57+
numberOfPages: totalPages,
58+
fetchFromApi,
59+
getApiUrl,
60+
getPageUrl,
61+
initialPage: page,
62+
}}
63+
/>
64+
),
65+
[totalPages, fetchFromApi, getApiUrl, page]
66+
);
1667

1768
return (
1869
<section className='idea-form-section'>
1970
<div className='voting-section' tabIndex='0'>
2071
<h3>Vote on Current Ideas</h3>
2172
{error ? (
2273
`${error}`
23-
) : isLoading ? (
74+
) : isLoading && !entries ? (
2475
'Loading...'
2576
) : (
2677
<ul className='idea-list'>
27-
{data?.data?.length === 0
78+
{entries === 0
2879
? "There's no ideas, add yours!"
29-
: data?.data.map(({ id, name, upvoted_by }) => {
80+
: entries.map(({ id, name, upvoted_by }) => {
3081
return (
3182
<Link to={`/ideas/${id}`} key={id}>
3283
<li className='idea-item'>
@@ -51,6 +102,7 @@ const IdeaFormSection = ({ count, sort = null }) => {
51102
})}
52103
</ul>
53104
)}
105+
{paginate && showPages && entries.length > 0 && <>{pagination}</>}
54106
</div>
55107
</section>
56108
);
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import { useState } from 'react';
2+
3+
export const Pagination = ({
4+
numberOfPages,
5+
initialPage,
6+
fetchFromApi,
7+
getApiUrl,
8+
getPageUrl,
9+
}) => {
10+
const [currentPage, setCurPage] = useState(initialPage);
11+
const pages = [...Array(numberOfPages).keys()];
12+
13+
const Href = ({ pageNo, text }) => {
14+
const apiUrl = getApiUrl(pageNo);
15+
const pageUrl = getPageUrl(pageNo);
16+
const onClick = async e => {
17+
e.preventDefault();
18+
await fetchFromApi(apiUrl);
19+
setCurPage(pageNo);
20+
window.history.pushState(null, '', pageUrl);
21+
};
22+
return (
23+
<a href={pageUrl} className='nav-link' onClick={onClick}>
24+
{text}
25+
</a>
26+
);
27+
};
28+
29+
const NavigateToPrevPage = ({ currentPage }) =>
30+
currentPage <= 0 ? (
31+
<span className='nav-link'>{'<'}</span>
32+
) : (
33+
<Href pageNo={currentPage - 1} text={'<'} />
34+
);
35+
36+
const NavigateToNextPage = ({ currentPage }) =>
37+
currentPage >= numberOfPages - 1 ? (
38+
<span className='nav-link'>{'>'}</span>
39+
) : (
40+
<Href pageNo={currentPage + 1} text={'>'} />
41+
);
42+
43+
return (
44+
<div className='nav-list'>
45+
<NavigateToPrevPage currentPage={currentPage} />
46+
{pages.map(pageNo =>
47+
currentPage !== pageNo ? (
48+
<Href key={pageNo} pageNo={pageNo} text={pageNo + 1} />
49+
) : (
50+
<a className='nav-link active' key={pageNo}>
51+
{pageNo + 1}
52+
</a>
53+
)
54+
)}
55+
<NavigateToNextPage currentPage={currentPage} />
56+
</div>
57+
);
58+
};
59+
60+
export default Pagination;
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1+
import { useParams } from 'react-router';
12
import IdeaFormSection from '../../components/IdeaFormSection';
23

34
export const IdeasPage = () => {
4-
return <IdeaFormSection count={20} />;
5+
const { page = 1 } = useParams('page');
6+
return <IdeaFormSection {...{ count: 20, page: page - 1, paginate: true }} />;
57
};
68

79
export default IdeasPage;

0 commit comments

Comments
 (0)