Files
dark-lord/app/exchange/ledger/page.tsx
drjones 78a071ba02 Harden onion boot flow and deepen site surfaces
Add persistent onion key backup and restore, improve startup resilience, and flesh out the major site verticals with richer navigation, search coverage, and operator documentation.

Made-with: Cursor
2026-04-07 21:35:52 -07:00

94 lines
3.8 KiB
TypeScript

"use client";
import { useEffect, useMemo, useState } from "react";
import Link from "next/link";
import { loadExchange, type ExchangeListing } from "@/lib/exchangeState";
export default function ExchangeLedgerPage() {
const [rows, setRows] = useState<ExchangeListing[]>([]);
const [ready, setReady] = useState(false);
useEffect(() => {
setRows(loadExchange());
setReady(true);
}, []);
const stats = useMemo(() => {
const wts = rows.filter((r) => r.kind === "wts").length;
const wtb = rows.filter((r) => r.kind === "wtb").length;
const byCur = { BTC: 0, XMR: 0, USD: 0 } as Record<string, number>;
for (const r of rows) {
byCur[r.currency] = (byCur[r.currency] ?? 0) + 1;
}
const newest = rows.length ? Math.max(...rows.map((r) => r.ts)) : 0;
const oldest = rows.length ? Math.min(...rows.map((r) => r.ts)) : 0;
return { wts, wtb, byCur, newest, oldest, n: rows.length };
}, [rows]);
return (
<div className="space-y-8">
<header className="border-2 border-[#4a3f36] bg-[#12100e] p-6 md:p-8">
<h1 className="text-2xl font-black uppercase text-[#e8ddd0]">Ledger view</h1>
<p className="mt-2 text-sm text-[#8a7b6b]">
Aggregates whatever is stored under <code className="text-[#c4a574]">cyberlux-exchange-v1</code> in this browser. No external analytics SDK just local state, same source the board reads.
</p>
</header>
{!ready ? (
<p className="text-sm text-[#8a7b6b]">Loading ledger</p>
) : (
<>
<div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-4">
<div className="border-2 border-[#4a3f36] bg-[#12100e] p-4">
<p className="text-[10px] font-black uppercase text-[#6b5c4d]">Total rows</p>
<p className="mt-1 text-3xl font-black text-[#f0e6d8]">{stats.n}</p>
</div>
<div className="border-2 border-[#4a3f36] bg-[#12100e] p-4">
<p className="text-[10px] font-black uppercase text-[#6b5c4d]">WTS</p>
<p className="mt-1 text-3xl font-black text-[#c4a574]">{stats.wts}</p>
</div>
<div className="border-2 border-[#4a3f36] bg-[#12100e] p-4">
<p className="text-[10px] font-black uppercase text-[#6b5c4d]">WTB</p>
<p className="mt-1 text-3xl font-black text-[#8d6e4a]">{stats.wtb}</p>
</div>
<div className="border-2 border-[#4a3f36] bg-[#12100e] p-4">
<p className="text-[10px] font-black uppercase text-[#6b5c4d]">Newest post</p>
<p className="mt-1 font-mono text-sm text-[#e8ddd0]">
{stats.newest ? new Date(stats.newest).toLocaleString() : "—"}
</p>
</div>
</div>
<section className="border-2 border-[#4a3f36] bg-[#12100e] p-6">
<h2 className="text-sm font-black uppercase text-[#c4a574]">Currency tags</h2>
<ul className="mt-4 space-y-2 font-mono text-xs text-[#b8a99a]">
{(["BTC", "XMR", "USD"] as const).map((c) => (
<li key={c} className="flex justify-between border-b border-[#2a2218] py-2">
<span>{c}</span>
<span className="text-[#e8c49a]">{stats.byCur[c] ?? 0}</span>
</li>
))}
</ul>
</section>
{stats.oldest && stats.newest ? (
<p className="text-xs text-[#6b5c4d]">
Span: {new Date(stats.oldest).toLocaleDateString()} {new Date(stats.newest).toLocaleDateString()}
</p>
) : null}
</>
)}
<p className="text-xs">
<Link href="/exchange" className="text-[#d4a574] underline">
Board
</Link>
{" · "}
<Link href="/exchange/guidelines" className="text-[#d4a574] underline">
Guidelines
</Link>
</p>
</div>
);
}