"use client"; import Link from "next/link"; import { useState, useEffect, useCallback, useRef } from "react"; import { useAccount } from "@/contexts/AccountContext"; // ─── Types ─────────────────────────────────────────────────────────────────── type Msg = { id: string; from: string; text: string; ts: number; channel?: string }; type HandleRecord = { voidCredits: number; claimedInvoices: Record; unlockedTools: { toolId: string; unlockedAt: number; voidSpent: number }[]; }; type LedgerResponse = { ok: boolean; handles: Record; invoiceIndex: Record }; type RaffleStats = { ok: boolean; drawAt: number; totalTickets: number; participants: number }; type RaffleEntry = { handle: string; tickets: number; boughtAt: number }; // ─── Helpers ───────────────────────────────────────────────────────────────── function fmt(ms: number) { if (ms <= 0) return "READY TO DRAW"; 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")}`; } function StatCard({ label, value, sub, accent }: { label: string; value: string | number; sub?: string; accent?: string }) { return (
{label}
{value}
{sub &&
{sub}
}
); } type Tab = "overview" | "credits" | "raffle" | "comms" | "logs"; // ─── Main ───────────────────────────────────────────────────────────────────── export default function VaultAdminDashboard() { const { user, hydrated, signIn } = useAccount(); const [tab, setTab] = useState("overview"); const [loginPass, setLoginPass] = useState(""); const [loginError, setLoginError] = useState(""); // BTC const [btcUsd, setBtcUsd] = useState(null); // Ledger const [handles, setHandles] = useState>({}); const [invoiceIndex, setInvoiceIndex] = useState>({}); // Raffle const [raffleStats, setRaffleStats] = useState(null); const [raffleEntries, setRaffleEntries] = useState([]); const [raffleRemaining, setRaffleRemaining] = useState(0); const [drawResult, setDrawResult] = useState(null); // Credit ops const [creditHandle, setCreditHandle] = useState(""); const [creditAmt, setCreditAmt] = useState(""); const [creditMsg, setCreditMsg] = useState<{ text: string; ok: boolean } | null>(null); // Comms const [messages, setMessages] = useState([]); const [broadcast, setBroadcast] = useState(""); // Logs const [terminalLines, setTerminalLines] = useState([ `[BOOT] CyberLux admin shell initialised.`, `[AUTH] drjones authenticated via local session.`, ]); const logRef = useRef(null); // ── Fetchers ──────────────────────────────────────────────────────────────── const fetchBtc = useCallback(async () => { try { const r = await fetch("https://api.coingecko.com/api/v3/simple/price?ids=bitcoin&vs_currencies=usd", { cache: "no-store" }); const j = await r.json() as { bitcoin?: { usd?: number } }; setBtcUsd(j.bitcoin?.usd ?? null); } catch { /* ignore */ } }, []); const fetchLedger = useCallback(async () => { try { const r = await fetch("/api/admin/ledger"); if (!r.ok) return; const d = await r.json() as LedgerResponse; if (d.ok) { setHandles(d.handles); setInvoiceIndex(d.invoiceIndex); } } catch { /* ignore */ } }, []); const fetchRaffle = useCallback(async () => { try { const r = await fetch("/api/raffle/buy"); const d = await r.json() as RaffleStats; if (d.ok) setRaffleStats(d); const r2 = await fetch("/api/admin/raffle"); if (r2.ok) { const d2 = await r2.json() as { ok: boolean; entries: RaffleEntry[] }; if (d2.ok) setRaffleEntries(d2.entries); } } catch { /* ignore */ } }, []); const fetchMessages = useCallback(async () => { try { const r = await fetch("/api/messages?channel=all"); const d = await r.json() as { ok: boolean; messages: Msg[] }; if (d.ok) setMessages(d.messages); } catch { /* ignore */ } }, []); const log = useCallback((line: string) => { setTerminalLines((prev) => { const next = [...prev.slice(-99), line]; return next; }); setTimeout(() => { logRef.current?.scrollTo({ top: 9999, behavior: "smooth" }); }, 50); }, []); // ── Init ──────────────────────────────────────────────────────────────────── useEffect(() => { if (!user || user.username !== "drjones") return; void fetchBtc(); void fetchLedger(); void fetchRaffle(); void fetchMessages(); const id = setInterval(() => { void fetchBtc(); void fetchLedger(); void fetchRaffle(); void fetchMessages(); }, 15_000); return () => clearInterval(id); }, [user, fetchBtc, fetchLedger, fetchRaffle, fetchMessages]); // Raffle countdown useEffect(() => { if (!raffleStats) return; const t = setInterval(() => setRaffleRemaining(raffleStats.drawAt - Date.now()), 1000); return () => clearInterval(t); }, [raffleStats]); // Simulated terminal feed useEffect(() => { if (!user || user.username !== "drjones") return; const lines = [ () => `[TOR] Circuit ping ${Math.floor(Math.random() * 80 + 20)}ms — relay healthy`, () => `[NGINX] 127.0.0.1:8080 → next:3000 — ${Math.floor(Math.random() * 8 + 1)} req/s`, () => `[SHIELD] Null-routing clearnet probe from ${Math.floor(Math.random() * 255)}.${Math.floor(Math.random() * 255)}.x.x`, () => `[BTC] Block height ${895000 + Math.floor(Math.random() * 500)} confirmed`, () => `[LEDGER] Balances consistent — ${Object.keys(handles).length} handles tracked`, ]; const t = setInterval(() => log(lines[Math.floor(Math.random() * lines.length)]!()), 4000); return () => clearInterval(t); }, [user, handles, log]); // ── Actions ────────────────────────────────────────────────────────────────── const handleLogin = async (e: React.FormEvent) => { e.preventDefault(); setLoginError(""); const res = await signIn("drjones", loginPass); if (!res.ok) setLoginError(res.error ?? "Access Denied"); }; const adminCredit = async () => { const amt = parseFloat(creditAmt); if (!creditHandle.trim() || isNaN(amt) || amt <= 0) { setCreditMsg({ text: "Handle and positive amount required", ok: false }); return; } setCreditMsg(null); try { const r = await fetch("/api/admin/credit", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ handle: creditHandle.trim(), amount: Math.floor(amt) }), }); const d = await r.json() as { ok: boolean; newBalance?: number; error?: string }; if (d.ok) { setCreditMsg({ text: `✓ ${Math.floor(amt)} VOID → @${creditHandle.trim()} (new balance: ${d.newBalance})`, ok: true }); log(`[ADMIN] Manual credit: +${Math.floor(amt)} VOID → @${creditHandle.trim()}`); void fetchLedger(); } else { setCreditMsg({ text: d.error ?? "Failed", ok: false }); } } catch { setCreditMsg({ text: "Network error", ok: false }); } }; const doDraw = () => { if (raffleEntries.length === 0) { setDrawResult("No entries yet."); return; } const pool: string[] = []; for (const e of raffleEntries) { for (let i = 0; i < e.tickets; i++) pool.push(e.handle); } const winner = pool[Math.floor(Math.random() * pool.length)]!; setDrawResult(`🎉 WINNER: @${winner} (${raffleEntries.find((e) => e.handle === winner)?.tickets ?? 1} tickets)`); log(`[RAFFLE] Manual draw executed — winner: @${winner}`); }; const handleBroadcast = async () => { if (!broadcast.trim()) return; try { await fetch("/api/messages", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ from: "System Admin", text: `[BROADCAST] ${broadcast}`, channel: "global" }), }); log(`[COMMS] Global broadcast sent: "${broadcast}"`); setBroadcast(""); void fetchMessages(); } catch { log("[COMMS] Broadcast failed!"); } }; const deleteMessage = async (id: string) => { await fetch(`/api/messages?id=${id}`, { method: "DELETE" }); log(`[COMMS] Deleted message ${id}`); void fetchMessages(); }; const nukeMessages = async () => { if (!confirm("Nuke all comms?")) return; await fetch(`/api/messages?all=true`, { method: "DELETE" }); log("[COMMS] All messages purged."); void fetchMessages(); }; // ── Auth gate ──────────────────────────────────────────────────────────────── if (!hydrated) return (
Initializing…
); if (!user || user.username !== "drjones") return (
CYBERLUX NODE

RESTRICTED

Operator access only

setLoginPass(e.target.value)} className="w-full rounded border border-[#00ff66]/25 bg-black px-4 py-3 text-[#00ff66] text-sm placeholder:text-zinc-700 focus:border-[#00ff66]/60 focus:outline-none" placeholder="passphrase" autoFocus /> {loginError &&
{loginError}
}
← Return to hub
); // ── Computed stats ─────────────────────────────────────────────────────────── const allHandles = Object.entries(handles); const totalVoidCirculating = allHandles.reduce((s, [, r]) => s + r.voidCredits, 0); const totalInvoices = Object.keys(invoiceIndex).length; const totalToolUnlocks = allHandles.reduce((s, [, r]) => s + r.unlockedTools.length, 0); const TABS: { id: Tab; label: string; icon: string }[] = [ { id: "overview", label: "Overview", icon: "◈" }, { id: "credits", label: "Credit Ops", icon: "◆" }, { id: "raffle", label: "Raffle", icon: "🎟" }, { id: "comms", label: "Comms", icon: "📡" }, { id: "logs", label: "System Log", icon: "▸" }, ]; // ── Render ──────────────────────────────────────────────────────────────────── return (
{/* Header */}
CYBERLUX COMMAND // OPERATOR CONSOLE
@drjones GOD MODE {btcUsd && BTC ${btcUsd.toLocaleString()}} ← Hub
{/* Tab bar */} {/* Body */}
{/* ── OVERVIEW ─────────────────────────────────────────────────────── */} {tab === "overview" && (
{/* Stats */}
{/* Raffle snapshot */}
Next Draw
{raffleStats ? fmt(raffleStats.drawAt - Date.now()) : "…"}
{/* Top handles by VOID */}

Top VOID Holders

{allHandles.length === 0 ? (
No handles registered yet.
) : (
{[...allHandles].sort((a, b) => b[1].voidCredits - a[1].voidCredits).slice(0, 10).map(([handle, rec]) => (
@{handle}
{rec.voidCredits} V {rec.unlockedTools.length} tools
))}
)}
{/* Quick ops */}
{[ { label: "VOID Tools", href: "/tools", desc: "Tool marketplace", icon: "🔓" }, { label: "DarkHost", href: "/hosting", desc: "Hosting panel", icon: "🖥️" }, { label: "Add Funds", href: "/account/add-funds", desc: "BTC deposit", icon: "₿" }, ].map((l) => ( {l.icon}
{l.label}
{l.desc}
))}
)} {/* ── CREDIT OPS ───────────────────────────────────────────────────── */} {tab === "credits" && (
{/* Manual credit */}

Manual VOID Credit

setCreditHandle(e.target.value)} placeholder="username" className="rounded border border-zinc-800 bg-black px-3 py-2 text-sm text-[#00ff66] placeholder:text-zinc-700 focus:border-[#00ff66]/50 focus:outline-none w-44" />
setCreditAmt(e.target.value)} placeholder="50" type="number" min="1" className="rounded border border-zinc-800 bg-black px-3 py-2 text-sm text-[#00ff66] placeholder:text-zinc-700 focus:border-[#00ff66]/50 focus:outline-none w-32" />
{creditMsg && (
{creditMsg.text}
)}
{/* Full ledger table */}

