Replace remote with local repo
This commit is contained in:
150
app/nexus/page.tsx
Normal file
150
app/nexus/page.tsx
Normal file
@@ -0,0 +1,150 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import Navbar from "@/components/Navbar";
|
||||
|
||||
const NODES = [
|
||||
{ id: "n1", name: "Void Exchange", onion: "voidexxxxx…onion", category: "Market", status: "up", uptime: "99.1%", last: "12s ago", guard: true },
|
||||
{ id: "n2", name: "Phantom Library", onion: "phantomll…onion", category: "Wiki", status: "up", uptime: "97.4%", last: "2m ago", guard: false },
|
||||
{ id: "n3", name: "Ghost Relay #7", onion: "ghostrly7…onion", category: "Relay", status: "up", uptime: "99.8%", last: "5s ago", guard: true },
|
||||
{ id: "n4", name: "Ember Forum", onion: "emberfrum…onion", category: "Forum", status: "down", uptime: "81.2%", last: "4h ago", guard: false },
|
||||
{ id: "n5", name: "Cipher Drop", onion: "ciphdrpxx…onion", category: "Drop", status: "up", uptime: "95.5%", last: "30s ago", guard: false },
|
||||
{ id: "n6", name: "Zero Wiki", onion: "zerowikxx…onion", category: "Wiki", status: "degraded", uptime: "91.0%", last: "8m ago", guard: true },
|
||||
{ id: "n7", name: "Dark Market", onion: "drkmarket…onion", category: "Market", status: "up", uptime: "98.3%", last: "18s ago", guard: true },
|
||||
{ id: "n8", name: "Nexus Gate", onion: "nexusgte…onion", category: "Relay", status: "up", uptime: "99.9%", last: "2s ago", guard: true },
|
||||
];
|
||||
|
||||
const STATUS_COLORS: Record<string, string> = {
|
||||
up: "text-neon-green bg-neon-green/10 border-neon-green/30",
|
||||
down: "text-red-400 bg-red-500/10 border-red-500/30",
|
||||
degraded: "text-yellow-400 bg-yellow-500/10 border-yellow-500/30",
|
||||
};
|
||||
const STATUS_DOT: Record<string, string> = {
|
||||
up: "bg-neon-green",
|
||||
down: "bg-red-500",
|
||||
degraded: "bg-yellow-400",
|
||||
};
|
||||
|
||||
const CATS = ["All", "Market", "Wiki", "Forum", "Relay", "Drop"];
|
||||
|
||||
export default function NexusPage() {
|
||||
const [cat, setCat] = useState("All");
|
||||
const [search, setSearch] = useState("");
|
||||
|
||||
const filtered = NODES.filter((n) => {
|
||||
const matchCat = cat === "All" || n.category === cat;
|
||||
const matchSearch = !search || n.name.toLowerCase().includes(search.toLowerCase()) || n.onion.includes(search.toLowerCase());
|
||||
return matchCat && matchSearch;
|
||||
});
|
||||
|
||||
const up = NODES.filter((n) => n.status === "up").length;
|
||||
const down = NODES.filter((n) => n.status === "down").length;
|
||||
const degraded = NODES.filter((n) => n.status === "degraded").length;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Navbar />
|
||||
<main className="min-h-screen bg-[#0a0a0a] pt-24 pb-20">
|
||||
<section className="container mx-auto max-w-6xl px-4 mb-10">
|
||||
<p className="mb-2 font-mono text-[10px] uppercase tracking-[0.35em] text-neon-cyan/70">nexus · hidden service network tracker</p>
|
||||
<h1 className="font-orbitron text-4xl font-bold md:text-5xl">
|
||||
THE <span className="text-neon-cyan">NEXUS</span>
|
||||
</h1>
|
||||
<p className="mt-3 max-w-xl text-foreground/60">
|
||||
Real-time monitoring of trusted hidden services across the Tor network. Submit your node to join the monitored ring.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
{/* Status overview */}
|
||||
<section className="container mx-auto max-w-6xl px-4 mb-8">
|
||||
<div className="flex flex-wrap gap-4">
|
||||
{[
|
||||
{ label: "Online", count: up, color: "neon-green" },
|
||||
{ label: "Degraded", count: degraded, color: "yellow-400" },
|
||||
{ label: "Offline", count: down, color: "red-400" },
|
||||
].map((s) => (
|
||||
<div key={s.label} className="flex items-center gap-3 rounded-xl border border-zinc-700/40 bg-black/40 px-5 py-3">
|
||||
<span className={`h-2.5 w-2.5 rounded-full bg-${s.color} animate-pulse`} />
|
||||
<div>
|
||||
<p className="text-xl font-bold font-orbitron">{s.count}</p>
|
||||
<p className="text-xs text-foreground/40">{s.label}</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Filters */}
|
||||
<section className="container mx-auto max-w-6xl px-4 mb-6">
|
||||
<div className="flex flex-wrap items-center gap-3">
|
||||
<input
|
||||
className="rounded-lg border border-zinc-700 bg-zinc-900 px-4 py-2 text-sm text-foreground placeholder-foreground/30 w-56 focus:border-neon-cyan/40 outline-none"
|
||||
placeholder="Search nodes…"
|
||||
value={search}
|
||||
onChange={(e) => setSearch(e.target.value)}
|
||||
/>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{CATS.map((c) => (
|
||||
<button
|
||||
key={c}
|
||||
onClick={() => setCat(c)}
|
||||
className={`rounded-full px-3 py-1.5 text-xs font-semibold border transition ${cat === c ? "border-neon-cyan/50 bg-neon-cyan/10 text-neon-cyan" : "border-zinc-700 bg-zinc-900 text-foreground/60 hover:border-zinc-500"}`}
|
||||
>
|
||||
{c}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Node table */}
|
||||
<section className="container mx-auto max-w-6xl px-4">
|
||||
<div className="overflow-hidden rounded-xl border border-zinc-800">
|
||||
<table className="w-full text-sm">
|
||||
<thead className="border-b border-zinc-800 bg-black/60">
|
||||
<tr>
|
||||
{["Node", "Category", "Onion", "Status", "Uptime", "Last seen", "Guard"].map((h) => (
|
||||
<th key={h} className="px-4 py-3 text-left text-[10px] uppercase tracking-wider text-foreground/40">{h}</th>
|
||||
))}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{filtered.map((node, i) => (
|
||||
<tr key={node.id} className={`border-b border-zinc-800/50 ${i % 2 === 0 ? "bg-black/20" : "bg-black/40"} hover:bg-white/5 transition`}>
|
||||
<td className="px-4 py-3 font-semibold text-foreground">{node.name}</td>
|
||||
<td className="px-4 py-3">
|
||||
<span className="rounded-full border border-zinc-600 bg-zinc-800 px-2 py-0.5 text-[9px] uppercase tracking-wider text-zinc-400">{node.category}</span>
|
||||
</td>
|
||||
<td className="px-4 py-3 font-mono text-xs text-foreground/50">{node.onion}</td>
|
||||
<td className="px-4 py-3">
|
||||
<span className={`inline-flex items-center gap-1.5 rounded-full border px-2.5 py-0.5 text-[10px] font-bold ${STATUS_COLORS[node.status]}`}>
|
||||
<span className={`h-1.5 w-1.5 rounded-full ${STATUS_DOT[node.status]}`} />
|
||||
{node.status}
|
||||
</span>
|
||||
</td>
|
||||
<td className="px-4 py-3 font-mono text-xs text-foreground/70">{node.uptime}</td>
|
||||
<td className="px-4 py-3 text-xs text-foreground/40">{node.last}</td>
|
||||
<td className="px-4 py-3 text-center">{node.guard ? <span className="text-neon-cyan text-base">✓</span> : <span className="text-foreground/20">—</span>}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Submit */}
|
||||
<section className="container mx-auto max-w-6xl px-4 mt-12">
|
||||
<div className="rounded-2xl border border-neon-cyan/20 bg-neon-cyan/5 p-6 flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
|
||||
<div>
|
||||
<h2 className="font-orbitron text-lg font-bold">Submit your node</h2>
|
||||
<p className="text-sm text-foreground/60 mt-1">Add your hidden service to the Nexus monitoring ring. Requires a signed verification message.</p>
|
||||
</div>
|
||||
<button className="shrink-0 rounded-lg border border-neon-cyan/40 bg-neon-cyan/10 px-5 py-2.5 text-sm font-bold text-neon-cyan hover:bg-neon-cyan/20 transition">
|
||||
Submit Node →
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
</>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user