Update the featured articles section to automatically rotate and display the latest articles

Modify the `FeaturedSection` component to include auto-rotation functionality, displaying up to 6 articles in a carousel. Update the `getFeaturedArticles` database query to retrieve the latest 6 articles instead of only those marked as featured.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 413891e8-d784-4bea-b9f5-91a5a68316b4
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: e6e519fc-41a3-463e-88ea-3bde9e02f7f4
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/f209e72a-0939-48fa-84fc-57854de71967/413891e8-d784-4bea-b9f5-91a5a68316b4/kmpcO4B
Replit-Helium-Checkpoint-Created: true
This commit is contained in:
sebastjanartic 2026-02-28 17:16:06 +00:00
parent 5f628350f8
commit db5ec8b74c
2 changed files with 47 additions and 5 deletions

View File

@ -8,15 +8,42 @@ import { Skeleton } from "@/components/ui/skeleton";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import Header from "@/components/header"; import Header from "@/components/header";
import Footer from "@/components/footer"; import Footer from "@/components/footer";
import { useState, useEffect, useCallback } from "react";
function FeaturedSection({ articles }: { articles: Article[] }) { function FeaturedSection({ articles }: { articles: Article[] }) {
if (articles.length === 0) return null; const pool = articles.slice(0, 6);
const totalPages = Math.ceil(pool.length / 3);
const [page, setPage] = useState(0);
const [paused, setPaused] = useState(false);
const hero = articles[0]; const next = useCallback(() => {
const side = articles.slice(1, 3); setPage((p) => (p + 1) % totalPages);
}, [totalPages]);
useEffect(() => {
if (paused || totalPages <= 1) return;
const timer = setInterval(next, 5000);
return () => clearInterval(timer);
}, [paused, next, totalPages]);
if (pool.length === 0) return null;
const start = page * 3;
const visible = pool.slice(start, start + 3);
while (visible.length < 3 && pool.length >= 3) {
visible.push(pool[visible.length % pool.length]);
}
const hero = visible[0];
const side = visible.slice(1, 3);
return ( return (
<section className="mb-12" data-testid="featured-carousel"> <section
className="mb-12"
data-testid="featured-carousel"
onMouseEnter={() => setPaused(true)}
onMouseLeave={() => setPaused(false)}
>
<div className="grid grid-cols-1 lg:grid-cols-5 gap-5"> <div className="grid grid-cols-1 lg:grid-cols-5 gap-5">
<Link href={`/article/${hero.slug}`} className="lg:col-span-3"> <Link href={`/article/${hero.slug}`} className="lg:col-span-3">
<div <div
@ -75,6 +102,21 @@ function FeaturedSection({ articles }: { articles: Article[] }) {
))} ))}
</div> </div>
</div> </div>
{totalPages > 1 && (
<div className="flex justify-center gap-2 mt-4" data-testid="carousel-dots">
{Array.from({ length: totalPages }).map((_, i) => (
<button
key={i}
onClick={() => setPage(i)}
className={`w-2.5 h-2.5 rounded-full transition-all duration-300 ${
i === page ? "bg-primary w-6" : "bg-muted-foreground/30 hover:bg-muted-foreground/50"
}`}
data-testid={`button-carousel-dot-${i}`}
/>
))}
</div>
)}
</section> </section>
); );
} }

View File

@ -31,7 +31,7 @@ export class DatabaseStorage implements IStorage {
} }
async getFeaturedArticles(): Promise<Article[]> { async getFeaturedArticles(): Promise<Article[]> {
return db.select().from(articles).where(eq(articles.featured, true)).orderBy(desc(articles.publishedAt)).limit(3); return db.select().from(articles).orderBy(desc(articles.publishedAt)).limit(6);
} }
async getPopularArticles(limit: number): Promise<Article[]> { async getPopularArticles(limit: number): Promise<Article[]> {