"use client"; import { useState, useMemo } from "react"; import { Server, Search, Wifi, WifiOff, Clock, MapPin, Shield, } from "lucide-react"; interface TunnelConnection { id: string; is_pending_reconnect: boolean; origin_ip: string; opened_at: string; } interface Tunnel { id: string; name: string; status: string; created_at: string; deleted_at: string | null; connections: TunnelConnection[]; } function timeAgo(dateStr: string): string { const diff = Date.now() - new Date(dateStr).getTime(); const mins = Math.floor(diff / 60000); if (mins < 1) return "Just now"; if (mins < 60) return `${mins}m ago`; const hrs = Math.floor(mins / 60); if (hrs < 24) return `${hrs}h ago`; const days = Math.floor(hrs / 24); if (days < 30) return `${days}d ago`; return `${Math.floor(days / 30)}mo ago`; } export default function TunnelList({ tunnels }: { tunnels: Tunnel[] }) { const [search, setSearch] = useState(""); const filtered = useMemo(() => { const q = search.toLowerCase(); if (!q) return tunnels; return tunnels.filter( (t) => t.name.toLowerCase().includes(q) || t.id.toLowerCase().includes(q) ); }, [tunnels, search]); const activeCount = tunnels.filter( (t) => t.connections && t.connections.length > 0 ).length; return (
{/* Header */}

Tunnels

{tunnels.length} total · {activeCount} active

{/* Search */}
setSearch(e.target.value)} className="w-full rounded-xl border border-zinc-800 bg-zinc-900/80 backdrop-blur-sm pl-10 pr-4 py-2.5 text-sm text-zinc-200 placeholder-zinc-600 outline-none transition focus:border-blue-500/50 focus:ring-1 focus:ring-blue-500/20" />
{/* Empty */} {filtered.length === 0 && (

No tunnels found

)} {/* Tunnel cards */}
{filtered.map((tunnel) => { const isActive = tunnel.connections && tunnel.connections.length > 0; return (
{/* Left */}
{isActive ? ( ) : ( )} {tunnel.name} {isActive ? "Active" : "Inactive"}

{tunnel.id}

{/* Right meta */}
Created {timeAgo(tunnel.created_at)}
{/* Connections */} {isActive && tunnel.connections.length > 0 && (
{tunnel.connections.map((conn) => (
{conn.origin_ip} · {timeAgo(conn.opened_at)}
))}
)}
); })}
{/* Footer */}
Cloudflare Tunnel · auto-refreshes every 30s
); }