Related Article section implemented

This commit is contained in:
adilallo
2025-09-11 14:06:31 -06:00
parent ec2db8be22
commit 8a31671bbc
5 changed files with 152 additions and 40 deletions
+8 -23
View File
@@ -1,8 +1,8 @@
import { notFound } from "next/navigation";
import Link from "next/link";
import { getBlogPostBySlug, getAllPosts } from "../../../lib/contentProcessor";
import ContentThumbnailTemplate from "../../components/ContentThumbnailTemplate";
import ContentBanner from "../../components/ContentBanner";
import RelatedArticles from "../../components/RelatedArticles";
import { getAssetPath, ASSETS } from "../../../lib/assetUtils";
/**
@@ -70,9 +70,9 @@ export default async function BlogPostPage({ params }) {
notFound();
}
// Get related posts (for now, just get other posts)
// Get related articles (for now, just get other posts)
const allPosts = getAllPosts();
const relatedPosts = allPosts.filter((p) => p.slug !== post.slug).slice(0, 3); // Show up to 3 related posts
const relatedArticles = allPosts; // Pass all posts to RelatedArticles component for filtering
return (
<div className="min-h-screen bg-[#F4F3F1] relative overflow-hidden">
@@ -122,26 +122,11 @@ export default async function BlogPostPage({ params }) {
</div>
</article>
{/* Related Posts Section */}
{relatedPosts.length > 0 && (
<section className="bg-white border-t border-gray-200">
<div className="max-w-6xl mx-auto px-4 py-12">
<h2 className="text-3xl font-bold text-gray-900 mb-8 text-center">
Related Articles
</h2>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
{relatedPosts.map((relatedPost) => (
<ContentThumbnailTemplate
key={relatedPost.slug}
post={relatedPost}
variant="vertical"
/>
))}
</div>
</div>
</section>
)}
{/* Related Articles Section */}
<RelatedArticles
relatedPosts={relatedArticles}
currentPostSlug={post.slug}
/>
</div>
);
}
+10 -7
View File
@@ -14,13 +14,16 @@ const ContentContainer = ({ post, width = "200px", size = "responsive" }) => {
if (!slug) return icons[0];
// Use the same hash logic as background images to ensure matching
const hash = slug.split("").reduce((a, b) => {
a = (a << 5) - a + b.charCodeAt(0);
return a & a;
}, 0);
return icons[Math.abs(hash) % icons.length];
// Use the same cycling logic as background images to ensure matching
const slugOrder = [
"building-community-trust",
"operational-security-mutual-aid",
"making-decisions-without-hierarchy",
"resolving-active-conflicts",
];
const index = slugOrder.indexOf(slug);
const finalIndex = index >= 0 ? index % icons.length : 0;
return icons[finalIndex];
};
const iconImage = getIconImage(post.slug);
+10 -10
View File
@@ -32,16 +32,16 @@ const ContentThumbnailTemplate = ({
if (!slug) return images[0];
// Use the slug to deterministically select an image
// More robust hash function using djb2 algorithm
let hash = 5381;
for (let i = 0; i < slug.length; i++) {
hash = (hash << 5) + hash + slug.charCodeAt(i);
}
// Ensure positive number and get index
const index = Math.abs(hash) % images.length;
return images[index];
// Simple cycling approach to ensure different styles
const slugOrder = [
"building-community-trust",
"operational-security-mutual-aid",
"making-decisions-without-hierarchy",
"resolving-active-conflicts",
];
const index = slugOrder.indexOf(slug);
const finalIndex = index >= 0 ? index % images.length : 0;
return images[finalIndex];
};
const backgroundImage = getBackgroundImage(post.slug, variant);
+102
View File
@@ -0,0 +1,102 @@
"use client";
import { useState, useEffect } from "react";
import ContentThumbnailTemplate from "./ContentThumbnailTemplate";
export default function RelatedArticles({ relatedPosts, currentPostSlug }) {
// Filter out the current post from related posts
const filteredPosts = relatedPosts.filter(
(post) => post.slug !== currentPostSlug
);
const [currentIndex, setCurrentIndex] = useState(0);
const [progress, setProgress] = useState(0);
// Auto-advance every 3 seconds
useEffect(() => {
if (filteredPosts.length <= 1) return;
const interval = setInterval(() => {
setProgress(0);
setCurrentIndex((prev) => (prev + 1) % filteredPosts.length);
}, 3000);
return () => clearInterval(interval);
}, [filteredPosts.length]);
// Progress animation
useEffect(() => {
if (filteredPosts.length <= 1) return;
const progressInterval = setInterval(() => {
setProgress((prev) => {
if (prev >= 100) {
return 0;
}
return prev + 1;
});
}, 30); // 30ms intervals for smooth animation
return () => clearInterval(progressInterval);
}, [currentIndex, filteredPosts.length]);
if (filteredPosts.length === 0) {
return null;
}
return (
<section className="py-[var(--spacing-scale-032)]">
<div className="flex flex-col gap-[var(--spacing-scale-032)]">
<h2 className="text-[32px] leading-[110%] font-medium text-[var(--color-content-inverse-primary)] text-center">
Related Articles
</h2>
{/* Horizontal Articles Row - All Visible with Centering */}
<div className="flex justify-center overflow-hidden">
<div
className="flex gap-0 transition-transform duration-500 ease-in-out"
style={{
transform: `translateX(calc(50% - 130px - ${
currentIndex * 260
}px))`,
}}
>
{filteredPosts.map((relatedPost, index) => (
<div
key={relatedPost.slug}
className="flex flex-col items-center"
>
<ContentThumbnailTemplate
post={relatedPost}
variant="vertical"
/>
</div>
))}
</div>
</div>
{/* Three separate progress bars below the carousel */}
<div className="flex justify-center gap-[var(--measures-spacing-008)] px-[var(--measures-spacing-064)]">
{filteredPosts.map((relatedPost, index) => (
<div
key={relatedPost.slug}
className="max-w-[var(--measures-spacing-056)] w-full h-[var(--measures-spacing-004)] bg-gray-200 rounded-full overflow-hidden"
>
<div
className="h-full bg-gray-600 rounded-full transition-all duration-75 ease-linear"
style={{
width:
index === currentIndex
? `${progress}%`
: index < currentIndex
? "100%"
: "0%",
}}
/>
</div>
))}
</div>
</div>
</section>
);
}
+22
View File
@@ -0,0 +1,22 @@
---
title: "Building Community Trust"
description: "Strategies for fostering trust, transparency, and accountability in community organizations"
author: "Author name"
date: "2025-04-20"
related:
[
"resolving-active-conflicts",
"operational-security-mutual-aid",
"making-decisions-without-hierarchy",
]
---
Trust is the foundation of any successful community organization. Without it, even the best structures and processes will struggle to function effectively. Building and maintaining trust requires intentional effort, clear communication, and consistent follow-through on commitments.
One key element of building trust is transparency. When community members understand how decisions are made, where resources go, and what challenges the organization faces, they're more likely to feel invested and supportive. This doesn't mean sharing every detail, but it does mean being open about the big picture and the reasoning behind important choices.
Another crucial factor is accountability. When people make mistakes or fail to follow through on commitments, there need to be clear, fair processes for addressing these issues. This might involve mediation, restorative justice practices, or other approaches that focus on learning and repair rather than punishment.
Regular communication also plays a vital role. Whether through newsletters, community meetings, or informal conversations, keeping people informed about what's happening helps prevent misunderstandings and builds a sense of shared purpose. It's especially important to communicate both successes and challenges honestly.
Finally, trust is built through consistent action over time. When community members see that the organization follows through on its promises and treats people fairly, even in difficult situations, trust grows stronger. This consistency creates a foundation that can weather conflicts and challenges when they inevitably arise.