Full VOID Ledger — {allHandles.length} handles

{allHandles.length === 0 ? ( ) : ( [...allHandles] .sort((a, b) => b[1].voidCredits - a[1].voidCredits) .map(([handle, rec]) => { const spent = rec.unlockedTools.reduce((s, t) => s + t.voidSpent, 0); return ( ); }) )}
Handle VOID Invoices Tools Spent (VOID)
No handles yet.
@{handle} {rec.voidCredits} {Object.keys(rec.claimedInvoices).length} {rec.unlockedTools.length} {spent}
{/* Invoice index */}

Invoice Index — {totalInvoices} total

{Object.entries(invoiceIndex).slice(-30).reverse().map(([inv, handle]) => ( ))} {totalInvoices === 0 && ( )}
Invoice ID Handle
{inv.slice(0, 20)}… @{handle}
No invoices yet.
)} {/* ── RAFFLE ───────────────────────────────────────────────────────── */} {tab === "raffle" && (
Time to Draw
{fmt(raffleRemaining)}
{/* Draw control */}

Manual Draw

Executes a weighted random draw client-side for preview purposes. Winner is selected proportional to ticket count.

{drawResult && (
{drawResult}
)}
{/* Entries */}

Current Entries

{raffleEntries.length === 0 ? (
No entries yet.
) : (
{[...raffleEntries].sort((a, b) => b.tickets - a.tickets).map((e) => { const pct = raffleStats ? Math.round((e.tickets / raffleStats.totalTickets) * 100) : 0; return (
@{e.handle}
{e.tickets} tickets {pct}%
); })}
)}
)} {/* ── COMMS ────────────────────────────────────────────────────────── */} {tab === "comms" && (
{/* Broadcast */}

Global Network Broadcast