Files
spore-ui/public/index.html
2025-10-26 18:58:53 +01:00

295 lines
16 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SPORE UI</title>
<link rel="stylesheet" href="styles/main.css">
<link rel="stylesheet" href="styles/theme.css?v=1757159926">
</head>
<body>
<div class="container">
<div class="main-navigation">
<button class="burger-btn" id="burger-btn" aria-label="Menu" title="Menu">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M3 6h18M3 12h18M3 18h18" />
</svg>
</button>
<div class="nav-left">
<a href="/cluster" class="nav-tab active" data-view="cluster">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="16" height="16" style="margin-right:6px;">
<circle cx="12" cy="12" r="9"/>
<circle cx="8" cy="10" r="1.5"/>
<circle cx="16" cy="8" r="1.5"/>
<circle cx="14" cy="15" r="1.5"/>
<path d="M9 11l3 3M9 11l6-3"/>
</svg>
Cluster
</a>
<a href="/topology" class="nav-tab" data-view="topology">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="16" height="16" style="margin-right:6px;">
<circle cx="12" cy="4" r="1.6"/>
<circle cx="19" cy="9" r="1.6"/>
<circle cx="16" cy="18" r="1.6"/>
<circle cx="8" cy="18" r="1.6"/>
<circle cx="5" cy="9" r="1.6"/>
<path d="M12 4L16 18M16 18L5 9M5 9L19 9M19 9L8 18M8 18L12 4"/>
</svg>
Topology
</a>
<a href="/events" class="nav-tab" data-view="events">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="16" height="16" style="margin-right:6px;">
<circle cx="5" cy="12" r="1.5" fill="currentColor"/>
<circle cx="18" cy="6" r="1.5" fill="currentColor"/>
<circle cx="18" cy="12" r="1.5" fill="currentColor"/>
<circle cx="18" cy="18" r="1.5" fill="currentColor"/>
<line x1="6.5" y1="12" x2="16.5" y2="6"/>
<line x1="6.5" y1="12" x2="16.5" y2="12"/>
<line x1="6.5" y1="12" x2="16.5" y2="18"/>
</svg>
Events
</a>
<a href="/monitoring" class="nav-tab" data-view="monitoring">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="16" height="16" style="margin-right:6px;">
<path d="M3 12h3l2 7 4-14 3 10 2-6h4"/>
</svg>
Monitoring
</a>
<a href="/firmware" class="nav-tab" data-view="firmware">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="16" height="16" style="margin-right:6px;">
<path d="M4 7l8-4 8 4v10l-8 4-8-4z"/>
<path d="M12 8v8"/>
</svg>
Firmware
</a>
</div>
<div class="nav-right">
<div class="random-primary-switcher">
<button class="random-primary-toggle" id="random-primary-toggle" title="Select random primary node">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="20" height="20">
<path d="M1 4v6h6M23 20v-6h-6" />
<path d="M20.49 9A9 9 0 0 0 5.64 5.64L1 10m22 4l-4.64 4.36A9 9 0 0 1 3.51 15" />
</svg>
</button>
</div>
<div class="theme-switcher">
<button class="theme-toggle" id="theme-toggle" title="Toggle theme">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<circle cx="12" cy="12" r="5"/>
<path d="M12 1v2M12 21v2M4.22 4.22l1.42 1.42M18.36 18.36l1.42 1.42M1 12h2M21 12h2M4.22 19.78l1.42-1.42M18.36 5.64l1.42-1.42"/>
</svg>
</button>
</div>
<div class="cluster-status">Cluster</div>
</div>
</div>
<div id="cluster-view" class="view-content active">
<div class="cluster-section">
<div class="cluster-header">
<div class="cluster-header-left">
<div class="primary-node-info">
<span class="primary-node-label">API:</span>
<span class="primary-node-ip" id="primary-node-ip">Discovering...</span>
<button class="primary-node-refresh" id="select-random-primary-btn"
title="Select random primary node">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="14"
height="14">
<path d="M1 4v6h6M23 20v-6h-6" />
<path d="M20.49 9A9 9 0 0 0 5.64 5.64L1 10m22 4l-4.64 4.36A9 9 0 0 1 3.51 15" />
</svg>
</button>
</div>
<div class="cluster-filters">
<div class="filter-group">
<label for="label-key-filter" class="filter-label">Filter by Label:</label>
<select id="label-key-filter" class="filter-select">
<option value="">All Labels</option>
</select>
<select id="label-value-filter" class="filter-select">
<option value="">All Values</option>
</select>
<div class="filter-pills-container" id="filter-pills-container">
<!-- Active filter pills will be dynamically added here -->
</div>
<button id="clear-filters-btn" class="clear-filters-btn" title="Clear all filters">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="14" height="14">
<path d="M18 6L6 18M6 6l12 12"/>
</svg>
</button>
</div>
</div>
</div>
<div class="cluster-header-right">
<button class="config-btn" id="config-wifi-btn" title="Configure WiFi settings for visible nodes">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="16" height="16" style="margin-right:6px;">
<path d="M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z"/>
<circle cx="12" cy="12" r="3"/>
</svg>
Config
</button>
<button class="deploy-btn" id="deploy-firmware-btn" title="Deploy firmware to visible nodes">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="16" height="16" style="margin-right:6px;">
<path d="M12 16V4"/>
<path d="M8 8l4-4 4 4"/>
<path d="M20 16v2a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2v-2"/>
</svg>
Deploy
</button>
<button class="refresh-btn" id="refresh-cluster-btn">
<svg class="refresh-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="2">
<path d="M1 4v6h6M23 20v-6h-6" />
<path d="M20.49 9A9 9 0 0 0 5.64 5.64L1 10m22 4l-4.64 4.36A9 9 0 0 1 3.51 15" />
</svg>
Refresh
</button>
</div>
</div>
<div id="cluster-members-container">
<div class="loading">
<div>Loading cluster members...</div>
</div>
</div>
</div>
</div>
<div id="topology-view" class="view-content">
<div id="topology-graph-container">
<div class="loading">
<div>Loading network topology...</div>
</div>
</div>
</div>
<div id="firmware-view" class="view-content">
<div class="firmware-section">
<div class="firmware-header">
<div class="firmware-search">
<div class="search-input-wrapper">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="16" height="16" class="search-icon">
<circle cx="11" cy="11" r="8"/>
<path d="M21 21l-4.35-4.35"/>
</svg>
<input type="text" id="firmware-search" placeholder="Search firmware by name, version, or labels (e.g., '1.0.0 base')...">
</div>
</div>
<div class="header-actions">
<div id="registry-status" class="registry-status">
<span class="status-indicator disconnected">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="12" height="12">
<circle cx="12" cy="12" r="10"/>
<line x1="15" y1="9" x2="9" y2="15"/>
<line x1="9" y1="9" x2="15" y2="15"/>
</svg>
Registry Disconnected
</span>
</div>
<button class="refresh-btn" id="refresh-firmware-btn" title="Refresh firmware list">
<svg class="refresh-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8" />
<path d="M21 3v5h-5" />
<path d="M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16" />
<path d="M3 21v-5h5" />
</svg>
</button>
<button class="add-btn" id="add-firmware-btn" title="Add new firmware">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="16" height="16">
<line x1="12" y1="5" x2="12" y2="19"/>
<line x1="5" y1="12" x2="19" y2="12"/>
</svg>
Add Firmware
</button>
</div>
</div>
<div class="firmware-content">
<div id="firmware-list-container" class="firmware-list-container">
<div class="loading-state">
<div class="loading-spinner"></div>
<div class="loading-text">Loading firmware...</div>
</div>
</div>
</div>
</div>
</div>
<div id="monitoring-view" class="view-content">
<div class="monitoring-view-section">
<div class="monitoring-header">
<h2>
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="18" height="18" style="margin-right:8px; vertical-align: -2px;">
<path d="M3 12h3l2 7 4-14 3 10 2-6h4"/>
</svg>
Monitoring
</h2>
<button class="refresh-btn" id="refresh-monitoring-btn">
<svg class="refresh-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M1 4v6h6M23 20v-6h-6" />
<path d="M20.49 9A9 9 0 0 0 5.64 5.64L1 10m22 4l-4.64 4.36A9 9 0 0 1 3.51 15" />
</svg>
Refresh
</button>
</div>
<div class="monitoring-content">
<div class="cluster-summary" id="cluster-summary">
<div class="loading">
<div>Loading cluster resource summary...</div>
</div>
</div>
<div class="nodes-monitoring" id="nodes-monitoring">
<div class="loading">
<div>Loading node resource data...</div>
</div>
</div>
</div>
</div>
</div>
<div id="events-view" class="view-content">
<div class="view-section" style="height: 100%; display: flex; flex-direction: column;">
<div id="events-graph-container" style="flex: 1; min-height: 0;">
<div class="loading">
<div>Waiting for websocket events...</div>
</div>
</div>
</div>
</div>
</div>
<script src="./vendor/d3.v7.min.js"></script>
<script src="./scripts/constants.js"></script>
<script src="./scripts/icons.js"></script>
<script src="./scripts/framework.js"></script>
<script src="./scripts/api-client.js"></script>
<script src="./scripts/view-models.js"></script>
<!-- Base/leaf components first -->
<script src="./scripts/components/DrawerComponent.js"></script>
<script src="./scripts/components/TerminalPanelComponent.js"></script>
<script src="./scripts/components/PrimaryNodeComponent.js"></script>
<script src="./scripts/components/NodeDetailsComponent.js"></script>
<script src="./scripts/components/ClusterMembersComponent.js"></script>
<script src="./scripts/components/OverlayDialogComponent.js"></script>
<script src="./scripts/components/FirmwareComponent.js"></script>
<script src="./scripts/components/FirmwareFormComponent.js"></script>
<script src="./scripts/components/FirmwareUploadComponent.js"></script>
<script src="./scripts/components/RolloutComponent.js"></script>
<script src="./scripts/components/WiFiConfigComponent.js"></script>
<!-- Container/view components after their deps -->
<script src="./scripts/components/FirmwareViewComponent.js"></script>
<script src="./scripts/components/ClusterViewComponent.js"></script>
<script src="./scripts/components/ClusterStatusComponent.js"></script>
<script src="./scripts/components/TopologyGraphComponent.js"></script>
<script src="./scripts/components/MonitoringViewComponent.js"></script>
<script src="./scripts/components/EventComponent.js"></script>
<script src="./scripts/components/ComponentsLoader.js"></script>
<script src="./scripts/theme-manager.js"></script>
<script src="./scripts/app.js"></script>
</body>
</html>