Add dynamic footer badge marquee with admin management.
Store badges in Postgres, expose public/admin APIs, render an infinite footer slider, and ship /admin for paste-embed CRUD.
This commit is contained in:
@@ -0,0 +1,62 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import { BadgeEmbed } from "@/components/BadgeEmbed";
|
||||
|
||||
export type PublicFooterBadge = {
|
||||
id: string;
|
||||
name: string;
|
||||
embedHtml: string | null;
|
||||
imageUrl: string | null;
|
||||
linkUrl: string | null;
|
||||
sortOrder: number;
|
||||
};
|
||||
|
||||
type Props = {
|
||||
initialBadges?: PublicFooterBadge[];
|
||||
};
|
||||
|
||||
export function FooterBadgeMarquee({ initialBadges = [] }: Props) {
|
||||
const [badges, setBadges] = useState<PublicFooterBadge[]>(initialBadges);
|
||||
|
||||
useEffect(() => {
|
||||
let cancelled = false;
|
||||
fetch("/api/badges")
|
||||
.then(async (res) => {
|
||||
if (!res.ok) return;
|
||||
const data = await res.json();
|
||||
if (!cancelled && Array.isArray(data.badges)) {
|
||||
setBadges(data.badges);
|
||||
}
|
||||
})
|
||||
.catch(() => {});
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, []);
|
||||
|
||||
if (badges.length === 0) return null;
|
||||
|
||||
// Duplicate track for seamless infinite scroll
|
||||
const loop = [...badges, ...badges];
|
||||
const durationSec = Math.max(18, badges.length * 10);
|
||||
|
||||
return (
|
||||
<div className="badge-marquee mb-12" aria-label="Featured on">
|
||||
<div
|
||||
className="badge-marquee-track"
|
||||
style={{ ["--badge-marquee-duration" as string]: `${durationSec}s` }}
|
||||
>
|
||||
{loop.map((badge, index) => (
|
||||
<div
|
||||
key={`${badge.id}-${index}`}
|
||||
className="badge-marquee-item"
|
||||
aria-hidden={index >= badges.length ? true : undefined}
|
||||
>
|
||||
<BadgeEmbed badge={badge} />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user