categories page has pagination now

This commit is contained in:
parsa aghaei 2024-10-14 15:03:12 +03:30
parent bf384c1d67
commit fe6344ece5
11 changed files with 216 additions and 31 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 156 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 91 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 172 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 400 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 159 KiB

View File

@ -11,7 +11,7 @@ export default function NotFound({ params }: PropsType) {
const imageUrl = `/images/404/${Math.floor(Math.random() * 5) + 1}.jpg`; const imageUrl = `/images/404/${Math.floor(Math.random() * 5) + 1}.jpg`;
return ( return (
<div className="flex flex-col items-center justify-center gap-3 mb-10 sm:mb-0"> <div className="mb-10 flex flex-col items-center justify-center gap-3 sm:mb-0">
<Image <Image
src={imageUrl} src={imageUrl}
width={500} width={500}

View File

@ -1,10 +1,19 @@
import { CategoriesListType, CategoryType } from "@/app/types/types"; import {
CategoriesListType,
categoryPropsType,
CategoryType,
} from "@/app/types/types";
import Image from "next/image"; import Image from "next/image";
import Link from "next/link"; import Link from "next/link";
import Pagination from "../components/pagination/pagination";
import NoDataFounded from "../components/no-data-founded/noDataFounded";
export default async function Categories() { export default async function Categories({
params,
searchParams,
}: categoryPropsType) {
const CategoriesData: CategoriesListType = await fetch( const CategoriesData: CategoriesListType = await fetch(
`${process.env.NEXT_PUBLIC_API_URL}/blog/categories`, `${process.env.NEXT_PUBLIC_API_URL}/blog/categories?per_page=${searchParams.per_page ? searchParams.per_page : process.env.NEXT_PUBLIC_CATEGORIES_PER_CATEGORY_PAGE}${searchParams.page ? `&page=${searchParams.page}` : ""}`,
{ {
cache: "no-store", cache: "no-store",
}, },
@ -43,34 +52,42 @@ export default async function Categories() {
</li> </li>
</ul> </ul>
<div className="flex w-full flex-wrap justify-center gap-10"> <div className="mb-10 flex w-full flex-wrap justify-center gap-10">
{CategoriesData.data.map((category: CategoryType) => ( {CategoriesData.data.length > 0 ? (
<div CategoriesData.data.map((category: CategoryType) => (
key={category.id} <div
className="flex w-full items-start gap-5 md:w-[45%]" key={category.id}
> className="flex w-full items-start gap-5 md:w-[45%]"
<Image >
className="max-w-[50%] rounded-md" <Image
src={category.image} className="max-w-[50%] rounded-md"
alt={`${category.name} category image`} src={category.image}
loading="lazy" alt={`${category.name} category image`}
width={350} loading="lazy"
height={350} width={350}
/> height={350}
<div className="flex w-[45%] flex-col gap-1"> />
<Link <div className="flex w-[45%] flex-col gap-1">
className="text-[24px] font-[600] text-blog-text-default hover:underline" <Link
href={`/blog/categories/${category.slug}`} className="text-[24px] font-[600] text-blog-text-default hover:underline"
> href={`/blog/categories/${category.slug}`}
{category.name} >
</Link> {category.name}
<p className="text-[16px] font-[400] text-blog-text-default"> </Link>
{category.short_description} <p className="text-[16px] font-[400] text-blog-text-default">
</p> {category.short_description}
</p>
</div>
</div> </div>
</div> ))
))} ) : (
<NoDataFounded />
)}
</div> </div>
{CategoriesData.data.length > 0 &&
CategoriesData.meta.last_page > 1 && (
<Pagination meta={CategoriesData.meta} />
)}
</div> </div>
</div> </div>
); );

View File

@ -0,0 +1,19 @@
import Image from "next/image";
import Link from "next/link";
export default function NoDataFounded() {
const imageUrl = `/images/no-data-founded/${Math.floor(Math.random() * 6) + 1}.jpg`;
return (
<div className="mb-10 flex flex-col items-center justify-center gap-3 sm:mb-0">
<Image
src={imageUrl}
width={500}
height={500}
alt="no data founded illustration"
className="teste"
/>
<h2 className="text-[30px] font-[800]">No Data founded!</h2>
</div>
);
}

View File

@ -0,0 +1,140 @@
// components/Pagination.tsx
"use client";
import Link from "next/link";
import { useSearchParams } from "next/navigation";
import React from "react";
interface Meta {
current_page: number;
last_page: number;
total: number; // اضافه کردن total برای بررسی وجود داده
}
interface PaginationProps {
meta: Meta;
}
const Pagination: React.FC<PaginationProps> = ({ meta }) => {
const { current_page, last_page, total } = meta;
const searchParams = useSearchParams();
// اگر هیچ داده‌ای وجود نداشته باشد یا تنها یک صفحه باشد، کامپوننت را رندر نکن
if (total === 0 || last_page <= 1) {
return null;
}
const generatePageLink = (page: number): string => {
const params = new URLSearchParams(searchParams.toString());
params.set("page", page.toString());
return `/blog/categories?${params.toString()}`;
};
const renderPageNumbers = (): (JSX.Element | string)[] => {
const pages: JSX.Element[] = [];
const delta = 1; // تعداد صفحات اطراف صفحه فعلی که نمایش داده شوند
const range: number[] = [];
const rangeWithDots: (number | string)[] = [];
let previous: number | undefined;
for (let i = 1; i <= last_page; i++) {
if (
i === 1 ||
i === last_page ||
(i >= current_page - delta && i <= current_page + delta)
) {
range.push(i);
}
}
for (let i of range) {
if (previous) {
if (i - previous === 2) {
rangeWithDots.push(previous + 1);
} else if (i - previous > 2) {
rangeWithDots.push("...");
}
}
rangeWithDots.push(i);
previous = i;
}
return rangeWithDots.map((page, index) => {
if (page === "...") {
return (
<span key={`ellipsis-${index}`} className="px-3 py-1">
...
</span>
);
} else {
return (
<Link
key={page}
href={generatePageLink(Number(page))}
className={`rounded-full border px-3 py-1 ${
page === current_page
? "bg-blog-red text-white"
: "bg-[#EAEAEA] text-blog-text-default hover:bg-[#d6d6d6]"
}`}
aria-current={page === current_page ? "page" : undefined}
>
{page}
</Link>
);
}
});
};
return (
<div className="mt-4 flex items-center justify-center gap-2">
{/* First Page */}
{current_page > 1 && (
<Link
href={generatePageLink(1)}
className="rounded-full border bg-[#EAEAEA] px-3 py-1 text-blog-text-default hover:bg-[#d6d6d6]"
aria-label="First Page"
>
First
</Link>
)}
{/* Previous Page */}
{current_page > 1 && (
<Link
href={generatePageLink(current_page - 1)}
className="rounded-full border bg-[#EAEAEA] px-4 py-0 text-[24px] text-blog-text-default hover:bg-[#d6d6d6]"
aria-label="Previous Page"
>
{"<"}
</Link>
)}
{/* Page Numbers */}
{renderPageNumbers()}
{/* Next Page */}
{current_page < last_page && (
<Link
href={generatePageLink(current_page + 1)}
className="rounded-full border bg-[#EAEAEA] px-4 py-0 text-[24px] text-blog-text-default hover:bg-[#d6d6d6]"
aria-label="Next Page"
>
{">"}
</Link>
)}
{/* Last Page */}
{current_page < last_page && (
<Link
href={generatePageLink(last_page)}
className="rounded-full border bg-[#EAEAEA] px-3 py-1 text-blog-text-default hover:bg-[#d6d6d6]"
aria-label="Last Page"
>
Last
</Link>
)}
</div>
);
};
export default Pagination;

View File

@ -19,7 +19,11 @@ export type CategoryType = {
updated_at: string; updated_at: string;
}; };
export type CategoriesListType = { data: CategoryType[] }; export type CategoriesListType = {
data: CategoryType[];
meta: any;
links: any;
};
// تعریف نوع Tag // تعریف نوع Tag
export type TagType = { export type TagType = {
@ -52,6 +56,11 @@ export type categorySlugPropsType = {
params: { categorySlug: string }; params: { categorySlug: string };
}; };
export type categoryPropsType = {
params: { categorySlug: string };
searchParams: any;
};
export type PaginationType = { export type PaginationType = {
total: number; total: number;
per_page: number; per_page: number;