diff --git a/AppImage/components/latency-detail-modal.tsx b/AppImage/components/latency-detail-modal.tsx index c55de82b..827f398f 100644 --- a/AppImage/components/latency-detail-modal.tsx +++ b/AppImage/components/latency-detail-modal.tsx @@ -170,6 +170,49 @@ const generateLatencyReport = (report: ReportData) => { endTime: new Date(report.data[report.data.length - 1].timestamp * 1000).toLocaleString(), } : null + // Generate chart SVG + const chartData = report.isRealtime + ? report.realtimeResults.map(r => r.latency_avg || 0) + : report.data.map(d => d.value || 0) + + let chartSvg = '
Not enough data points for chart
' + if (chartData.length >= 2) { + const minVal = Math.min(...chartData) + const maxVal = Math.max(...chartData) + const range = maxVal - minVal || 1 + const width = 700 + const height = 120 + const padding = 30 + + const points = chartData.map((val, i) => { + const x = padding + (i / (chartData.length - 1)) * (width - padding * 2) + const y = height - padding - ((val - minVal) / range) * (height - padding * 2) + return `${x},${y}` + }).join(' ') + + const areaPoints = `${padding},${height - padding} ${points} ${width - padding},${height - padding}` + + chartSvg = ` + + ` + } + const html = ` @@ -248,11 +291,22 @@ const generateLatencyReport = (report: ReportData) => { .exec-text h3 { font-size: 16px; margin-bottom: 4px; } .exec-text p { font-size: 12px; color: #64748b; line-height: 1.5; } - /* Score bar */ - .score-bar-wrap { margin: 10px 0 6px; } - .score-bar-bg { height: 10px; background: #e2e8f0; border-radius: 5px; position: relative; overflow: hidden; } - .score-bar-fill { height: 100%; border-radius: 5px; } - .score-bar-labels { display: flex; justify-content: space-between; font-size: 9px; color: #94a3b8; margin-top: 3px; } + /* Latency gauge */ + .latency-gauge { + display: flex; flex-direction: column; align-items: center; flex-shrink: 0; width: 160px; + } + .gauge-value { display: flex; align-items: baseline; gap: 2px; margin-top: -10px; } + .gauge-num { font-size: 32px; font-weight: 800; line-height: 1; } + .gauge-unit { font-size: 14px; font-weight: 600; opacity: 0.8; } + .gauge-status { font-size: 10px; font-weight: 700; letter-spacing: 0.1em; text-transform: uppercase; margin-top: 2px; } + + /* Latency range display */ + .latency-range { + display: flex; gap: 24px; margin-top: 12px; padding-top: 12px; border-top: 1px solid #e2e8f0; + } + .range-item { display: flex; flex-direction: column; gap: 2px; } + .range-label { font-size: 10px; font-weight: 600; color: #94a3b8; text-transform: uppercase; } + .range-value { font-size: 16px; font-weight: 700; } /* Grids */ .grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; margin-bottom: 8px; } @@ -297,6 +351,58 @@ const generateLatencyReport = (report: ReportData) => { margin-top: 32px; padding-top: 12px; border-top: 1px solid #e2e8f0; display: flex; justify-content: space-between; font-size: 10px; color: #94a3b8; } + + /* Print styles */ + @media print { + * { -webkit-print-color-adjust: exact !important; print-color-adjust: exact !important; } + body { padding: 10mm; font-size: 10pt; } + .no-print { display: none !important; } + .container { max-width: 100%; padding: 0; } + + /* Prevent page breaks inside elements */ + .section { page-break-inside: avoid; break-inside: avoid; } + .exec-box { page-break-inside: avoid; break-inside: avoid; } + .card { page-break-inside: avoid; break-inside: avoid; } + .threshold-item { page-break-inside: avoid; break-inside: avoid; } + .info-box { page-break-inside: avoid; break-inside: avoid; } + .chk-tbl { page-break-inside: avoid; break-inside: avoid; } + .latency-gauge { page-break-inside: avoid; break-inside: avoid; } + .latency-range { page-break-inside: avoid; break-inside: avoid; } + + /* Force page breaks before major sections if needed */ + .section { page-break-before: auto; } + + /* Keep headers with their content */ + .section-title { page-break-after: avoid; break-after: avoid; } + + /* Ensure grids don't break awkwardly */ + .grid-2, .grid-3, .grid-4 { page-break-inside: avoid; break-inside: avoid; } + + /* Table rows - try to keep together */ + .chk-tbl tr { page-break-inside: avoid; break-inside: avoid; } + .chk-tbl thead { display: table-header-group; } + + /* Footer always at bottom */ + .rpt-footer { + page-break-inside: avoid; break-inside: avoid; + margin-top: 20px; + } + + /* Reduce spacing for print */ + .section { margin-bottom: 15px; } + .exec-box { padding: 12px; } + + /* Ensure SVG charts print correctly */ + svg { max-width: 100%; height: auto; } + } + + /* Mobile print adjustments */ + @media print and (max-width: 600px) { + .exec-box { flex-direction: column; gap: 15px; } + .latency-gauge { width: 100%; } + .grid-2, .grid-3, .grid-4 { grid-template-columns: 1fr 1fr; } + .latency-range { flex-wrap: wrap; gap: 12px; } + } @@ -343,10 +449,27 @@ function pmxPrint(){Excellent (< 50ms): Optimal for real-time applications, gaming, and video calls.
-Good (50-100ms): Acceptable for most applications with minimal impact.
-Fair (100-200ms): Noticeable delay. May affect VoIP and interactive applications.
-Poor (> 200ms): Significant latency. Investigation recommended.
-Not enough data points for chart
'; - - const minVal = Math.min(...chartData); - const maxVal = Math.max(...chartData); - const range = maxVal - minVal || 1; - const width = 700; - const height = 120; - const padding = 30; - - const points = chartData.map((val, i) => { - const x = padding + (i / (chartData.length - 1)) * (width - padding * 2); - const y = height - padding - ((val - minVal) / range) * (height - padding * 2); - return \`\${x},\${y}\`; - }).join(' '); - - const areaPoints = \`\${padding},\${height - padding} \${points} \${width - padding},\${height - padding}\`; - - return \` - - \`; - })()} + ${chartSvg}Excellent (< 50ms): Optimal for real-time applications, gaming, and video calls.
@@ -538,7 +598,7 @@ ${report.isRealtime && report.realtimeResults.length > 0 ? `\${ +
${ 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.' : @@ -572,10 +632,10 @@ ${report.isRealtime && report.realtimeResults.length > 0 ? `