Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions public/css/aurora.css
Original file line number Diff line number Diff line change
Expand Up @@ -438,6 +438,16 @@ input[type=date]:focus, select:focus {
.kpi .val{font-family:var(--font-mono); font-weight:700; font-size:28px; line-height:1; letter-spacing:-.02em}
.kpi .delta{font-size:11.5px; font-weight:600; display:inline-flex; align-items:center; gap:4px}
.kpi .delta.up{color:var(--green)} .kpi .delta.down{color:var(--coral)}
/* Dashboard KPI row: all (5–6) cards in one row down to ~1200px, then they
progressively wrap/stack as the viewport narrows. auto-fit fills the row for
either 5 or 6 cards (the Availability KPI is conditionally hidden) with no
empty trailing slot. ~140px min lets six fit in the content area at 1200px. */
.aurora-kpi-grid{display:grid; grid-template-columns:repeat(auto-fit,minmax(130px,1fr)); gap:14px; margin-bottom:18px}
.aurora-kpi-grid .card.kpi{padding:15px; gap:9px}
.aurora-kpi-grid .kpi .ic{width:30px;height:30px;border-radius:9px}
.aurora-kpi-grid .kpi .ic svg{width:16px;height:16px}
.aurora-kpi-grid .kpi .lab{font-size:11px}
.aurora-kpi-grid .kpi .val{font-size:23px}

.kv{display:flex; flex-direction:column; gap:12px; margin-top:14px}
.kv .row{display:flex; align-items:center; justify-content:space-between; gap:14px}
Expand Down
27 changes: 16 additions & 11 deletions public/js/dashboard.js
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@
let v = Number(bytes);
while (v >= 1024 && i < units.length - 1) { v /= 1024; i++; }
const val = v < 10 ? v.toFixed(1) : Math.round(v);
return `${val}<span style="font-size:14px;font-weight:400"> ${units[i]}</span>`;

Check warning on line 106 in public/js/dashboard.js

View workflow job for this annotation

GitHub Actions / ESLint Security

Generic Object Injection Sink

Check warning on line 106 in public/js/dashboard.js

View workflow job for this annotation

GitHub Actions / ESLint Security

Generic Object Injection Sink
}

// ─── System Resources ───────────────────────────────────────────────────────
Expand Down Expand Up @@ -210,7 +210,7 @@
const step = w / Math.max(1, points.length - 1);
return points.map((p, i) => {
const x = i * step;
const y = h - 10 - ((p[key] / maxVal) * (h - 20));

Check warning on line 213 in public/js/dashboard.js

View workflow job for this annotation

GitHub Actions / ESLint Security

Generic Object Injection Sink

Check warning on line 213 in public/js/dashboard.js

View workflow job for this annotation

GitHub Actions / ESLint Security

Generic Object Injection Sink
return `${i === 0 ? 'M' : 'L'}${x.toFixed(1)},${y.toFixed(1)}`;
}).join(' ');
}
Expand Down Expand Up @@ -256,7 +256,7 @@
}

// ─── Auto-update status (header) ────────────────────────────────────────────
function T(k, d) { return (window.GC && GC.t && GC.t[k]) || d; }

Check warning on line 259 in public/js/dashboard.js

View workflow job for this annotation

GitHub Actions / ESLint Security

Generic Object Injection Sink

Check warning on line 259 in public/js/dashboard.js

View workflow job for this annotation

GitHub Actions / ESLint Security

Generic Object Injection Sink

function auAgo(s) { if (s == null) return '—'; return s < 60 ? T('autoupdate.ago_seconds','{x}s').replace('{x}', s) : T('autoupdate.ago_minutes','{x}m').replace('{x}', Math.round(s/60)); }

Expand Down Expand Up @@ -301,13 +301,21 @@
host.appendChild(p);
}

var recheck = document.createElement('button'); recheck.className = 'btn btn-ghost';
recheck.textContent = T('autoupdate.recheck', 'Re-check');
// Re-check is an icon button (refresh) rather than a text button.
var recheck = document.createElement('button'); recheck.className = 'icon-btn';
recheck.style.cssText = 'width:30px;height:30px';
recheck.title = T('autoupdate.recheck', 'Re-check');
recheck.setAttribute('aria-label', recheck.title);
recheck.innerHTML = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 2v6h-6M3 12a9 9 0 0 1 15-6.7L21 8M3 22v-6h6M21 12a9 9 0 0 1-15 6.7L3 16"/></svg>';
recheck.addEventListener('click', loadAutoUpdate); host.appendChild(recheck);

