-
-
Notifications
You must be signed in to change notification settings - Fork 758
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
feat: added pagination in blog page #3591
base: master
Are you sure you want to change the base?
Changes from all commits
5fee8c9
396a102
9d57d6c
e78abef
e260c39
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
import React, { useEffect, useState } from 'react'; | ||
|
||
import { ButtonIconPosition } from '@/types/components/buttons/ButtonPropsType'; | ||
|
||
import Button from '../buttons/Button'; | ||
import IconArrowLeft from '../icons/ArrowLeft'; | ||
import IconArrowRight from '../icons/ArrowRight'; | ||
|
||
/** | ||
* Props for the BlogPagination component | ||
* @property {number} blogsPerPage - Number of blogs to display per page | ||
* @property {number} totalBlogs - Total number of blogs | ||
* @property {function} paginate - Callback function to handle page changes | ||
* @property {number} currentPage - Current active page number | ||
*/ | ||
interface BlogPaginationProps { | ||
// eslint-disable-next-line prettier/prettier | ||
|
||
blogsPerPage: number; | ||
totalBlogs: number; | ||
paginate: (pageNumber: number) => void; | ||
currentPage: number; | ||
} | ||
|
||
/** | ||
* A pagination component for blog posts that displays page numbers and navigation buttons | ||
* @param {BlogPaginationProps} props - The props for the component | ||
* @returns {JSX.Element} A navigation element with pagination controls | ||
*/ | ||
export default function BlogPagination({ | ||
blogsPerPage, | ||
totalBlogs, | ||
paginate, | ||
currentPage, | ||
}: BlogPaginationProps) { | ||
const totalPages: number = Math.ceil(totalBlogs / blogsPerPage); | ||
const pagesToShow: number = 6; | ||
const [pageNumbers, setPageNumbers] = useState<(number | string)[]>([]); | ||
|
||
const calculatePageNumbers = () => { | ||
const numbers: (number | string)[] = []; | ||
|
||
if (totalPages < 1) return []; | ||
if (totalPages <= pagesToShow) { | ||
for (let i = 1; i <= totalPages; i++) { | ||
numbers.push(i); | ||
} | ||
} else if (currentPage <= 2) { | ||
for (let i = 1; i <= 3; i++) { | ||
numbers.push(i); | ||
} | ||
numbers.push('...'); | ||
numbers.push(totalPages - 2); | ||
numbers.push(totalPages - 1); | ||
numbers.push(totalPages); | ||
} else if (currentPage >= totalPages - 1) { | ||
numbers.push(1); | ||
numbers.push(2); | ||
numbers.push(3); | ||
numbers.push('...'); | ||
for (let i = totalPages - 2; i <= totalPages; i++) { | ||
numbers.push(i); | ||
} | ||
} else { | ||
numbers.push(1); | ||
numbers.push('...'); | ||
numbers.push(currentPage - 1); | ||
numbers.push(currentPage); | ||
numbers.push(currentPage + 1); | ||
numbers.push('...'); | ||
numbers.push(totalPages); | ||
} | ||
|
||
return numbers; | ||
}; | ||
|
||
useEffect(() => { | ||
setPageNumbers(calculatePageNumbers()); | ||
}, [currentPage, totalBlogs]); | ||
|
||
return ( | ||
<nav | ||
aria-label="Blog pagination" | ||
className="mt-8 flex items-center justify-center gap-2 p-4" | ||
> | ||
{/* Previous button */} | ||
<Button | ||
className={`${currentPage === 1 ? 'cursor-not-allowed opacity-50' : ''} size-[120px] rounded-l-md px-4 py-2`} | ||
aria-label="Previous page" | ||
bgClassName="bg-white" | ||
textClassName="text-[#212525] font-inter text-[14px] font-normal" | ||
text="Previous" | ||
disabled={currentPage === 1} | ||
onClick={() => paginate(currentPage - 1)} | ||
icon={<IconArrowLeft className="inline-block size-4" />} | ||
iconPosition={ButtonIconPosition.LEFT} | ||
/> | ||
{/* Page numbers */} | ||
<div className="flex w-[35vw] justify-center gap-3"> | ||
{pageNumbers.map((number, index) => ( | ||
<button | ||
key={index} | ||
className={`size-[40px] ${number === currentPage ? 'rounded border bg-[#6200EE] text-white' : 'text-[#6B6B6B]'}`} | ||
aria-label={`${typeof number === 'number' ? `Go to page ${number}` : 'More pages'}`} | ||
aria-current={number === currentPage ? 'page' : undefined} | ||
onClick={() => typeof number === 'number' && paginate(number)} | ||
disabled={number === '...'} | ||
> | ||
{number} | ||
</button> | ||
))} | ||
</div> | ||
{/* Next button */} | ||
<Button | ||
className={`${currentPage === totalPages && 'cursor-not-allowed opacity-50'} h-[35px] w-[120px] rounded-l-md px-4 py-2`} | ||
bgClassName="bg-white" | ||
textClassName="text-[#212525] font-inter text-[14px] font-normal" | ||
text="Next" | ||
disabled={currentPage === totalPages} | ||
onClick={() => paginate(currentPage + 1)} | ||
icon={<IconArrowRight className="inline-block size-4" />} | ||
iconPosition={ButtonIconPosition.RIGHT} | ||
/> | ||
</nav> | ||
); | ||
} |
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -1,9 +1,10 @@ | ||||||||||||||||||||||||||||||||||||||||||||
import { useRouter } from 'next/router'; | ||||||||||||||||||||||||||||||||||||||||||||
import React, { useContext, useEffect, useState } from 'react'; | ||||||||||||||||||||||||||||||||||||||||||||
import React, { useContext, useEffect, useMemo, useState } from 'react'; | ||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
import Empty from '@/components/illustrations/Empty'; | ||||||||||||||||||||||||||||||||||||||||||||
import GenericLayout from '@/components/layout/GenericLayout'; | ||||||||||||||||||||||||||||||||||||||||||||
import Loader from '@/components/Loader'; | ||||||||||||||||||||||||||||||||||||||||||||
import BlogPagination from '@/components/navigation/BlogPagination'; | ||||||||||||||||||||||||||||||||||||||||||||
import BlogPostItem from '@/components/navigation/BlogPostItem'; | ||||||||||||||||||||||||||||||||||||||||||||
import Filter from '@/components/navigation/Filter'; | ||||||||||||||||||||||||||||||||||||||||||||
import Heading from '@/components/typography/Heading'; | ||||||||||||||||||||||||||||||||||||||||||||
|
@@ -32,106 +33,184 @@ export default function BlogIndexPage() { | |||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
return i2Date.getTime() - i1Date.getTime(); | ||||||||||||||||||||||||||||||||||||||||||||
}) | ||||||||||||||||||||||||||||||||||||||||||||
: [] | ||||||||||||||||||||||||||||||||||||||||||||
: [], | ||||||||||||||||||||||||||||||||||||||||||||
); | ||||||||||||||||||||||||||||||||||||||||||||
const [isClient, setIsClient] = useState(false); | ||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
const onFilter = (data: IBlogPost[]) => setPosts(data); | ||||||||||||||||||||||||||||||||||||||||||||
const onFilter = (data: IBlogPost[]) => { | ||||||||||||||||||||||||||||||||||||||||||||
setPosts(data); | ||||||||||||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||||||||||||
const toFilter = [ | ||||||||||||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||||||||||||
name: 'type' | ||||||||||||||||||||||||||||||||||||||||||||
name: 'type', | ||||||||||||||||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||||||||||||
name: 'authors', | ||||||||||||||||||||||||||||||||||||||||||||
unique: 'name' | ||||||||||||||||||||||||||||||||||||||||||||
unique: 'name', | ||||||||||||||||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||||||||||||
name: 'tags' | ||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||
name: 'tags', | ||||||||||||||||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||||||||||||||||
]; | ||||||||||||||||||||||||||||||||||||||||||||
const clearFilters = () => { | ||||||||||||||||||||||||||||||||||||||||||||
router.push(`${router.pathname}`, undefined, { | ||||||||||||||||||||||||||||||||||||||||||||
shallow: true | ||||||||||||||||||||||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||||||||||||||||||||
const { page } = router.query; | ||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
router.push( | ||||||||||||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||||||||||||
pathname: router.pathname, | ||||||||||||||||||||||||||||||||||||||||||||
query: { ...(page && { page }) }, | ||||||||||||||||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||||||||||||||||
undefined, | ||||||||||||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||||||||||||
shallow: true, | ||||||||||||||||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||||||||||||||||
); | ||||||||||||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||||||||||||
const showClearFilters = Object.keys(router.query).length > 0; | ||||||||||||||||||||||||||||||||||||||||||||
const showClearFilters = Object.keys(router.query).length > 1; | ||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
const description = 'Find the latest and greatest stories from our community'; | ||||||||||||||||||||||||||||||||||||||||||||
const image = '/img/social/blog.webp'; | ||||||||||||||||||||||||||||||||||||||||||||
const blogsPerPage = 9; | ||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
const currentPage = parseInt(router.query.page as string, 10) || 1; | ||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
const currentPosts = useMemo(() => { | ||||||||||||||||||||||||||||||||||||||||||||
const indexOfLastPost = currentPage * blogsPerPage; | ||||||||||||||||||||||||||||||||||||||||||||
const indexOfFirstPost = indexOfLastPost - blogsPerPage; | ||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
return posts.slice(indexOfFirstPost, indexOfLastPost); | ||||||||||||||||||||||||||||||||||||||||||||
}, [currentPage, posts]); | ||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
const paginate = (pageNumber: number) => { | ||||||||||||||||||||||||||||||||||||||||||||
const { query } = router; | ||||||||||||||||||||||||||||||||||||||||||||
const newQuery = { | ||||||||||||||||||||||||||||||||||||||||||||
...query, | ||||||||||||||||||||||||||||||||||||||||||||
page: pageNumber, | ||||||||||||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
router.push( | ||||||||||||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||||||||||||
pathname: router.pathname, | ||||||||||||||||||||||||||||||||||||||||||||
query: newQuery, | ||||||||||||||||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||||||||||||||||
undefined, | ||||||||||||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||||||||||||
shallow: true, | ||||||||||||||||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||||||||||||||||
); | ||||||||||||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
useEffect(() => { | ||||||||||||||||||||||||||||||||||||||||||||
if (router.isReady && !router.query.page) { | ||||||||||||||||||||||||||||||||||||||||||||
router.replace( | ||||||||||||||||||||||||||||||||||||||||||||
{ pathname: router.pathname, query: { page: '1' } }, | ||||||||||||||||||||||||||||||||||||||||||||
undefined, | ||||||||||||||||||||||||||||||||||||||||||||
{ shallow: true }, | ||||||||||||||||||||||||||||||||||||||||||||
); | ||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||
}, [router.isReady]); | ||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+103
to
+111
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Potential infinite loop in router updates. The useEffect hook might cause an infinite loop if router.replace triggers a re-render. Consider adding a state variable to track whether the initial page has been set. +const [initialPageSet, setInitialPageSet] = useState(false);
useEffect(() => {
- if (router.isReady && !router.query.page) {
+ if (router.isReady && !router.query.page && !initialPageSet) {
+ setInitialPageSet(true);
router.replace(
{ pathname: router.pathname, query: { page: '1' } },
undefined,
{ shallow: true },
);
}
-}, [router.isReady]);
+}, [router.isReady, router.query.page, initialPageSet]); 📝 Committable suggestion
Suggested change
🧰 Tools🪛 eslint[error] 105-109: Replace (prettier/prettier) [error] 108-108: Unexpected trailing comma. (comma-dangle) |
||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
useEffect(() => { | ||||||||||||||||||||||||||||||||||||||||||||
setIsClient(true); | ||||||||||||||||||||||||||||||||||||||||||||
}, []); | ||||||||||||||||||||||||||||||||||||||||||||
if (router.isReady) { | ||||||||||||||||||||||||||||||||||||||||||||
setIsClient(true); | ||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||
}, [router.isReady]); | ||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
return ( | ||||||||||||||||||||||||||||||||||||||||||||
<GenericLayout title='Blog' description={description} image={image} wide> | ||||||||||||||||||||||||||||||||||||||||||||
<div className='relative px-4 pb-20 pt-8 sm:px-6 lg:px-8 lg:pb-28 lg:pt-12' id='main-content'> | ||||||||||||||||||||||||||||||||||||||||||||
<div className='absolute inset-0'> | ||||||||||||||||||||||||||||||||||||||||||||
<div className='h-1/3 bg-white sm:h-2/3'></div> | ||||||||||||||||||||||||||||||||||||||||||||
<GenericLayout title="Blog" description={description} image={image} wide> | ||||||||||||||||||||||||||||||||||||||||||||
<div | ||||||||||||||||||||||||||||||||||||||||||||
className="relative px-4 pb-20 pt-8 sm:px-6 lg:px-8 lg:pb-28 lg:pt-12" | ||||||||||||||||||||||||||||||||||||||||||||
id="main-content" | ||||||||||||||||||||||||||||||||||||||||||||
> | ||||||||||||||||||||||||||||||||||||||||||||
<div className="absolute inset-0"> | ||||||||||||||||||||||||||||||||||||||||||||
<div className="h-1/3 bg-white sm:h-2/3"></div> | ||||||||||||||||||||||||||||||||||||||||||||
</div> | ||||||||||||||||||||||||||||||||||||||||||||
<div className='relative mx-auto max-w-7xl'> | ||||||||||||||||||||||||||||||||||||||||||||
<div className='text-center'> | ||||||||||||||||||||||||||||||||||||||||||||
<div className="relative mx-auto max-w-7xl"> | ||||||||||||||||||||||||||||||||||||||||||||
<div className="text-center"> | ||||||||||||||||||||||||||||||||||||||||||||
<Heading level={HeadingLevel.h1} typeStyle={HeadingTypeStyle.lg}> | ||||||||||||||||||||||||||||||||||||||||||||
Welcome to our blog! | ||||||||||||||||||||||||||||||||||||||||||||
</Heading> | ||||||||||||||||||||||||||||||||||||||||||||
<Paragraph className='mx-auto mt-3 max-w-2xl sm:mt-4'> | ||||||||||||||||||||||||||||||||||||||||||||
<Paragraph className="mx-auto mt-3 max-w-2xl sm:mt-4"> | ||||||||||||||||||||||||||||||||||||||||||||
Find the latest and greatest stories from our community | ||||||||||||||||||||||||||||||||||||||||||||
</Paragraph> | ||||||||||||||||||||||||||||||||||||||||||||
<Paragraph typeStyle={ParagraphTypeStyle.md} className='mx-auto mt-4 max-w-2xl'> | ||||||||||||||||||||||||||||||||||||||||||||
<Paragraph | ||||||||||||||||||||||||||||||||||||||||||||
typeStyle={ParagraphTypeStyle.md} | ||||||||||||||||||||||||||||||||||||||||||||
className="mx-auto mt-4 max-w-2xl" | ||||||||||||||||||||||||||||||||||||||||||||
> | ||||||||||||||||||||||||||||||||||||||||||||
Want to publish a blog post? We love community stories.{' '} | ||||||||||||||||||||||||||||||||||||||||||||
<TextLink href='https://github.com/asyncapi/website/issues/new?template=blog.md' target='_blank'> | ||||||||||||||||||||||||||||||||||||||||||||
<TextLink | ||||||||||||||||||||||||||||||||||||||||||||
href="https://github.com/asyncapi/website/issues/new?template=blog.md" | ||||||||||||||||||||||||||||||||||||||||||||
target="_blank" | ||||||||||||||||||||||||||||||||||||||||||||
> | ||||||||||||||||||||||||||||||||||||||||||||
Submit yours! | ||||||||||||||||||||||||||||||||||||||||||||
</TextLink> | ||||||||||||||||||||||||||||||||||||||||||||
</Paragraph> | ||||||||||||||||||||||||||||||||||||||||||||
<Paragraph typeStyle={ParagraphTypeStyle.md} className='mx-auto mt-1 max-w-2xl'> | ||||||||||||||||||||||||||||||||||||||||||||
<Paragraph | ||||||||||||||||||||||||||||||||||||||||||||
typeStyle={ParagraphTypeStyle.md} | ||||||||||||||||||||||||||||||||||||||||||||
className="mx-auto mt-1 max-w-2xl" | ||||||||||||||||||||||||||||||||||||||||||||
> | ||||||||||||||||||||||||||||||||||||||||||||
We have an | ||||||||||||||||||||||||||||||||||||||||||||
<img | ||||||||||||||||||||||||||||||||||||||||||||
className='ml-1 text-primary-500 hover:text-primary-300' | ||||||||||||||||||||||||||||||||||||||||||||
className="ml-1 text-primary-500 hover:text-primary-300" | ||||||||||||||||||||||||||||||||||||||||||||
style={{ display: 'inline' }} | ||||||||||||||||||||||||||||||||||||||||||||
src='/img/logos/rss.svg' | ||||||||||||||||||||||||||||||||||||||||||||
alt='RSS feed' | ||||||||||||||||||||||||||||||||||||||||||||
height='18px' | ||||||||||||||||||||||||||||||||||||||||||||
width='18px' | ||||||||||||||||||||||||||||||||||||||||||||
src="/img/logos/rss.svg" | ||||||||||||||||||||||||||||||||||||||||||||
alt="RSS feed" | ||||||||||||||||||||||||||||||||||||||||||||
height="18px" | ||||||||||||||||||||||||||||||||||||||||||||
width="18px" | ||||||||||||||||||||||||||||||||||||||||||||
/> | ||||||||||||||||||||||||||||||||||||||||||||
<TextLink href='/rss.xml'> RSS Feed</TextLink>, too! | ||||||||||||||||||||||||||||||||||||||||||||
<TextLink href="/rss.xml"> RSS Feed</TextLink>, too! | ||||||||||||||||||||||||||||||||||||||||||||
</Paragraph> | ||||||||||||||||||||||||||||||||||||||||||||
</div> | ||||||||||||||||||||||||||||||||||||||||||||
<div className='mx:64 mt-12 md:flex md:justify-center lg:justify-start'> | ||||||||||||||||||||||||||||||||||||||||||||
<div className="mx:64 mt-12 md:flex md:justify-center lg:justify-start"> | ||||||||||||||||||||||||||||||||||||||||||||
<Filter | ||||||||||||||||||||||||||||||||||||||||||||
data={navItems || []} | ||||||||||||||||||||||||||||||||||||||||||||
onFilter={onFilter} | ||||||||||||||||||||||||||||||||||||||||||||
className='md: mx-px mt-1 w-full md:mt-0 md:w-1/5 md:text-sm' | ||||||||||||||||||||||||||||||||||||||||||||
className="md: mx-px mt-1 w-full md:mt-0 md:w-1/5 md:text-sm" | ||||||||||||||||||||||||||||||||||||||||||||
checks={toFilter} | ||||||||||||||||||||||||||||||||||||||||||||
/> | ||||||||||||||||||||||||||||||||||||||||||||
{showClearFilters && ( | ||||||||||||||||||||||||||||||||||||||||||||
<button | ||||||||||||||||||||||||||||||||||||||||||||
type='button' | ||||||||||||||||||||||||||||||||||||||||||||
className='bg-none text-md mt-1 rounded-md border border-gray-200 px-4 py-2 font-semibold tracking-heading text-gray-800 shadow-none transition-all duration-500 ease-in-out hover:text-gray-700 md:mt-0 md:py-0' | ||||||||||||||||||||||||||||||||||||||||||||
type="button" | ||||||||||||||||||||||||||||||||||||||||||||
className="bg-none text-md mt-1 rounded-md border border-gray-200 px-4 py-2 font-semibold tracking-heading text-gray-800 shadow-none transition-all duration-500 ease-in-out hover:text-gray-700 md:mt-0 md:py-0" | ||||||||||||||||||||||||||||||||||||||||||||
onClick={clearFilters} | ||||||||||||||||||||||||||||||||||||||||||||
> | ||||||||||||||||||||||||||||||||||||||||||||
<span className='inline-block'>Clear filters</span> | ||||||||||||||||||||||||||||||||||||||||||||
<span className="inline-block">Clear filters</span> | ||||||||||||||||||||||||||||||||||||||||||||
</button> | ||||||||||||||||||||||||||||||||||||||||||||
)} | ||||||||||||||||||||||||||||||||||||||||||||
</div> | ||||||||||||||||||||||||||||||||||||||||||||
<div> | ||||||||||||||||||||||||||||||||||||||||||||
{Object.keys(posts).length === 0 && ( | ||||||||||||||||||||||||||||||||||||||||||||
<div className='mt-16 flex flex-col items-center justify-center'> | ||||||||||||||||||||||||||||||||||||||||||||
{(Object.keys(posts).length === 0 || | ||||||||||||||||||||||||||||||||||||||||||||
Object.keys(currentPosts).length === 0) && ( | ||||||||||||||||||||||||||||||||||||||||||||
<div className="mt-16 flex flex-col items-center justify-center"> | ||||||||||||||||||||||||||||||||||||||||||||
<Empty /> | ||||||||||||||||||||||||||||||||||||||||||||
<p className='mx-auto mt-3 max-w-2xl text-xl leading-7 text-gray-500'>No post matches your filter</p> | ||||||||||||||||||||||||||||||||||||||||||||
<p className="mx-auto mt-3 max-w-2xl text-xl leading-7 text-gray-500"> | ||||||||||||||||||||||||||||||||||||||||||||
No post matches your filter | ||||||||||||||||||||||||||||||||||||||||||||
</p> | ||||||||||||||||||||||||||||||||||||||||||||
</div> | ||||||||||||||||||||||||||||||||||||||||||||
)} | ||||||||||||||||||||||||||||||||||||||||||||
{Object.keys(posts).length > 0 && isClient && ( | ||||||||||||||||||||||||||||||||||||||||||||
<ul className='mx-auto mt-12 grid max-w-lg gap-5 lg:max-w-none lg:grid-cols-3'> | ||||||||||||||||||||||||||||||||||||||||||||
{posts.map((post, index) => ( | ||||||||||||||||||||||||||||||||||||||||||||
<ul className="mx-auto mt-12 grid max-w-lg gap-5 lg:max-w-none lg:grid-cols-3"> | ||||||||||||||||||||||||||||||||||||||||||||
{currentPosts.map((post, index) => ( | ||||||||||||||||||||||||||||||||||||||||||||
<BlogPostItem key={index} post={post} /> | ||||||||||||||||||||||||||||||||||||||||||||
))} | ||||||||||||||||||||||||||||||||||||||||||||
</ul> | ||||||||||||||||||||||||||||||||||||||||||||
)} | ||||||||||||||||||||||||||||||||||||||||||||
{Object.keys(posts).length > 0 && !isClient && ( | ||||||||||||||||||||||||||||||||||||||||||||
<div className='h-screen w-full'> | ||||||||||||||||||||||||||||||||||||||||||||
<Loader loaderText='Loading Blogs' className='mx-auto my-60' pulsating /> | ||||||||||||||||||||||||||||||||||||||||||||
<div className="h-screen w-full"> | ||||||||||||||||||||||||||||||||||||||||||||
<Loader | ||||||||||||||||||||||||||||||||||||||||||||
loaderText="Loading Blogs" | ||||||||||||||||||||||||||||||||||||||||||||
className="mx-auto my-60" | ||||||||||||||||||||||||||||||||||||||||||||
pulsating | ||||||||||||||||||||||||||||||||||||||||||||
/> | ||||||||||||||||||||||||||||||||||||||||||||
</div> | ||||||||||||||||||||||||||||||||||||||||||||
)} | ||||||||||||||||||||||||||||||||||||||||||||
{/* Pagination component */} | ||||||||||||||||||||||||||||||||||||||||||||
<BlogPagination | ||||||||||||||||||||||||||||||||||||||||||||
blogsPerPage={blogsPerPage} | ||||||||||||||||||||||||||||||||||||||||||||
totalBlogs={posts.length} | ||||||||||||||||||||||||||||||||||||||||||||
paginate={paginate} | ||||||||||||||||||||||||||||||||||||||||||||
currentPage={currentPage} | ||||||||||||||||||||||||||||||||||||||||||||
/> | ||||||||||||||||||||||||||||||||||||||||||||
</div> | ||||||||||||||||||||||||||||||||||||||||||||
</div> | ||||||||||||||||||||||||||||||||||||||||||||
</div> | ||||||||||||||||||||||||||||||||||||||||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add validation for page number parsing.
The current page parsing could fail with invalid input. Consider adding validation and fallback.
📝 Committable suggestion