Related Article section implemented
This commit is contained in:
+8
-23
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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.
|
||||
Reference in New Issue
Block a user