feat: implement dynamic home page with configurable blog and category settings
This commit is contained in:
77
app/page.tsx
77
app/page.tsx
@@ -3,15 +3,19 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import Link from 'next/link';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { Search, MapPin, Briefcase, TrendingUp, Loader2 } from 'lucide-react';
|
||||
import { CATEGORIES, Business } from '../types';
|
||||
import { Search, MapPin, Briefcase, TrendingUp, Loader2, ArrowRight } from 'lucide-react';
|
||||
import { CATEGORIES, Business, BlogPost } from '../types';
|
||||
import BusinessCard from '../components/BusinessCard';
|
||||
import { useUser } from '../components/UserProvider';
|
||||
|
||||
const HomePage = () => {
|
||||
const router = useRouter();
|
||||
const { settings } = useUser();
|
||||
const [searchTerm, setSearchTerm] = useState('');
|
||||
const [featuredBusinesses, setFeaturedBusinesses] = useState<Business[]>([]);
|
||||
const [posts, setPosts] = useState<BlogPost[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [loadingPosts, setLoadingPosts] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchFeatured = async () => {
|
||||
@@ -28,6 +32,21 @@ const HomePage = () => {
|
||||
}
|
||||
};
|
||||
fetchFeatured();
|
||||
|
||||
const fetchPosts = async () => {
|
||||
try {
|
||||
const res = await fetch('/api/blog');
|
||||
const data = await res.json();
|
||||
if (Array.isArray(data)) {
|
||||
setPosts(data);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error fetching blog posts:', error);
|
||||
} finally {
|
||||
setLoadingPosts(false);
|
||||
}
|
||||
};
|
||||
fetchPosts();
|
||||
}, []);
|
||||
|
||||
const handleSearch = (e: React.FormEvent) => {
|
||||
@@ -77,7 +96,7 @@ const HomePage = () => {
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-16">
|
||||
<h2 className="text-3xl font-bold text-gray-900 mb-8 font-serif">Secteurs en vedette</h2>
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
|
||||
{CATEGORIES.slice(0, 4).map((cat, idx) => (
|
||||
{(settings?.homeCategories || CATEGORIES.slice(0, 4)).map((cat: string, idx: number) => (
|
||||
<Link
|
||||
key={idx}
|
||||
href={`/annuaire?category=${encodeURIComponent(cat)}`}
|
||||
@@ -118,6 +137,58 @@ const HomePage = () => {
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Blog Section */}
|
||||
{settings?.homeBlogShow !== false && (
|
||||
<div className="py-16">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div className="flex justify-between items-end mb-8">
|
||||
<div>
|
||||
<h2 className="text-3xl font-bold text-gray-900 font-serif">{settings?.homeBlogTitle || "Derniers Articles"}</h2>
|
||||
<p className="text-gray-500 mt-2">{settings?.homeBlogSubtitle || "Toutes les actualités de l'entrepreneuriat africain."}</p>
|
||||
</div>
|
||||
<Link href="/blog" className="text-brand-600 font-medium hover:text-brand-700 flex items-center">
|
||||
Voir tout le blog <ArrowRight className="w-4 h-4 ml-1" />
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
{loadingPosts ? (
|
||||
<div className="flex justify-center py-12">
|
||||
<Loader2 className="w-10 h-10 text-brand-600 animate-spin" />
|
||||
</div>
|
||||
) : (
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
|
||||
{posts
|
||||
.filter(post => {
|
||||
if (settings?.homeBlogSelection === 'manual') {
|
||||
return settings.homeBlogIds?.includes(post.id);
|
||||
}
|
||||
if (settings?.homeBlogSelection === 'category') {
|
||||
return post.tags?.some((tag: string) => settings.homeBlogCategories?.includes(tag));
|
||||
}
|
||||
return true;
|
||||
})
|
||||
.slice(0, settings?.homeBlogCount || 3)
|
||||
.map(post => (
|
||||
<Link key={post.id} href={`/blog/${post.slug || post.id}`} className="group bg-white rounded-xl shadow-sm border border-gray-100 overflow-hidden hover:shadow-md transition-all">
|
||||
<div className="h-48 overflow-hidden">
|
||||
<img src={post.imageUrl} alt={post.title} className="w-full h-full object-cover group-hover:scale-105 transition-transform duration-500" />
|
||||
</div>
|
||||
<div className="p-6">
|
||||
<h3 className="text-xl font-bold text-gray-900 mb-2 line-clamp-2 group-hover:text-brand-600 transition-colors">{post.title}</h3>
|
||||
<p className="text-gray-500 text-sm line-clamp-3 mb-4">{post.excerpt}</p>
|
||||
<div className="flex items-center justify-between mt-auto pt-4 border-t border-gray-50">
|
||||
<span className="text-xs font-medium text-gray-400">{new Date(post.date).toLocaleDateString('fr-FR')}</span>
|
||||
<span className="text-brand-600 font-bold text-xs">Lire la suite →</span>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user