Files
dark-lord/app/vendors/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

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 · sim</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> fictional 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>
);
}