MeshDD-Bot/static/js/app.js
ppfeiffer 6187bb4419 feat: v0.6.10 - Paket-Log-Seite und größeres Node-Modal
- Neue öffentliche Seite /packets: Echtzeit-Tabelle aller empfangenen
  Meshtastic-Pakete via WebSocket mit Typ-Filter, Pause und Clear
- DB: packets-Tabelle + insert_packet / get_recent_packets
- bot.py: alle Pakete loggen + WS-Broadcast (public)
- webserver.py: /packets Route + /api/packets + initial_packets im WS
- Sidebar: Eintrag 'Pakete' (öffentlich)
- Node-Modal: modal-xl + scrollable, Kartenhöhe 250→300px

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

106 lines
4.5 KiB
JavaScript
Raw 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.

// MeshDD-Bot Shared page module
// Provides: initPage(), escapeHtml(), applyTheme()
// ── Sidebar definition ────────────────────────────────────────
const _SIDEBAR_LINKS = [
{ href: '/', icon: 'bi-speedometer2', label: 'Dashboard', admin: false },
{ href: '/scheduler', icon: 'bi-clock-history', label: 'Scheduler', admin: true },
{ href: '/map', icon: 'bi-map', label: 'Karte', admin: false },
{ href: '/packets', icon: 'bi-reception-4', label: 'Pakete', admin: false },
{ href: '/settings', icon: 'bi-gear', label: 'Einstellungen',admin: true },
{ href: '/admin', icon: 'bi-people', label: 'Benutzer', admin: true },
];
function _injectSidebar() {
const sidebar = document.getElementById('sidebar');
if (!sidebar) return;
const currentPath = window.location.pathname;
sidebar.innerHTML = '<nav class="sidebar-nav">' +
_SIDEBAR_LINKS.map(link => {
const active = currentPath === link.href ? ' active' : '';
const adm = link.admin ? ' sidebar-admin' : '';
return `<a href="${link.href}" class="sidebar-link${active}${adm}">` +
`<i class="bi ${link.icon}"></i><span>${link.label}</span></a>`;
}).join('') +
'</nav>';
}
// ── Navbar ────────────────────────────────────────────────────
function _updateNavbar(user) {
const userMenu = document.getElementById('userMenu');
const loginBtn = document.getElementById('loginBtn');
const userName = document.getElementById('userName');
if (user) {
if (userName) userName.textContent = user.name;
if (userMenu) userMenu.classList.remove('d-none');
if (loginBtn) loginBtn.classList.add('d-none');
} else {
if (userMenu) userMenu.classList.add('d-none');
if (loginBtn) loginBtn.classList.remove('d-none');
}
}
function _updateSidebar(user) {
const isAdmin = user && user.role === 'admin';
document.querySelectorAll('.sidebar-admin').forEach(el => {
el.style.display = isAdmin ? '' : 'none';
});
}
// ── Theme ─────────────────────────────────────────────────────
function applyTheme(theme) {
document.documentElement.setAttribute('data-bs-theme', theme);
const icon = document.getElementById('themeIcon');
if (icon) icon.className = theme === 'dark' ? 'bi bi-sun-fill' : 'bi bi-moon-fill';
localStorage.setItem('theme', theme);
document.dispatchEvent(new CustomEvent('themechange', { detail: { theme } }));
}
function _setupTheme() {
applyTheme(localStorage.getItem('theme') || 'dark');
const btn = document.getElementById('themeToggle');
if (btn) {
btn.addEventListener('click', () => {
const current = document.documentElement.getAttribute('data-bs-theme');
applyTheme(current === 'dark' ? 'light' : 'dark');
});
}
}
// ── Sidebar toggle (mobile) ───────────────────────────────────
function _setupSidebarToggle() {
const toggle = document.getElementById('sidebarToggle');
const sidebar = document.getElementById('sidebar');
const backdrop = document.getElementById('sidebarBackdrop');
if (toggle && sidebar) toggle.addEventListener('click', () => sidebar.classList.toggle('open'));
if (backdrop && sidebar) backdrop.addEventListener('click', () => sidebar.classList.remove('open'));
}
// ── Utilities ─────────────────────────────────────────────────
function escapeHtml(str) {
if (!str) return '';
const div = document.createElement('div');
div.textContent = str;
return div.innerHTML;
}
// ── Public init ───────────────────────────────────────────────
function initPage({ onAuth = null } = {}) {
_injectSidebar();
_setupTheme();
fetch('/api/auth/me')
.then(r => r.ok ? r.json() : null)
.then(user => {
_updateNavbar(user);
_updateSidebar(user);
_setupSidebarToggle();
if (onAuth) onAuth(user);
});
}