204 lines
8.0 KiB
TypeScript
204 lines
8.0 KiB
TypeScript
"use client";
|
|
|
|
import React, { useState, useEffect, useMemo } from "react";
|
|
import Link from "next/link";
|
|
import { BlogData } from "@/utils/constant.utils";
|
|
|
|
interface BlogSidebarProps {
|
|
activeCategory?: string;
|
|
onCategoryClick?: (category: string) => void;
|
|
initialSearchTerm?: string;
|
|
onSearch?: (term: string) => void;
|
|
excludeSlug?: string;
|
|
}
|
|
|
|
const BlogSidebar = ({
|
|
activeCategory,
|
|
onCategoryClick,
|
|
initialSearchTerm = "",
|
|
onSearch,
|
|
excludeSlug
|
|
}: BlogSidebarProps) => {
|
|
const [searchTerm, setSearchTerm] = useState(initialSearchTerm);
|
|
|
|
// Debounced real-time search
|
|
useEffect(() => {
|
|
const timer = setTimeout(() => {
|
|
if (searchTerm !== initialSearchTerm && onSearch) {
|
|
onSearch(searchTerm);
|
|
}
|
|
}, 500);
|
|
return () => clearTimeout(timer);
|
|
}, [searchTerm, onSearch, initialSearchTerm]);
|
|
|
|
// Update search term when initial changes
|
|
useEffect(() => {
|
|
setSearchTerm(initialSearchTerm);
|
|
}, [initialSearchTerm]);
|
|
|
|
// Recent News (Latest 3 blogs, excluding current one, filtered by category if active)
|
|
const recentPosts = useMemo(() => {
|
|
let list = [...BlogData];
|
|
if (excludeSlug) {
|
|
list = list.filter(p => p.slug !== excludeSlug);
|
|
}
|
|
if (activeCategory) {
|
|
// Priority to same category, but if not enough, show others
|
|
const sameCat = list.filter(p => p.category === activeCategory);
|
|
const others = list.filter(p => p.category !== activeCategory);
|
|
list = [...sameCat, ...others];
|
|
}
|
|
return list
|
|
.sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime())
|
|
.slice(0, 3);
|
|
}, [activeCategory, excludeSlug]);
|
|
|
|
// Categories and Counts (Sorted to show active category at the top, then by most recent post date)
|
|
const categories = useMemo(() => {
|
|
const catMap = BlogData.reduce((acc: any, item: any) => {
|
|
if (!acc[item.category]) {
|
|
acc[item.category] = { count: 0, latestDate: new Date(0) };
|
|
}
|
|
acc[item.category].count += 1;
|
|
const itemDate = new Date(item.date);
|
|
if (itemDate > acc[item.category].latestDate) {
|
|
acc[item.category].latestDate = itemDate;
|
|
}
|
|
return acc;
|
|
}, {});
|
|
|
|
const allBlogsCat = {
|
|
name: "All Blogs",
|
|
count: BlogData.length,
|
|
latestDate: new Date() // Doesn't matter for sorting as we'll put it first
|
|
};
|
|
|
|
let result = Object.keys(catMap)
|
|
.map(cat => ({
|
|
name: cat,
|
|
count: catMap[cat].count,
|
|
latestDate: catMap[cat].latestDate
|
|
}))
|
|
.sort((a, b) => {
|
|
const normalizedActive = (activeCategory || "").trim().toLowerCase();
|
|
const normalizedA = (a.name || "").trim().toLowerCase();
|
|
const normalizedB = (b.name || "").trim().toLowerCase();
|
|
|
|
// Active category always first (if not All Blogs)
|
|
if (normalizedActive && normalizedA === normalizedActive) return -1;
|
|
if (normalizedActive && normalizedB === normalizedActive) return 1;
|
|
// Then sort by most recent post date
|
|
return b.latestDate.getTime() - a.latestDate.getTime();
|
|
});
|
|
|
|
// Optimize for Detail Page (excludeSlug exists)
|
|
if (excludeSlug && result.length > 6) {
|
|
return [allBlogsCat, ...result.slice(0, 5)];
|
|
}
|
|
|
|
return [allBlogsCat, ...result];
|
|
}, [activeCategory, excludeSlug]);
|
|
|
|
const handleSearchSubmit = (e: React.FormEvent) => {
|
|
e.preventDefault();
|
|
if (onSearch) {
|
|
onSearch(searchTerm);
|
|
}
|
|
};
|
|
|
|
const handleCategoryClick = (e: React.MouseEvent, category: string) => {
|
|
if (onCategoryClick) {
|
|
e.preventDefault();
|
|
onCategoryClick(category);
|
|
} else {
|
|
// Default behavior if no handler is provided
|
|
// Redirect to results page
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div className="main-sidebar">
|
|
{/* Search Widget */}
|
|
<div className="single-sidebar-widget search-widget">
|
|
<div className="wid-title">
|
|
<h3>Search</h3>
|
|
</div>
|
|
<div className="search_widget">
|
|
<form onSubmit={handleSearchSubmit}>
|
|
<input
|
|
type="text"
|
|
placeholder="Search blogs..."
|
|
value={searchTerm}
|
|
onChange={(e) => setSearchTerm(e.target.value)}
|
|
/>
|
|
<button type="submit"><i className="fa-solid fa-magnifying-glass"></i></button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Recent News Widget */}
|
|
<div className="single-sidebar-widget">
|
|
<div className="wid-title">
|
|
<h3>Recent Blogs</h3>
|
|
</div>
|
|
<div className="popular-posts">
|
|
{recentPosts.map((post: any) => (
|
|
<div key={post.id} className="single-post-item">
|
|
<div className="thumb bg-cover">
|
|
<Link href={`/${post.slug}`}>
|
|
<img loading="lazy" src={post.image} alt={post.title} />
|
|
</Link>
|
|
</div>
|
|
<div className="post-content">
|
|
<h5>
|
|
<Link href={`/${post.slug}`}>{post.title}</Link>
|
|
</h5>
|
|
<div className="post-date">
|
|
<i className="fa-regular fa-calendar"></i> {post.date}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
))}
|
|
{recentPosts.length === 0 && <p className="no-posts-found">No recent posts found.</p>}
|
|
</div>
|
|
</div>
|
|
|
|
{/* Categories Widget */}
|
|
<div className="single-sidebar-widget mb-0">
|
|
<div className="wid-title">
|
|
<h3>{excludeSlug ? "Blog Related Category" : "Categories"}</h3>
|
|
</div>
|
|
<div className="widget_categories">
|
|
<ul className="list-unstyled">
|
|
{categories.map((cat) => {
|
|
const isAllBlogs = cat.name === "All Blogs";
|
|
// Normalize for comparison to handle case-sensitivity or leading/trailing spaces
|
|
const normalizedActive = (activeCategory || "").trim().toLowerCase();
|
|
const normalizedCat = (cat.name || "").trim().toLowerCase();
|
|
|
|
const isActive = isAllBlogs
|
|
? (normalizedActive === "")
|
|
: (normalizedActive === normalizedCat);
|
|
|
|
return (
|
|
<li key={cat.name} className={isActive ? "active" : ""}>
|
|
<Link
|
|
href={isAllBlogs ? "/blog" : `/blog/results?category=${encodeURIComponent(cat.name)}`}
|
|
onClick={(e) => handleCategoryClick(e, isAllBlogs ? "" : cat.name)}
|
|
className="category-item"
|
|
>
|
|
<span className="category-name">{cat.name}</span>
|
|
<span className="category-count">{cat.count < 10 ? `0${cat.count}` : cat.count}</span>
|
|
</Link>
|
|
</li>
|
|
);
|
|
})}
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default BlogSidebar;
|