From b90097d8420e6b6f21b21359507ff7cef047bfcb Mon Sep 17 00:00:00 2001 From: Adi-204 Date: Sun, 26 Jan 2025 14:04:18 +0530 Subject: [PATCH 1/3] initial commit --- components/helpers/applyFilter.ts | 3 + components/navigation/BlogPagination.tsx | 126 +++++++++++++++++++++++ pages/blog/index.tsx | 66 ++++++++++-- 3 files changed, 187 insertions(+), 8 deletions(-) create mode 100644 components/navigation/BlogPagination.tsx diff --git a/components/helpers/applyFilter.ts b/components/helpers/applyFilter.ts index a7f3f217d5eb..c2772cce63e1 100644 --- a/components/helpers/applyFilter.ts +++ b/components/helpers/applyFilter.ts @@ -137,6 +137,9 @@ export const onFilterApply = ( if (query && Object.keys(query).length >= 1) { Object.keys(query).forEach((property) => { + if (property === 'page') { + return; + } const res = result.filter((e) => { if (!query[property] || e[property] === query[property]) { return e[property]; diff --git a/components/navigation/BlogPagination.tsx b/components/navigation/BlogPagination.tsx new file mode 100644 index 000000000000..4ce1085ca1be --- /dev/null +++ b/components/navigation/BlogPagination.tsx @@ -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): JSX.Element { + 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 ( + + ); +} \ No newline at end of file diff --git a/pages/blog/index.tsx b/pages/blog/index.tsx index 37958cec4308..7f7096bc482e 100644 --- a/pages/blog/index.tsx +++ b/pages/blog/index.tsx @@ -1,5 +1,5 @@ import { useRouter } from 'next/router'; -import React, { useContext, useEffect, useState } from 'react'; +import React, { useContext, useEffect, useState, useMemo } from 'react'; import Empty from '@/components/illustrations/Empty'; import GenericLayout from '@/components/layout/GenericLayout'; @@ -13,6 +13,7 @@ import BlogContext from '@/context/BlogContext'; import type { IBlogPost } from '@/types/post'; import { HeadingLevel, HeadingTypeStyle } from '@/types/typography/Heading'; import { ParagraphTypeStyle } from '@/types/typography/Paragraph'; +import BlogPagination from '@/components/navigation/BlogPagination'; /** * @description The BlogIndexPage is the blog index page of the website. @@ -35,7 +36,6 @@ export default function BlogIndexPage() { : [] ); const [isClient, setIsClient] = useState(false); - const onFilter = (data: IBlogPost[]) => setPosts(data); const toFilter = [ { @@ -50,14 +50,56 @@ export default function BlogIndexPage() { } ]; 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, setCurrentPage] = useState(1); + const currentPosts = useMemo(() => { + const indexOfLastPost = currentPage * blogsPerPage; + const indexOfFirstPost = indexOfLastPost - blogsPerPage; + + return posts.slice(indexOfFirstPost, indexOfLastPost); + }, [currentPage, posts]); + + const paginate = (pageNumber: number) => { + setCurrentPage(pageNumber); + const { query } = router; + const newQuery = { + ...query, + page: pageNumber, + }; + router.push( + { + pathname: router.pathname, + query: newQuery, + }, + undefined, + { + shallow: true, + }, + ); + }; + + useEffect(()=>{ + const currentPageNumber = Math.max(1, Number.isNaN(parseInt(router.query.page as string, 10)) ? 1 : parseInt(router.query.page as string, 10)); + setCurrentPage(currentPageNumber); + },[router.query.page]); useEffect(() => { setIsClient(true); @@ -114,7 +156,8 @@ export default function BlogIndexPage() { )}
- {Object.keys(posts).length === 0 && ( + {(Object.keys(posts).length === 0 || + Object.keys(currentPosts).length === 0) && (

No post matches your filter

@@ -122,7 +165,7 @@ export default function BlogIndexPage() { )} {Object.keys(posts).length > 0 && isClient && (
    - {posts.map((post, index) => ( + {currentPosts.map((post, index) => ( ))}
@@ -132,6 +175,13 @@ export default function BlogIndexPage() {
)} + {/* Pagination component */} +
From 1ff9f2f127e195f1f755934effcb59a06a9c0cb8 Mon Sep 17 00:00:00 2001 From: Adi-204 Date: Sun, 2 Feb 2025 12:05:44 +0530 Subject: [PATCH 2/3] feat: add pagination in blog page --- components/helpers/applyFilter.ts | 5 +++-- components/icons/Next.tsx | 20 ++++++++++++++++++++ components/icons/Previous.tsx | 20 ++++++++++++++++++++ components/navigation/BlogPagination.tsx | 12 ++++++------ pages/blog/index.tsx | 14 +++++++------- 5 files changed, 56 insertions(+), 15 deletions(-) create mode 100644 components/icons/Next.tsx create mode 100644 components/icons/Previous.tsx diff --git a/components/helpers/applyFilter.ts b/components/helpers/applyFilter.ts index c2772cce63e1..9386990f9d6f 100644 --- a/components/helpers/applyFilter.ts +++ b/components/helpers/applyFilter.ts @@ -133,12 +133,13 @@ export const onFilterApply = ( onFilter: (result: DataObject[], query: Filter) => void, query: Filter ): void => { + const nonFilterableKeys = ['page']; let result = inputData; if (query && Object.keys(query).length >= 1) { Object.keys(query).forEach((property) => { - if (property === 'page') { - return; + if (nonFilterableKeys.includes(property)) { + return; // Skip non-filterable keys like 'page' } const res = result.filter((e) => { if (!query[property] || e[property] === query[property]) { diff --git a/components/icons/Next.tsx b/components/icons/Next.tsx new file mode 100644 index 000000000000..f2afbe68fe55 --- /dev/null +++ b/components/icons/Next.tsx @@ -0,0 +1,20 @@ +import React from 'react'; + +/* eslint-disable max-len */ +/** + * @description Icons for Next button + */ +export default function IconNext() { + return ( + + + + ); +} \ No newline at end of file diff --git a/components/icons/Previous.tsx b/components/icons/Previous.tsx new file mode 100644 index 000000000000..99f2cc9fdcb6 --- /dev/null +++ b/components/icons/Previous.tsx @@ -0,0 +1,20 @@ +import React from 'react'; + +/* eslint-disable max-len */ +/** + * @description Icons for Previous button in pagination + */ +export default function IconPrevious() { + return ( + + + + ); +} \ No newline at end of file diff --git a/components/navigation/BlogPagination.tsx b/components/navigation/BlogPagination.tsx index 4ce1085ca1be..833e977eef3e 100644 --- a/components/navigation/BlogPagination.tsx +++ b/components/navigation/BlogPagination.tsx @@ -3,8 +3,8 @@ 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'; +import IconNext from '../icons/Next'; +import IconPrevious from '../icons/Previous'; /** * Props for the BlogPagination component @@ -85,14 +85,14 @@ export default function BlogPagination({ > {/* Previous button */}