import { useState, useRef, useEffect } from 'react' import { Link } from 'react-router-dom' import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query' import { systemAPI, NetworkInterface } from '@/api/system' export default function System() { const [snmpEnabled, setSnmpEnabled] = useState(false) const [openMenu, setOpenMenu] = useState(null) const [editingInterface, setEditingInterface] = useState(null) const [timezone, setTimezone] = useState('Etc/UTC') const [ntpServers, setNtpServers] = useState(['pool.ntp.org', 'time.google.com']) const menuRef = useRef(null) const queryClient = useQueryClient() // Save NTP settings mutation const saveNTPSettingsMutation = useMutation({ mutationFn: (data: { timezone: string; ntp_servers: string[] }) => systemAPI.saveNTPSettings(data), onSuccess: () => { // Refetch NTP settings to get the updated values queryClient.invalidateQueries({ queryKey: ['system', 'ntp'] }) // Show success message (you can add a toast notification here) alert('NTP settings saved successfully!') }, onError: (error: any) => { alert(`Failed to save NTP settings: ${error.message || 'Unknown error'}`) }, }) // Fetch network interfaces const { data: interfaces = [], isLoading: interfacesLoading } = useQuery({ queryKey: ['system', 'interfaces'], queryFn: () => systemAPI.listNetworkInterfaces(), refetchInterval: 5000, // Refresh every 5 seconds }) // Fetch NTP settings on mount const { data: ntpSettings } = useQuery({ queryKey: ['system', 'ntp'], queryFn: () => systemAPI.getNTPSettings(), }) // Update state when NTP settings are loaded useEffect(() => { if (ntpSettings) { setTimezone(ntpSettings.timezone) setNtpServers(ntpSettings.ntp_servers) } }, [ntpSettings]) // Close menu when clicking outside useEffect(() => { const handleClickOutside = (event: MouseEvent) => { if (menuRef.current && !menuRef.current.contains(event.target as Node)) { setOpenMenu(null) } } document.addEventListener('mousedown', handleClickOutside) return () => document.removeEventListener('mousedown', handleClickOutside) }, []) return (
{/* Top Navigation */}
{/* Breadcrumbs */}
System / Configuration
System Healthy
{/* Scrollable Content */}
{/* Page Header */}

System Configuration

Manage network interfaces, time synchronization, service states, and remote management protocols.

{/* Grid Layout */}
{/* Network Card */}
lan

Network Interfaces

{interfacesLoading ? (
Loading interfaces...
) : interfaces.length === 0 ? (
No network interfaces found
) : ( interfaces.map((iface: NetworkInterface) => { const isConnected = iface.status === 'Connected' const roleBgColor = iface.role === 'ISCSI' ? 'bg-purple-500/20' : 'bg-primary/20' const roleTextColor = iface.role === 'ISCSI' ? 'text-purple-400' : 'text-primary' return (
settings_ethernet

{iface.name}

{iface.role && ( {iface.role} )}
{iface.ip_address ? (

{iface.ip_address} / {iface.subnet}

) : (

No Carrier

)}
{isConnected ? ( <>
Connected
{iface.speed && iface.speed !== 'Unknown' && ( {iface.speed} )} ) : (
Down
)}
{openMenu === iface.name && (
)}
) }) )}
{/* Services Card */}
memory

Service Control

All Systems Normal
{/* Service Row */}
terminal

SSH Service

Remote command line access

RUNNING
{/* Service Row */}
folder_shared

SMB / CIFS

Windows file sharing

RUNNING
{/* Service Row */}
storage

iSCSI Target

Block storage sharing

STOPPED
{/* Service Row */}
share

NFS Service

Unix file sharing

RUNNING
{/* Service Row - VTL (MHVTL) */}
album

VTL Service

Virtual tape library emulation

RUNNING
{/* Date & Time Card */}
schedule

Date & Time

expand_more
{ntpServers.map((server, index) => (
{server}
Stratum 2 • 12ms
))}
{/* Management & SNMP Card */}
hub

Management

SNMP Monitoring

Enable Simple Network Management Protocol

setSnmpEnabled(e.target.checked)} className="toggle-checkbox absolute block w-5 h-5 rounded-full bg-white border-4 appearance-none cursor-pointer checked:right-0 checked:border-primary transition-all duration-300" id="snmp-toggle" name="toggle" type="checkbox" />

Syslog Forwarding

{/* Bottom Spacer */}
{/* Edit Connection Modal */} {editingInterface && ( setEditingInterface(null)} /> )}
) } // Edit Connection Modal Component interface EditConnectionModalProps { interface: NetworkInterface onClose: () => void } function EditConnectionModal({ interface: iface, onClose }: EditConnectionModalProps) { const queryClient = useQueryClient() const [formData, setFormData] = useState({ ip_address: iface.ip_address || '', subnet: iface.subnet || '24', gateway: iface.gateway || '', dns1: iface.dns1 || '', dns2: iface.dns2 || '', role: iface.role || '', }) const updateMutation = useMutation({ mutationFn: (data: { ip_address: string; subnet: string; gateway?: string; dns1?: string; dns2?: string; role?: string }) => systemAPI.updateNetworkInterface(iface.name, data), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['system', 'interfaces'] }) onClose() }, onError: (error: any) => { alert(`Failed to update interface: ${error.response?.data?.error || error.message}`) }, }) const handleSubmit = (e: React.FormEvent) => { e.preventDefault() updateMutation.mutate({ ip_address: formData.ip_address, subnet: formData.subnet, gateway: formData.gateway || undefined, dns1: formData.dns1 || undefined, dns2: formData.dns2 || undefined, role: formData.role || undefined, }) } return (
settings_ethernet

Edit Connection - {iface.name}

{/* IP Address */}
setFormData({ ...formData, ip_address: e.target.value })} className="block w-full rounded-lg border-border-dark bg-[#111a22] p-2.5 text-sm text-white placeholder-gray-500 focus:border-primary focus:ring-1 focus:ring-primary" placeholder="192.168.1.100" required />
{/* Subnet Mask */}
setFormData({ ...formData, subnet: e.target.value })} className="block w-full rounded-lg border-border-dark bg-[#111a22] p-2.5 text-sm text-white placeholder-gray-500 focus:border-primary focus:ring-1 focus:ring-primary" placeholder="24" required />

Enter CIDR notation (e.g., 24 for 255.255.255.0)

{/* Gateway */}
setFormData({ ...formData, gateway: e.target.value })} className="block w-full rounded-lg border-border-dark bg-[#111a22] p-2.5 text-sm text-white placeholder-gray-500 focus:border-primary focus:ring-1 focus:ring-primary" placeholder="192.168.1.1" />
{/* DNS Servers */}
setFormData({ ...formData, dns1: e.target.value })} className="block w-full rounded-lg border-border-dark bg-[#111a22] p-2.5 text-sm text-white placeholder-gray-500 focus:border-primary focus:ring-1 focus:ring-primary" placeholder="8.8.8.8" />
setFormData({ ...formData, dns2: e.target.value })} className="block w-full rounded-lg border-border-dark bg-[#111a22] p-2.5 text-sm text-white placeholder-gray-500 focus:border-primary focus:ring-1 focus:ring-primary" placeholder="8.8.4.4" />
{/* Role */}
) }