import { useState, useEffect } from 'react'; import { NavLink } from 'react-router'; import { Sun, Moon, Monitor, Shield, Lock, ShieldAlert, ShieldBan, ShieldCheck, Wifi, LayoutDashboard, Globe, Network, ChevronRight, KeyRound } from 'lucide-react'; import { Collapsible, CollapsibleContent, CollapsibleTrigger } from './ui/collapsible'; import { Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarMenu, SidebarMenuButton, SidebarMenuItem, SidebarRail, useSidebar, } from './ui/sidebar'; import { useTheme } from '../contexts/ThemeContext'; import { useCentralStatus } from '../hooks/useCentralStatus'; function WildCentralLogo({ className }: { className?: string }) { return ( ); } const ADVANCED_OPEN_KEY = 'wild-central:advanced-open'; export function AppSidebar() { const { theme, setTheme } = useTheme(); const { state } = useSidebar(); const { data: centralStatus } = useCentralStatus(); const [advancedOpen, setAdvancedOpen] = useState(() => localStorage.getItem(ADVANCED_OPEN_KEY) === 'true'); useEffect(() => { localStorage.setItem(ADVANCED_OPEN_KEY, String(advancedOpen)); }, [advancedOpen]); const cycleTheme = () => { if (theme === 'light') { setTheme('dark'); } else if (theme === 'dark') { setTheme('system'); } else { setTheme('light'); } }; const getThemeIcon = () => { switch (theme) { case 'light': return ; case 'dark': return ; default: return ; } }; const getThemeLabel = () => { switch (theme) { case 'light': return 'Light mode'; case 'dark': return 'Dark mode'; default: return 'System theme'; } }; const domainsItems = [ { to: '/', icon: LayoutDashboard, label: 'Dashboard', end: true }, { to: '/domains', icon: Globe, label: 'Domains' }, ]; const centralItems = [ { to: '/auth', icon: KeyRound, label: 'Authentication', daemon: 'authelia' as const }, { to: '/vpn', icon: Lock, label: 'VPN', daemon: 'wireguard' as const }, { to: '/firewall', icon: Shield, label: 'Firewall', daemon: 'nftables' as const }, { to: '/crowdsec', icon: ShieldAlert, label: 'CrowdSec', daemon: 'crowdsec' as const }, { to: '/dhcp', icon: Wifi, label: 'DHCP', daemon: 'dnsmasq' as const }, { to: '/dns-filter', icon: ShieldBan, label: 'DNS Filter', daemon: 'dnsmasq' as const }, ]; const advancedItems = [ { to: '/advanced/haproxy', icon: Network, label: 'HAProxy', daemon: 'haproxy' as const }, { to: '/advanced/dnsmasq', icon: Wifi, label: 'dnsmasq', daemon: 'dnsmasq' as const }, { to: '/advanced/nftables', icon: Shield, label: 'nftables', daemon: 'nftables' as const }, { to: '/advanced/wireguard', icon: Lock, label: 'WireGuard', daemon: 'wireguard' as const }, { to: '/advanced/crowdsec', icon: ShieldAlert, label: 'CrowdSec', daemon: 'crowdsec' as const }, { to: '/advanced/authelia', icon: KeyRound, label: 'Authelia', daemon: 'authelia' as const }, { to: '/advanced/ddns', icon: Globe, label: 'DDNS', daemon: undefined }, { to: '/advanced/certificates', icon: ShieldCheck, label: 'Certificates', daemon: 'certbot' as const }, ]; return (

Wild Central

{state === 'collapsed' ? ( {[...domainsItems, ...centralItems, ...advancedItems].map((item) => { const daemon = 'daemon' in item ? (item as any).daemon : undefined; const active = daemon ? centralStatus?.daemons?.[daemon]?.active : undefined; return ( {({ isActive }) => ( {item.label} {active !== undefined && ( )} )} ); })} ) : ( <> Domains {domainsItems.map(({ to, icon: Icon, label, end }) => ( {({ isActive }) => ( {label} )} ))} Central {centralItems.map(({ to, icon: Icon, label, daemon }) => { const active = centralStatus?.daemons?.[daemon]?.active; return ( {({ isActive }) => ( {label} {active !== undefined && ( )} )} ); })} Advanced {advancedItems.map(({ to, icon: Icon, label, daemon }) => { const active = daemon ? centralStatus?.daemons?.[daemon]?.active : undefined; return ( {({ isActive }) => ( {label} {active !== undefined && ( )} )} ); })} )} {getThemeIcon()} {getThemeLabel()}
); }