feat: v0.3.5 - AdminLTE-style layout, fix channel names in messages
Redesign dashboard and scheduler with AdminLTE-inspired layout: fixed sidebar navigation, top navbar, info-boxes, card-outline styling, table-striped. Fix channel names missing on initial load by sending channels before messages. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
6bfb1595e4
commit
65703b6389
21
CHANGELOG.md
21
CHANGELOG.md
|
|
@ -1,5 +1,26 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## [0.3.5] - 2026-02-15
|
||||||
|
### Changed
|
||||||
|
- Dashboard und Scheduler auf AdminLTE-Style umgestellt
|
||||||
|
- Feste Sidebar-Navigation (Dashboard, Scheduler, Karte) mit Active-State
|
||||||
|
- Fixed Top-Navbar mit Branding, Status-Dot und Theme-Toggle
|
||||||
|
- Content-Wrapper mit leicht abgesetztem Hintergrund
|
||||||
|
- Info-Boxes im AdminLTE-Stil (Icon-Spalte + Inhalt) statt Cards
|
||||||
|
- Card-Outline mit farbiger Oberkante (info/warning) statt Borders
|
||||||
|
- Table-Striped fuer bessere Lesbarkeit
|
||||||
|
- Sidebar responsive: auf Mobile als Overlay mit Backdrop
|
||||||
|
- Einheitliches Layout auf Dashboard und Scheduler
|
||||||
|
|
||||||
|
## [0.3.4] - 2026-02-15
|
||||||
|
### Fixed
|
||||||
|
- Kanalnamen in Nachrichten fehlten beim Laden (Channels werden jetzt vor Messages gesendet)
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Dashboard deutlich kompakter: weniger Padding, kleinere Schriftgroessen
|
||||||
|
- Stat-Cards, Navbar, Panels und Nachrichten platzsparender
|
||||||
|
- Hover-Animationen und Pulse-Effekt entfernt (schlichter)
|
||||||
|
|
||||||
## [0.3.3] - 2026-02-15
|
## [0.3.3] - 2026-02-15
|
||||||
### Changed
|
### Changed
|
||||||
- Dashboard-Layout modernisiert: Glassmorphism-Navbar (sticky, blur-Effekt)
|
- Dashboard-Layout modernisiert: Glassmorphism-Navbar (sticky, blur-Effekt)
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
version: "0.3.3"
|
version: "0.3.5"
|
||||||
|
|
||||||
bot:
|
bot:
|
||||||
name: "MeshDD-Bot"
|
name: "MeshDD-Bot"
|
||||||
|
|
|
||||||
|
|
@ -67,13 +67,13 @@ class WebServer:
|
||||||
stats["version"] = config.get("version", "0.0.0")
|
stats["version"] = config.get("version", "0.0.0")
|
||||||
await ws.send_str(json.dumps({"type": "stats_update", "data": stats}))
|
await ws.send_str(json.dumps({"type": "stats_update", "data": stats}))
|
||||||
|
|
||||||
messages = await self.db.get_recent_messages(50)
|
|
||||||
await ws.send_str(json.dumps({"type": "initial_messages", "data": messages}))
|
|
||||||
|
|
||||||
if self.bot:
|
if self.bot:
|
||||||
channels = self.bot.get_channels()
|
channels = self.bot.get_channels()
|
||||||
await ws.send_str(json.dumps({"type": "channels", "data": channels}))
|
await ws.send_str(json.dumps({"type": "channels", "data": channels}))
|
||||||
|
|
||||||
|
messages = await self.db.get_recent_messages(50)
|
||||||
|
await ws.send_str(json.dumps({"type": "initial_messages", "data": messages}))
|
||||||
|
|
||||||
async for msg in ws:
|
async for msg in ws:
|
||||||
pass # We only send, not receive
|
pass # We only send, not receive
|
||||||
finally:
|
finally:
|
||||||
|
|
|
||||||
|
|
@ -1,102 +1,189 @@
|
||||||
/* Status indicator */
|
/* ── AdminLTE-style Layout ─────────────────────────── */
|
||||||
|
|
||||||
|
/* Top Navbar */
|
||||||
|
.top-navbar {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
height: 46px;
|
||||||
|
z-index: 1030;
|
||||||
|
background: var(--bs-body-bg);
|
||||||
|
border-bottom: 1px solid var(--bs-border-color);
|
||||||
|
font-size: .875rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sidebar */
|
||||||
|
.sidebar {
|
||||||
|
position: fixed;
|
||||||
|
top: 46px;
|
||||||
|
left: 0;
|
||||||
|
bottom: 0;
|
||||||
|
width: 200px;
|
||||||
|
background: var(--bs-body-bg);
|
||||||
|
border-right: 1px solid var(--bs-border-color);
|
||||||
|
z-index: 1020;
|
||||||
|
overflow-y: auto;
|
||||||
|
transition: transform 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-nav {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 0.5rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-link {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.6rem;
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
color: var(--bs-body-color);
|
||||||
|
text-decoration: none;
|
||||||
|
font-size: .85rem;
|
||||||
|
border-left: 3px solid transparent;
|
||||||
|
transition: background 0.15s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-link:hover {
|
||||||
|
background: rgba(var(--bs-emphasis-color-rgb), 0.06);
|
||||||
|
color: var(--bs-body-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-link.active {
|
||||||
|
background: rgba(var(--bs-info-rgb), 0.08);
|
||||||
|
border-left-color: var(--bs-info);
|
||||||
|
color: var(--bs-info);
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-link i {
|
||||||
|
font-size: 1rem;
|
||||||
|
width: 1.25rem;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mobile sidebar */
|
||||||
|
.sidebar-backdrop {
|
||||||
|
display: none;
|
||||||
|
position: fixed;
|
||||||
|
inset: 0;
|
||||||
|
background: rgba(0,0,0,0.4);
|
||||||
|
z-index: 1019;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 991.98px) {
|
||||||
|
.sidebar {
|
||||||
|
transform: translateX(-100%);
|
||||||
|
}
|
||||||
|
.sidebar.open {
|
||||||
|
transform: translateX(0);
|
||||||
|
}
|
||||||
|
.sidebar.open ~ .sidebar-backdrop {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
.content-wrapper {
|
||||||
|
margin-left: 0 !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Content wrapper */
|
||||||
|
.content-wrapper {
|
||||||
|
margin-top: 46px;
|
||||||
|
margin-left: 200px;
|
||||||
|
padding: 0.75rem;
|
||||||
|
min-height: calc(100vh - 46px);
|
||||||
|
background: rgba(var(--bs-emphasis-color-rgb), 0.03);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Info Boxes (AdminLTE style) ──────────────────── */
|
||||||
|
|
||||||
|
.info-box {
|
||||||
|
display: flex;
|
||||||
|
align-items: stretch;
|
||||||
|
background: var(--bs-body-bg);
|
||||||
|
border: 1px solid var(--bs-border-color);
|
||||||
|
border-radius: 0.375rem;
|
||||||
|
min-height: 60px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-box-icon {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 56px;
|
||||||
|
font-size: 1.4rem;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-box-content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 0.4rem 0.75rem;
|
||||||
|
line-height: 1.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-box-label {
|
||||||
|
font-size: .75rem;
|
||||||
|
color: var(--bs-secondary-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-box-number {
|
||||||
|
font-size: 1.25rem;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Card outline (AdminLTE style) ────────────────── */
|
||||||
|
|
||||||
|
.card-outline {
|
||||||
|
border-top: 3px solid var(--bs-border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-outline.card-info {
|
||||||
|
border-top-color: var(--bs-info);
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-outline.card-warning {
|
||||||
|
border-top-color: var(--bs-warning);
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-outline.card-success {
|
||||||
|
border-top-color: var(--bs-success);
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-outline.card-primary {
|
||||||
|
border-top-color: var(--bs-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-header {
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: .875rem;
|
||||||
|
padding: 0.5rem 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Status dot ──────────────────────────────────── */
|
||||||
|
|
||||||
.status-dot {
|
.status-dot {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
width: 10px;
|
width: 8px;
|
||||||
height: 10px;
|
height: 8px;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
background: var(--bs-danger);
|
background: var(--bs-danger);
|
||||||
transition: all 0.3s ease;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.status-dot.connected {
|
.status-dot.connected {
|
||||||
background: var(--bs-success);
|
background: var(--bs-success);
|
||||||
box-shadow: 0 0 8px var(--bs-success);
|
box-shadow: 0 0 5px var(--bs-success);
|
||||||
animation: pulse-dot 2s infinite;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes pulse-dot {
|
/* ── Messages ────────────────────────────────────── */
|
||||||
0%, 100% { box-shadow: 0 0 4px var(--bs-success); }
|
|
||||||
50% { box-shadow: 0 0 12px var(--bs-success); }
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Navbar glass effect */
|
|
||||||
.navbar-glass {
|
|
||||||
backdrop-filter: blur(12px);
|
|
||||||
-webkit-backdrop-filter: blur(12px);
|
|
||||||
background: rgba(var(--bs-body-bg-rgb), 0.85) !important;
|
|
||||||
border-bottom: 1px solid rgba(var(--bs-emphasis-color-rgb), 0.08);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Scrollbar */
|
|
||||||
.table-responsive::-webkit-scrollbar,
|
|
||||||
.msg-scroll::-webkit-scrollbar {
|
|
||||||
width: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.table-responsive::-webkit-scrollbar-track,
|
|
||||||
.msg-scroll::-webkit-scrollbar-track {
|
|
||||||
background: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
.table-responsive::-webkit-scrollbar-thumb,
|
|
||||||
.msg-scroll::-webkit-scrollbar-thumb {
|
|
||||||
background: var(--bs-border-color);
|
|
||||||
border-radius: 3px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Stat cards */
|
|
||||||
.stat-card {
|
|
||||||
border: none;
|
|
||||||
border-radius: 0.75rem;
|
|
||||||
overflow: hidden;
|
|
||||||
position: relative;
|
|
||||||
transition: transform 0.15s ease, box-shadow 0.15s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.stat-card:hover {
|
|
||||||
transform: translateY(-2px);
|
|
||||||
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
|
|
||||||
}
|
|
||||||
|
|
||||||
.stat-card .stat-accent {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
height: 3px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.stat-card .stat-icon {
|
|
||||||
font-size: 1.8rem;
|
|
||||||
opacity: 0.15;
|
|
||||||
position: absolute;
|
|
||||||
right: 12px;
|
|
||||||
top: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Panel cards */
|
|
||||||
.panel-card {
|
|
||||||
border: none;
|
|
||||||
border-radius: 0.75rem;
|
|
||||||
box-shadow: 0 1px 6px rgba(0, 0, 0, 0.08);
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.panel-card .card-header {
|
|
||||||
background: transparent;
|
|
||||||
border-bottom: 1px solid rgba(var(--bs-emphasis-color-rgb), 0.06);
|
|
||||||
padding: 0.65rem 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Messages */
|
|
||||||
.msg-item {
|
.msg-item {
|
||||||
padding: 0.5rem 0.75rem;
|
padding: 0.35rem 0.65rem;
|
||||||
border-bottom: 1px solid rgba(var(--bs-emphasis-color-rgb), 0.04);
|
border-bottom: 1px solid var(--bs-border-color-translucent);
|
||||||
transition: background 0.15s ease;
|
font-size: .8rem;
|
||||||
}
|
|
||||||
|
|
||||||
.msg-item:hover {
|
|
||||||
background: rgba(var(--bs-emphasis-color-rgb), 0.03);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.msg-item:last-child {
|
.msg-item:last-child {
|
||||||
|
|
@ -104,42 +191,28 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.msg-bubble {
|
.msg-bubble {
|
||||||
background: rgba(var(--bs-info-rgb), 0.08);
|
background: rgba(var(--bs-info-rgb), 0.06);
|
||||||
border-radius: 0.5rem 0.5rem 0.5rem 0.1rem;
|
border-radius: 0.3rem;
|
||||||
padding: 0.35rem 0.6rem;
|
padding: 0.2rem 0.45rem;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
word-break: break-word;
|
word-break: break-word;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Node table rows */
|
/* ── Scrollbar ───────────────────────────────────── */
|
||||||
.nodes-table tbody tr {
|
|
||||||
transition: background 0.1s ease;
|
.table-responsive::-webkit-scrollbar,
|
||||||
|
.card-body::-webkit-scrollbar {
|
||||||
|
width: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Breakdown bar */
|
.table-responsive::-webkit-scrollbar-track,
|
||||||
.breakdown-bar {
|
.card-body::-webkit-scrollbar-track {
|
||||||
border: none;
|
background: transparent;
|
||||||
border-radius: 0.5rem;
|
|
||||||
background: rgba(var(--bs-primary-rgb), 0.06);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Send input */
|
.table-responsive::-webkit-scrollbar-thumb,
|
||||||
.send-bar {
|
.card-body::-webkit-scrollbar-thumb {
|
||||||
border-top: 1px solid rgba(var(--bs-emphasis-color-rgb), 0.06);
|
background: var(--bs-border-color);
|
||||||
background: rgba(var(--bs-emphasis-color-rgb), 0.02);
|
border-radius: 3px;
|
||||||
padding: 0.5rem 0.75rem;
|
|
||||||
border-radius: 0 0 0.75rem 0.75rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.send-bar .form-control:focus,
|
|
||||||
.send-bar .form-select:focus {
|
|
||||||
box-shadow: none;
|
|
||||||
border-color: var(--bs-info);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Badge pills */
|
|
||||||
.badge-pill {
|
|
||||||
border-radius: 2rem;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,97 +9,110 @@
|
||||||
<link rel="stylesheet" href="/static/css/style.css">
|
<link rel="stylesheet" href="/static/css/style.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<!-- Navbar -->
|
<!-- Top Navbar -->
|
||||||
<nav class="navbar navbar-expand-sm navbar-glass sticky-top">
|
<nav class="top-navbar d-flex align-items-center px-3">
|
||||||
<div class="container-fluid">
|
<button class="btn btn-link text-body p-0 me-2 d-lg-none" id="sidebarToggle">
|
||||||
<a class="navbar-brand d-flex align-items-center gap-2" href="/">
|
<i class="bi bi-list fs-5"></i>
|
||||||
<i class="bi bi-broadcast-pin text-info fs-5"></i>
|
|
||||||
<span class="fw-bold">MeshDD-Bot</span>
|
|
||||||
<span class="badge badge-pill bg-body-secondary text-body-secondary fw-normal small" id="versionLabel"></span>
|
|
||||||
</a>
|
|
||||||
<div class="d-flex align-items-center gap-2">
|
|
||||||
<a href="/scheduler" class="btn btn-outline-info btn-sm rounded-pill px-3">
|
|
||||||
<i class="bi bi-clock-history me-1"></i>Scheduler
|
|
||||||
</a>
|
|
||||||
<a href="/map" target="_blank" class="btn btn-outline-info btn-sm rounded-pill px-3">
|
|
||||||
<i class="bi bi-map me-1"></i>Karte
|
|
||||||
</a>
|
|
||||||
<button class="btn btn-outline-secondary btn-sm rounded-circle" id="themeToggle" title="Theme wechseln" style="width:32px;height:32px;">
|
|
||||||
<i class="bi bi-sun-fill" id="themeIcon"></i>
|
|
||||||
</button>
|
</button>
|
||||||
<span class="badge badge-pill bg-body-secondary d-flex align-items-center gap-2 px-3 py-2" id="statusBadge">
|
<span class="fw-bold me-auto">
|
||||||
<span class="status-dot" id="statusDot"></span>
|
<i class="bi bi-broadcast-pin text-info me-1"></i>MeshDD-Bot
|
||||||
<span class="small" id="statusText">Verbinde...</span>
|
<small class="text-body-secondary fw-normal" id="versionLabel"></small>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
<div class="d-flex align-items-center gap-2">
|
||||||
|
<span class="d-flex align-items-center gap-1">
|
||||||
|
<span class="status-dot" id="statusDot"></span>
|
||||||
|
<small class="text-body-secondary" id="statusText">Verbinde...</small>
|
||||||
|
</span>
|
||||||
|
<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>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<div class="container-fluid py-3">
|
<!-- Sidebar -->
|
||||||
<!-- Stats Cards -->
|
<aside class="sidebar" id="sidebar">
|
||||||
|
<nav class="sidebar-nav">
|
||||||
|
<a href="/" class="sidebar-link active">
|
||||||
|
<i class="bi bi-speedometer2"></i><span>Dashboard</span>
|
||||||
|
</a>
|
||||||
|
<a href="/scheduler" class="sidebar-link">
|
||||||
|
<i class="bi bi-clock-history"></i><span>Scheduler</span>
|
||||||
|
</a>
|
||||||
|
<a href="/map" target="_blank" class="sidebar-link">
|
||||||
|
<i class="bi bi-map"></i><span>Karte</span>
|
||||||
|
</a>
|
||||||
|
</nav>
|
||||||
|
</aside>
|
||||||
|
|
||||||
|
<!-- Backdrop for mobile sidebar -->
|
||||||
|
<div class="sidebar-backdrop" id="sidebarBackdrop"></div>
|
||||||
|
|
||||||
|
<!-- Content -->
|
||||||
|
<main class="content-wrapper">
|
||||||
|
<!-- Info Boxes -->
|
||||||
<div class="row g-2 mb-2">
|
<div class="row g-2 mb-2">
|
||||||
<div class="col-4">
|
<div class="col-sm-4">
|
||||||
<div class="card stat-card">
|
<div class="info-box">
|
||||||
<div class="stat-accent" style="background: linear-gradient(90deg, var(--bs-info), transparent);"></div>
|
<span class="info-box-icon bg-info bg-opacity-10 text-info">
|
||||||
<i class="bi bi-router stat-icon text-info"></i>
|
<i class="bi bi-router"></i>
|
||||||
<div class="card-body py-2 ps-3">
|
</span>
|
||||||
<div class="fs-4 fw-bold text-info" id="statNodes">0</div>
|
<div class="info-box-content">
|
||||||
<div class="text-body-secondary small">Nodes gesamt</div>
|
<span class="info-box-label">Nodes gesamt</span>
|
||||||
|
<span class="info-box-number" id="statNodes">0</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-4">
|
<div class="col-sm-4">
|
||||||
<div class="card stat-card">
|
<div class="info-box">
|
||||||
<div class="stat-accent" style="background: linear-gradient(90deg, var(--bs-success), transparent);"></div>
|
<span class="info-box-icon bg-success bg-opacity-10 text-success">
|
||||||
<i class="bi bi-activity stat-icon text-success"></i>
|
<i class="bi bi-activity"></i>
|
||||||
<div class="card-body py-2 ps-3">
|
</span>
|
||||||
<div class="fs-4 fw-bold text-success" id="statNodes24h">0</div>
|
<div class="info-box-content">
|
||||||
<div class="text-body-secondary small">Aktiv (24h)</div>
|
<span class="info-box-label">Aktiv (24h)</span>
|
||||||
|
<span class="info-box-number" id="statNodes24h">0</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-4">
|
<div class="col-sm-4">
|
||||||
<div class="card stat-card">
|
<div class="info-box">
|
||||||
<div class="stat-accent" style="background: linear-gradient(90deg, var(--bs-warning), transparent);"></div>
|
<span class="info-box-icon bg-warning bg-opacity-10 text-warning">
|
||||||
<i class="bi bi-terminal stat-icon text-warning"></i>
|
<i class="bi bi-terminal"></i>
|
||||||
<div class="card-body py-2 ps-3">
|
</span>
|
||||||
<div class="fs-4 fw-bold text-warning" id="statCommands">0</div>
|
<div class="info-box-content">
|
||||||
<div class="text-body-secondary small">Anfragen</div>
|
<span class="info-box-label">Anfragen</span>
|
||||||
</div>
|
<span class="info-box-number" id="statCommands">0</span>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row g-2 mb-3">
|
|
||||||
<div class="col-12">
|
|
||||||
<div class="card breakdown-bar">
|
|
||||||
<div class="card-body py-2 d-flex align-items-center gap-3 flex-wrap">
|
|
||||||
<span class="text-body-secondary small me-1"><i class="bi bi-bar-chart-fill me-1"></i>Anfragen:</span>
|
|
||||||
<span id="commandBreakdown" class="d-flex gap-2 flex-wrap"></span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Panels -->
|
<!-- Command Breakdown -->
|
||||||
<div class="row g-3">
|
<div class="card card-outline mb-2">
|
||||||
<!-- Nodes Table -->
|
<div class="card-body py-2 px-3 d-flex align-items-center gap-2 flex-wrap">
|
||||||
<div class="col-lg-7">
|
<small class="text-body-secondary"><i class="bi bi-bar-chart-fill me-1"></i>Anfragen:</small>
|
||||||
<div class="card panel-card">
|
<span id="commandBreakdown" class="d-flex gap-1 flex-wrap"></span>
|
||||||
<div class="card-header d-flex align-items-center">
|
|
||||||
<i class="bi bi-router me-2 text-info"></i>
|
|
||||||
<span class="fw-semibold">Nodes</span>
|
|
||||||
<span class="badge badge-pill bg-info bg-opacity-25 text-info ms-auto" id="nodeCountBadge">0</span>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body p-0 table-responsive" style="max-height: 500px; overflow-y: auto;">
|
</div>
|
||||||
<table class="table table-hover table-sm mb-0 align-middle nodes-table">
|
|
||||||
|
<!-- Main Cards -->
|
||||||
|
<div class="row g-2">
|
||||||
|
<!-- Nodes -->
|
||||||
|
<div class="col-lg-7">
|
||||||
|
<div class="card card-outline card-info">
|
||||||
|
<div class="card-header">
|
||||||
|
<i class="bi bi-router me-1"></i>Nodes
|
||||||
|
<span class="badge bg-info float-end" id="nodeCountBadge">0</span>
|
||||||
|
</div>
|
||||||
|
<div class="card-body p-0 table-responsive" style="max-height:520px;overflow-y:auto">
|
||||||
|
<table class="table table-hover table-sm table-striped mb-0 align-middle">
|
||||||
<thead class="table-dark sticky-top">
|
<thead class="table-dark sticky-top">
|
||||||
<tr>
|
<tr>
|
||||||
<th>Name</th>
|
<th>Name</th>
|
||||||
<th>Hardware</th>
|
<th>Hardware</th>
|
||||||
<th class="text-end px-1">SNR</th>
|
<th class="text-end">SNR</th>
|
||||||
<th class="px-1">Batterie</th>
|
<th>Batterie</th>
|
||||||
<th class="text-center px-1">Hops</th>
|
<th class="text-center">Hops</th>
|
||||||
<th class="text-end px-1">Zuletzt</th>
|
<th class="text-end">Zuletzt</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody id="nodesTable"></tbody>
|
<tbody id="nodesTable"></tbody>
|
||||||
|
|
@ -110,19 +123,18 @@
|
||||||
|
|
||||||
<!-- Messages -->
|
<!-- Messages -->
|
||||||
<div class="col-lg-5">
|
<div class="col-lg-5">
|
||||||
<div class="card panel-card">
|
<div class="card card-outline card-warning">
|
||||||
<div class="card-header d-flex align-items-center">
|
<div class="card-header">
|
||||||
<i class="bi bi-chat-dots me-2 text-warning"></i>
|
<i class="bi bi-chat-dots me-1"></i>Nachrichten
|
||||||
<span class="fw-semibold">Nachrichten</span>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body p-0 msg-scroll" style="max-height: 500px; overflow-y: auto;">
|
<div class="card-body p-0" style="max-height:520px;overflow-y:auto">
|
||||||
<div id="messagesList"></div>
|
<div id="messagesList"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="send-bar">
|
<div class="card-footer py-2 px-2">
|
||||||
<div class="input-group input-group-sm">
|
<div class="input-group input-group-sm">
|
||||||
<select class="form-select form-select-sm rounded-start-pill" id="sendChannel" style="max-width: 120px;"></select>
|
<select class="form-select form-select-sm" id="sendChannel" style="max-width:110px"></select>
|
||||||
<input type="text" class="form-control form-control-sm" id="sendText" placeholder="Nachricht senden...">
|
<input type="text" class="form-control form-control-sm" id="sendText" placeholder="Nachricht senden...">
|
||||||
<button class="btn btn-info btn-sm rounded-end-pill px-3" id="btnSend" type="button">
|
<button class="btn btn-info btn-sm" id="btnSend" type="button">
|
||||||
<i class="bi bi-send-fill"></i>
|
<i class="bi bi-send-fill"></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -130,7 +142,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</main>
|
||||||
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||||
<script src="/static/js/dashboard.js"></script>
|
<script src="/static/js/dashboard.js"></script>
|
||||||
|
|
|
||||||
|
|
@ -209,4 +209,14 @@ themeToggle.addEventListener('click', () => {
|
||||||
applyTheme(current === 'dark' ? 'light' : 'dark');
|
applyTheme(current === 'dark' ? 'light' : 'dark');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Sidebar toggle (mobile)
|
||||||
|
const sidebarToggle = document.getElementById('sidebarToggle');
|
||||||
|
const sidebar = document.getElementById('sidebar');
|
||||||
|
const sidebarBackdrop = document.getElementById('sidebarBackdrop');
|
||||||
|
|
||||||
|
if (sidebarToggle) {
|
||||||
|
sidebarToggle.addEventListener('click', () => sidebar.classList.toggle('open'));
|
||||||
|
sidebarBackdrop.addEventListener('click', () => sidebar.classList.remove('open'));
|
||||||
|
}
|
||||||
|
|
||||||
connectWebSocket();
|
connectWebSocket();
|
||||||
|
|
|
||||||
|
|
@ -208,5 +208,15 @@ document.getElementById('btnSaveJob').addEventListener('click', async () => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Sidebar toggle (mobile)
|
||||||
|
const sidebarToggle = document.getElementById('sidebarToggle');
|
||||||
|
const sidebar = document.getElementById('sidebar');
|
||||||
|
const sidebarBackdrop = document.getElementById('sidebarBackdrop');
|
||||||
|
|
||||||
|
if (sidebarToggle) {
|
||||||
|
sidebarToggle.addEventListener('click', () => sidebar.classList.toggle('open'));
|
||||||
|
sidebarBackdrop.addEventListener('click', () => sidebar.classList.remove('open'));
|
||||||
|
}
|
||||||
|
|
||||||
loadJobs();
|
loadJobs();
|
||||||
connectWebSocket();
|
connectWebSocket();
|
||||||
|
|
|
||||||
|
|
@ -9,37 +9,48 @@
|
||||||
<link rel="stylesheet" href="/static/css/style.css">
|
<link rel="stylesheet" href="/static/css/style.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<!-- Navbar -->
|
<!-- Top Navbar -->
|
||||||
<nav class="navbar navbar-expand-sm bg-body-tertiary border-bottom">
|
<nav class="top-navbar d-flex align-items-center px-3">
|
||||||
<div class="container-fluid">
|
<button class="btn btn-link text-body p-0 me-2 d-lg-none" id="sidebarToggle">
|
||||||
<a class="navbar-brand" href="/">
|
<i class="bi bi-list fs-5"></i>
|
||||||
<i class="bi bi-broadcast-pin text-info me-2"></i>MeshDD-Bot
|
</button>
|
||||||
</a>
|
<span class="fw-bold me-auto">
|
||||||
<div class="d-flex align-items-center gap-3">
|
<i class="bi bi-broadcast-pin text-info me-1"></i>MeshDD-Bot
|
||||||
<a href="/" class="btn btn-outline-info btn-sm">
|
</span>
|
||||||
<i class="bi bi-speedometer2 me-1"></i>Dashboard
|
<button class="btn btn-sm btn-outline-secondary py-0 px-1" id="themeToggle" title="Theme wechseln">
|
||||||
</a>
|
<i class="bi bi-sun-fill" id="themeIcon" style="font-size:.75rem"></i>
|
||||||
<a href="/map" target="_blank" class="btn btn-outline-info btn-sm">
|
|
||||||
<i class="bi bi-map me-1"></i>Karte
|
|
||||||
</a>
|
|
||||||
<button class="btn btn-outline-secondary btn-sm" id="themeToggle" title="Theme wechseln">
|
|
||||||
<i class="bi bi-sun-fill" id="themeIcon"></i>
|
|
||||||
</button>
|
</button>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<div class="container-fluid py-3">
|
<!-- Sidebar -->
|
||||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
<aside class="sidebar" id="sidebar">
|
||||||
<h5 class="mb-0"><i class="bi bi-clock-history me-2 text-info"></i>Scheduler</h5>
|
<nav class="sidebar-nav">
|
||||||
|
<a href="/" class="sidebar-link">
|
||||||
|
<i class="bi bi-speedometer2"></i><span>Dashboard</span>
|
||||||
|
</a>
|
||||||
|
<a href="/scheduler" class="sidebar-link active">
|
||||||
|
<i class="bi bi-clock-history"></i><span>Scheduler</span>
|
||||||
|
</a>
|
||||||
|
<a href="/map" target="_blank" class="sidebar-link">
|
||||||
|
<i class="bi bi-map"></i><span>Karte</span>
|
||||||
|
</a>
|
||||||
|
</nav>
|
||||||
|
</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-clock-history me-1 text-info"></i>Scheduler</h6>
|
||||||
<button class="btn btn-info btn-sm" id="btnAddJob">
|
<button class="btn btn-info btn-sm" id="btnAddJob">
|
||||||
<i class="bi bi-plus-lg me-1"></i>Neuer Job
|
<i class="bi bi-plus-lg me-1"></i>Neuer Job
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card">
|
<div class="card card-outline card-info">
|
||||||
<div class="card-body p-0 table-responsive">
|
<div class="card-body p-0 table-responsive">
|
||||||
<table class="table table-hover table-sm mb-0 align-middle">
|
<table class="table table-hover table-sm table-striped mb-0 align-middle">
|
||||||
<thead class="table-dark">
|
<thead class="table-dark">
|
||||||
<tr>
|
<tr>
|
||||||
<th>Name</th>
|
<th>Name</th>
|
||||||
|
|
@ -58,7 +69,7 @@
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</main>
|
||||||
|
|
||||||
<!-- Job Modal -->
|
<!-- Job Modal -->
|
||||||
<div class="modal fade" id="jobModal" tabindex="-1">
|
<div class="modal fade" id="jobModal" tabindex="-1">
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue