Files
dark-lord/app/vendors/page.tsx
drjones e0a4a20862 Tor ops: launch UI, onion list/health scripts, systemd installer, README glow-up
- Replace fake-site footer with dark-web launch CTA; fix HubChatter inside AppProviders (client crash)
- Add /launch, DEPLOY.md, onions:list|status|health, list-onion-urls, install-systemd, onion-status
- start.sh CYBERLUX_PREPARE_ONLY; copy pass on satellite pages; Navbar launch link
- README: operator cheat sheet, phone Tor note, systemd via install-systemd.sh

Made-with: Cursor
2026-04-15 22:12:44 -07:00

113 lines
5.5 KiB
TypeScript

import Link from "next/link";
import { SHOP_PRODUCTS } from "@/lib/shopCatalog";
import { SHOP_SELLERS, SHOP_SELLER_COUNT } from "@/lib/shopSellers";
export default function VendorsHallPage() {
const listingsByVendor = new Map<string, number>();
for (const p of SHOP_PRODUCTS) {
listingsByVendor.set(p.sellerId, (listingsByVendor.get(p.sellerId) ?? 0) + 1);
}
const totalSales = SHOP_SELLERS.reduce((a, s) => a + s.completedSales, 0);
const avgRating = SHOP_SELLERS.reduce((a, s) => a + s.rating, 0) / SHOP_SELLERS.length;
return (
<div className="min-h-screen bg-[#060508] text-[#e8e4dc]">
<div className="border-b border-amber-900/40 bg-gradient-to-r from-[#1a0f08] via-[#120a06] to-[#1a0f08]">
<div className="mx-auto max-w-6xl px-4 py-10">
<p className="font-mono text-[10px] uppercase tracking-[0.45em] text-amber-500/70">bazaar density</p>
<h1 className="mt-2 font-orbitron text-3xl font-black text-amber-100 md:text-5xl">Vendor hall</h1>
<p className="mt-3 max-w-2xl text-sm text-amber-200/60">
<strong className="text-amber-300">{SHOP_SELLER_COUNT}</strong> vendor stalls fueling{" "}
<strong className="text-amber-300">{SHOP_PRODUCTS.length}</strong> SKUs scroll the arcade, click a handle for their shelf.
</p>
<div className="mt-6 flex flex-wrap items-center gap-3">
<Link
href="/vendor/apply"
className="rounded border-2 border-amber-400/60 bg-amber-500/15 px-5 py-2.5 font-mono text-xs font-bold uppercase tracking-wider text-amber-100 hover:bg-amber-500/25"
>
Apply to open a stall
</Link>
</div>
<div className="mt-6 flex flex-wrap gap-4 font-mono text-xs">
<div className="rounded border border-amber-700/40 bg-black/40 px-4 py-2">
<span className="text-amber-500/60">Aggregate sales (lore)</span>
<span className="ml-2 text-amber-200">{totalSales.toLocaleString()}</span>
</div>
<div className="rounded border border-amber-700/40 bg-black/40 px-4 py-2">
<span className="text-amber-500/60">Avg rating</span>
<span className="ml-2 text-amber-200"> {avgRating.toFixed(2)}</span>
</div>
<div className="rounded border border-amber-700/40 bg-black/40 px-4 py-2">
<span className="text-amber-500/60">Median reply</span>
<span className="ml-2 text-amber-200">~42m</span>
</div>
</div>
</div>
</div>
<div className="border-b border-amber-900/25 bg-[#0a0604] px-2 py-3">
<p className="mb-2 text-center font-mono text-[9px] uppercase tracking-[0.35em] text-amber-700/70">
Live handles · pick one
</p>
<div className="flex flex-wrap justify-center gap-x-3 gap-y-2 font-mono text-[11px]">
{SHOP_SELLERS.map((s) => (
<Link
key={s.id}
href={`/vendor/${s.slug}`}
className="rounded border border-orange-900/35 bg-black/30 px-2 py-1 text-amber-400/95 hover:border-amber-600/50 hover:text-amber-100"
>
{s.handle}
</Link>
))}
</div>
</div>
<main className="mx-auto max-w-6xl px-4 py-10">
<div className="mb-8 flex flex-wrap gap-3 text-xs font-mono">
<Link href="/" className="rounded border border-amber-800/50 px-3 py-2 text-amber-300 hover:bg-amber-950/80">
Hub
</Link>
<Link href="/market" className="rounded border border-amber-800/50 px-3 py-2 text-amber-300 hover:bg-amber-950/80">
Full catalog
</Link>
<Link
href="/vendor/apply"
className="rounded border border-amber-400/50 px-3 py-2 text-amber-200 hover:bg-amber-950/80"
>
Apply to vend
</Link>
</div>
<ul className="grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
{SHOP_SELLERS.map((v) => {
const n = listingsByVendor.get(v.id) ?? 0;
return (
<li key={v.id}>
<Link
href={`/vendor/${v.slug}`}
className="flex h-full flex-col rounded-lg border border-amber-900/40 bg-[#0c0907] p-5 shadow-[inset_0_0_0_1px_rgba(251,191,36,0.05)] transition-colors hover:border-amber-600/50 hover:bg-[#100b08]"
>
<div className="flex items-start justify-between gap-2">
<h2 className="font-orbitron text-lg font-bold text-amber-100">{v.handle}</h2>
<span className="shrink-0 rounded bg-amber-500/15 px-2 py-0.5 font-mono text-[10px] text-amber-300">{v.rating.toFixed(2)}</span>
</div>
<p className="mt-1 font-mono text-[10px] uppercase tracking-wider text-amber-600/80">{v.tagline}</p>
<p className="mt-3 flex-1 text-sm leading-relaxed text-amber-200/75">{v.bio}</p>
<div className="mt-4 flex flex-wrap gap-3 border-t border-amber-950/80 pt-3 font-mono text-[10px] text-amber-500/70">
<span>{n} listings here</span>
<span>·</span>
<span>{v.completedSales.toLocaleString()} lore sales</span>
<span>·</span>
<span>~{v.responseMins}m reply</span>
</div>
</Link>
</li>
);
})}
</ul>
</main>
</div>
);
}