var setup = document.createElement('button'); setup.className = 'btn btn-ghost';
setup.textContent = T('autoupdate.setup', 'Set up');
setup.addEventListener('click', openAuSetup); host.appendChild(setup);
// Setup button only while auto-update is NOT actually set up (status !== 'active');
// once configured (cron running + recent check) it is hidden.
if (d.status !== 'active') {
var setup = document.createElement('button'); setup.className = 'btn btn-ghost btn-sm';
setup.textContent = T('autoupdate.setup', 'Set up auto-update');
setup.addEventListener('click', openAuSetup); host.appendChild(setup);
}

if (d.mode === 'manual') {
var trig = document.createElement('button'); trig.className = 'btn btn-primary';
Expand Down Expand Up @@ -359,12 +367,6 @@
});
});

// ─── Reload button ─────────────────────────────────────────────────────────
const reloadBtn = document.getElementById('btn-reload');
if (reloadBtn) {
reloadBtn.addEventListener('click', () => refreshAll());
}

// ─── Refresh all ───────────────────────────────────────────────────────────
async function refreshAll() {
await Promise.all([
Expand Down Expand Up @@ -422,6 +424,9 @@
var up = parseInt(data.monitoring.up, 10) || 0;
var total = parseInt(data.monitoring.total, 10) || 0;
var down = parseInt(data.monitoring.down, 10) || 0;
// Availability KPI only shows when ≥1 route has monitoring enabled.
var monCard = document.getElementById('kpi-monitoring');
if (monCard) monCard.style.display = total > 0 ? '' : 'none';
if (total > 0) {
monitorEl.textContent = up + '/' + total;
if (monitorSubEl) {
Expand Down
4 changes: 2 additions & 2 deletions src/i18n/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -686,7 +686,7 @@
"rdp.feature.gateway_locked": "Home-Gateway-Routing für RDP erfordert eine Pro-Lizenz.",
"rdp.feature.gateway_locked_upgrade": "Upgrade für RDP über Home-Gateway",

"status.wg_active": "WireGuard aktiv",
"status.wg_active": "Tunnel aktiv",
"status.wg_inactive": "WireGuard inaktiv",

"time.now": "jetzt",
Expand Down Expand Up @@ -1051,7 +1051,7 @@
"autoupdate.mode_auto": "Automatisch",
"autoupdate.mode_manual": "Manuell",
"autoupdate.recheck": "Neu prüfen",
"autoupdate.setup": "Einrichten",
"autoupdate.setup": "Auto Update einrichten",
"autoupdate.trigger": "Jetzt aktualisieren",
"autoupdate.trigger_queued": "Update eingeplant — greift beim nächsten Cron-Lauf",
"autoupdate.setup_title": "Auto-Update einrichten",
Expand Down
4 changes: 2 additions & 2 deletions src/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -686,7 +686,7 @@
"rdp.feature.gateway_locked": "Routing RDP through a Home Gateway requires a Pro license.",
"rdp.feature.gateway_locked_upgrade": "Upgrade for RDP-via-Home-Gateway",

"status.wg_active": "WireGuard active",
"status.wg_active": "Tunnel active",
"status.wg_inactive": "WireGuard inactive",

"time.now": "now",
Expand Down Expand Up @@ -1051,7 +1051,7 @@
"autoupdate.mode_auto": "Automatic",
"autoupdate.mode_manual": "Manual",
"autoupdate.recheck": "Re-check",
"autoupdate.setup": "Set up",
"autoupdate.setup": "Set up auto-update",
"autoupdate.trigger": "Update now",
"autoupdate.trigger_queued": "Update queued — applies on the next cron run",
"autoupdate.setup_title": "Set up auto-update",
Expand Down
23 changes: 9 additions & 14 deletions templates/aurora/pages/dashboard.njk
Original file line number Diff line number Diff line change
Expand Up @@ -26,23 +26,18 @@
<div class="page-sub">{{ t('dashboard.subtitle') }}</div>
</div>
<div style="display:flex;gap:8px;flex-wrap:wrap;margin-top:4px">
<span id="au-status" class="au-actions"></span>
<button class="btn btn-ghost" id="btn-reload">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="23 4 23 10 17 10"/><path d="M20.49 15a9 9 0 11-2.12-9.36L23 10"/></svg>
{{ t('dashboard.reload') }}
</button>
<a href="/peers" class="btn btn-primary">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>
{{ t('dashboard.add_peer') }}
</a>
</div>
</div>

<!-- ── Row 1: KPI Cards ───────────────────────────────────────────────────── -->
<div class="grid" style="margin-bottom:18px">
<!-- ── Row 1: KPI Cards (6-across down to 1200px, then progressively stacked) ── -->
<div class="aurora-kpi-grid">

<!-- Peers -->
<div class="card kpi span3">
<div class="card kpi">
<div class="top">
<div class="ic">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M17 21v-2a4 4 0 00-4-4H5a4 4 0 00-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 00-3-3.87"/><path d="M16 3.13a4 4 0 010 7.75"/></svg>
Expand All @@ -53,7 +48,7 @@
</div>

<!-- Routes -->
<div class="card kpi span3">
<div class="card kpi">
<div class="top">
<div class="ic">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 014 10 15.3 15.3 0 01-4 10 15.3 15.3 0 01-4-10 15.3 15.3 0 014-10z"/></svg>
Expand All @@ -64,7 +59,7 @@
</div>

<!-- Traffic -->
<div class="card kpi span3">
<div class="card kpi">
<div class="top">
<div class="ic">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="22 12 18 12 15 21 9 3 6 12 2 12"/></svg>
Expand All @@ -75,7 +70,7 @@
</div>

<!-- Gateways (Aurora-only KPI; populated by auroraRefreshStats) -->
<div class="card kpi span3">
<div class="card kpi">
<div class="top">
<div class="ic">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="4" width="18" height="6" rx="2"/><rect x="3" y="14" width="18" height="6" rx="2"/></svg>
Expand All @@ -86,7 +81,7 @@
</div>

<!-- Latency -->
<div class="card kpi span3">
<div class="card kpi">
<div class="top">
<div class="ic">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="3" width="20" height="14" rx="2"/><line x1="8" y1="21" x2="16" y2="21"/><line x1="12" y1="17" x2="12" y2="21"/></svg>
Expand All @@ -96,8 +91,8 @@
<div class="val" id="stat-latency">—</div>
</div>

<!-- Monitoring -->
<a class="card kpi span3" href="/routes" style="text-decoration:none;color:inherit" title="{{ t('monitoring.dashboard_link_title') or 'Routen-Monitoring verwalten' }}">
<!-- Monitoring / Verfügbarkeit — only shown when ≥1 route has monitoring enabled (toggled by JS) -->
<a class="card kpi" id="kpi-monitoring" href="/routes" style="text-decoration:none;color:inherit;display:none" title="{{ t('monitoring.dashboard_link_title') or 'Routen-Monitoring verwalten' }}">
<div class="top">
<div class="ic">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="22 12 18 12 15 21 9 3 6 12 2 12"/></svg>
Expand Down
1 change: 1 addition & 0 deletions templates/aurora/partials/topbar.njk
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
<div class="page-crumb">{{ title }}</div>
<div class="topbar-spacer"></div>
<div class="topbar-status" id="wg-status"><span class="pulse-dot"></span><span>{{ t('status.wg_active') }}</span></div>
<span id="au-status" class="au-actions"></span>
<div class="topbar-lang" id="lang-switcher">
{% for lang in availableLanguages %}
<button class="lang-btn {{ 'active' if lang == language }}" data-lang="{{ lang }}">{{ lang }}</button>
Expand Down
4 changes: 3 additions & 1 deletion tests/aurora_theme.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,9 @@ describe('aurora theme — dashboard layout (Task P2-1)', () => {
assert.match(res.text, /class="app"/, 'aurora .app shell present');
// Grid layout
assert.match(res.text, /class="grid"/, '.grid container present');
assert.match(res.text, /class="card kpi span3"/, '.kpi.span3 KPI cards present');
// KPI cards live in the responsive .aurora-kpi-grid (6-across → progressive stack)
assert.match(res.text, /class="aurora-kpi-grid"/, '.aurora-kpi-grid present');
assert.match(res.text, /class="card kpi"/, '.kpi KPI cards present');
assert.match(res.text, /class="card-title"/, '.card-title present');
});

Expand Down
Loading