pagenation updated blog page

This commit is contained in:
akash 2025-12-23 16:48:44 +05:30
parent fd451520f2
commit a2df3a5fea
4 changed files with 283 additions and 57 deletions

View File

@ -2,27 +2,13 @@
import Link from "next/link";
import Layout from "@/components/layout/Layout";
import Blogs from "@/utils/constant.utils";
import BlogList from "@/components/elements/BlogList";
export const metadata = {
title: "Street-Food Stories from Sixty5 Street",
description: "Dive into the Sixty5 Street blog for behind-the-scenes stories, flavor experiments, new dish launches and street-food culture updates.",
};
const truncateWords = (text, limit) => {
const words = text.split(" ");
return words.length > limit ? words.slice(0, limit).join(" ") + " ..." : text;
};
const stripHtml = (html) => {
if (!html) return "";
return html.replace(/<[^>]*>/g, "");
};
const truncateTitle = (text, limit) => {
if (!text) return "";
return text.length > limit ? text.slice(0, limit) + "..." : text;
};
export default function Blog() {
return (
<Layout
@ -31,7 +17,7 @@ export default function Blog() {
breadcrumbTitle="Blog"
bgImage={"/assets/images/inner-banner/blog-banner.webp"}
>
<div className="sidebar-page-container">
<div className="sidebar-page-container pt-5">
<div className="auto-container">
<div className="sec-title mb-4 centered">
<div className="title">Our Blogs</div>
@ -39,47 +25,7 @@ export default function Blog() {
<div className="separate"></div>
</div>
<div className="row clearfix">
{Blogs.sort((a, b) => new Date(b.date) - new Date(a.date)).map((blog) => (
<div
key={blog.id}
className="col-lg-4 col-md-6 col-sm-12 mb-4"
>
<div className="blog-card rounded shadow-sm overflow-hidden">
{/* Blog Image */}
<div className="blog-image">
<img
src={blog.imageDetail}
alt={blog.title}
className="img-fluid w-100"
/>
</div>
{/* Blog Title */}
<div className="blog-content p-3">
<h3 className="mb-2 text-lg font-semibold blog-title-hover">
<Link href={`/blog/${blog.slug}`} className="blog-title-link">
{truncateTitle(stripHtml(blog.title), 40)}
</Link>
</h3>
{/* Blog Excerpt */}
<p className="text-gray-700 mb-3">
{truncateWords(stripHtml(blog.para))}
</p>
{/* Read More Button */}
<Link
href={`/blog/${blog.slug}`}
className="font-medium blog-title-link"
>
Read More
</Link>
</div>
</div>
</div>
))}
</div>
<BlogList blogs={Blogs} />
</div>
</div>
</Layout>

View File

@ -0,0 +1,95 @@
"use client";
import { useState } from "react";
import Link from "next/link";
import Pagination from "./Pagination";
import "public/assets/css/Pagination.css";
const truncateWords = (text, limit = 20) => {
const words = text.split(" ");
return words.length > limit ? words.slice(0, limit).join(" ") + " ..." : text;
};
const stripHtml = (html) => {
if (!html) return "";
return html.replace(/<[^>]*>/g, "");
};
const truncateTitle = (text, limit) => {
if (!text) return "";
return text.length > limit ? text.slice(0, limit) + "..." : text;
};
const BlogList = ({ blogs }) => {
const [currentPage, setCurrentPage] = useState(1);
const postsPerPage = 12;
// Sort posts by date descending (newest first) - exactly like original code
const sortedPosts = [...blogs].sort((a, b) => new Date(b.date) - new Date(a.date));
// Calculate pagination
const totalPages = Math.ceil(sortedPosts.length / postsPerPage);
const indexOfLastPost = currentPage * postsPerPage;
const indexOfFirstPost = indexOfLastPost - postsPerPage;
const currentPosts = sortedPosts.slice(indexOfFirstPost, indexOfLastPost);
const handlePageChange = (page) => {
setCurrentPage(page);
};
return (
<div className="blog-section"> {/* Wrapper for scroll functionality */}
<div className="row clearfix">
{currentPosts.map((blog) => (
<div
key={blog.id}
className="col-lg-4 col-md-6 col-sm-12 mb-4 pt-5"
>
<div className="blog-card rounded shadow-sm overflow-hidden">
{/* Blog Image */}
<div className="blog-image">
<img
src={blog.imageDetail}
alt={blog.title}
className="img-fluid w-100"
/>
</div>
{/* Blog Title */}
<div className="blog-content p-3">
<h3 className="mb-2 text-lg font-semibold blog-title-hover">
<Link href={`/blog/${blog.slug}`} className="blog-title-link">
{truncateTitle(stripHtml(blog.title), 40)}
</Link>
</h3>
{/* Blog Excerpt */}
<p className="text-gray-700 mb-3">
{truncateWords(stripHtml(blog.para))}
</p>
{/* Read More Button */}
<Link
href={`/blog/${blog.slug}`}
className="font-medium blog-title-link"
>
Read More
</Link>
</div>
</div>
</div>
))}
</div>
{/* Pagination Component */}
{totalPages > 1 && (
<Pagination
currentPage={currentPage}
totalPages={totalPages}
onPageChange={handlePageChange}
/>
)}
</div>
);
};
export default BlogList;

View File

@ -0,0 +1,83 @@
import React from 'react';
const Pagination = ({ currentPage, totalPages, onPageChange }) => {
const handlePageClick = (e, page) => {
if (e) {
e.preventDefault();
}
if (page !== currentPage && page >= 1 && page <= totalPages) {
onPageChange(page);
// Scroll to top of blog section smoothly
const blogSection = document.querySelector('.blog-section');
if (blogSection) {
const offset = 100; // Offset from top
const elementPosition = blogSection.getBoundingClientRect().top;
const offsetPosition = elementPosition + window.pageYOffset - offset;
window.scrollTo({
top: offsetPosition,
behavior: 'smooth'
});
}
}
};
const renderPageNumbers = () => {
const pages = [];
const maxVisiblePages = 5;
let startPage = Math.max(1, currentPage - Math.floor(maxVisiblePages / 2));
let endPage = Math.min(totalPages, startPage + maxVisiblePages - 1);
if (endPage - startPage < maxVisiblePages - 1) {
startPage = Math.max(1, endPage - maxVisiblePages + 1);
}
for (let i = startPage; i <= endPage; i++) {
pages.push(
<button
key={i}
type="button"
className={`pagination-number ${currentPage === i ? 'active' : ''}`}
onClick={(e) => handlePageClick(e, i)}
>
{i}
</button>
);
}
return pages;
};
return (
<div className="pagination-wrapper">
<div className="pagination-container">
{/* Previous Button */}
<button
type="button"
className="pagination-arrow pagination-arrow-prev"
onClick={(e) => handlePageClick(e, currentPage - 1)}
disabled={currentPage === 1}
>
«
</button>
{/* Page Numbers */}
{renderPageNumbers()}
{/* Next Button */}
<button
type="button"
className="pagination-arrow pagination-arrow-next"
onClick={(e) => handlePageClick(e, currentPage + 1)}
disabled={currentPage === totalPages}
>
»
</button>
</div>
</div>
);
};
export default Pagination;

View File

@ -0,0 +1,102 @@
:root {
--theme: #cf2d1f;
--title: #ffffff;
--text: #777777;
--body-font: 'Poppins', sans-serif;
}
.pagination-wrapper {
display: flex;
justify-content: center;
align-items: center;
margin-top: 60px;
margin-bottom: 40px;
}
.pagination-container {
display: flex;
gap: 8px;
align-items: center;
}
.pagination-arrow {
min-width: 45px;
height: 45px;
display: flex;
align-items: center;
justify-content: center;
border: 1px solid #e0e0e0;
background-color: #fff;
color: var(--theme);
font-family: var(--body-font);
font-size: 20px;
font-weight: 700;
cursor: pointer;
transition: all 0.3s ease;
border-radius: 4px;
padding: 0 15px;
}
.pagination-arrow:hover:not(:disabled) {
background-color: var(--theme);
border-color: var(--theme);
color: var(--title);
}
.pagination-arrow:disabled {
opacity: 0.4;
cursor: not-allowed;
}
.pagination-number {
min-width: 45px;
height: 45px;
display: flex;
align-items: center;
justify-content: center;
border: 1px solid #e0e0e0;
background-color: #fff;
color: var(--text);
font-family: var(--body-font);
font-size: 16px;
font-weight: 500;
cursor: pointer;
transition: all 0.3s ease;
border-radius: 4px;
padding: 0 12px;
}
.pagination-number:hover {
background-color: var(--theme);
border-color: var(--theme);
color: var(--title);
}
.pagination-number.active {
background-color: var(--theme);
border-color: var(--theme);
color: var(--title);
font-weight: 600;
}
/* Responsive Design */
@media (max-width: 575px) {
.pagination-container {
gap: 5px;
}
.pagination-arrow,
.pagination-number {
min-width: 38px;
height: 38px;
font-size: 14px;
}
.pagination-arrow {
padding: 0 12px;
}
.pagination-number {
padding: 0 10px;
}
}