"use client"; import { FormEvent, useEffect, useMemo, useState } from "react"; import Link from "next/link"; import { useRouter } from "next/navigation"; import Navbar from "@/components/Navbar"; import { useAccount } from "@/contexts/AccountContext"; import { useWallet } from "@/contexts/WalletContext"; import { computeUserActivityStats } from "@/lib/userActivityStats"; import { loadVendorApplications, type VendorApplication } from "@/lib/vendorApplicationState"; export default function DashboardPage() { const router = useRouter(); const { user, hydrated, signOut, saveDisplayName } = useAccount(); const { isConnected, address, luxCredits, usdStoreCredit, vault } = useWallet(); const [dn, setDn] = useState(""); const [profileMsg, setProfileMsg] = useState(null); const [vendorApps, setVendorApps] = useState([]); useEffect(() => { if (hydrated && !user) router.replace("/sign-in"); }, [hydrated, user, router]); useEffect(() => { if (user) setDn(user.displayName); }, [user]); useEffect(() => { if (!user) return; setVendorApps(loadVendorApplications().filter((a) => a.accountUsername === user.username)); }, [user]); const stats = useMemo(() => { if (!user) return null; return computeUserActivityStats(user.username, user.displayName); }, [user]); const onProfile = (e: FormEvent) => { e.preventDefault(); setProfileMsg(null); const res = saveDisplayName(dn); if (!res.ok) setProfileMsg(res.error); else setProfileMsg("Display name saved — use it when posting for accurate stats."); }; if (!hydrated || !user) { return (
Loading…
); } const since = new Date(user.createdAt).toLocaleDateString(undefined, { year: "numeric", month: "short", day: "numeric", }); return (

personal relay

Dashboard

Signed in as {user.displayName}{" "} (@{user.username})

Profile

Member since {since}

setDn(e.target.value)} className="glass w-full rounded-lg border border-white/10 bg-transparent px-3 py-2 font-mono text-sm" /> {profileMsg ?

{profileMsg}

: null}

Wallet (sim)

Status: {isConnected ? connected : not connected}

{address ? (

{address.slice(0, 10)}…{address.slice(-6)}

) : null}
  • LUX credits {luxCredits.toLocaleString()}
  • USD store credit ${usdStoreCredit.toFixed(2)}
  • Vault keys {vault.keys.length}
Checkout →

Vendor stall (sim)

Queue a fiction application — stored in this browser only.{" "} Apply to vend →

{vendorApps.length ? (
    {vendorApps.map((a) => (
  • {a.desiredHandle} · {a.stallName.slice(0, 48)} {a.stallName.length > 48 ? "…" : ""} {new Date(a.ts).toLocaleString()} · ref {a.id.slice(0, 8)}
  • ))}
) : (

No applications logged for @{user.username} yet.

)}
{stats ? (

Activity (matched by display name or handle)

  • Forum threads {stats.forumThreads}
  • Forum replies {stats.forumReplies}
  • Exchange listings {stats.exchangeListings}
  • Barter posts {stats.barterListings}
New forum post Exchange Barter Vault
) : null}

← Hub

); }