"use client"; import { useEffect, useState, useCallback } from "react"; import Link from "next/link"; import { useAccount } from "@/contexts/AccountContext"; const EPOCH = new Date("2026-04-14T00:00:00Z").getTime(); const INTERVAL = 7 * 24 * 60 * 60 * 1000; function getNextDraw() { const now = Date.now(); return EPOCH + (Math.floor((now - EPOCH) / INTERVAL) + 1) * INTERVAL; } function fmt(ms: number) { if (ms <= 0) return "DRAWING NOW"; const s = Math.floor(ms / 1000); const d = Math.floor(s / 86400); const h = Math.floor((s % 86400) / 3600); const m = Math.floor((s % 3600) / 60); const sec = s % 60; if (d > 0) return `${d}d ${String(h).padStart(2, "0")}:${String(m).padStart(2, "0")}:${String(sec).padStart(2, "0")}`; return `${String(h).padStart(2, "0")}:${String(m).padStart(2, "0")}:${String(sec).padStart(2, "0")}`; } type RaffleStats = { totalTickets: number; participants: number; drawAt: number }; export default function RaffleCountdownBanner() { const { user } = useAccount(); const [remaining, setRemaining] = useState(() => getNextDraw() - Date.now()); const [stats, setStats] = useState(null); const [buying, setBuying] = useState(false); const [qty, setQty] = useState(1); const [msg, setMsg] = useState<{ text: string; ok: boolean } | null>(null); const [open, setOpen] = useState(false); const [dismissed, setDismissed] = useState(false); useEffect(() => { const t = setInterval(() => setRemaining(getNextDraw() - Date.now()), 1000); return () => clearInterval(t); }, []); const fetchStats = useCallback(async () => { try { const r = await fetch("/api/raffle/buy"); const d = (await r.json()) as RaffleStats & { ok: boolean }; if (d.ok) setStats(d); } catch { /* ignore */ } }, []); useEffect(() => { void fetchStats(); }, [fetchStats]); const buyTickets = async () => { if (!user) return; setBuying(true); setMsg(null); try { const r = await fetch("/api/raffle/buy", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ handle: user.username, quantity: qty }), }); const d = (await r.json()) as { ok: boolean; error?: string; ticketsBought?: number; voidBalance?: number }; if (d.ok) { setMsg({ text: `✓ ${d.ticketsBought} ticket${(d.ticketsBought ?? 1) > 1 ? "s" : ""} entered! Balance: ${d.voidBalance} VOID`, ok: true }); void fetchStats(); } else { setMsg({ text: d.error ?? "Failed", ok: false }); } } catch { setMsg({ text: "Network error", ok: false }); } finally { setBuying(false); } }; if (dismissed) return null; return (
{/* Left: timer + pot */}
◈ WEEKLY VOID RAFFLE
{fmt(remaining)}
{stats && ( {stats.totalTickets} tickets · {stats.participants} players )}
{/* Centre: quick buy */}
{user ? ( open ? (
{qty} {msg && ( {msg.text} )}
) : ( ) ) : ( 🎟 WEEKLY RAFFLE )}
{/* Right: prize + close */}
500 VOID top prize →
); }