mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-09-25 04:33:03 +00:00
485 lines
44 KiB
HTML
485 lines
44 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en" class="scrollbar">
|
|
<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>
|
|
@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;
|
|
}
|
|
.pulse-logo-outer { fill: #2563eb; }
|
|
.pulse-logo-inner { fill: #ffffff; }
|
|
.dark .pulse-logo-outer { fill: #3b82f6; }
|
|
.dark .pulse-logo-inner { fill: #dbeafe; }
|
|
|
|
/* Custom Slider Thumb Styles */
|
|
input[type=range].custom-slider::-webkit-slider-thumb {
|
|
-webkit-appearance: none; /* Override default look */
|
|
appearance: none;
|
|
margin-top: 0; /* Track height and thumb height match */
|
|
background-color: #2563eb; /* Blue-600 */
|
|
height: 12px; /* Larger thumb */
|
|
width: 12px; /* Larger thumb */
|
|
border-radius: 50%;
|
|
cursor: pointer;
|
|
}
|
|
.dark input[type=range].custom-slider::-webkit-slider-thumb {
|
|
background-color: #3b82f6; /* Blue-500 */
|
|
}
|
|
input[type=range].custom-slider::-moz-range-thumb {
|
|
background-color: #2563eb; /* Blue-600 */
|
|
height: 12px; /* Larger thumb */
|
|
width: 12px; /* Larger thumb */
|
|
border-radius: 50%;
|
|
border: none; /* Reset border */
|
|
cursor: pointer;
|
|
}
|
|
.dark input[type=range].custom-slider::-moz-range-thumb {
|
|
background-color: #3b82f6; /* Blue-500 */
|
|
}
|
|
|
|
/* Style for header when threshold is active */
|
|
.threshold-active-header {
|
|
@apply font-semibold;
|
|
}
|
|
|
|
/* Dot animation for logging indicator */
|
|
@keyframes dot-pulse {
|
|
0%, 100% { opacity: 0.2; }
|
|
50% { opacity: 1; }
|
|
}
|
|
.dot-animate {
|
|
animation: dot-pulse 1.4s infinite ease-in-out;
|
|
display: inline-block; /* Needed for animation */
|
|
width: 0.25em; /* Adjust spacing slightly */
|
|
}
|
|
.dot-animate:nth-child(1) { animation-delay: 0s; }
|
|
.dot-animate:nth-child(2) { animation-delay: 0.2s; }
|
|
.dot-animate:nth-child(3) { animation-delay: 0.4s; }
|
|
|
|
/* Subtle pattern for empty threshold cells */
|
|
.threshold-pattern-bg {
|
|
background-image: linear-gradient(45deg, rgba(0, 0, 0, 0.05) 25%, transparent 25%, transparent 50%, rgba(0, 0, 0, 0.05) 50%, rgba(0, 0, 0, 0.05) 75%, transparent 75%, transparent);
|
|
background-size: 10px 10px;
|
|
}
|
|
.dark .threshold-pattern-bg {
|
|
background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.04) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.04) 50%, rgba(255, 255, 255, 0.04) 75%, transparent 75%, transparent);
|
|
}
|
|
</style>
|
|
</head>
|
|
<body class="bg-gray-100 dark:bg-gray-900 text-gray-800 dark:text-gray-200 p-2 font-sans">
|
|
<div id="loading-overlay" class="fixed inset-0 z-50 flex items-center justify-center bg-gray-100 dark:bg-gray-900 bg-opacity-80 dark:bg-opacity-80">
|
|
<div class="text-center">
|
|
<svg width="40" height="40" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg" class="pulse-logo mx-auto mb-2">
|
|
<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>
|
|
<p class="text-lg font-medium text-gray-700 dark:text-gray-300">Loading data...</p>
|
|
</div>
|
|
</div>
|
|
<div class="container w-[95%] max-w-screen-xl mx-auto">
|
|
<div class="header flex flex-row justify-between items-center mb-2">
|
|
<div class="hidden md:block md:flex-1"></div>
|
|
<div class="flex items-center gap-1 flex-none">
|
|
<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>
|
|
<div class="header-controls flex justify-end items-center gap-4 md:flex-1">
|
|
<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">
|
|
<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>
|
|
<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>
|
|
</div>
|
|
</div>
|
|
<div class="tabs flex mb-2 border-b border-gray-300 dark:border-gray-700 overflow-x-auto overflow-y-hidden whitespace-nowrap scrollbar">
|
|
<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 flex items-center gap-1.5" data-tab="main">
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="inline-block align-middle"><path d="m3 9 9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"></path><polyline points="9 22 9 12 15 12 15 22"></polyline></svg>
|
|
<span class="hidden md:inline">Main</span>
|
|
</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 flex items-center gap-1.5" data-tab="storage">
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="inline-block align-middle"><ellipse cx="12" cy="5" rx="9" ry="3"></ellipse><path d="M21 12c0 1.66-4 3-9 3s-9-1.34-9-3"></path><path d="M3 5v14c0 1.66 4 3 9 3s9-1.34 9-3V5"></path></svg>
|
|
<span class="hidden md:inline">Storage</span>
|
|
</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 flex items-center gap-1.5" data-tab="backups">
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="inline-block align-middle"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"></path><path d="m9 12 2 2 4-4"></path></svg>
|
|
<span class="hidden md:inline">Backups</span>
|
|
</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 flex items-center gap-1.5" data-tab="pbs">
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="inline-block align-middle"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"></path></svg>
|
|
<span class="hidden md:inline">PBS</span>
|
|
</div>
|
|
</div>
|
|
<div id="main" class="tab-content block bg-white dark:bg-gray-800 rounded-b rounded-tr shadow mb-2">
|
|
<!-- Nested Tab Content START -->
|
|
<div id="nested-tab-dashboard" class="nested-tab-content p-3">
|
|
|
|
<div id="node-summary-cards-container" class="mb-3">
|
|
<!-- Node summary cards will be injected here -->
|
|
<p class="text-sm text-gray-500 dark:text-gray-400">Loading node summary...</p>
|
|
</div>
|
|
|
|
<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 Name, ID, Node (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" />
|
|
<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"><circle cx="12" cy="12" r="10"></circle><line x1="15" y1="9" x2="9" y2="15"></line><line x1="9" y1="9" x2="15" y2="15"></line></svg>
|
|
</button>
|
|
<button id="toggle-thresholds-button" title="Toggle Threshold Filters" class="relative 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">
|
|
<span class="relative inline-block">
|
|
<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">
|
|
<line x1="4" y1="21" x2="4" y2="14"></line>
|
|
<line x1="4" y1="10" x2="4" y2="3"></line>
|
|
<line x1="12" y1="21" x2="12" y2="12"></line>
|
|
<line x1="12" y1="8" x2="12" y2="3"></line>
|
|
<line x1="20" y1="21" x2="20" y2="16"></line>
|
|
<line x1="20" y1="12" x2="20" y2="3"></line>
|
|
<line x1="1" y1="14" x2="7" y2="14"></line>
|
|
<line x1="9" y1="8" x2="15" y2="8"></line>
|
|
<line x1="17" y1="16" x2="23" y2="16"></line>
|
|
</svg>
|
|
<span id="threshold-count-badge" class="absolute -top-1.5 -right-1.5 inline-flex items-center justify-center px-1 py-0.5 text-[9px] font-bold leading-none text-white transform translate-x-1/2 -translate-y-1/2 bg-blue-600 rounded-full hidden">0</span>
|
|
</span>
|
|
</button>
|
|
</div>
|
|
<div class="filter-toggles-wrapper flex items-center gap-4 flex-wrap">
|
|
<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="flex items-center justify-center gap-1 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">
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="inline md:hidden"><path d="M4 4h4v4H4zM10 4h4v4h-4zM16 4h4v4h-4zM4 10h4v4H4zM10 10h4v4h-4zM16 10h4v4h-4zM4 16h4v4H4zM10 16h4v4h-4zM16 16h4v4h-4z"/></svg>
|
|
<span class="hidden md:inline">Grouped</span>
|
|
</label>
|
|
<input type="radio" id="group-list" name="group-filter" value="list" class="hidden peer/list">
|
|
<label for="group-list" class="flex items-center justify-center gap-1 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">
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="inline md:hidden"><line x1="8" x2="21" y1="6" y2="6"/><line x1="8" x2="21" y1="12" y2="12"/><line x1="8" x2="21" y1="18" y2="18"/><line x1="3" x2="3.01" y1="6" y2="6"/><line x1="3" x2="3.01" y1="12" y2="12"/><line x1="3" x2="3.01" y1="18" y2="18"/></svg>
|
|
<span class="hidden md:inline">List</span>
|
|
</label>
|
|
</div>
|
|
</div>
|
|
<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-blue-100 dark:peer-checked/all:bg-blue-800/50 peer-checked/all:text-blue-700 dark:peer-checked/all:text-blue-300 hover:bg-gray-50 dark:hover:bg-gray-700 select-none">
|
|
<span class="inline">All</span>
|
|
</label>
|
|
<input type="radio" id="filter-vm" name="type-filter" value="vm" class="hidden peer/vm">
|
|
<label for="filter-vm" class="flex items-center justify-center gap-1 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-blue-100 dark:peer-checked/vm:bg-blue-800/50 peer-checked/vm:text-blue-700 dark:peer-checked/vm:text-blue-300 hover:bg-gray-50 dark:hover:bg-gray-700 select-none">
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="inline md:hidden"><rect width="20" height="8" x="2" y="2" rx="2" ry="2"/><rect width="20" height="8" x="2" y="14" rx="2" ry="2"/><line x1="6" x2="6.01" y1="6" y2="6"/><line x1="6" x2="6.01" y1="18" y2="18"/></svg>
|
|
<span class="hidden md:inline">VM</span>
|
|
</label>
|
|
<input type="radio" id="filter-lxc" name="type-filter" value="lxc" class="hidden peer/lxc">
|
|
<label for="filter-lxc" class="flex items-center justify-center gap-1 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">
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="inline md:hidden"><path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"/><polyline points="3.27 6.96 12 12.01 20.73 6.96"/><line x1="12" x2="12" y1="22.08" y2="12"/></svg>
|
|
<span class="hidden md:inline">LXC</span>
|
|
</label>
|
|
</div>
|
|
</div>
|
|
<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">
|
|
<span class="inline">All</span>
|
|
</label>
|
|
<input type="radio" id="filter-status-running" name="status-filter" value="running" class="hidden peer/status-running">
|
|
<label for="filter-status-running" class="flex items-center justify-center gap-1 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-green-100 dark:peer-checked/status-running:bg-green-800/50 peer-checked/status-running:text-green-700 dark:peer-checked/status-running:text-green-300 hover:bg-gray-50 dark:hover:bg-gray-700 select-none">
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="inline md:hidden"><polygon points="5 3 19 12 5 21 5 3"/></svg>
|
|
<span class="hidden md:inline">Running</span>
|
|
</label>
|
|
<input type="radio" id="filter-status-stopped" name="status-filter" value="stopped" class="hidden peer/status-stopped">
|
|
<label for="filter-status-stopped" class="flex items-center justify-center gap-1 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-gray-100 dark:peer-checked/status-stopped:bg-gray-600/50 peer-checked/status-stopped:text-gray-700 dark:peer-checked/status-stopped:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-700 select-none">
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="inline md:hidden"><rect width="18" height="18" x="3" y="3" rx="2" ry="2"/></svg>
|
|
<span class="hidden md:inline">Stopped</span>
|
|
</label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="table-container max-h-[70vh] overflow-y-auto overflow-x-auto mb-2 border border-gray-200 dark:border-gray-700 rounded overflow-hidden scrollbar">
|
|
<table id="main-table" class="w-full table-fixed text-sm">
|
|
<colgroup>
|
|
<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">
|
|
<tr class="text-xs font-medium tracking-wider text-left text-gray-600 uppercase bg-gray-100 dark:bg-gray-700 dark:text-gray-300 border-b border-gray-300 dark:border-gray-600">
|
|
<th class="sortable p-1 px-2" data-sort="name" style="width: var(--name-col-width, 150px);">Name</th>
|
|
<th class="p-1 px-2" style="width: 50px;">Type</th>
|
|
<th class="sortable p-1 px-2" data-sort="id" style="width: 50px;">ID</th>
|
|
<th class="sortable p-1 px-2" data-sort="uptime" style="width: var(--uptime-col-width, 80px);">Uptime</th>
|
|
<th class="sortable p-1 px-2" data-sort="cpu" style="width: 110px;">CPU</th>
|
|
<th class="sortable p-1 px-2" data-sort="memory" style="width: 110px;">Memory</th>
|
|
<th class="sortable p-1 px-2" data-sort="disk" style="width: 110px;">Disk</th>
|
|
<th class="sortable p-1 px-2" data-sort="diskread" style="width: 70px;">Read</th>
|
|
<th class="sortable p-1 px-2" data-sort="diskwrite" style="width: 70px;">Write</th>
|
|
<th class="sortable p-1 px-2" data-sort="netin" style="width: 70px;">Net In</th>
|
|
<th class="sortable p-1 px-2" data-sort="netout" style="width: 70px;">Net Out</th>
|
|
</tr>
|
|
<tr id="threshold-slider-row" class="threshold-sliders text-xs border-t border-gray-200 dark:border-gray-700 hidden bg-white dark:bg-gray-800">
|
|
<th class="py-1 px-2 align-middle"></th>
|
|
<th class="py-1 px-1 align-middle"></th>
|
|
<th class="py-1 px-2 align-middle"></th>
|
|
<th class="py-1 px-2 align-middle">
|
|
<!-- Empty cell for Uptime column -->
|
|
</th>
|
|
<th class="py-1 px-2 align-middle">
|
|
<div class="flex flex-row items-center gap-1">
|
|
<input type="range" id="threshold-slider-cpu" min="0" max="100" step="1" value="0" class="custom-slider w-full h-3 rounded-full bg-gray-300 dark:bg-gray-600 appearance-none cursor-pointer flex-grow">
|
|
</div>
|
|
</th>
|
|
<th class="py-1 px-2 align-middle">
|
|
<div class="flex flex-row items-center gap-1">
|
|
<input type="range" id="threshold-slider-memory" min="0" max="100" step="1" value="0" class="custom-slider w-full h-3 rounded-full bg-gray-300 dark:bg-gray-600 appearance-none cursor-pointer flex-grow">
|
|
</div>
|
|
</th>
|
|
<th class="py-1 px-2 align-middle">
|
|
<div class="flex flex-row items-center gap-1">
|
|
<input type="range" id="threshold-slider-disk" min="0" max="100" step="1" value="0" class="custom-slider w-full h-3 rounded-full bg-gray-300 dark:bg-gray-600 appearance-none cursor-pointer flex-grow">
|
|
</div>
|
|
</th>
|
|
<th class="py-1 px-2 align-middle">
|
|
<select id="threshold-select-diskread" class="threshold-select px-1 py-0 h-5 border border-gray-300 dark:border-gray-600 rounded bg-white dark:bg-gray-700 text-[10px] w-full focus:outline-none focus:ring-1 focus:ring-blue-500">
|
|
<option value="0" selected>Any</option>
|
|
<option value="1048576">> 1 MB/s</option>
|
|
<option value="10485760">> 10 MB/s</option>
|
|
<option value="52428800">> 50 MB/s</option>
|
|
<option value="104857600">> 100 MB/s</option>
|
|
</select>
|
|
</th>
|
|
<th class="py-1 px-2 align-middle">
|
|
<select id="threshold-select-diskwrite" class="threshold-select px-1 py-0 h-5 border border-gray-300 dark:border-gray-600 rounded bg-white dark:bg-gray-700 text-[10px] w-full focus:outline-none focus:ring-1 focus:ring-blue-500">
|
|
<option value="0" selected>Any</option>
|
|
<option value="1048576">> 1 MB/s</option>
|
|
<option value="10485760">> 10 MB/s</option>
|
|
<option value="52428800">> 50 MB/s</option>
|
|
<option value="104857600">> 100 MB/s</option>
|
|
</select>
|
|
</th>
|
|
<th class="py-1 px-2 align-middle">
|
|
<select id="threshold-select-netin" class="threshold-select px-1 py-0 h-5 border border-gray-300 dark:border-gray-600 rounded bg-white dark:bg-gray-700 text-[10px] w-full focus:outline-none focus:ring-1 focus:ring-blue-500">
|
|
<option value="0" selected>Any</option>
|
|
<option value="1048576">> 1 MB/s</option>
|
|
<option value="10485760">> 10 MB/s</option>
|
|
<option value="52428800">> 50 MB/s</option>
|
|
<option value="104857600">> 100 MB/s</option>
|
|
</select>
|
|
</th>
|
|
<th class="py-1 px-2 align-middle">
|
|
<select id="threshold-select-netout" class="threshold-select px-1 py-0 h-5 border border-gray-300 dark:border-gray-600 rounded bg-white dark:bg-gray-700 text-[10px] w-full focus:outline-none focus:ring-1 focus:ring-blue-500">
|
|
<option value="0" selected>Any</option>
|
|
<option value="1048576">> 1 MB/s</option>
|
|
<option value="10485760">> 10 MB/s</option>
|
|
<option value="52428800">> 50 MB/s</option>
|
|
<option value="104857600">> 100 MB/s</option>
|
|
</select>
|
|
</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 dark:text-gray-400">Loading data...</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<span id="dashboard-status-text" class="text-xs text-gray-500 dark:text-gray-400">Loading dashboard data...</span>
|
|
|
|
<!-- Separator -->
|
|
<hr class="border-gray-200 dark:border-gray-700 my-2">
|
|
|
|
<div id="log-controls-container" class="flex justify-start items-center gap-2 mt-2">
|
|
<button
|
|
id="start-log-button"
|
|
class="px-2 py-1 text-xs rounded bg-gray-200 hover:bg-gray-300 dark:bg-gray-600 dark:hover:bg-gray-500 text-gray-700 dark:text-gray-200 flex items-center h-7"
|
|
title="Start New Threshold Log"
|
|
>
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="mr-1"><path d="M14.5 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7.5L14.5 2z"></path><polyline points="14 2 14 8 20 8"></polyline><line x1="12" y1="18" x2="12" y2="12"></line><line x1="9" y1="15" x2="15" y2="15"></line></svg>
|
|
<span>Start Log</span>
|
|
</button>
|
|
<button id="clear-all-logs-button" class="hidden px-2 py-1 text-xs rounded bg-red-100 hover:bg-red-200 dark:bg-red-800/50 dark:hover:bg-red-700/50 text-red-700 dark:text-red-300 flex items-center h-7" title="Clear All Stopped Log Panels">
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="mr-1"><polyline points="3 6 5 6 21 6"></polyline><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path><line x1="10" y1="11" x2="10" y2="17"></line><line x1="14" y1="11" x2="14" y2="17"></line></svg>
|
|
<span>Clear Logs</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="log-session-area" class="hidden mt-4 p-3 bg-white dark:bg-gray-800 rounded shadow border border-gray-200 dark:border-gray-700">
|
|
<div class="nested-tabs flex border-b border-gray-200 dark:border-gray-700 mb-2">
|
|
</div>
|
|
<div id="log-content-area" class="space-y-2">
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div id="storage" class="tab-content hidden bg-white dark:bg-gray-800 rounded-b rounded-tr shadow p-3 mb-2">
|
|
<div class="table-container max-h-[80vh] overflow-y-auto overflow-x-auto mb-2 border border-gray-200 dark:border-gray-700 rounded overflow-hidden scrollbar">
|
|
<div id="storage-info-content">
|
|
<p class="text-gray-500 dark:text-gray-400 p-4 text-center">Loading storage data...</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div id="pbs" class="tab-content hidden bg-white dark:bg-gray-800 p-4 border border-t-0 border-gray-300 dark:border-gray-700 rounded-b-lg shadow-sm">
|
|
<div id="pbs-instances-container" class="space-y-6">
|
|
<p id="pbs-loading-message" class="text-gray-500 dark:text-gray-400">Loading PBS data...</p>
|
|
</div>
|
|
</div>
|
|
<div id="backups" class="tab-content hidden bg-white dark:bg-gray-800 rounded-b rounded-tr shadow p-3 mb-2">
|
|
<p id="backups-loading-message" class="text-gray-500 dark:text-gray-400 p-4 text-center mb-2">Loading backup status overview...</p>
|
|
<div class="backups-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="backups-search" placeholder="Search Name, ID, Node (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" />
|
|
<button id="reset-backups-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"><circle cx="12" cy="12" r="10"></circle><line x1="15" y1="9" x2="9" y2="15"></line><line x1="9" y1="9" x2="15" y2="15"></line></svg>
|
|
</button>
|
|
</div>
|
|
<div class="filter-toggles-wrapper flex items-center gap-4 flex-wrap">
|
|
<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="backups-filter-type-all" name="backups-type-filter" value="all" class="hidden peer/btype-all" checked>
|
|
<label for="backups-filter-type-all" class="px-2 py-0.5 text-xs cursor-pointer bg-white dark:bg-gray-800 peer-checked/btype-all:bg-blue-100 dark:peer-checked/btype-all:bg-blue-800/50 peer-checked/btype-all:text-blue-700 dark:peer-checked/btype-all:text-blue-300 hover:bg-gray-50 dark:hover:bg-gray-700 select-none">
|
|
<span class="hidden md:inline">All</span>
|
|
<span class="inline md:hidden">All</span>
|
|
</label>
|
|
<input type="radio" id="backups-filter-type-vm" name="backups-type-filter" value="vm" class="hidden peer/btype-vm">
|
|
<label for="backups-filter-type-vm" class="flex items-center justify-center gap-1 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/btype-vm:bg-blue-100 dark:peer-checked/btype-vm:bg-blue-800/50 peer-checked/btype-vm:text-blue-700 dark:peer-checked/btype-vm:text-blue-300 hover:bg-gray-50 dark:hover:bg-gray-700 select-none">
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="inline md:hidden"><rect width="20" height="8" x="2" y="2" rx="2" ry="2"/><rect width="20" height="8" x="2" y="14" rx="2" ry="2"/><line x1="6" x2="6.01" y1="6" y2="6"/><line x1="6" x2="6.01" y1="18" y2="18"/></svg>
|
|
<span class="hidden md:inline">VM</span>
|
|
</label>
|
|
<input type="radio" id="backups-filter-type-ct" name="backups-type-filter" value="lxc" class="hidden peer/btype-ct">
|
|
<label for="backups-filter-type-ct" class="flex items-center justify-center gap-1 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/btype-ct:bg-green-100 dark:peer-checked/btype-ct:bg-green-800/50 peer-checked/btype-ct:text-green-700 dark:peer-checked/btype-ct:text-green-300 hover:bg-gray-50 dark:hover:bg-gray-700 select-none">
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="inline md:hidden"><path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"/><polyline points="3.27 6.96 12 12.01 20.73 6.96"/><line x1="12" x2="12" y1="22.08" y2="12"/></svg>
|
|
<span class="hidden md:inline">LXC</span>
|
|
</label>
|
|
</div>
|
|
</div>
|
|
<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="backups-filter-status-all" name="backups-status-filter" value="all" class="hidden peer/bhealth-all" checked>
|
|
<label for="backups-filter-status-all" class="px-2 py-0.5 text-xs cursor-pointer bg-white dark:bg-gray-800 peer-checked/bhealth-all:bg-blue-100 dark:peer-checked/bhealth-all:bg-blue-800/50 peer-checked/bhealth-all:text-blue-700 dark:peer-checked/bhealth-all:text-blue-300 hover:bg-gray-50 dark:hover:bg-gray-700 select-none">
|
|
<span class="hidden md:inline">All</span>
|
|
<span class="inline md:hidden">All</span>
|
|
</label>
|
|
<input type="radio" id="backups-filter-status-ok" name="backups-status-filter" value="ok" class="hidden peer/bhealth-ok">
|
|
<label for="backups-filter-status-ok" class="flex items-center justify-center gap-1 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/bhealth-ok:bg-green-100 dark:peer-checked/bhealth-ok:bg-green-800/50 peer-checked/bhealth-ok:text-green-700 dark:peer-checked/bhealth-ok:text-green-300 hover:bg-gray-50 dark:hover:bg-gray-700 select-none">
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="inline md:hidden text-green-600 dark:text-green-400"><path d="M20 6 9 17l-5-5"/></svg>
|
|
<span class="hidden md:inline">OK</span>
|
|
</label>
|
|
<input type="radio" id="backups-filter-status-stale" name="backups-status-filter" value="stale" class="hidden peer/bhealth-stale">
|
|
<label for="backups-filter-status-stale" class="flex items-center justify-center gap-1 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/bhealth-stale:bg-yellow-100 dark:peer-checked/bhealth-stale:bg-yellow-800/50 peer-checked/bhealth-stale:text-yellow-700 dark:peer-checked/bhealth-stale:text-yellow-300 hover:bg-gray-50 dark:hover:bg-gray-700 select-none">
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="inline md:hidden text-yellow-600 dark:text-yellow-400"><circle cx="12" cy="12" r="10"/><path d="M12 6v6l4 2"/></svg>
|
|
<span class="hidden md:inline">Stale</span>
|
|
</label>
|
|
<input type="radio" id="backups-filter-status-old" name="backups-status-filter" value="old" class="hidden peer/bhealth-old">
|
|
<label for="backups-filter-status-old" class="flex items-center justify-center gap-1 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/bhealth-old:bg-orange-100 dark:peer-checked/bhealth-old:bg-orange-800/50 peer-checked/bhealth-old:text-orange-700 dark:peer-checked/bhealth-old:text-orange-300 hover:bg-gray-50 dark:hover:bg-gray-700 select-none">
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="inline md:hidden text-orange-600 dark:text-orange-400"><rect width="20" height="5" x="2" y="3" rx="1"/><path d="M4 8v11a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8"/><path d="M10 12h4"/></svg>
|
|
<span class="hidden md:inline">Old</span>
|
|
</label>
|
|
<input type="radio" id="backups-filter-status-failed" name="backups-status-filter" value="failed" class="hidden peer/bhealth-failed">
|
|
<label for="backups-filter-status-failed" class="flex items-center justify-center gap-1 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/bhealth-failed:bg-red-100 dark:peer-checked/bhealth-failed:bg-red-800/50 peer-checked/bhealth-failed:text-red-700 dark:peer-checked/bhealth-failed:text-red-300 hover:bg-gray-50 dark:hover:bg-gray-700 select-none">
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="inline md:hidden text-red-600 dark:text-red-400"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg>
|
|
<span class="hidden md:inline">Failed</span>
|
|
</label>
|
|
<input type="radio" id="backups-filter-status-none" name="backups-status-filter" value="none" class="hidden peer/bhealth-none">
|
|
<label for="backups-filter-status-none" class="flex items-center justify-center gap-1 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/bhealth-none:bg-gray-100 dark:peer-checked/bhealth-none:bg-gray-600/50 peer-checked/bhealth-none:text-gray-700 dark:peer-checked/bhealth-none:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-700 select-none">
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="inline md:hidden text-gray-400 dark:text-gray-500"><circle cx="12" cy="12" r="10"/><path d="M8 12h8"/></svg>
|
|
<span class="hidden md:inline">None</span>
|
|
</label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div id="backups-table-container" class="hidden space-y-2 mb-2">
|
|
<div class="overflow-x-auto border border-gray-200 dark:border-gray-700 rounded scrollbar">
|
|
<table id="backups-overview-table" class="min-w-full divide-y divide-gray-200 dark:divide-gray-700 text-sm">
|
|
<thead class="sticky top-0 z-10 bg-gray-100 dark:bg-gray-800">
|
|
<tr class="text-xs font-medium tracking-wider text-left text-gray-600 uppercase bg-gray-100 dark:bg-gray-700 dark:text-gray-300 border-b border-gray-300 dark:border-gray-600">
|
|
<th scope="col" class="sortable p-1 px-2 cursor-pointer select-none whitespace-nowrap" data-sort="guestName">Guest Name</th>
|
|
<th scope="col" class="sortable p-1 px-2 cursor-pointer select-none whitespace-nowrap" data-sort="guestId">ID</th>
|
|
<th scope="col" class="sortable p-1 px-2 cursor-pointer select-none whitespace-nowrap" data-sort="guestType">Type</th>
|
|
<th scope="col" class="sortable p-1 px-2 cursor-pointer select-none whitespace-nowrap" data-sort="node">Node</th>
|
|
<th scope="col" class="sortable p-1 px-2 cursor-pointer select-none whitespace-nowrap" data-sort="latestBackupTime">Latest Backup</th>
|
|
<th scope="col" class="sortable p-1 px-2 cursor-pointer select-none whitespace-nowrap" data-sort="pbsInstanceName">PBS Instance</th>
|
|
<th scope="col" class="sortable p-1 px-2 cursor-pointer select-none whitespace-nowrap" data-sort="datastoreName">Datastore</th>
|
|
<th scope="col" class="sortable p-1 px-2 cursor-pointer select-none whitespace-nowrap" data-sort="totalBackups"># Backups</th>
|
|
<th scope="col" class="p-1 px-2 text-center select-none whitespace-nowrap">7-Day History</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="backups-overview-tbody" class="divide-y divide-gray-200 dark:divide-gray-700"></tbody>
|
|
</table>
|
|
</div>
|
|
<p id="backups-no-data-message" class="text-gray-500 dark:text-gray-400 p-4 text-center hidden">No backup data found matching filters.</p>
|
|
</div>
|
|
<span id="backups-status-text" class="text-xs text-gray-500 dark:text-gray-400">Loading backup status...</span>
|
|
</div>
|
|
</div>
|
|
<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>
|
|
<div id="slider-value-tooltip" class="absolute hidden z-[60] px-1.5 py-0.5 text-xs text-white bg-blue-600/90 dark:bg-blue-500/90 rounded shadow-lg pointer-events-none"></div>
|
|
<div id="snapshot-modal" class="fixed inset-0 z-50 hidden items-center justify-center bg-black bg-opacity-50"><div class="modal-content bg-white dark:bg-gray-800 rounded-lg shadow-xl p-6 w-full max-w-3xl max-h-[80vh] flex flex-col"><div class="modal-header flex justify-between items-center border-b border-gray-300 dark:border-gray-700 pb-3 mb-4"><h3 id="snapshot-modal-title" class="text-lg font-semibold text-gray-900 dark:text-gray-100">Snapshot Details</h3>
|
|
<button id="snapshot-modal-close" class="text-gray-400 hover:text-gray-600 dark:hover:text-gray-300"><svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" /></svg></button>
|
|
</div>
|
|
<div id="snapshot-modal-body" class="overflow-y-auto flex-grow scrollbar"><p>Loading snapshots...</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script src="/socket.io/socket.io.js"></script>
|
|
<script>window.PulseApp = {};</script>
|
|
<!-- <script src="/app.js" defer></script> -->
|
|
<script src="/js/config.js" defer></script>
|
|
<script src="/js/state.js" defer></script>
|
|
<script src="/js/utils.js" defer></script>
|
|
<script src="/js/theme.js" defer></script>
|
|
<script src="/js/tooltips.js" defer></script>
|
|
<script src="/js/socketHandler.js" defer></script>
|
|
<script src="/js/tabs.js" defer></script>
|
|
<script src="/js/ui/thresholds.js" defer></script>
|
|
<script src="/js/ui/common.js" defer></script>
|
|
<script src="/js/ui/nodes.js" defer></script>
|
|
<script src="/js/ui/dashboard.js" defer></script>
|
|
<script src="/js/ui/storage.js" defer></script>
|
|
<script src="/js/ui/pbs.js" defer></script>
|
|
<script src="/js/ui/backups.js" defer></script>
|
|
<script src="/js/thresholds/logging.js" defer></script>
|
|
<script src="/js/hotReload.js" defer></script>
|
|
<script src="/js/main.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> |