244 lines
10 KiB
TypeScript
244 lines
10 KiB
TypeScript
"use client";
|
|
|
|
import React, { useState, useEffect, useMemo } from "react";
|
|
import { BlogData } from "@/utils/constant.utils";
|
|
import Link from "next/link";
|
|
import Slider from "react-slick";
|
|
|
|
const allBlogs = [...BlogData].sort(
|
|
(a, b) => new Date(b.date).getTime() - new Date(a.date).getTime()
|
|
);
|
|
|
|
interface BlogSectionProps {
|
|
hideHeader?: boolean;
|
|
isPaginated?: boolean;
|
|
searchTerm?: string;
|
|
selectedCategory?: string;
|
|
blogsPerPage?: number;
|
|
columns?: string; // e.g., "col-xxl-4 col-xl-4 col-lg-6"
|
|
}
|
|
|
|
const BlogSection = ({
|
|
hideHeader = false,
|
|
isPaginated = false,
|
|
searchTerm = "",
|
|
selectedCategory = "",
|
|
blogsPerPage = 12,
|
|
columns = "col-xxl-4 col-xl-4 col-lg-6"
|
|
}: BlogSectionProps) => {
|
|
const [currentPage, setCurrentPage] = useState(1);
|
|
const [isSlider, setIsSlider] = useState(false);
|
|
const [isMounted, setIsMounted] = useState(false);
|
|
|
|
// Reset to first page when filtering changes
|
|
useEffect(() => {
|
|
setCurrentPage(1);
|
|
}, [searchTerm, selectedCategory]);
|
|
|
|
// Handle mount and resize
|
|
useEffect(() => {
|
|
setIsMounted(true);
|
|
if (isPaginated) {
|
|
setIsSlider(false);
|
|
return;
|
|
}
|
|
|
|
const handleResize = () => {
|
|
setIsSlider(window.innerWidth <= 1024);
|
|
};
|
|
|
|
handleResize();
|
|
window.addEventListener("resize", handleResize);
|
|
return () => window.removeEventListener("resize", handleResize);
|
|
}, [isPaginated]);
|
|
|
|
// Filter blogs based on search and category
|
|
const filteredBlogs = useMemo(() => {
|
|
let list = [...allBlogs];
|
|
if (selectedCategory) {
|
|
list = list.filter(blog => blog.category === selectedCategory);
|
|
}
|
|
if (searchTerm) {
|
|
const lowerTerm = searchTerm.toLowerCase();
|
|
list = list.filter(blog =>
|
|
blog.title.toLowerCase().includes(lowerTerm) ||
|
|
blog.category.toLowerCase().includes(lowerTerm)
|
|
);
|
|
}
|
|
return list;
|
|
}, [searchTerm, selectedCategory]);
|
|
|
|
if (!isMounted) {
|
|
return null; // Avoid hydration mismatch
|
|
}
|
|
|
|
// Total pages
|
|
const totalPages = Math.ceil(filteredBlogs.length / blogsPerPage);
|
|
|
|
// Get current blogs
|
|
const indexOfLastBlog = currentPage * blogsPerPage;
|
|
const indexOfFirstBlog = indexOfLastBlog - blogsPerPage;
|
|
const currentBlogs = isPaginated ? filteredBlogs.slice(indexOfFirstBlog, indexOfLastBlog) : filteredBlogs.slice(0, 3);
|
|
|
|
const paginate = (pageNumber: number) => {
|
|
if (pageNumber >= 1 && pageNumber <= totalPages) {
|
|
setCurrentPage(pageNumber);
|
|
window.scrollTo({ top: 0, behavior: 'smooth' });
|
|
}
|
|
};
|
|
|
|
// Pagination display logic: show up to 5 numbers then arrows
|
|
const renderPageNumbers = () => {
|
|
const pages = [];
|
|
let startPage = Math.max(1, currentPage - 2);
|
|
let endPage = Math.min(totalPages, startPage + 4);
|
|
|
|
if (endPage - startPage < 4) {
|
|
startPage = Math.max(1, endPage - 4);
|
|
}
|
|
|
|
for (let i = startPage; i <= endPage; i++) {
|
|
pages.push(
|
|
<button
|
|
key={i}
|
|
onClick={() => paginate(i)}
|
|
className={`pagination-btn ${currentPage === i ? 'active' : ''}`}
|
|
suppressHydrationWarning
|
|
>
|
|
{i}
|
|
</button>
|
|
);
|
|
}
|
|
return pages;
|
|
};
|
|
|
|
const sliderSettings = {
|
|
dots: false,
|
|
infinite: true,
|
|
speed: 500,
|
|
slidesToShow: 2,
|
|
slidesToScroll: 1,
|
|
arrows: false,
|
|
autoplay: true,
|
|
responsive: [
|
|
{
|
|
breakpoint: 767, // Below 768px show 1 card
|
|
settings: {
|
|
slidesToShow: 1,
|
|
slidesToScroll: 1,
|
|
},
|
|
},
|
|
],
|
|
};
|
|
|
|
return (
|
|
<section className={`blog-section-one section-space ${isPaginated ? "pb-0 pt-0" : ""}`}>
|
|
<div className={`small-container ${isSlider ? "blog-slider-active" : ""}`}>
|
|
{!hideHeader && (
|
|
<div className="row justify-content-center mb-30 text-center">
|
|
<div className="col-lg-8">
|
|
<div className="sec-title text-center">
|
|
<div className="sec-title__shape"></div>
|
|
<h6 className="sec-title__tagline">OUR BLOGS</h6>
|
|
<h3 className="sec-title__title">Latest Blogs Post</h3>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)}
|
|
{isSlider ? (
|
|
<Slider {...sliderSettings} className="blog-slider-inner">
|
|
{currentBlogs.map((blog) => (
|
|
<div key={blog.id} className="blog-slider-item px-3">
|
|
<div className="blog-style-one relative overflow-hidden">
|
|
<Link className="blog-image w-img block relative" href={`/${blog.slug}`}>
|
|
<img loading="lazy" src={blog.image} alt={blog.title} />
|
|
<span className="blog-category-tag">{blog.category}</span>
|
|
</Link>
|
|
<div className="blog-content">
|
|
<div className="post-meta">
|
|
<span className="p-relative"><i className="fa-solid fa-user"></i> By Admin</span>
|
|
<span className="p-relative"><i className="fa-solid fa-calendar-days"></i> {blog.date}</span>
|
|
</div>
|
|
<h5 className="blog-title mb-20 mt-15">
|
|
<Link href={`/${blog.slug}`}>{blog.title}</Link>
|
|
</h5>
|
|
<div className="blog-footer">
|
|
<div className="blog-link-learn">
|
|
<Link href={`/${blog.slug}`} className="learn-more-link">
|
|
Learn More <span>+</span>
|
|
</Link>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</Slider>
|
|
) : (
|
|
<div className="row g-4">
|
|
{currentBlogs.map((blog) => (
|
|
<div key={blog.id} className={columns}>
|
|
<div className="blog-style-one relative overflow-hidden">
|
|
<Link className="blog-image w-img block relative" href={`/${blog.slug}`}>
|
|
<img loading="lazy" src={blog.image} alt={blog.title} />
|
|
<span className="blog-category-tag">{blog.category}</span>
|
|
</Link>
|
|
<div className="blog-content">
|
|
<div className="post-meta">
|
|
<span className="p-relative"><i className="fa-solid fa-user"></i> By Admin</span>
|
|
<span className="p-relative"><i className="fa-solid fa-calendar-days"></i> {blog.date}</span>
|
|
</div>
|
|
<h5 className="blog-title mb-20 mt-15">
|
|
<Link href={`/${blog.slug}`}>{blog.title}</Link>
|
|
</h5>
|
|
<div className="blog-footer">
|
|
<div className="blog-link-learn">
|
|
<Link href={`/${blog.slug}`} className="learn-more-link">
|
|
Learn More <span>+</span>
|
|
</Link>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
))}
|
|
{currentBlogs.length === 0 && (
|
|
<div className="col-12 text-center py-5">
|
|
<h3 className="no-blog-found">No blogs found matching your criteria.</h3>
|
|
</div>
|
|
)}
|
|
</div>
|
|
)}
|
|
|
|
{isPaginated && totalPages > 1 && (
|
|
<div className="pagination-wrapper text-center">
|
|
<div className="pagination-items d-flex justify-content-center align-items-center gap-2">
|
|
<button
|
|
onClick={() => paginate(currentPage - 1)}
|
|
disabled={currentPage === 1}
|
|
className={`pagination-arrow ${currentPage === 1 ? 'disabled' : ''}`}
|
|
suppressHydrationWarning
|
|
>
|
|
<i className="fa-solid fa-angles-left"></i>
|
|
</button>
|
|
|
|
{renderPageNumbers()}
|
|
|
|
<button
|
|
onClick={() => paginate(currentPage + 1)}
|
|
disabled={currentPage === totalPages}
|
|
className={`pagination-arrow ${currentPage === totalPages ? 'disabled' : ''}`}
|
|
suppressHydrationWarning
|
|
>
|
|
<i className="fa-solid fa-angles-right"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</section>
|
|
);
|
|
};
|
|
|
|
export default BlogSection;
|