"use client"; import { FormEvent, useEffect, useState } from "react"; import Link from "next/link"; import { useAccount } from "@/contexts/AccountContext"; import { shopCategoryCounts } from "@/lib/shopCatalog"; import { appendVendorApplication } from "@/lib/vendorApplicationState"; const OTHER = "Other / mixed"; export default function VendorApplyPage() { const { user, hydrated } = useAccount(); const [categories, setCategories] = useState<{ name: string; icon: string }[]>([]); const [desiredHandle, setDesiredHandle] = useState(""); const [stallName, setStallName] = useState(""); const [specialtyCategory, setSpecialtyCategory] = useState(""); const [pitch, setPitch] = useState(""); const [pgpFingerprint, setPgpFingerprint] = useState(""); const [deadDropNotes, setDeadDropNotes] = useState(""); const [accepted, setAccepted] = useState(false); const [done, setDone] = useState(false); const [refId, setRefId] = useState(null); const [err, setErr] = useState(null); useEffect(() => { setCategories(shopCategoryCounts().map(({ name, icon }) => ({ name, icon }))); }, []); useEffect(() => { if (hydrated && user && !stallName) { setStallName(`${user.displayName} stall`); setDesiredHandle((h) => h || user.username); } }, [hydrated, user, stallName]); const onSubmit = (e: FormEvent) => { e.preventDefault(); setErr(null); const h = desiredHandle.trim(); if (h.length < 3 || !/^[a-zA-Z0-9_]+$/.test(h)) { setErr("Desired handle: 3+ chars, letters, numbers, underscores only."); return; } if (!stallName.trim() || pitch.trim().length < 40) { setErr("Stall name required; pitch must be at least 40 characters — describe stock, bond, and delivery lane."); return; } if (!specialtyCategory) { setErr("Pick a specialty aisle."); return; } if (!accepted) { setErr("Confirm vendor policy attestation — check the box."); return; } const saved = appendVendorApplication({ accountUsername: user?.username ?? null, desiredHandle: h, stallName: stallName.trim(), specialtyCategory, pitch: pitch.trim(), pgpFingerprint: pgpFingerprint.trim(), deadDropNotes: deadDropNotes.trim(), acceptedSimulatorTerms: true, }); setRefId(saved.id); setDone(true); }; return (

vendor intake · encrypted queue

Apply to vend

Bond review opens after you file this form. Submissions are encrypted client-side and queued for moderator triage — expect PGP-signed follow-up within 48–72h on business mirrors. Scam lanes get burned; do not paste live customer data into fields unless you are comfortable with archival retention on this host.

{done ? (

Application accepted · pending review

Reference id: {refId}

Keep this reference id for bond wire and mirror signing. If you lose session access before approval, re-file with the same PGP fingerprint and ops will merge threads.

Back to vendor hall Browse catalog
) : (
{err ? (

{err}

) : null}
setDesiredHandle(e.target.value)} className="mt-2 w-full border border-emerald-900/50 bg-black/50 px-4 py-3 font-mono text-sm text-emerald-100 placeholder:text-emerald-800 focus:border-emerald-500/60 focus:outline-none" placeholder="ledger_rat_2" autoComplete="off" />
setStallName(e.target.value)} className="mt-2 w-full border border-emerald-900/50 bg-black/50 px-4 py-3 font-mono text-sm text-emerald-100 placeholder:text-emerald-800 focus:border-emerald-500/60 focus:outline-none" placeholder="Midnight solder circus" />