From f29bce550f72a2f726461d2bb9e8d4a054c7a99d Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 18 Jun 2026 17:59:40 +0000 Subject: [PATCH] Add Network page First pass built from the blueprint spec: status cards row, Top Talkers, Network Topology dot map, Interface Utilization + Alert Summary, Traffic Over Time, Protocol Distribution, Recent Events, and footer stats bar. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01BbJV5nm8KPVH1oNJYKpnoF --- src/App.tsx | 2 + src/pages/Network.tsx | 383 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 385 insertions(+) create mode 100644 src/pages/Network.tsx diff --git a/src/App.tsx b/src/App.tsx index 53b5c8c..8a3f948 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -4,6 +4,7 @@ import Sidebar from './components/Sidebar' import TopBar from './components/TopBar' import Glance from './pages/Glance' import Infrastructure from './pages/Infrastructure' +import Network from './pages/Network' function App() { const [sidebarCollapsed, setSidebarCollapsed] = useState(false) @@ -55,6 +56,7 @@ function App() { } /> } /> + } /> diff --git a/src/pages/Network.tsx b/src/pages/Network.tsx new file mode 100644 index 0000000..3f5bc30 --- /dev/null +++ b/src/pages/Network.tsx @@ -0,0 +1,383 @@ +import { useState } from 'react' +import { PieChart, Pie, Cell, ResponsiveContainer, AreaChart, Area, XAxis } from 'recharts' +import { Activity, Gauge, ShieldAlert, Radio, Zap, Download, ChevronDown, Globe2 } from 'lucide-react' + +const subTabs = ['Overview'] + +const statusCards = [ + { label: 'Network Health', value: '98.7%', icon: Activity, sub: '↑ 2.3% yesterday', color: '#2ECC71' }, + { label: 'Total Traffic', value: '1.23 Tbps', icon: Radio, sub: '↑ 18.6% yesterday' }, + { label: 'Packet Loss', value: '0.02%', icon: Gauge, sub: '↓ 0.01% yesterday', color: '#2ECC71' }, + { label: 'Active Connections', value: '28,457', icon: Zap, sub: '↑ 6.7% yesterday' }, + { label: 'Threats Blocked', value: '152', icon: ShieldAlert, sub: '↑ 12.4% yesterday', color: '#E67E22' }, + { label: 'Avg. Latency', value: '12.4 ms', icon: Activity, sub: '↓ 8.1% yesterday', color: '#2ECC71' }, +] + +const topTalkers = [ + { name: 'web-app-01', rate: '203.4 Gbps' }, + { name: 'db-primary', rate: '156.7 Gbps' }, + { name: 'cache-cluster', rate: '98.3 Gbps' }, + { name: 'api-gateway', rate: '76.1 Gbps' }, + { name: 'backup-server', rate: '54.8 Gbps' }, +] + +const regions = [ + { name: 'us-east-1', x: '22%', y: '38%', status: 'live' }, + { name: 'us-west-2', x: '12%', y: '42%', status: 'live' }, + { name: 'eu-west-1', x: '48%', y: '30%', status: 'warning' }, + { name: 'ap-southeast-1', x: '78%', y: '58%', status: 'live' }, + { name: 'sa-east-1', x: '32%', y: '72%', status: 'live' }, + { name: 'ap-northeast-1', x: '85%', y: '40%', status: 'critical' }, +] + +const regionStatusColor: Record = { + live: '#2ECC71', + warning: '#E67E22', + critical: '#E74C3C', +} + +const interfaces = [ + { name: 'ethernet1/1', percentage: 85 }, + { name: 'ethernet1/2', percentage: 72 }, + { name: 'ethernet1/3', percentage: 68 }, + { name: 'ethernet1/4', percentage: 55 }, + { name: 'ethernet1/5', percentage: 48 }, +] + +const alertSummary = [ + { label: 'Critical', value: 2, color: '#E74C3C' }, + { label: 'Warning', value: 5, color: '#E67E22' }, + { label: 'Info', value: 12, color: '#2ECC71' }, +] + +const trafficData = Array.from({ length: 12 }, (_, i) => ({ + hour: i * 2, + inbound: 500 + i * 40 + Math.sin(i / 2) * 60, + outbound: 350 + i * 30 + Math.cos(i / 3) * 50, + total: 900 + i * 60 + Math.sin(i / 2.5) * 80, +})) + +const protocolData = [ + { name: 'TCP', value: 65, color: '#C8A434' }, + { name: 'UDP', value: 19, color: '#E67E22' }, + { name: 'ICMP', value: 7, color: '#2ECC71' }, + { name: 'DNS', value: 4, color: '#7A7D85' }, + { name: 'Others', value: 5, color: '#3B82F6' }, +] + +const recentEvents = [ + { title: 'ethernet1/2 UP', source: 'Interface restored', time: '2m ago' }, + { title: 'High bandwidth detected', source: 'web-app-01', time: '8m ago' }, + { title: 'New device joined', source: '10.0.1.45', time: '15m ago' }, + { title: 'VPN tunnel established', source: 'us-west-2', time: '22m ago' }, + { title: 'Blocked threat', source: '185.199.108.153', time: '35m ago' }, +] + +const cardBase: React.CSSProperties = { + backgroundColor: 'rgba(10, 10, 12, 0.92)', + border: '1px solid rgba(200, 164, 52, 0.08)', + borderRadius: '12px', + padding: '20px', + boxShadow: '0 0 20px rgba(200, 164, 52, 0.03)', + transition: 'border-color 0.2s ease', + position: 'relative', + overflow: 'hidden', + height: '100%', + display: 'flex', + flexDirection: 'column', +} + +const sectionTitle: React.CSSProperties = { + fontSize: '11px', + textTransform: 'uppercase', + letterSpacing: '1.5px', + color: '#7A7D85', + fontWeight: 500, + marginBottom: '16px', +} + +function framedCard(bgUrl: string): React.CSSProperties { + return { + backgroundImage: `url(${bgUrl})`, + backgroundSize: '100% 100%', + backgroundPosition: 'center', + backgroundRepeat: 'no-repeat', + position: 'relative', + overflow: 'hidden', + height: '100%', + display: 'flex', + flexDirection: 'column', + padding: '20px 20px 64px 20px', + } +} + +const cardVignette: React.CSSProperties = { + position: 'absolute', + inset: 0, + pointerEvents: 'none', + background: 'radial-gradient(ellipse closest-side at center, transparent 70%, var(--color-page) 100%)', +} + +const cardDim: React.CSSProperties = { + position: 'absolute', + inset: 0, + pointerEvents: 'none', + backgroundColor: 'rgba(8, 8, 10, 0.45)', +} + +function Donut({ data, centerLabel }: { data: { name: string; value: number; color: string }[]; centerLabel?: string }) { + return ( +
+
+ + + + {data.map((entry) => ( + + ))} + + + + {centerLabel && ( +
+ {centerLabel} +
+ )} +
+
+ {data.map((entry) => ( +
+ + {entry.name} + {entry.value}% +
+ ))} +
+
+ ) +} + +export default function Network() { + const [activeTab, setActiveTab] = useState('Overview') + + return ( + <> + {/* Sub-tabs + Actions */} +
+
+ {subTabs.map((tab) => { + const active = tab === activeTab + return ( + + ) + })} +
+
+ + +
+
+ + {/* Status Cards */} +
+ {statusCards.map((card) => { + const Icon = card.icon + return ( +
+

+ {card.label} +

+
+ + {card.value} +
+

{card.sub}

+
+ ) + })} +
+ + {/* Middle Row */} +
+
+ {/* Top Talkers */} +
+
+

Top Talkers

+
+ {topTalkers.map((t) => ( +
+ {t.name} + {t.rate} +
+ ))} +
+
+
+ + {/* Network Topology */} +
+
+
+
+
+

Network Topology

+
+ Live + Warning + Critical +
+
+
+ + {regions.map((r) => ( +
+
+
+ ))} +
+
+
+ + {/* Interface Utilization + Alert Summary, stacked */} +
+
+

Interface Utilization

+
+ {interfaces.map((iface) => ( +
+
+ {iface.name} + {iface.percentage}% +
+
+
+
+
+ ))} +
+
+
+

Alert Summary

+
+ {alertSummary.map((a) => ( +
+ {a.value} + {a.label} +
+ ))} +
+
+
+
+
+ + {/* Bottom Row */} +
+
+ {/* Traffic Over Time */} +
+

Traffic Over Time

+
+ + + + + + + + +
+
+ + {/* Protocol Distribution */} +
+

Protocol Distribution

+ +
+ + {/* Recent Events */} +
+

Recent Events

+
+ {recentEvents.map((item, i) => ( +
+
+

{item.title}

+

{item.source}

+
+ {item.time} +
+ ))} +
+
+
+
+ + {/* Footer stats bar */} +
+ 6 Regions| + 42 Sites| + 248 Devices| + 1,245 Interfaces| + 28,457 Connections| + 98.7% Health +
+ + ) +}