diff --git a/src/components/blogCarousel/blogCard.tsx b/src/components/blogCarousel/blogCard.tsx index 077e3c24..4ced981b 100644 --- a/src/components/blogCarousel/blogCard.tsx +++ b/src/components/blogCarousel/blogCard.tsx @@ -1,7 +1,7 @@ "use client"; import React from "react"; import Link from "@docusaurus/Link"; -import { getAuthorProfiles, getAuthorTooltip } from "../../utils/authors"; +import { getAuthorProfiles } from "../../utils/authors"; interface BlogCardProps { type: string; @@ -11,15 +11,48 @@ interface BlogCardProps { imageUrl: string; id: string; authors?: string[]; + tags?: string[]; + category?: string; +} + +const TAG_COLORS = [ + { dot: "#f59e0b", border: "#fde68a", bg: "#fffbeb", text: "#92400e" }, + { dot: "#6366f1", border: "#c7d2fe", bg: "#eef2ff", text: "#3730a3" }, + { dot: "#ec4899", border: "#fbcfe8", bg: "#fdf2f8", text: "#9d174d" }, + { dot: "#10b981", border: "#a7f3d0", bg: "#ecfdf5", text: "#065f46" }, + { dot: "#3b82f6", border: "#bfdbfe", bg: "#eff6ff", text: "#1e40af" }, + { dot: "#8b5cf6", border: "#ddd6fe", bg: "#f5f3ff", text: "#5b21b6" }, + { dot: "#f97316", border: "#fed7aa", bg: "#fff7ed", text: "#9a3412" }, + { dot: "#14b8a6", border: "#99f6e4", bg: "#f0fdfa", text: "#134e4a" }, +]; + +function tagColor(label: string) { + let hash = 0; + for (let i = 0; i < label.length; i++) + hash = label.charCodeAt(i) + ((hash << 5) - hash); + return TAG_COLORS[Math.abs(hash) % TAG_COLORS.length]; +} + +function formatDate(dateStr?: string) { + if (!dateStr) return ""; + const d = new Date(dateStr); + if (isNaN(d.getTime())) return dateStr; + return d.toLocaleDateString("en-US", { + month: "short", + day: "2-digit", + year: "numeric", + }); } const BlogCard = ({ type, + date, title, - content, imageUrl, id, authors, + tags, + category, }: BlogCardProps) => { const authorProfiles = getAuthorProfiles(authors || []); @@ -27,119 +60,118 @@ const BlogCard = ({ return
data not fetched properly, imageId and entryId not found
; } - // Get category from title for demo purposes - const getCategory = (title) => { - if ( - title.toLowerCase().includes("design") || - title.toLowerCase().includes("ux") - ) - return "Design"; - if ( - title.toLowerCase().includes("ai") || - title.toLowerCase().includes("deepmind") - ) - return "AI & Tech"; - if ( - title.toLowerCase().includes("github") || - title.toLowerCase().includes("git") - ) - return "Development"; - return "Resources"; - }; + // Tags: use tags array first, then category, skip generic "blog" + const rawTags = Array.isArray(tags) && tags.length > 0 + ? tags + : category + ? [category] + : []; - const category = getCategory(title); + const tagList = rawTags.filter( + (t) => t && t.toLowerCase() !== "blog" && t.toLowerCase() !== "post" + ); return ( -
-
- {/* Category Badge */} -
{category}
- - {/* Card Image */} -
- {title} -
+
+ {/* Image */} +
+ {title} +
- {/* Card Content */} -
-

- - {title} - -

-

{content}

+ {/* Content */} +
+ {/* Title */} +

+ + {title} + +

- {/* Card Meta */} -
-
- {/* Stacked Author Avatars */} - {authorProfiles.length > 0 && - (() => { - const max = 3; - const visible = authorProfiles.slice(0, max); - const extra = Math.max(0, authorProfiles.length - max); - return ( -
- {visible.map((a, i) => ( -
- {a.imageUrl ? ( - {a.name} { - const target = e.currentTarget; - target.style.display = "none"; - const fallback = - target.nextElementSibling as HTMLElement | null; - if (fallback) fallback.style.display = "flex"; - }} - /> - ) : ( - - {a.name.charAt(0).toUpperCase()} - - )} -
- ))} - {extra > 0 && ( -
+{extra}
- )} -
- ); - })()} + {/* Tag pills */} + {tagList.length > 0 && ( +
+ {tagList.slice(0, 5).map((tag) => { + const c = tagColor(tag); + return ( + + + {tag} + + ); + })} +
+ )} - {/* Author Names */} -
- {authorProfiles.map((author, authorIndex) => ( - - {authorIndex > 0 && ( - , - )} - +
+ {/* Avatar stack — shows all authors overlapped */} + {authorProfiles.length > 0 && ( +
+ {authorProfiles.map((author, i) => ( +
+ {author.imageUrl ? ( + {author.name} { + const t = e.currentTarget; + t.style.display = "none"; + const fb = t.nextElementSibling as HTMLElement | null; + if (fb) fb.style.display = "flex"; + }} + /> + ) : null} + - {author.name} - - + {author.name.charAt(0).toUpperCase()} + +
))}
+ )} + +
+ {/* All author names inline, separated by commas */} + {authorProfiles.length > 0 && ( +
+ {authorProfiles.map((author, i) => ( + + {i > 0 && , } + + @{author.id || author.name.toLowerCase().replace(/\s+/g, "")} + + + ))} +
+ )} + {date && {formatDate(date)}}
- 5 min read
- {/* Read More Button */} - - Read Article → + + Read →
@@ -147,4 +179,4 @@ const BlogCard = ({ ); }; -export default BlogCard; +export default BlogCard; \ No newline at end of file diff --git a/src/components/blogCarousel/blogCarousel.tsx b/src/components/blogCarousel/blogCarousel.tsx index 1f2c5d2d..524b2132 100644 --- a/src/components/blogCarousel/blogCarousel.tsx +++ b/src/components/blogCarousel/blogCarousel.tsx @@ -40,7 +40,7 @@ export function BlogCarousel() {

From the Blog

-

+

Latest articles from our contributors

@@ -74,13 +74,15 @@ export function BlogCarousel() { className="basis-full sm:basis-1/2 lg:basis-1/3 xl:basis-1/4" > ))} @@ -98,8 +100,8 @@ export function BlogCarousel() { onClick={() => api?.scrollTo(index)} aria-label={`Go to slide ${index + 1}`} className={`h-2 rounded-full transition-all duration-300 ${current === index + 1 - ? "w-5 bg-indigo-500" - : "w-2 bg-gray-300 hover:bg-gray-400 dark:bg-gray-600 dark:hover:bg-gray-500" + ? "w-5 bg-indigo-500" + : "w-2 bg-gray-300 hover:bg-gray-400 dark:bg-gray-600 dark:hover:bg-gray-500" }`} /> ))}