// Fleet Dashboard — World Map (Acme Bank light theme) // Light map bg, white cluster cards, green brand accents /* --- Cluster Card (HTML-rendered, light theme) --- */ function ClusterCard({ cluster, mode, placementData, upgradeData }) { let borderColor = '#059669'; let dimmed = false; let glowColor = null; let badge = null; let extraInfo = null; const healthColor = cluster.health === 'healthy' ? '#059669' : cluster.health === 'degraded' ? '#D97706' : '#DC2626'; if (mode === 'placement' && placementData) { const boundList = placementData.boundClusters || [placementData.boundCluster]; if (boundList.includes(cluster.id)) { borderColor = '#059669'; glowColor = '#059669'; badge = { label: '✓ BOUND', color: '#065F46', bg: '#D1FAE5' }; extraInfo = placementData.resources[0]; } else if (placementData.rejectedClusters.includes(cluster.id)) { borderColor = '#DC2626'; dimmed = true; badge = { label: '✗ NOT SELECTED', color: '#991B1B', bg: '#FEE2E2' }; extraInfo = placementData.rejectReason; } } if (mode === 'upgrade' && upgradeData) { const stage = upgradeData.stages.find(s => s.cluster === cluster.id); if (stage) { if (stage.status === 'completed') { borderColor = '#059669'; badge = { label: `✓ Stage ${stage.id} Complete · ${stage.duration}`, color: '#065F46', bg: '#D1FAE5' }; } else if (stage.status === 'waiting-approval') { borderColor = '#D97706'; glowColor = '#D97706'; badge = { label: `⏸ Stage ${stage.id} · Awaiting Approval`, color: '#92400E', bg: '#FEF3C7' }; } else if (stage.status === 'in-progress') { borderColor = '#1D4ED8'; glowColor = '#3B82F6'; badge = { label: `▶ Stage ${stage.id} · In Progress`, color: '#1D4ED8', bg: '#DBEAFE' }; } else { borderColor = FC.chromeSubtle; dimmed = true; badge = { label: `⏳ Stage ${stage.id} · Queued`, color: FC.chromeSubtle, bg: '#F3F4F6' }; } } } const cardStyles = { width: 178, padding: '14px 16px', background: dimmed ? 'rgba(255,255,255,0.75)' : '#FFFFFF', border: `1.5px ${dimmed ? 'dashed' : 'solid'} ${borderColor}${dimmed ? '88' : ''}`, borderRadius: 12, opacity: dimmed ? 0.55 : 1, transition: 'opacity 0.4s, box-shadow 0.4s', pointerEvents: 'auto', boxShadow: '0 1px 3px rgba(0,0,0,0.08), 0 2px 8px rgba(0,0,0,0.04)', }; if (glowColor) { cardStyles.boxShadow = `0 1px 3px rgba(0,0,0,0.08), 0 0 20px ${glowColor}18, 0 0 40px ${glowColor}08`; } return (
{/* Region label */}
{cluster.regionLabel}
{/* Cluster name + health */}
{cluster.name}
{/* Version + nodes */}
v{cluster.k8sVersion} · {cluster.nodeCount} nodes
{/* Labels */}
{cluster.region}
{/* Badge */} {badge && (
{badge.label}
)} {/* Extra info */} {extraInfo && (
↳ {extraInfo}
)}
); } /* --- Fleet Hub Badge (HTML, light theme) --- */ function FleetHubBadge({ fleetHub = FLEET_HUB }) { return (
{fleetHub.name}
Fleet Hub · Azure
); } /* --- World Map (composite, light theme) --- */ function WorldMap({ mode, placementData, upgradeData, clusters = CLUSTERS, fleetHub = FLEET_HUB, children }) { const outerRef = React.useRef(null); const [mapSize, setMapSize] = React.useState({ w: 0, h: 0 }); React.useLayoutEffect(() => { const el = outerRef.current; if (!el) return; function measure() { const { width: cw, height: ch } = el.getBoundingClientRect(); if (cw === 0 || ch === 0) return; const aspect = 2; // 1000:500 let w, h; if (cw / ch > aspect) { h = ch; w = ch * aspect; } else { w = cw; h = cw / aspect; } setMapSize({ w, h }); } measure(); const ro = new ResizeObserver(measure); ro.observe(el); return () => ro.disconnect(); }, []); const pct = (svgX, svgY) => ({ left: `${(svgX / 1000) * 100}%`, top: `${(svgY / 500) * 100}%`, }); function lineProps(cluster) { if (mode === 'placement' && placementData) { const boundList = placementData.boundClusters || [placementData.boundCluster]; const bound = boundList.includes(cluster.id); return { stroke: bound ? '#059669' : FC.chromeSubtle, strokeWidth: bound ? 1.6 : 0.7, strokeDasharray: bound ? 'none' : '6 4', opacity: bound ? 0.5 : 0.2, isBound: bound, }; } if (mode === 'upgrade' && upgradeData) { const stage = upgradeData.stages.find(s => s.cluster === cluster.id); const done = stage && stage.status === 'completed'; return { stroke: done ? '#059669' : FC.chromeSubtle, strokeWidth: done ? 1.4 : 0.7, strokeDasharray: done ? 'none' : '6 4', opacity: done ? 0.45 : 0.2, isBound: false, }; } return { stroke: FC.primary, strokeWidth: 0.6, strokeDasharray: '4 6', opacity: 0.2, isBound: false }; } const hx = fleetHub.x, hy = fleetHub.y; return (
{mapSize.w > 0 && (
{/* Layer 1: SVG Background */} {/* Dot pattern for continent masking */} {/* Background radial glow */} {/* Cluster region glows */} {clusters.map(c => { let glowC = 'rgba(13,124,74,0.05)'; if (mode === 'placement' && placementData) { glowC = placementData.boundCluster === c.id ? 'rgba(5,150,105,0.10)' : 'rgba(13,124,74,0.03)'; } if (mode === 'upgrade' && upgradeData) { const st = upgradeData.stages.find(s => s.cluster === c.id); glowC = st && st.status === 'waiting-approval' ? 'rgba(217,119,6,0.08)' : 'rgba(13,124,74,0.03)'; } return ( ); })} {/* Background */} {/* Subtle grid lines */} {[125, 250, 375].map(y => ( ))} {[200, 400, 600, 800].map(x => ( ))} {/* Continent dot matrix */} {CONTINENT_PATHS.map((d, i) => ( ))} {/* Region glows */} {clusters.map(c => ( ))} {/* Connection lines */} {clusters.map(c => { const lp = lineProps(c); const pathD = `M${hx},${hy} L${c.x},${c.y}`; return ( {lp.isBound && ( <> )} ); })} {/* Layer 2: HTML Cluster Cards */}
{/* Fleet Hub */}
{/* Cluster cards — offset horizontally to prevent overlap */} {clusters.map(c => { const xPct = c.x / 1000; const tx = xPct < 0.4 ? '-70%' : xPct > 0.6 ? '-30%' : '-50%'; return (
); })}
{/* Layer 3: View-specific overlays */} {children && (
{children}
)}
)}
); } Object.assign(window, { WorldMap, ClusterCard, FleetHubBadge });