"use client" import { useState, useEffect, useCallback } from "react" import { Dialog, DialogContent, DialogHeader, DialogTitle } from "./ui/dialog" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "./ui/select" import { Button } from "./ui/button" import { Badge } from "./ui/badge" import { Activity, TrendingDown, TrendingUp, Minus, RefreshCw, Wifi, FileText } from "lucide-react" import { AreaChart, Area, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer, LineChart, Line } from "recharts" import { useIsMobile } from "../hooks/use-mobile" import { fetchApi } from "@/lib/api-config" const TIMEFRAME_OPTIONS = [ { value: "hour", label: "1 Hour" }, { value: "6hour", label: "6 Hours" }, { value: "day", label: "24 Hours" }, { value: "3day", label: "3 Days" }, { value: "week", label: "7 Days" }, ] const TARGET_OPTIONS = [ { value: "gateway", label: "Gateway (Router)", realtime: false }, { value: "cloudflare", label: "Cloudflare (1.1.1.1)", realtime: true }, { value: "google", label: "Google DNS (8.8.8.8)", realtime: true }, ] interface LatencyHistoryPoint { timestamp: number value: number min?: number max?: number packet_loss?: number } interface LatencyStats { min: number max: number avg: number current: number } interface RealtimeResult { target: string target_ip: string latency_avg: number | null latency_min: number | null latency_max: number | null packet_loss: number status: string timestamp?: number } interface LatencyDetailModalProps { open: boolean onOpenChange: (open: boolean) => void currentLatency?: number } const CustomTooltip = ({ active, payload, label }: any) => { if (active && payload && payload.length) { const entry = payload[0] const packetLoss = entry?.payload?.packet_loss return (

{label}

Latency: {entry.value} ms
{packetLoss !== undefined && packetLoss > 0 && (
Pkt Loss: {packetLoss}%
)}
) } return null } const getStatusColor = (latency: number) => { if (latency >= 200) return "#ef4444" if (latency >= 100) return "#f59e0b" return "#22c55e" } const getStatusInfo = (latency: number | null) => { if (latency === null || latency === 0) return { status: "N/A", color: "bg-gray-500/10 text-gray-500 border-gray-500/20" } if (latency < 50) return { status: "Excellent", color: "bg-green-500/10 text-green-500 border-green-500/20" } if (latency < 100) return { status: "Good", color: "bg-green-500/10 text-green-500 border-green-500/20" } if (latency < 200) return { status: "Fair", color: "bg-yellow-500/10 text-yellow-500 border-yellow-500/20" } return { status: "Poor", color: "bg-red-500/10 text-red-500 border-red-500/20" } } const getStatusText = (latency: number | null): string => { if (latency === null || latency === 0) return "N/A" if (latency < 50) return "Excellent" if (latency < 100) return "Good" if (latency < 200) return "Fair" return "Poor" } interface ReportData { target: string targetLabel: string isRealtime: boolean stats: LatencyStats realtimeResults: RealtimeResult[] data: LatencyHistoryPoint[] timeframe: string } const generateLatencyReport = (report: ReportData) => { const now = new Date().toLocaleString() const statusText = report.isRealtime ? getStatusText(report.realtimeResults[report.realtimeResults.length - 1]?.latency_avg ?? null) : getStatusText(report.stats.current) const statusColorMap: Record = { "Excellent": "#22c55e", "Good": "#22c55e", "Fair": "#f59e0b", "Poor": "#ef4444", "N/A": "#888888" } const statusColor = statusColorMap[statusText] || "#888888" const timeframeLabel = TIMEFRAME_OPTIONS.find(t => t.value === report.timeframe)?.label || report.timeframe // Build test results table for realtime mode const realtimeTableRows = report.realtimeResults.map((r, i) => ` ${i + 1} ${new Date(r.timestamp || Date.now()).toLocaleTimeString()} ${r.latency_avg !== null ? r.latency_avg + ' ms' : 'Failed'} ${r.latency_min !== null ? r.latency_min + ' ms' : '-'} ${r.latency_max !== null ? r.latency_max + ' ms' : '-'} ${r.packet_loss}% ${getStatusText(r.latency_avg)} `).join('') // Build history summary for gateway mode const historyStats = report.data.length > 0 ? { samples: report.data.length, avgPacketLoss: (report.data.reduce((acc, d) => acc + (d.packet_loss || 0), 0) / report.data.length).toFixed(2), startTime: new Date(report.data[0].timestamp * 1000).toLocaleString(), endTime: new Date(report.data[report.data.length - 1].timestamp * 1000).toLocaleString(), } : null const html = ` Network Latency Report - ProxMenux Monitor

Network Latency Report

ProxMenux Monitor - Network Performance Analysis

Generated: ${now}
Target: ${report.targetLabel}
Mode: ${report.isRealtime ? 'Real-time Test' : 'Historical Analysis'}
ID: PMXL-${Date.now().toString(36).toUpperCase()}
1. Executive Summary
Overall Status
${statusText}
Current Latency
${report.isRealtime ? (report.realtimeResults[report.realtimeResults.length - 1]?.latency_avg ?? 'N/A') + (report.realtimeResults[report.realtimeResults.length - 1]?.latency_avg ? ' ms' : '') : report.stats.current + ' ms'}
Analysis Summary

${report.isRealtime ? `This report contains ${report.realtimeResults.length} real-time latency test(s) performed against ${report.targetLabel}. ${ report.realtimeResults.length > 0 ? `The average latency across all tests is ${(report.realtimeResults.reduce((acc, r) => acc + (r.latency_avg || 0), 0) / report.realtimeResults.length).toFixed(1)} ms.` : '' }` : `This report analyzes ${report.data.length} latency samples collected over ${timeframeLabel.toLowerCase()} against the network gateway. The average latency during this period was ${report.stats.avg} ms with a minimum of ${report.stats.min} ms and maximum of ${report.stats.max} ms.` }

Performance Rating

${ statusText === 'Excellent' ? 'Network latency is excellent. No action required.' : statusText === 'Good' ? 'Network latency is within acceptable parameters.' : statusText === 'Fair' ? 'Network latency is elevated. Consider investigating network congestion or routing issues.' : statusText === 'Poor' ? 'Network latency is critically high. Immediate investigation recommended.' : 'Unable to determine network status.' }

2. Latency Statistics
Current
${report.isRealtime ? (report.realtimeResults[report.realtimeResults.length - 1]?.latency_avg ?? 'N/A') + ' ms' : report.stats.current + ' ms'}
Minimum
${report.isRealtime ? (report.realtimeResults.length > 0 ? Math.min(...report.realtimeResults.map(r => r.latency_min || Infinity)).toFixed(1) : 'N/A') + ' ms' : report.stats.min + ' ms'}
Average
${report.isRealtime ? (report.realtimeResults.length > 0 ? (report.realtimeResults.reduce((acc, r) => acc + (r.latency_avg || 0), 0) / report.realtimeResults.length).toFixed(1) : 'N/A') + ' ms' : report.stats.avg + ' ms'}
Maximum
${report.isRealtime ? (report.realtimeResults.length > 0 ? Math.max(...report.realtimeResults.map(r => r.latency_max || 0)).toFixed(1) : 'N/A') + ' ms' : report.stats.max + ' ms'}
${!report.isRealtime && historyStats ? `
Sample Count
${historyStats.samples}
Period Start
${historyStats.startTime}
Period End
${historyStats.endTime}
` : ''}
${report.isRealtime && report.realtimeResults.length > 0 ? `
3. Test Results
${realtimeTableRows}
# Time Avg Latency Min Max Packet Loss Status
` : ''}
${report.isRealtime ? '4' : '3'}. Reference Thresholds
Excellent (< 50ms): Optimal network performance for all applications including real-time gaming and video calls.
Good (50-100ms): Acceptable latency for most applications. Minor impact on real-time interactions.
Fair (100-200ms): Noticeable delay in interactive applications. May affect VoIP and gaming quality.
Poor (> 200ms): Significant latency causing degraded user experience. Investigation recommended.
${report.isRealtime ? '5' : '4'}. Methodology

Test Method: ICMP Echo Request (Ping)

Target: ${report.targetLabel} ${report.target === 'gateway' ? '(Default network gateway)' : `(${report.target === 'cloudflare' ? '1.1.1.1' : '8.8.8.8'})`}

Samples per Test: 3 consecutive pings

Metrics Collected: Round-trip time (RTT) minimum, average, maximum, and packet loss percentage

` const printWindow = window.open('', '_blank') if (printWindow) { printWindow.document.write(html) printWindow.document.close() } } export function LatencyDetailModal({ open, onOpenChange, currentLatency }: LatencyDetailModalProps) { const [timeframe, setTimeframe] = useState("hour") const [target, setTarget] = useState("gateway") const [data, setData] = useState([]) const [stats, setStats] = useState({ min: 0, max: 0, avg: 0, current: 0 }) const [loading, setLoading] = useState(true) const [realtimeResults, setRealtimeResults] = useState([]) const [realtimeTesting, setRealtimeTesting] = useState(false) const isMobile = useIsMobile() const isRealtime = TARGET_OPTIONS.find(t => t.value === target)?.realtime ?? false // Fetch history for gateway useEffect(() => { if (open && target === "gateway") { fetchHistory() } }, [open, timeframe, target]) // Auto-test when switching to realtime target useEffect(() => { if (open && isRealtime) { // Clear previous results and run initial test setRealtimeResults([]) runRealtimeTest() } }, [open, target]) const fetchHistory = async () => { setLoading(true) try { const result = await fetchApi<{ data: LatencyHistoryPoint[]; stats: LatencyStats; target: string }>( `/api/network/latency/history?target=${target}&timeframe=${timeframe}` ) if (result && result.data) { setData(result.data) setStats(result.stats) } } catch (err) { // Silently fail } finally { setLoading(false) } } const runRealtimeTest = useCallback(async () => { if (realtimeTesting) return setRealtimeTesting(true) try { const result = await fetchApi(`/api/network/latency/current?target=${target}`) if (result) { const newResult = { ...result, timestamp: Date.now() } setRealtimeResults(prev => [...prev.slice(-19), newResult]) // Keep last 20 results } } catch (err) { // Silently fail } finally { setRealtimeTesting(false) } }, [target, realtimeTesting]) const formatTime = (timestamp: number) => { const date = new Date(timestamp * 1000) if (timeframe === "hour" || timeframe === "6hour") { return date.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" }) } else if (timeframe === "day") { return date.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" }) } else { return date.toLocaleDateString([], { month: "short", day: "numeric", hour: "2-digit", minute: "2-digit" }) } } const formatRealtimeTime = (timestamp: number) => { return new Date(timestamp).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit", second: "2-digit" }) } // Gateway view data const chartData = data.map((d) => ({ ...d, time: formatTime(d.timestamp), })) // Realtime view data const realtimeChartData = realtimeResults.map(r => ({ time: formatRealtimeTime(r.timestamp || Date.now()), value: r.latency_avg || 0, packet_loss: r.packet_loss, })) const lastRealtimeResult = realtimeResults[realtimeResults.length - 1] const realtimeLatency = lastRealtimeResult?.latency_avg ?? null const currentLat = isRealtime ? realtimeLatency : (currentLatency && currentLatency > 0 ? Math.round(currentLatency * 10) / 10 : stats.current) const currentStatus = getStatusInfo(currentLat) const chartColor = getStatusColor(currentLat || 0) const values = data.map((d) => d.value).filter(v => v !== null && v !== undefined) const yMin = 0 const yMax = values.length > 0 ? Math.ceil(Math.max(...values) * 1.2) : 200 const realtimeValues = realtimeResults.map(r => r.latency_avg).filter(v => v !== null) as number[] const realtimeYMax = realtimeValues.length > 0 ? Math.ceil(Math.max(...realtimeValues) * 1.2) : 200 return (
Network Latency
{!isRealtime && ( )} {isRealtime && ( )}
{/* Realtime mode indicator */} {isRealtime && (
Real-time test mode - Results are not stored. Click "Test Again" for new measurements.
)} {/* Stats bar */}
Current
{currentLat !== null ? `${currentLat} ms` : '---'}
{isRealtime ? ( <>
Min
{lastRealtimeResult?.latency_min !== null ? `${lastRealtimeResult?.latency_min} ms` : '---'}
Max
{lastRealtimeResult?.latency_max !== null ? `${lastRealtimeResult?.latency_max} ms` : '---'}
Packet Loss
0 ? 'text-red-500' : 'text-foreground'}`}> {lastRealtimeResult?.packet_loss !== undefined ? `${lastRealtimeResult.packet_loss}%` : '---'}
) : ( <>
Min
{stats.min} ms
Avg
{stats.avg} ms
Max
{stats.max} ms
)}
{/* Chart */}
{isRealtime ? ( // Realtime chart - shows test results from this session realtimeChartData.length === 0 ? (
{realtimeTesting ? ( <>

Running latency test...

) : ( <>

Click "Test Again" to run a latency test

)}
) : ( `${v}ms`} width={isMobile ? 45 : 50} /> } /> ) ) : ( // Gateway historical chart loading ? (
) : chartData.length === 0 ? (

No latency data available for this period

Data is collected every 60 seconds

) : ( `${v}ms`} width={isMobile ? 45 : 50} /> } /> ) )}
{/* Test history for realtime mode */} {isRealtime && realtimeResults.length > 0 && (
{realtimeResults.length} test{realtimeResults.length > 1 ? 's' : ''} this session
)}
) }