- ChatWidget: remove illegal seeds, real localStorage per-handle chat, honest bot replies about market/forum/funds - ForumBoard: wire to real forumState (loadForum/addThread/vote), kill fake stats and illegal seed posts - Home page: privacy features list reflects reality, footer links real - Links: kill all alert() calls, replace fake onions with real clearnet privacy resources + internal route grid - Support: per-coin copied state, env-driven addresses, real BTC addr - Inner circle: wire to AccountContext, tier system from LUX balance, remove hardcoded admin/shadow credentials and fake trading signals - Drop box: real sealed-note localStorage system, honest about no anonymous upload capability, real file picker with receipt - Messages: fully functional per-handle localStorage chat, AI-style contextual bot replies, clear history, honest about local storage - Wallets: pivot from fake PayPal accounts to Digital Access Passes, wire Buy Now to cart via ShopProduct interface - Testimonials: wire submit form to localStorage, interactive star rating 1-10, display submitted reviews above the fold - Raffle: use real merchant BTC address, real per-handle entry storage, honest LUX-only prize disclaimer, fix 0x address - Drops/Lotto: real number picker 1-49 with Quick Pick, ticket submission, match display against drawn numbers, demo disclaimer - Sanctuary: real 4-4-6-2 breathing timer, meditation passage with timer, candle-lighting with localStorage notes - Game: full playable Void Pong with canvas physics, CPU AI, scoring, rally counter, localStorage high score - Security analysis: honest architecture breakdown with real grades, layer-by-layer analysis, practical OPSEC guide, fiction banner - Trust: compute real scores from actual localStorage data (LUX, USD, forum posts, testimonials), FAQ accordion Made-with: Cursor
130 lines
4.9 KiB
TypeScript
130 lines
4.9 KiB
TypeScript
"use client";
|
|
|
|
import { FormEvent, useEffect, useState } from "react";
|
|
import Link from "next/link";
|
|
import { useRouter, useSearchParams } from "next/navigation";
|
|
import { useAccount } from "@/contexts/AccountContext";
|
|
|
|
export default function SignUpPage() {
|
|
const router = useRouter();
|
|
const searchParams = useSearchParams();
|
|
const { user, hydrated, signUp } = useAccount();
|
|
const [username, setUsername] = useState("");
|
|
const [displayName, setDisplayName] = useState("");
|
|
const [password, setPassword] = useState("");
|
|
const [error, setError] = useState<string | null>(null);
|
|
const [busy, setBusy] = useState(false);
|
|
|
|
useEffect(() => {
|
|
if (!hydrated || !user) return;
|
|
const next = searchParams.get("next");
|
|
router.replace(next && next.startsWith("/") && !next.startsWith("//") ? next : "/dashboard");
|
|
}, [hydrated, user, router, searchParams]);
|
|
|
|
const onSubmit = async (e: FormEvent) => {
|
|
e.preventDefault();
|
|
setError(null);
|
|
setBusy(true);
|
|
const res = await signUp(username, password, displayName || undefined);
|
|
setBusy(false);
|
|
if (!res.ok) {
|
|
setError(res.error);
|
|
return;
|
|
}
|
|
const next = searchParams.get("next");
|
|
router.push(next && next.startsWith("/") && !next.startsWith("//") ? next : "/dashboard");
|
|
};
|
|
|
|
if (!hydrated || user) {
|
|
return (
|
|
<div className="flex min-h-[50vh] items-center justify-center font-mono text-sm text-zinc-500">
|
|
Loading…
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className="min-h-screen bg-[#0a0a0a] px-4 py-16 text-zinc-200">
|
|
<div className="mx-auto max-w-md">
|
|
<p className="font-mono text-[10px] uppercase tracking-[0.35em] text-neon-purple/80">provision</p>
|
|
<h1 className="mt-3 font-orbitron text-3xl font-bold">Create account</h1>
|
|
<p className="mt-3 text-sm text-zinc-500">
|
|
Provisioning writes credentials only to this browser profile. Match your{" "}
|
|
<strong className="text-zinc-400">display name</strong> to forum signatures so reputation and escrow
|
|
threads resolve cleanly.
|
|
</p>
|
|
<p className="mt-3 text-sm text-zinc-500">
|
|
Separate forum / exchange / wiki onions each need their own copy:{" "}
|
|
<Link href="/account/hidden-services" className="text-neon-cyan hover:underline">
|
|
Hidden services account guide
|
|
</Link>
|
|
.
|
|
</p>
|
|
|
|
<form onSubmit={onSubmit} className="glass mt-10 space-y-5 rounded-2xl border border-white/10 p-8">
|
|
{error ? (
|
|
<p className="rounded-lg border border-red-500/40 bg-red-500/10 px-4 py-2 text-sm text-red-300">{error}</p>
|
|
) : null}
|
|
<div>
|
|
<label className="mb-2 block text-xs font-medium text-zinc-400">Handle (unique, login id)</label>
|
|
<input
|
|
value={username}
|
|
onChange={(e) => setUsername(e.target.value)}
|
|
autoComplete="username"
|
|
required
|
|
className="glass w-full rounded-xl border border-white/10 bg-transparent px-4 py-3 font-mono text-sm"
|
|
placeholder="ledger_rat"
|
|
/>
|
|
</div>
|
|
<div>
|
|
<label className="mb-2 block text-xs font-medium text-zinc-400">Display name (shown on posts)</label>
|
|
<input
|
|
value={displayName}
|
|
onChange={(e) => setDisplayName(e.target.value)}
|
|
className="glass w-full rounded-xl border border-white/10 bg-transparent px-4 py-3 font-mono text-sm"
|
|
placeholder="Defaults to handle if empty"
|
|
/>
|
|
</div>
|
|
<div>
|
|
<label className="mb-2 block text-xs font-medium text-zinc-400">Passphrase (8+ characters)</label>
|
|
<input
|
|
type="password"
|
|
value={password}
|
|
onChange={(e) => setPassword(e.target.value)}
|
|
autoComplete="new-password"
|
|
required
|
|
className="glass w-full rounded-xl border border-white/10 bg-transparent px-4 py-3 font-mono text-sm"
|
|
/>
|
|
</div>
|
|
<button
|
|
type="submit"
|
|
disabled={busy}
|
|
className="w-full rounded-xl bg-gradient-to-r from-neon-purple to-neon-pink py-3 font-bold text-white disabled:opacity-50"
|
|
>
|
|
{busy ? "Provisioning…" : "Create & enter"}
|
|
</button>
|
|
</form>
|
|
|
|
<p className="mt-8 text-center text-sm text-zinc-500">
|
|
Already have a handle?{" "}
|
|
<Link
|
|
href={
|
|
searchParams.get("next")
|
|
? `/sign-in?next=${encodeURIComponent(searchParams.get("next")!)}`
|
|
: "/sign-in"
|
|
}
|
|
className="text-neon-cyan hover:underline"
|
|
>
|
|
Sign in
|
|
</Link>
|
|
</p>
|
|
<p className="mt-6 text-center">
|
|
<Link href="/" className="text-xs text-zinc-600 hover:text-zinc-400">
|
|
← Hub
|
|
</Link>
|
|
</p>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|