Mkae columns sortabel agian, check hbc version, provide modile html pages

This commit is contained in:
Andreas Wrede
2026-04-12 12:53:00 -04:00
parent 24b0e362fb
commit 19f7c8312e
12 changed files with 282 additions and 51 deletions
+20 -35
View File
@@ -24,55 +24,40 @@
}
.summary-cards {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 20px;
margin-bottom: 30px;
display: flex;
flex-wrap: wrap;
gap: 10px;
margin-bottom: 16px;
}
.summary-card {
background: white;
border-radius: 8px;
padding: 20px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
text-align: center;
border-radius: 6px;
padding: 6px 14px;
box-shadow: 0 1px 4px rgba(0,0,0,0.1);
display: flex;
align-items: center;
gap: 8px;
border-left: 4px solid #ddd;
}
.summary-card.critical {
border-left: 5px solid #f44336;
}
.summary-card.warning {
border-left: 5px solid #ff9800;
}
.summary-card.ok {
border-left: 5px solid #4caf50;
}
.summary-card.critical { border-left-color: #f44336; }
.summary-card.warning { border-left-color: #ff9800; }
.summary-card.ok { border-left-color: #4caf50; }
.summary-number {
font-size: 3em;
font-size: 1.4em;
font-weight: bold;
margin: 10px 0;
line-height: 1;
}
.summary-number.critical {
color: #f44336;
}
.summary-number.warning {
color: #ff9800;
}
.summary-number.ok {
color: #4caf50;
}
.summary-number.critical { color: #f44336; }
.summary-number.warning { color: #ff9800; }
.summary-number.ok { color: #4caf50; }
.summary-label {
color: #666;
text-transform: uppercase;
font-size: 0.9em;
letter-spacing: 1px;
font-size: 0.85em;
}
.filters {
+39 -1
View File
@@ -1,5 +1,6 @@
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="/static/style.css" type="text/css" />
<link rel="icon" href="/static/images/favicon.ico" sizes="32x32" />
<title>{{ title }}</title>
@@ -15,8 +16,10 @@
display: flex;
align-items: center;
justify-content: space-between;
flex-wrap: wrap;
gap: 8px;
}
.nav-links { display: flex; align-items: center; }
.nav-links { display: flex; align-items: center; flex-wrap: wrap; gap: 4px; }
.nav a {
margin-right: 20px;
text-decoration: none;
@@ -57,5 +60,40 @@
font-weight: 700;
flex-shrink: 0;
}
/* ── Mobile nav: hamburger toggle ── */
.nav-hamburger {
display: none;
flex-direction: column;
justify-content: space-between;
width: 26px; height: 20px;
cursor: pointer;
flex-shrink: 0;
background: none;
border: none;
padding: 0;
}
.nav-hamburger span {
display: block;
height: 3px;
background: #555;
border-radius: 2px;
}
@media (max-width: 640px) {
.nav-hamburger { display: flex; }
.nav-links {
display: none;
width: 100%;
flex-direction: column;
align-items: flex-start;
padding-top: 8px;
border-top: 1px solid #eee;
order: 3;
}
.nav-links.nav-open { display: flex; }
.nav-links a { margin-right: 0; padding: 6px 0; font-size: 1em; }
}
</style>
<script src="static/sorttable.js"></script>
</head>
+43 -8
View File
@@ -14,6 +14,26 @@
overflow: hidden;
}
@media (max-width: 640px) {
body {
height: auto;
min-height: 100vh;
overflow: auto;
flex-direction: column;
}
.container {
max-height: none;
overflow: visible;
}
.table-section {
max-height: 55vh;
}
.log-section {
flex: none;
max-height: 40vh;
}
}
.container {
flex: 1;
min-height: 0;
@@ -59,6 +79,9 @@
border-radius: 6px;
padding: 15px;
box-shadow: 0 1px 4px rgba(0,0,0,0.1);
overflow-x: auto;
overflow-y: auto;
max-height: 60vh;
}
.log-section {
@@ -82,6 +105,7 @@
border: 1px solid #e0e0e0;
text-align: left;
padding: 2px 4px;
white-space: nowrap;
}
#ntable tr:nth-child(even) {
@@ -221,15 +245,24 @@
var nTable = document;
var name_idx = {};
var c = 0;
var HBD_VERSION = "{{ hbd_version }}";
function hostNameHtml(data) {
var nameHtml = data.name;
if (!data.hbc_version || data.hbc_version !== HBD_VERSION) {
nameHtml += ' 🥀';
}
return data.dyn ? '<b>' + nameHtml + '</b>' : nameHtml;
}
function setup() {
name_idx = {};
nTable = document.getElementById("ntable");
for (var i = 0, row; (row = nTable.rows[i]); i++) {
if (i == 0) continue;
name = nTable.rows[i].cells[0].innerText;
var cell = nTable.rows[i].cells[0];
var name = cell.dataset.name || cell.innerText.replace(/\s*🥀\s*$/, '').trim();
name_idx[name] = nTable.rows[i];
/* console.log("name_Id[" + name + "]: " + name_idx[name].innerText); */
}
}
@@ -280,11 +313,8 @@
row.appendChild(c_ipv6state);
row.appendChild(c_ipv6latency);
row.appendChild(c_ipv6statets);
if (data.dyn) {
c_name.innerHTML = "<b>" + data.name + "</b>";
} else {
c_name.innerHTML = data.name;
}
c_name.dataset.name = data.name;
c_name.innerHTML = hostNameHtml(data);
// Set alert counts in "x/y" format (unacked/acked)
var warningUnacked = data.alert_warning_unacked || 0;
@@ -346,6 +376,11 @@
setup();
}
// Update name cell (version indicator)
var nameCell = name_idx[data.name].cells[0];
nameCell.dataset.name = data.name;
nameCell.innerHTML = hostNameHtml(data);
// Update warning and critical counts in "x/y" format (unacked/acked)
var warningUnacked = data.alert_warning_unacked || 0;
var warningAcked = data.alert_warning_acked || 0;
@@ -477,7 +512,7 @@
<tbody id="ntablebody">
{% for host in hosts %}
<tr class="{% if host.alert_critical_unacked > 0 or host.alert_critical_acked > 0 %}row-critical{% elif host.alert_warning_unacked > 0 or host.alert_warning_acked > 0 %}row-warning{% endif %}">
<td>{{ host.name }}</td>
<td data-name="{{ host.name }}">{{ host.name }}{% if not host.hbc_version or host.hbc_version != hbd_version %} 🥀{% endif %}</td>
<td style="text-align: center; color: #ff9800; font-weight: bold;">
{%- set warning_unacked = host.alert_warning_unacked -%}
{%- set warning_acked = host.alert_warning_acked -%}
+17 -1
View File
@@ -1,5 +1,8 @@
<div class="nav">
<div class="nav-links">
<button class="nav-hamburger" id="nav-hamburger-btn" aria-label="Menu" aria-expanded="false">
<span></span><span></span><span></span>
</button>
<div class="nav-links" id="nav-links">
<a href="/live"{% if active_page == "live" %} class="active"{% endif %}>Live Dashboard</a>
<a href="/plugins"{% if active_page == "plugins" %} class="active"{% endif %}>Plugin Metrics</a>
<a href="/alerts"{% if active_page == "alerts" %} class="active"{% endif %}>Alerts</a>
@@ -14,6 +17,19 @@
{% else %}
<span class="nav-initials">{{ (current_user.full_name or current_user.username)[:1] | upper }}</span>
{% endif %}
<span class="nav-username">{{ current_user.full_name or current_user.username }}</span>
</a>
{% endif %}
</div>
<script>
(function() {
var btn = document.getElementById('nav-hamburger-btn');
var links = document.getElementById('nav-links');
if (btn && links) {
btn.addEventListener('click', function() {
var open = links.classList.toggle('nav-open');
btn.setAttribute('aria-expanded', open ? 'true' : 'false');
});
}
})();
</script>
+4
View File
@@ -3,6 +3,10 @@
{% include 'head.html' %}
<style>
html, body {
overflow: visible;
}
body {
margin: 20px;
background: #f5f5f5;
+67 -1
View File
@@ -217,6 +217,49 @@
.channel-field-value { color: #333; word-break: break-all; }
/* ---- Hosts table ---- */
/* ---- Mobile: collapsible sidebar ---- */
.sidebar-toggle {
display: none;
width: 100%;
padding: 8px 12px;
background: #e8eaf6;
border: none;
border-radius: 6px;
font-size: 0.9em;
font-weight: 600;
color: #283593;
cursor: pointer;
text-align: left;
margin-bottom: 16px;
}
.sidebar-toggle::after { content: ' ▾'; float: right; }
.sidebar-toggle.open::after { content: ' ▴'; }
@media (max-width: 640px) {
.sidebar-toggle { display: block; }
.settings-layout { flex-direction: column; gap: 0; }
.settings-sidebar {
width: 100%;
position: static;
margin-bottom: 0;
}
.sidebar-nav {
display: none;
background: white;
border-radius: 6px;
box-shadow: 0 1px 4px rgba(0,0,0,.1);
margin-bottom: 16px;
padding: 4px 0;
}
.sidebar-nav.open { display: block; }
.sidebar-nav a { padding: 10px 16px; font-size: 1em; }
.field-row { flex-direction: column; gap: 4px; }
.field-label { width: 100%; font-size: 0.82em; color: #888; }
}
.host-bool { text-align: center; }
.dot-yes { color: #2e7d32; font-size: 1.1em; }
.dot-no { color: #ddd; font-size: 1.1em; }
@@ -233,9 +276,10 @@
<!-- Sidebar navigation -->
<nav class="settings-sidebar">
<button class="sidebar-toggle" id="sidebar-toggle" aria-expanded="false">Sections</button>
<div class="sidebar-nav" id="sidebar-nav">
{% for section in sections %}
<a href="#{{ section.id }}">{{ section.title }}</a>
<a href="#{{ section.id }}" onclick="closeSidebar()">{{ section.title }}</a>
{% endfor %}
</div>
</nav>
@@ -428,6 +472,28 @@
}, { threshold: 0.25 });
sections.forEach(s => observer.observe(s));
// Collapsible sidebar on mobile
var sidebarToggle = document.getElementById('sidebar-toggle');
var sidebarNav = document.getElementById('sidebar-nav');
if (sidebarToggle && sidebarNav) {
sidebarToggle.addEventListener('click', function() {
var open = sidebarNav.classList.toggle('open');
sidebarToggle.classList.toggle('open', open);
sidebarToggle.setAttribute('aria-expanded', open ? 'true' : 'false');
});
}
</script>
<script>
function closeSidebar() {
var sidebarNav = document.getElementById('sidebar-nav');
var sidebarToggle = document.getElementById('sidebar-toggle');
if (sidebarNav) { sidebarNav.classList.remove('open'); }
if (sidebarToggle) {
sidebarToggle.classList.remove('open');
sidebarToggle.setAttribute('aria-expanded', 'false');
}
}
</script>
</body>
</html>