"use client"; import { useCallback, useEffect, useState, type FormEvent } from "react"; const STORAGE_KEY = "s2vid_admin_api_key"; export type AdminBadge = { id: string; name: string; embedHtml: string | null; imageUrl: string | null; linkUrl: string | null; isActive: boolean; sortOrder: number; createdAt: string; updatedAt: string; }; function authHeaders(key: string): HeadersInit { return { Authorization: `Bearer ${key}`, "Content-Type": "application/json", }; } export function AdminBadgePanel() { const [apiKey, setApiKey] = useState(""); const [unlocked, setUnlocked] = useState(false); const [badges, setBadges] = useState([]); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const [success, setSuccess] = useState(null); const [name, setName] = useState(""); const [embedHtml, setEmbedHtml] = useState(""); const [imageUrl, setImageUrl] = useState(""); const [linkUrl, setLinkUrl] = useState(""); const [saving, setSaving] = useState(false); useEffect(() => { const stored = sessionStorage.getItem(STORAGE_KEY); if (stored) { setApiKey(stored); setUnlocked(true); } }, []); const loadBadges = useCallback(async (key: string) => { setLoading(true); setError(null); try { const res = await fetch("/api/admin/badges", { headers: authHeaders(key), }); const data = await res.json(); if (!res.ok) throw new Error(data.error || "Failed to load badges"); setBadges(data.badges); setUnlocked(true); sessionStorage.setItem(STORAGE_KEY, key); } catch (err) { setUnlocked(false); setBadges([]); setError(err instanceof Error ? err.message : "Failed to load badges"); } finally { setLoading(false); } }, []); useEffect(() => { if (unlocked && apiKey) { void loadBadges(apiKey); } }, [unlocked, apiKey, loadBadges]); async function unlock(e: FormEvent) { e.preventDefault(); const key = apiKey.trim(); if (!key) { setError("Enter ADMIN_API_KEY"); return; } await loadBadges(key); } function lock() { sessionStorage.removeItem(STORAGE_KEY); setApiKey(""); setUnlocked(false); setBadges([]); setSuccess(null); setError(null); } async function createBadge(e: FormEvent) { e.preventDefault(); setSaving(true); setError(null); setSuccess(null); try { const res = await fetch("/api/admin/badges", { method: "POST", headers: authHeaders(apiKey), body: JSON.stringify({ name: name.trim(), embedHtml: embedHtml.trim() || null, imageUrl: imageUrl.trim() || null, linkUrl: linkUrl.trim() || null, isActive: true, }), }); const data = await res.json(); if (!res.ok) throw new Error(data.error || "Failed to create badge"); setName(""); setEmbedHtml(""); setImageUrl(""); setLinkUrl(""); setSuccess(`Added “${data.badge.name}”`); await loadBadges(apiKey); } catch (err) { setError(err instanceof Error ? err.message : "Failed to create badge"); } finally { setSaving(false); } } async function toggleActive(badge: AdminBadge) { setError(null); setSuccess(null); try { const res = await fetch(`/api/admin/badges/${badge.id}`, { method: "PATCH", headers: authHeaders(apiKey), body: JSON.stringify({ isActive: !badge.isActive }), }); const data = await res.json(); if (!res.ok) throw new Error(data.error || "Failed to update badge"); setSuccess( data.badge.isActive ? `“${data.badge.name}” is now active` : `“${data.badge.name}” is now inactive`, ); await loadBadges(apiKey); } catch (err) { setError(err instanceof Error ? err.message : "Failed to update badge"); } } async function removeBadge(badge: AdminBadge) { if (!confirm(`Delete badge “${badge.name}”?`)) return; setError(null); setSuccess(null); try { const res = await fetch(`/api/admin/badges/${badge.id}`, { method: "DELETE", headers: authHeaders(apiKey), }); const data = await res.json(); if (!res.ok) throw new Error(data.error || "Failed to delete badge"); setSuccess(`Deleted “${badge.name}”`); await loadBadges(apiKey); } catch (err) { setError(err instanceof Error ? err.message : "Failed to delete badge"); } } if (!unlocked) { return (

Admin unlock

Enter ADMIN_API_KEY to manage footer badges. The key stays in this browser tab only (sessionStorage).

setApiKey(e.target.value)} placeholder="ADMIN_API_KEY" className="input-field flex-1" autoComplete="off" />
{error ?

{error}

: null}
); } return (

Authenticated with admin API key. Changes apply to the live footer marquee.

{error ?

{error}

: null} {success ?

{success}

: null}

Add badge

Paste a Product Hunt / LaunchBuff embed (HTML), or set image + link URLs.

setName(e.target.value)} className="input-field" placeholder="Product Hunt" required />