Files
pulse/public/index.html
T

214 lines
17 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Pulse</title>
<link rel="icon" href="/logo.svg" type="image/svg+xml">
<link rel="stylesheet" href="/output.css">
<style>
/* Remove all old CSS rules. Keep the style tag empty or remove it entirely */
/* Tailwind utilities will handle styling */
@keyframes pulse {
0%, 100% {
opacity: 1;
transform: scale(1);
}
50% {
opacity: 0.7;
transform: scale(1.1);
}
}
.pulse-logo-circle {
animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
transform-origin: center;
}
/* Light mode colors (default) */
.pulse-logo-outer { fill: #2563eb; } /* blue-600 */
.pulse-logo-inner { fill: #ffffff; }
/* Dark mode colors */
.dark .pulse-logo-outer { fill: #3b82f6; } /* blue-500 */
.dark .pulse-logo-inner { fill: #dbeafe; } /* blue-100 */
</style>
</head>
<body class="bg-gray-100 dark:bg-gray-900 text-gray-800 dark:text-gray-200 p-2 font-sans">
<div class="container max-w-[95%] mx-auto">
<!-- Header - removed bottom border and padding -->
<div class="header flex justify-between items-center mb-2">
<!-- Empty div for spacing, allows logo to center -->
<div class="flex-1"></div>
<!-- Logo and Title Container (Centered) -->
<div class="flex items-center gap-1 flex-none">
<!-- Pulse Logo SVG -->
<svg width="20" height="20" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg" class="pulse-logo">
<title>Pulse Logo</title>
<circle class="pulse-logo-outer" cx="50" cy="50" r="45"/>
<circle class="pulse-logo-inner pulse-logo-circle" cx="50" cy="50" r="25"/>
</svg>
<span class="text-lg font-medium text-gray-800 dark:text-gray-200">Pulse</span>
</div>
<!-- Header Controls (Aligned Right) -->
<div class="header-controls flex flex-1 justify-end items-center gap-4">
<!-- Replaced toggle switch with icon button - removed focus ring -->
<button id="theme-toggle-button" type="button" class="p-1 rounded-md text-gray-600 dark:text-gray-400 hover:bg-gray-200 dark:hover:bg-gray-700 focus:outline-none" aria-label="Toggle theme">
<!-- Sun icon (visible in light mode) -->
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 block dark:hidden" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round" d="M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z" />
</svg>
<!-- Moon icon (visible in dark mode) -->
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 hidden dark:block" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round" d="M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z" />
</svg>
</button>
<div id="connection-status" class="status disconnected text-xs px-2 py-1 rounded-full bg-gray-200 dark:bg-gray-700 text-gray-600 dark:text-gray-400">Disconnected</div>
<!-- JS will need to toggle classes like 'bg-red-100 dark:bg-red-800/30 text-red-700 dark:text-red-300' for disconnected -->
<!-- and 'bg-green-100 dark:bg-green-800/30 text-green-700 dark:text-green-300' for connected -->
</div>
</div>
<div class="tabs flex mb-2 border-b border-gray-300 dark:border-gray-700">
<!-- Add Tailwind classes, JS will toggle active classes -->
<div class="tab active px-3 py-1.5 cursor-pointer bg-white dark:bg-gray-800 border border-gray-300 dark:border-gray-700 border-b-0 rounded-t text-sm -mb-px" data-tab="main">Main</div>
<div class="tab px-3 py-1.5 cursor-pointer bg-gray-100 dark:bg-gray-700/50 border-transparent text-sm text-gray-600 dark:text-gray-400 hover:bg-gray-200 dark:hover:bg-gray-700 rounded-t" data-tab="nodes">Nodes</div>
<div class="tab px-3 py-1.5 cursor-pointer bg-gray-100 dark:bg-gray-700/50 border-transparent text-sm text-gray-600 dark:text-gray-400 hover:bg-gray-200 dark:hover:bg-gray-700 rounded-t" data-tab="storage">Storage</div>
</div>
<!-- Removed outer .section wrapper, apply styles directly to tab content where needed -->
<!-- Apply base styles to all tab contents, JS toggles 'block'/'hidden' -->
<div id="nodes" class="tab-content hidden bg-white dark:bg-gray-800 rounded-b rounded-tr shadow p-3 mb-2">
<h2 class="text-lg font-medium mb-2 text-gray-800 dark:text-gray-200">Nodes</h2>
<div class="overflow-x-auto">
<table id="nodes-table" class="min-w-full divide-y divide-gray-700 table-fixed">
<thead class="text-xs text-gray-700 uppercase bg-gray-50 dark:bg-gray-700 dark:text-gray-400 sticky top-0 z-10">
<tr>
<th scope="col" class="sortable px-6 py-3 w-2/12 text-left font-semibold cursor-pointer select-none whitespace-nowrap" data-sort="status">Status</th>
<th scope="col" class="sortable px-6 py-3 w-4/12 text-left font-semibold cursor-pointer select-none whitespace-nowrap" data-sort="node">Node Name</th>
<th scope="col" class="sortable px-6 py-3 w-2/12 text-right font-semibold cursor-pointer select-none whitespace-nowrap" data-sort="cpu">CPU</th>
<th scope="col" class="sortable px-6 py-3 w-2/12 text-right font-semibold cursor-pointer select-none whitespace-nowrap" data-sort="memory">Mem</th>
<th scope="col" class="sortable px-6 py-3 w-2/12 text-right font-semibold cursor-pointer select-none whitespace-nowrap" data-sort="disk">Disk</th>
</tr>
</thead>
<tbody id="nodes-table-body" class="divide-y divide-gray-200 dark:divide-gray-700">
<!-- Node rows will be inserted here -->
<tr><td colspan="5" class="px-4 py-4 text-sm text-gray-400 text-center">Loading node data...</td></tr>
</tbody>
</table>
</div>
</div>
<div id="main" class="tab-content block bg-white dark:bg-gray-800 rounded-b rounded-tr shadow p-3 mb-2">
<!-- --- Dashboard Filter Bar --- -->
<div class="dashboard-filter flex flex-col md:flex-row justify-between items-stretch md:items-center gap-3 mb-3 p-2 bg-gray-50 dark:bg-gray-700/50 border border-gray-200 dark:border-gray-700 rounded">
<div class="dashboard-filter-controls flex-grow flex items-center gap-2">
<input type="text" id="dashboard-search" placeholder="Search (use ',' for OR)" class="flex-grow p-1 px-2 h-7 text-sm border border-gray-300 dark:border-gray-600 rounded bg-white dark:bg-gray-800 text-gray-800 dark:text-gray-200 focus:ring-1 focus:ring-blue-500 focus:border-blue-500 outline-none" />
<!-- Reset Button -->
<button id="reset-filters-button" title="Reset Filters & Sort (Esc)" class="p-1 h-7 w-7 flex-shrink-0 rounded text-gray-500 dark:text-gray-400 hover:bg-gray-200 dark:hover:bg-gray-600 focus:outline-none">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="mx-auto"><path d="M21 12a9 9 0 1 1-6.219-8.56"></path></svg> <!-- RotateLeft icon -->
</button>
</div>
<!-- Combined Toggle Wrapper -->
<div class="filter-toggles-wrapper flex items-center gap-4 flex-wrap">
<!-- View Toggle -->
<div class="flex items-center gap-2">
<div class="segmented-control inline-flex border border-gray-300 dark:border-gray-600 rounded overflow-hidden">
<input type="radio" id="group-grouped" name="group-filter" value="grouped" class="hidden peer/grouped" checked>
<label for="group-grouped" class="px-2 py-0.5 text-xs cursor-pointer bg-white dark:bg-gray-800 peer-checked/grouped:bg-blue-100 dark:peer-checked/grouped:bg-blue-800/50 peer-checked/grouped:text-blue-700 dark:peer-checked/grouped:text-blue-300 hover:bg-gray-50 dark:hover:bg-gray-700 select-none">Grouped</label>
<input type="radio" id="group-list" name="group-filter" value="list" class="hidden peer/list">
<label for="group-list" class="px-2 py-0.5 text-xs cursor-pointer bg-white dark:bg-gray-800 border-l border-gray-300 dark:border-gray-600 peer-checked/list:bg-blue-100 dark:peer-checked/list:bg-blue-800/50 peer-checked/list:text-blue-700 dark:peer-checked/list:text-blue-300 hover:bg-gray-50 dark:hover:bg-gray-700 select-none">List</label>
</div>
</div>
<!-- Show Toggle -->
<div class="flex items-center gap-2 pl-2">
<div class="segmented-control inline-flex border border-gray-300 dark:border-gray-600 rounded overflow-hidden">
<input type="radio" id="filter-all" name="type-filter" value="all" class="hidden peer/all" checked>
<label for="filter-all" class="px-2 py-0.5 text-xs cursor-pointer bg-white dark:bg-gray-800 peer-checked/all:bg-green-100 dark:peer-checked/all:bg-green-800/50 peer-checked/all:text-green-700 dark:peer-checked/all:text-green-300 hover:bg-gray-50 dark:hover:bg-gray-700 select-none">All</label>
<input type="radio" id="filter-vm" name="type-filter" value="vm" class="hidden peer/vm">
<label for="filter-vm" class="px-2 py-0.5 text-xs cursor-pointer bg-white dark:bg-gray-800 border-l border-gray-300 dark:border-gray-600 peer-checked/vm:bg-green-100 dark:peer-checked/vm:bg-green-800/50 peer-checked/vm:text-green-700 dark:peer-checked/vm:text-green-300 hover:bg-gray-50 dark:hover:bg-gray-700 select-none">VMs</label>
<input type="radio" id="filter-lxc" name="type-filter" value="ct" class="hidden peer/lxc">
<label for="filter-lxc" class="px-2 py-0.5 text-xs cursor-pointer bg-white dark:bg-gray-800 border-l border-gray-300 dark:border-gray-600 peer-checked/lxc:bg-green-100 dark:peer-checked/lxc:bg-green-800/50 peer-checked/lxc:text-green-700 dark:peer-checked/lxc:text-green-300 hover:bg-gray-50 dark:hover:bg-gray-700 select-none">LXCs</label>
</div>
</div>
<!-- Status Toggle -->
<div class="flex items-center gap-2 pl-2">
<div class="segmented-control inline-flex border border-gray-300 dark:border-gray-600 rounded overflow-hidden">
<input type="radio" id="filter-status-all" name="status-filter" value="all" class="hidden peer/status-all" checked>
<label for="filter-status-all" class="px-2 py-0.5 text-xs cursor-pointer bg-white dark:bg-gray-800 peer-checked/status-all:bg-blue-100 dark:peer-checked/status-all:bg-blue-800/50 peer-checked/status-all:text-blue-700 dark:peer-checked/status-all:text-blue-300 hover:bg-gray-50 dark:hover:bg-gray-700 select-none">All</label>
<input type="radio" id="filter-status-running" name="status-filter" value="running" class="hidden peer/status-running">
<label for="filter-status-running" class="px-2 py-0.5 text-xs cursor-pointer bg-white dark:bg-gray-800 border-l border-gray-300 dark:border-gray-600 peer-checked/status-running:bg-blue-100 dark:peer-checked/status-running:bg-blue-800/50 peer-checked/status-running:text-blue-700 dark:peer-checked/status-running:text-blue-300 hover:bg-gray-50 dark:hover:bg-gray-700 select-none">Running</label>
<input type="radio" id="filter-status-stopped" name="status-filter" value="stopped" class="hidden peer/status-stopped">
<label for="filter-status-stopped" class="px-2 py-0.5 text-xs cursor-pointer bg-white dark:bg-gray-800 border-l border-gray-300 dark:border-gray-600 peer-checked/status-stopped:bg-blue-100 dark:peer-checked/status-stopped:bg-blue-800/50 peer-checked/status-stopped:text-blue-700 dark:peer-checked/status-stopped:text-blue-300 hover:bg-gray-50 dark:hover:bg-gray-700 select-none">Stopped</label>
</div>
</div>
</div>
</div>
<!-- --- Table Container --- -->
<div class="table-container max-h-[80vh] overflow-y-auto mb-2 border border-gray-200 dark:border-gray-700 rounded overflow-hidden">
<table id="main-table" class="w-full table-fixed text-sm">
<colgroup>
<!-- Define col widths using style attribute for Tailwind compatibility -->
<col class="name-col" style="width: var(--name-col-width, 150px);">
<col class="type-col" style="width: 50px;">
<col class="id-col" style="width: 50px;">
<col class="uptime-col" style="width: var(--uptime-col-width, 80px);">
<col class="usage-col" style="width: 150px;">
<col class="usage-col" style="width: 150px;">
<col class="usage-col" style="width: 150px;">
<col class="net-disk-col" style="width: 80px;">
<col class="net-disk-col" style="width: 80px;">
<col class="net-disk-col" style="width: 80px;">
<col class="net-disk-col" style="width: 80px;">
</colgroup>
<thead class="sticky top-0 z-10 bg-gray-100 dark:bg-gray-800">
<!-- Apply sticky header styles -->
<tr class="border-b border-gray-300 dark:border-gray-700">
<!-- Header cells with Tailwind classes -->
<th class="sortable text-left p-1 px-2 font-semibold text-gray-700 dark:text-gray-300 cursor-pointer select-none whitespace-nowrap" data-sort="name">Name</th>
<th class="sortable text-left p-1 px-2 font-semibold text-gray-700 dark:text-gray-300 cursor-pointer select-none whitespace-nowrap" data-sort="type">Type</th>
<th class="sortable text-left p-1 px-2 font-semibold text-gray-700 dark:text-gray-300 cursor-pointer select-none whitespace-nowrap" data-sort="id">ID</th>
<th class="sortable text-left p-1 px-2 font-semibold text-gray-700 dark:text-gray-300 cursor-pointer select-none whitespace-nowrap" data-sort="uptime">Uptime</th>
<th class="sortable text-center p-1 px-2 font-semibold text-gray-700 dark:text-gray-300 cursor-pointer select-none whitespace-nowrap" data-sort="cpu">CPU</th>
<th class="sortable text-center p-1 px-2 font-semibold text-gray-700 dark:text-gray-300 cursor-pointer select-none whitespace-nowrap" data-sort="memory">Mem</th>
<th class="sortable text-center p-1 px-2 font-semibold text-gray-700 dark:text-gray-300 cursor-pointer select-none whitespace-nowrap" data-sort="disk">Disk</th>
<th class="sortable text-right p-1 px-2 font-semibold text-gray-700 dark:text-gray-300 cursor-pointer select-none whitespace-nowrap" data-sort="diskread">Disk Read</th>
<th class="sortable text-right p-1 px-2 font-semibold text-gray-700 dark:text-gray-300 cursor-pointer select-none whitespace-nowrap" data-sort="diskwrite">Disk Write</th>
<th class="sortable text-right p-1 px-2 font-semibold text-gray-700 dark:text-gray-300 cursor-pointer select-none whitespace-nowrap" data-sort="netin">Net In</th>
<th class="sortable text-right p-1 px-2 font-semibold text-gray-700 dark:text-gray-300 cursor-pointer select-none whitespace-nowrap" data-sort="netout">Net Out</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-200 dark:divide-gray-700">
<tr>
<td colspan="11" class="p-4 text-center text-gray-500">Loading data...</td>
</tr>
<!-- Rows added by JS, JS needs to add Tailwind classes for hover, etc. -->
<!-- Node header row added by JS needs Tailwind classes: bg-gray-100 dark:bg-gray-700 font-bold px-2 py-1 text-xs -->
<!-- Type icons need Tailwind classes added by JS -->
<!-- Progress bars need Tailwind classes added by JS -->
<!-- Usage text needs Tailwind classes added by JS -->
</tbody>
</table>
</div>
<span id="dashboard-status-text" class="text-xs text-gray-500 dark:text-gray-400">Loading dashboard data...</span>
</div>
<div id="storage" class="tab-content hidden bg-white dark:bg-gray-800 rounded-b rounded-tr shadow p-3 mb-2">
<h2 class="text-lg font-medium mb-2 text-gray-800 dark:text-gray-200">Storage Information</h2>
<!-- Add table container wrapper similar to main dashboard -->
<div class="table-container max-h-[80vh] overflow-y-auto mb-2 border border-gray-200 dark:border-gray-700 rounded overflow-hidden">
<div id="storage-info-content">
<p class="text-gray-500 dark:text-gray-400 p-4 text-center">Loading storage data...</p> <!-- Keep placeholder -->
<!-- Storage table will be populated here by JS -->
</div>
</div>
</div>
</div>
<!-- Custom Tooltip Element - Styled with Tailwind -->
<div id="custom-tooltip" class="custom-tooltip absolute hidden z-50 px-2 py-1 text-xs text-white bg-gray-900/90 rounded shadow-lg pointer-events-none transition-opacity duration-100"></div>
<script src="/socket.io/socket.io.js"></script>
<script src="/app.js" defer></script>
<footer class="text-center text-xs text-gray-500 py-4">
Pulse for Proxmox | Version: <a href="https://github.com/rcourtman/Pulse/releases" target="_blank" rel="noopener noreferrer" id="app-version" class="text-blue-600 dark:text-blue-400 hover:underline">loading...</a>
</footer>
</body>
</html>