Update market, account, and onion operations
Capture the current CyberLux UI, commerce, messaging, and Tor ops updates so local main can be pushed to the remote. Made-with: Cursor
This commit is contained in:
@@ -1,98 +1,134 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import { useState, useRef, useEffect } from "react";
|
||||
import Link from "next/link";
|
||||
import { useWallet } from "@/contexts/WalletContext";
|
||||
import { useAccount } from "@/contexts/AccountContext";
|
||||
import { useCart } from "@/contexts/CartContext";
|
||||
import { SITE_NAV_GROUPS } from "@/lib/siteNav";
|
||||
|
||||
const Navbar = () => {
|
||||
const [isMenuOpen, setIsMenuOpen] = useState(false);
|
||||
const [pagesOpen, setPagesOpen] = useState(false);
|
||||
const pagesDropdownRef = useRef<HTMLDivElement>(null);
|
||||
const { luxCredits, usdStoreCredit } = useWallet();
|
||||
const { user, hydrated } = useAccount();
|
||||
const { itemCount, hydrated: cartReady } = useCart();
|
||||
|
||||
const navItems = [
|
||||
{ label: "Sanctuary", href: "/sanctuary", icon: "🛡️" },
|
||||
{ label: "Market", href: "/market", icon: "📈" },
|
||||
{ label: "Vendors", href: "/vendors", icon: "🏪" },
|
||||
{ label: "Checkout", href: "/checkout", icon: "₿" },
|
||||
{ label: "Forum", href: "/forum", icon: "◆" },
|
||||
{ label: "Exchange", href: "/exchange", icon: "📰" },
|
||||
{ label: "Barter", href: "/barter", icon: "♻️" },
|
||||
{ label: "Vault", href: "/vault", icon: "🗝️" },
|
||||
{ label: "Wiki", href: "/hidden-wiki", icon: "📚" },
|
||||
{ label: "Atlas", href: "/darknet-atlas", icon: "🗺️" },
|
||||
{ label: "Syndicate", href: "/syndicate", icon: "🕸️" },
|
||||
{ label: "Void crawl", href: "/search", icon: "🔦" },
|
||||
{ label: "Mirrors", href: "/account/hidden-services", icon: "🧅" },
|
||||
{ label: "Add funds", href: "/account/add-funds", icon: "₿" },
|
||||
{ label: "Dark web launch", href: "/launch", icon: "🔥" },
|
||||
];
|
||||
useEffect(() => {
|
||||
const onDoc = (e: MouseEvent) => {
|
||||
if (!pagesDropdownRef.current?.contains(e.target as Node)) setPagesOpen(false);
|
||||
};
|
||||
const onKey = (e: KeyboardEvent) => {
|
||||
if (e.key === "Escape") setPagesOpen(false);
|
||||
};
|
||||
document.addEventListener("mousedown", onDoc);
|
||||
document.addEventListener("keydown", onKey);
|
||||
return () => {
|
||||
document.removeEventListener("mousedown", onDoc);
|
||||
document.removeEventListener("keydown", onKey);
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<nav className="glass fixed top-0 left-0 right-0 z-50 mx-auto mt-4 max-w-7xl rounded-2xl border border-white/10 px-6 py-4 backdrop-blur-xl">
|
||||
<div className="flex items-center justify-between">
|
||||
{/* Logo */}
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="relative">
|
||||
<div className="h-10 w-10 rounded-full bg-gradient-to-br from-neon-cyan to-neon-purple p-0.5">
|
||||
<div className="h-full w-full rounded-full bg-background flex items-center justify-center">
|
||||
<span className="text-xl font-bold">⏣</span>
|
||||
<nav className="glass fixed top-0 left-0 right-0 z-50 mx-auto mt-2 max-w-7xl rounded-xl border border-white/10 px-4 py-2 backdrop-blur-xl md:px-5">
|
||||
<div className="flex items-center justify-between gap-3">
|
||||
<div className="flex min-w-0 flex-1 items-center gap-2 md:gap-3">
|
||||
<Link href="/" className="flex shrink-0 items-center gap-2">
|
||||
<div className="relative h-8 w-8 shrink-0 rounded-full bg-gradient-to-br from-neon-cyan to-neon-purple p-0.5">
|
||||
<div className="flex h-full w-full items-center justify-center rounded-full bg-background">
|
||||
<span className="text-sm font-bold">⏣</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="absolute -inset-1 -z-10 animate-pulse rounded-full bg-neon-cyan blur-md opacity-30"></div>
|
||||
</div>
|
||||
<Link href="/" className="font-orbitron text-2xl font-bold tracking-tighter">
|
||||
Cyber<span className="text-neon-cyan">Lux</span>
|
||||
<span className="font-orbitron truncate text-base font-bold tracking-tight md:text-lg">
|
||||
Eldritch<span className="text-neon-cyan">Weave</span>
|
||||
</span>
|
||||
</Link>
|
||||
<span className="hidden rounded-full bg-muted px-3 py-1 text-xs font-medium text-neon-green sm:inline">
|
||||
v1.0 • ALPHA
|
||||
<span className="hidden rounded-full bg-muted/80 px-2 py-0.5 text-[10px] font-medium text-neon-green/90 sm:inline">
|
||||
v1 · spellcraft
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* Desktop Navigation */}
|
||||
<div className="hidden max-w-lg flex-wrap items-center justify-end gap-x-3 gap-y-1 md:flex lg:max-w-2xl lg:gap-x-4 xl:max-w-none xl:flex-nowrap xl:gap-x-5">
|
||||
{navItems.map((item) => (
|
||||
<Link
|
||||
key={item.label}
|
||||
href={item.href}
|
||||
className="group relative text-sm font-medium text-foreground/80 transition-colors hover:text-neon-cyan"
|
||||
<div ref={pagesDropdownRef} className="relative ml-1 hidden md:block">
|
||||
<button
|
||||
type="button"
|
||||
aria-expanded={pagesOpen}
|
||||
aria-haspopup="true"
|
||||
onClick={() => setPagesOpen((o) => !o)}
|
||||
className="flex items-center gap-1 rounded-lg border border-white/15 bg-white/5 px-3 py-1.5 text-xs font-semibold text-foreground/85 transition hover:border-neon-cyan/35 hover:text-neon-cyan"
|
||||
>
|
||||
<span className="mr-1 opacity-60">{item.icon}</span>
|
||||
{item.label}
|
||||
<span className="absolute -bottom-1 left-0 h-0.5 w-0 bg-gradient-to-r from-neon-cyan to-neon-purple transition-all group-hover:w-full"></span>
|
||||
</Link>
|
||||
))}
|
||||
All pages
|
||||
<span className="text-[10px] opacity-70">{pagesOpen ? "▴" : "▾"}</span>
|
||||
</button>
|
||||
{pagesOpen ? (
|
||||
<div className="absolute left-0 top-full z-[60] mt-1 max-h-[min(70vh,520px)] w-[min(100vw-2rem,380px)] overflow-y-auto rounded-xl border border-white/15 bg-[#0a0a0f]/95 p-3 shadow-2xl shadow-black/50 backdrop-blur-xl">
|
||||
{SITE_NAV_GROUPS.map((group) => (
|
||||
<div key={group.title} className="mb-3 last:mb-0">
|
||||
<p className="mb-1.5 px-1 font-mono text-[9px] font-bold uppercase tracking-[0.2em] text-foreground/40">
|
||||
{group.title}
|
||||
</p>
|
||||
<ul className="space-y-0.5">
|
||||
{group.items.map((item) => (
|
||||
<li key={item.href}>
|
||||
<Link
|
||||
href={item.href}
|
||||
className="flex items-center gap-2 rounded-lg px-2 py-1.5 text-xs text-foreground/80 transition hover:bg-white/10 hover:text-neon-cyan"
|
||||
onClick={() => setPagesOpen(false)}
|
||||
>
|
||||
<span className="w-5 text-center opacity-70">{item.icon}</span>
|
||||
<span className="truncate">{item.label}</span>
|
||||
</Link>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
<Link
|
||||
href="/market"
|
||||
className="hidden text-xs font-medium text-foreground/70 transition hover:text-neon-cyan lg:inline"
|
||||
>
|
||||
Market
|
||||
</Link>
|
||||
<Link
|
||||
href="/checkout"
|
||||
className="hidden text-xs font-medium text-foreground/70 transition hover:text-neon-purple lg:inline"
|
||||
>
|
||||
Checkout
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
{/* Actions */}
|
||||
<div className="flex items-center gap-3 md:gap-4">
|
||||
<div className="flex shrink-0 items-center gap-2 md:gap-3">
|
||||
{cartReady ? (
|
||||
<Link
|
||||
href="/checkout"
|
||||
className="relative hidden rounded-full border border-white/10 px-3 py-2 text-sm text-foreground/80 hover:border-neon-cyan/40 hover:text-neon-cyan sm:inline-flex"
|
||||
className="relative inline-flex rounded-full border border-white/10 px-2.5 py-1.5 text-xs text-foreground/80 hover:border-neon-cyan/40 hover:text-neon-cyan"
|
||||
title="Cart"
|
||||
>
|
||||
🛒
|
||||
{itemCount > 0 ? (
|
||||
<span className="absolute -right-1 -top-1 flex h-5 min-w-5 items-center justify-center rounded-full bg-neon-pink px-1 text-[10px] font-bold text-background">
|
||||
<span className="absolute -right-1 -top-1 flex h-4 min-w-4 items-center justify-center rounded-full bg-neon-pink px-0.5 text-[9px] font-bold text-background">
|
||||
{itemCount > 99 ? "99+" : itemCount}
|
||||
</span>
|
||||
) : null}
|
||||
</Link>
|
||||
) : null}
|
||||
<div className="hidden md:flex items-center gap-3">
|
||||
<div className="flex items-center gap-2 rounded-full bg-white/5 px-4 py-2 border border-white/10">
|
||||
<span className="text-xs text-foreground/60">LUX</span>
|
||||
<span className="font-orbitron text-sm text-neon-green">{luxCredits.toLocaleString()}</span>
|
||||
<div className="hidden items-center gap-1.5 sm:flex">
|
||||
<div
|
||||
className="flex items-center gap-1 rounded-full border border-white/10 bg-white/5 px-2 py-1 text-[10px]"
|
||||
title="LUX credits"
|
||||
>
|
||||
<span className="text-foreground/50">LUX</span>
|
||||
<span className="font-orbitron text-[11px] text-neon-green">{luxCredits.toLocaleString()}</span>
|
||||
</div>
|
||||
<div
|
||||
className="flex items-center gap-2 rounded-full bg-white/5 px-4 py-2 border border-neon-cyan/20"
|
||||
title="Verified Bitcoin deposit balance (USD)"
|
||||
className="flex items-center gap-1 rounded-full border border-neon-cyan/20 bg-white/5 px-2 py-1 text-[10px]"
|
||||
title="Bitcoin-funded USD balance"
|
||||
>
|
||||
<span className="text-xs text-foreground/60">USD</span>
|
||||
<span className="font-orbitron text-sm text-neon-cyan">${usdStoreCredit.toFixed(2)}</span>
|
||||
<span className="text-foreground/50">$</span>
|
||||
<span className="font-orbitron text-[11px] text-neon-cyan">{usdStoreCredit.toFixed(2)}</span>
|
||||
</div>
|
||||
</div>
|
||||
{hydrated ? (
|
||||
@@ -100,28 +136,28 @@ const Navbar = () => {
|
||||
<>
|
||||
<Link
|
||||
href="/account/add-funds"
|
||||
className="hidden rounded-full border border-neon-green/35 bg-neon-green/10 px-3 py-2 text-xs font-bold uppercase tracking-wide text-neon-green hover:bg-neon-green/15 sm:inline-flex"
|
||||
className="hidden rounded-full border border-neon-green/30 bg-neon-green/10 px-2.5 py-1 text-[10px] font-bold uppercase tracking-wide text-neon-green hover:bg-neon-green/15 sm:inline-flex"
|
||||
>
|
||||
Add funds
|
||||
+Funds
|
||||
</Link>
|
||||
<Link
|
||||
href="/dashboard"
|
||||
className="hidden items-center gap-2 rounded-full border border-neon-cyan/30 bg-neon-cyan/10 px-4 py-2 text-sm font-medium text-neon-cyan hover:bg-neon-cyan/20 sm:flex"
|
||||
className="hidden max-w-[6rem] truncate rounded-full border border-neon-cyan/25 bg-neon-cyan/10 px-2.5 py-1 text-[11px] font-medium text-neon-cyan hover:bg-neon-cyan/18 sm:inline"
|
||||
>
|
||||
<span className="max-w-[8rem] truncate">{user.displayName}</span>
|
||||
{user.displayName}
|
||||
</Link>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Link
|
||||
href="/sign-in"
|
||||
className="hidden rounded-full border border-white/15 px-4 py-2 text-sm font-medium text-foreground/80 hover:border-neon-cyan/50 hover:text-neon-cyan sm:inline-flex"
|
||||
className="hidden rounded-full border border-white/12 px-2.5 py-1 text-[11px] font-medium text-foreground/80 hover:border-neon-cyan/45 hover:text-neon-cyan sm:inline-flex"
|
||||
>
|
||||
Sign in
|
||||
</Link>
|
||||
<Link
|
||||
href="/sign-up"
|
||||
className="hidden rounded-full border border-neon-purple/35 bg-neon-purple/10 px-4 py-2 text-sm font-medium text-neon-purple hover:bg-neon-purple/20 md:inline-flex"
|
||||
className="hidden rounded-full border border-neon-purple/30 bg-neon-purple/10 px-2.5 py-1 text-[11px] font-medium text-neon-purple hover:bg-neon-purple/18 md:inline-flex"
|
||||
>
|
||||
Register
|
||||
</Link>
|
||||
@@ -129,41 +165,39 @@ const Navbar = () => {
|
||||
)
|
||||
) : null}
|
||||
<button
|
||||
className="rounded-full bg-white/5 p-2 hover:bg-white/10"
|
||||
className="rounded-full bg-white/5 p-1.5 hover:bg-white/10 md:hidden"
|
||||
aria-label="Toggle menu"
|
||||
onClick={() => setIsMenuOpen(!isMenuOpen)}
|
||||
>
|
||||
<div className="h-5 w-5 space-y-1.5">
|
||||
<div className={`h-0.5 bg-neon-cyan transition-transform ${isMenuOpen ? "translate-y-2 rotate-45" : ""}`}></div>
|
||||
<div className={`h-0.5 bg-neon-purple transition-opacity ${isMenuOpen ? "opacity-0" : ""}`}></div>
|
||||
<div className={`h-0.5 bg-neon-pink transition-transform ${isMenuOpen ? "-translate-y-2 -rotate-45" : ""}`}></div>
|
||||
<div className="h-4 w-4 space-y-1">
|
||||
<div className={`h-0.5 bg-neon-cyan transition-transform ${isMenuOpen ? "translate-y-1.5 rotate-45" : ""}`} />
|
||||
<div className={`h-0.5 bg-neon-purple transition-opacity ${isMenuOpen ? "opacity-0" : ""}`} />
|
||||
<div className={`h-0.5 bg-neon-pink transition-transform ${isMenuOpen ? "-translate-y-1.5 -rotate-45" : ""}`} />
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Mobile Menu */}
|
||||
{/* Mobile: full menu + page list */}
|
||||
{isMenuOpen && (
|
||||
<div className="glass mt-4 rounded-xl border border-white/10 p-4 backdrop-blur-xl md:hidden">
|
||||
<div className="mb-3 flex gap-2 border-b border-white/10 pb-3">
|
||||
<div className="glass mt-2 max-h-[75vh] overflow-y-auto rounded-lg border border-white/10 p-3 backdrop-blur-xl md:hidden">
|
||||
<div className="mb-2 flex flex-wrap gap-2 border-b border-white/10 pb-2">
|
||||
{cartReady ? (
|
||||
<Link
|
||||
href="/checkout"
|
||||
className="relative flex-1 rounded-lg border border-white/15 py-2 text-center text-sm font-medium"
|
||||
className="relative flex-1 rounded-lg border border-white/15 py-2 text-center text-xs font-medium"
|
||||
onClick={() => setIsMenuOpen(false)}
|
||||
>
|
||||
🛒 Cart
|
||||
{itemCount > 0 ? (
|
||||
<span className="ml-1 rounded bg-neon-pink px-1.5 text-[10px] font-bold text-background">
|
||||
{itemCount}
|
||||
</span>
|
||||
<span className="ml-1 rounded bg-neon-pink px-1 text-[9px] font-bold text-background">{itemCount}</span>
|
||||
) : null}
|
||||
</Link>
|
||||
) : null}
|
||||
{hydrated && user ? (
|
||||
<Link
|
||||
href="/dashboard"
|
||||
className="flex-1 rounded-lg bg-neon-cyan/15 py-2 text-center text-sm font-bold text-neon-cyan"
|
||||
className="flex-1 rounded-lg bg-neon-cyan/15 py-2 text-center text-xs font-bold text-neon-cyan"
|
||||
onClick={() => setIsMenuOpen(false)}
|
||||
>
|
||||
Dashboard
|
||||
@@ -171,55 +205,35 @@ const Navbar = () => {
|
||||
) : hydrated ? (
|
||||
<Link
|
||||
href="/sign-in"
|
||||
className="flex-1 rounded-lg border border-white/15 py-2 text-center text-sm font-medium"
|
||||
className="flex-1 rounded-lg border border-white/15 py-2 text-center text-xs font-medium"
|
||||
onClick={() => setIsMenuOpen(false)}
|
||||
>
|
||||
Sign in
|
||||
</Link>
|
||||
) : null}
|
||||
{hydrated && !user ? (
|
||||
<Link
|
||||
href="/sign-up"
|
||||
className="flex-1 rounded-lg border border-neon-purple/40 py-2 text-center text-sm text-neon-purple"
|
||||
onClick={() => setIsMenuOpen(false)}
|
||||
>
|
||||
Register
|
||||
</Link>
|
||||
) : null}
|
||||
</div>
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
{navItems.map((item) => (
|
||||
<Link
|
||||
key={item.label}
|
||||
href={item.href}
|
||||
className="flex items-center gap-2 rounded-lg bg-white/5 p-3 text-sm font-medium transition-colors hover:bg-white/10"
|
||||
onClick={() => setIsMenuOpen(false)}
|
||||
>
|
||||
<span className="text-lg">{item.icon}</span>
|
||||
{item.label}
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
<div className="mt-4 flex gap-3 border-t border-white/10 pt-4 text-xs">
|
||||
<Link
|
||||
href="/sanctuary"
|
||||
className="flex-1 rounded-lg border border-white/10 px-3 py-2 text-center text-foreground/70 hover:border-neon-cyan/40 hover:text-neon-cyan"
|
||||
onClick={() => setIsMenuOpen(false)}
|
||||
>
|
||||
Sanctuary
|
||||
</Link>
|
||||
<Link
|
||||
href="/account/hidden-services"
|
||||
className="flex-1 rounded-lg border border-white/10 px-3 py-2 text-center text-foreground/70 hover:border-neon-purple/40 hover:text-neon-purple"
|
||||
onClick={() => setIsMenuOpen(false)}
|
||||
>
|
||||
Onion map
|
||||
</Link>
|
||||
</div>
|
||||
{SITE_NAV_GROUPS.map((group) => (
|
||||
<div key={group.title} className="mb-3">
|
||||
<p className="mb-1 font-mono text-[9px] font-bold uppercase tracking-widest text-foreground/35">{group.title}</p>
|
||||
<div className="grid grid-cols-2 gap-1">
|
||||
{group.items.map((item) => (
|
||||
<Link
|
||||
key={item.href}
|
||||
href={item.href}
|
||||
className="flex items-center gap-1.5 rounded-md bg-white/5 p-2 text-[11px] font-medium"
|
||||
onClick={() => setIsMenuOpen(false)}
|
||||
>
|
||||
<span>{item.icon}</span>
|
||||
<span className="truncate">{item.label}</span>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</nav>
|
||||
);
|
||||
};
|
||||
|
||||
export default Navbar;
|
||||
export default Navbar;
|
||||
|
||||
Reference in New Issue
Block a user