295 lines
16 KiB
HTML
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> |