MeshDD-Bot/static/nina.html
ppfeiffer 511ff20842 feat(ui): Sidebar-Gruppe Konfigurationen, /config-Seite, MeshDD-Dashboard (closes #4)
- app.js: Sidebar um Gruppen-Support erweitert; Konfigurationen-Gruppe
  mit Scheduler, NINA, Einstellungen (/config) als Untereinträge
- style.css: .sidebar-group-label + .sidebar-link-sub
- config.py: save()-Funktion für persistentes Schreiben in config.yaml
- webserver.py: GET/PUT /api/config + GET /config Route (Admin)
- static/config.html + static/js/config.js: neue Konfigurationsseite
  (Bot, Meshtastic, Web, Links editierbar)
- Alle HTML-Dateien: MeshDD-Bot → MeshDD-Dashboard

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-20 22:33:18 +01:00

219 lines
13 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="de" data-bs-theme="light">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>MeshDD-Dashboard NINA</title>
<link href="https://cdn.jsdelivr.net/npm/@tabler/core@1.4.0/dist/css/tabler.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css" rel="stylesheet">
<link rel="stylesheet" href="/static/css/style.css">
</head>
<body class="antialiased">
<!-- Top Navbar -->
<nav class="top-navbar d-flex align-items-center px-3">
<button class="btn btn-link text-body p-0 me-2 d-lg-none" id="sidebarToggle">
<i class="bi bi-list fs-5"></i>
</button>
<span class="fw-bold me-auto">
<i class="bi bi-broadcast-pin text-info me-1"></i>MeshDD-Dashboard
<small class="text-body-secondary fw-normal" id="versionLabel"></small>
</span>
<div class="d-flex align-items-center gap-2">
<span id="userMenu" class="d-none">
<small class="text-body-secondary me-1"><i class="bi bi-person-fill me-1"></i><span id="userName"></span></small>
<a href="/auth/logout" class="btn btn-sm btn-outline-secondary py-0 px-1" title="Abmelden">
<i class="bi bi-box-arrow-right" style="font-size:.75rem"></i>
</a>
</span>
<a href="/login" id="loginBtn" class="btn btn-sm btn-outline-info py-0 px-1 d-none">
<i class="bi bi-person" style="font-size:.75rem"></i> Login
</a>
<button class="btn btn-sm btn-outline-secondary py-0 px-1" id="themeToggle" title="Theme wechseln">
<i class="bi bi-sun-fill" id="themeIcon" style="font-size:.75rem"></i>
</button>
</div>
</nav>
<aside class="sidebar" id="sidebar"></aside>
<div class="sidebar-backdrop" id="sidebarBackdrop"></div>
<!-- Content -->
<main class="content-wrapper">
<div class="d-flex justify-content-between align-items-center mb-2">
<h6 class="mb-0"><i class="bi bi-shield-exclamation me-1 text-warning"></i>NINA Warnmeldungen</h6>
<div class="d-flex gap-2 align-items-center">
<span class="badge bg-secondary" id="statusBadge">Lade...</span>
</div>
</div>
<div class="row g-3">
<!-- Settings Card -->
<div class="col-12 col-xl-5">
<div class="card card-outline card-warning h-100">
<div class="card-header">
<h6 class="card-title mb-0"><i class="bi bi-gear me-1"></i>Einstellungen</h6>
</div>
<div class="card-body">
<form id="ninaForm">
<!-- Enable + send toggles -->
<div class="mb-3 d-flex gap-4 flex-wrap">
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" id="ninaEnabled" role="switch">
<label class="form-check-label fw-semibold" for="ninaEnabled">Aktiviert</label>
</div>
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" id="ninaSendToMesh" role="switch">
<label class="form-check-label" for="ninaSendToMesh">
Ins Mesh senden
<small class="text-body-secondary ms-1">(aus = nur Weboberfläche)</small>
</label>
</div>
</div>
<div class="row g-3 mb-3">
<div class="col-5">
<label for="pollInterval" class="form-label">Abfrage&shy;intervall (Min.)</label>
<input type="number" class="form-control form-control-sm" id="pollInterval"
min="1" max="60" step="1" value="5">
<div class="form-text">Neue Warnmeldungen
<span id="lastPoll" class="d-block text-body-secondary"></span>
<span id="lastSent" class="d-block text-body-secondary"></span>
</div>
</div>
<div class="col-5">
<label for="resendInterval" class="form-label">Wieder&shy;holungsintervall (Min.)</label>
<input type="number" class="form-control form-control-sm" id="resendInterval"
min="1" step="1" value="60">
<div class="form-text">Aktive Warnungen</div>
</div>
<div class="col-2">
<label for="ninaChannel" class="form-label">Kanal</label>
<input type="number" class="form-control form-control-sm" id="ninaChannel"
min="0" max="7" value="0">
</div>
</div>
<div class="mb-3">
<label for="minSeverity" class="form-label">Mindest-Schweregrad</label>
<select class="form-select form-select-sm" id="minSeverity">
<option value="Minor">Gering (Minor)</option>
<option value="Moderate">Mäßig (Moderate)</option>
<option value="Severe" selected>Schwerwiegend (Severe)</option>
<option value="Extreme">Extrem (Extreme)</option>
</select>
</div>
<!-- AGS Codes -->
<div class="mb-3">
<label class="form-label">AGS-Codes (Amtliche Gemeindeschlüssel)</label>
<div class="table-responsive mb-2" style="max-height:160px;overflow-y:auto">
<table class="table table-sm table-hover table-striped mb-0 align-middle">
<thead class="table-dark">
<tr>
<th style="width:130px">AGS-Code</th>
<th>Ort / Region</th>
<th style="width:42px"></th>
</tr>
</thead>
<tbody id="agsList"></tbody>
</table>
</div>
<div class="input-group input-group-sm">
<input type="text" class="form-control" id="agsInput"
list="agsSachsenList"
placeholder="Code oder Ort eingeben…" maxlength="50">
<button class="btn btn-outline-secondary" type="button" id="btnAddAgs">
<i class="bi bi-plus-lg"></i> Hinzufügen
</button>
</div>
<datalist id="agsSachsenList"></datalist>
<div class="form-text">
8- oder 12-stelliger AGS-Code des Landkreises/der kreisfreien Stadt.
<a href="https://www.destatis.de/DE/Themen/Laender-Regionen/Regionales/Gemeindeverzeichnis/_inhalt.html"
target="_blank" rel="noopener" class="text-info">AGS-Verzeichnis</a>
</div>
</div>
<!-- Sources -->
<div class="mb-3">
<label class="form-label">Quellen</label>
<div class="row g-2">
<div class="col-6">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="srcKatwarn" checked>
<label class="form-check-label" for="srcKatwarn">Katwarn</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="srcBiwapp" checked>
<label class="form-check-label" for="srcBiwapp">BIWAPP</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="srcMowas" checked>
<label class="form-check-label" for="srcMowas">MoWaS</label>
</div>
</div>
<div class="col-6">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="srcDwd" checked>
<label class="form-check-label" for="srcDwd">DWD (Wetter)</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="srcLhp" checked>
<label class="form-check-label" for="srcLhp">LHP (Hochwasser)</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="srcPolice">
<label class="form-check-label" for="srcPolice">Polizei</label>
</div>
</div>
</div>
</div>
<div class="d-flex gap-2">
<button type="button" class="btn btn-warning btn-sm" id="btnSaveNina">
<i class="bi bi-floppy me-1"></i>Speichern
</button>
<span id="saveStatus" class="d-none align-self-center small"></span>
</div>
</form>
</div>
</div>
</div>
<!-- Alerts Card -->
<div class="col-12 col-xl-7">
<div class="card card-outline card-danger h-100">
<div class="card-header d-flex justify-content-between align-items-center">
<h6 class="card-title mb-0"><i class="bi bi-bell-fill me-1 text-danger"></i>Letzte Warnmeldungen</h6>
<small class="text-body-secondary">Live via WebSocket</small>
</div>
<div class="card-body p-0 table-responsive">
<table class="table table-hover table-sm table-striped mb-0 align-middle">
<thead class="table-dark">
<tr>
<th style="width:90px">Schweregrad</th>
<th>Meldung</th>
<th style="width:140px">Gebiet</th>
<th style="width:110px">Typ</th>
<th style="width:60px" class="text-center">Mesh</th>
<th style="width:130px">Zeitstempel</th>
</tr>
</thead>
<tbody id="alertsTable">
<tr><td colspan="6" class="text-center text-body-secondary py-3">Keine Meldungen NINA aktivieren und AGS-Codes konfigurieren.</td></tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</main>
<footer id="pageFooter" class="page-footer"></footer>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
<script src="/static/js/app.js"></script>
<script src="/static/js/nina.js"></script>
</body>
</html>