feat: UI refactor — Dashboard + Services, 9 items → 6

Major restructure of Wild Central's web UI:

New pages:
- Dashboard: infrastructure health (Cloudflare token, DDNS sync,
  daemon status, gateway router guidance, cert warnings)
- Services: every registered service with expandable rows showing
  DNS, proxy, TLS, DDNS status. Custom TCP routes section.
  Advanced HAProxy management collapsed at bottom.

Sidebar restructured into two groups:
- Services: Dashboard, Services
- Central: Firewall, VPN, CrowdSec, DHCP

Deleted 7 pages + 7 components (2501 lines removed):
- CentralPage, CloudflarePage, DdnsPage, DnsPage, LanDnsPage,
  CertificatesPage, IngressProxyPage
- CentralComponent, CloudflareComponent, DdnsComponent,
  DnsComponent, LanDnsComponent, IngressProxyComponent,
  DnsmasqSection

All hooks and API services kept unchanged — reused in new components.
Type-check and production build pass.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-09 22:36:54 +00:00
parent c18217944f
commit 9ce9999d5c
21 changed files with 1017 additions and 2501 deletions

View File

@@ -1,5 +1,5 @@
import { NavLink } from 'react-router';
import { Sun, Moon, Monitor, Shield, ShieldCheck, Lock, Network, ShieldAlert, Router, Cloud, Wifi, Server } from 'lucide-react';
import { Sun, Moon, Monitor, Shield, Lock, ShieldAlert, Wifi, LayoutDashboard, Globe, CloudLightning } from 'lucide-react';
import {
Sidebar,
SidebarContent,
@@ -52,14 +52,14 @@ export function AppSidebar() {
}
};
const servicesItems = [
{ to: '/central', icon: LayoutDashboard, label: 'Dashboard', end: true },
{ to: '/central/services', icon: Globe, label: 'Services' },
];
const centralItems = [
{ to: '/central', icon: Server, label: 'Overview', end: true },
{ to: '/central/cloudflare', icon: Cloud, label: 'Cloudflare' },
{ to: '/central/dns', icon: Router, label: 'DNS' },
{ to: '/central/ingress', icon: Network, label: 'Ingress Proxy' },
{ to: '/central/firewall', icon: Shield, label: 'Firewall' },
{ to: '/central/vpn', icon: Lock, label: 'VPN' },
{ to: '/central/certificates', icon: ShieldCheck, label: 'Certificates' },
{ to: '/central/crowdsec', icon: ShieldAlert, label: 'CrowdSec' },
{ to: '/central/dhcp', icon: Wifi, label: 'DHCP' },
];
@@ -69,7 +69,7 @@ export function AppSidebar() {
<SidebarHeader>
<div className="flex items-center justify-center pb-2">
<div className="p-1 bg-primary/10 rounded-lg">
<Server className="h-6 w-6 text-primary" />
<CloudLightning className="h-6 w-6 text-primary" />
</div>
<div className="overflow-hidden transition-all duration-200 ease-linear ml-2 max-w-[200px] opacity-100 group-data-[collapsible=icon]:ml-0 group-data-[collapsible=icon]:max-w-0 group-data-[collapsible=icon]:opacity-0">
<h2 className="text-lg font-bold text-foreground whitespace-nowrap">Wild Central</h2>
@@ -80,13 +80,13 @@ export function AppSidebar() {
<SidebarContent>
{state === 'collapsed' ? (
<SidebarMenu>
{centralItems.map(({ to, icon: Icon, label, end }) => (
<SidebarMenuItem key={to}>
<NavLink to={to} end={end}>
{[...servicesItems, ...centralItems].map((item) => (
<SidebarMenuItem key={item.to}>
<NavLink to={item.to} end={'end' in item ? (item as any).end : undefined}>
{({ isActive }) => (
<SidebarMenuButton isActive={isActive} tooltip={label}>
<Icon className="h-4 w-4" />
<span>{label}</span>
<SidebarMenuButton isActive={isActive} tooltip={item.label}>
<item.icon className="h-4 w-4" />
<span>{item.label}</span>
</SidebarMenuButton>
)}
</NavLink>
@@ -94,25 +94,47 @@ export function AppSidebar() {
))}
</SidebarMenu>
) : (
<SidebarGroup>
<SidebarGroupLabel>Central</SidebarGroupLabel>
<SidebarGroupContent>
<SidebarMenu>
{centralItems.map(({ to, icon: Icon, label, end }) => (
<SidebarMenuItem key={to}>
<NavLink to={to} end={end}>
{({ isActive }) => (
<SidebarMenuButton isActive={isActive}>
<Icon className="h-4 w-4" />
<span className="truncate">{label}</span>
</SidebarMenuButton>
)}
</NavLink>
</SidebarMenuItem>
))}
</SidebarMenu>
</SidebarGroupContent>
</SidebarGroup>
<>
<SidebarGroup>
<SidebarGroupLabel>Services</SidebarGroupLabel>
<SidebarGroupContent>
<SidebarMenu>
{servicesItems.map(({ to, icon: Icon, label, end }) => (
<SidebarMenuItem key={to}>
<NavLink to={to} end={end}>
{({ isActive }) => (
<SidebarMenuButton isActive={isActive}>
<Icon className="h-4 w-4" />
<span className="truncate">{label}</span>
</SidebarMenuButton>
)}
</NavLink>
</SidebarMenuItem>
))}
</SidebarMenu>
</SidebarGroupContent>
</SidebarGroup>
<SidebarGroup>
<SidebarGroupLabel>Central</SidebarGroupLabel>
<SidebarGroupContent>
<SidebarMenu>
{centralItems.map(({ to, icon: Icon, label }) => (
<SidebarMenuItem key={to}>
<NavLink to={to}>
{({ isActive }) => (
<SidebarMenuButton isActive={isActive}>
<Icon className="h-4 w-4" />
<span className="truncate">{label}</span>
</SidebarMenuButton>
)}
</NavLink>
</SidebarMenuItem>
))}
</SidebarMenu>
</SidebarGroupContent>
</SidebarGroup>
</>
)}
</SidebarContent>