/* ESPILON Honeypot Dashboard — UI Components */ import { escHtml, $id } from './utils.js'; // ── Toasts ────────────────────────────────────────────────── export function showToast(title, msg, detail, type) { type = type || 'info'; const c = $id('toast-container'); if (!c) return; const icons = { success: '✓', error: '✗', warning: '⚠', info: 'ℹ' }; const colors = { success: 'var(--status-success)', error: 'var(--status-error)', warning: 'var(--status-warning)', info: 'var(--accent-secondary)' }; const d = document.createElement('div'); d.className = 'toast'; d.dataset.type = type; d.innerHTML = '
' + (icons[type] || '') + '
' + '
' + escHtml(title) + '
' + '
' + escHtml(msg) + '
' + '
'; c.appendChild(d); requestAnimationFrame(() => d.classList.add('show')); setTimeout(() => { d.classList.remove('show'); setTimeout(() => d.remove(), 300); }, 4000); } // ── Modals & Panels ───────────────────────────────────────── export function closeDetail() { $id('detail-panel')?.classList.remove('open'); } export function closeModal() { $id('attacker-modal')?.classList.remove('open'); } // ── Sidebar & Nav Toggles ─────────────────────────────────── export function toggleSidebar() { $id('sidebar')?.classList.toggle('sidebar-open'); $id('sidebar-overlay')?.classList.toggle('active'); } export function toggleNavMenu() { $id('nav-tabs')?.classList.toggle('nav-open'); } export function toggleSbSection(headerEl) { const section = headerEl.closest('.sb-section'); if (!section) return; section.classList.toggle('collapsed'); document.querySelectorAll('.sb-section').forEach((sec, i) => { if (sec === section) localStorage.setItem('sb_' + i, sec.classList.contains('collapsed')); }); } export function scrollToAlerts() { const el = $id('alerts-panel'); if (el) el.scrollIntoView({behavior: 'smooth'}); } export function restoreSidebarState() { document.querySelectorAll('.sb-section').forEach((sec, i) => { if (localStorage.getItem('sb_' + i) === 'true') sec.classList.add('collapsed'); }); } // ── Event Delegation ──────────────────────────────────────── // Central click handler that replaces inline onclick attributes. // HTML elements use data-action="..." attributes; the action map // is populated by app.js after all modules are loaded. const _actionHandlers = {}; export function registerAction(name, handler) { _actionHandlers[name] = handler; } export function registerActions(map) { Object.assign(_actionHandlers, map); } export function setupEventDelegation() { document.addEventListener('click', (e) => { const el = e.target.closest('[data-action]'); if (!el) return; const action = el.dataset.action; const handler = _actionHandlers[action]; if (handler) { e.preventDefault(); handler(el, e); } }); // Modal backdrop clicks $id('attacker-modal')?.addEventListener('click', function(e) { if (e.target === this) closeModal(); }); $id('replay-modal')?.addEventListener('click', function(e) { if (e.target === this) { // closeReplay is registered via registerAction from sessions.js; // for the backdrop we just hide the modal directly. $id('replay-modal')?.classList.remove('open'); } }); } // ── Responsive Sidebar ────────────────────────────────────── export function setupResponsive() { function checkResponsive() { const w = window.innerWidth; const sidebarBtn = $id('sidebar-toggle-btn'); if (sidebarBtn) sidebarBtn.style.display = w < 1024 ? '' : 'none'; // On resize to desktop, remove mobile sidebar state if (w >= 1024) { $id('sidebar')?.classList.remove('sidebar-open'); $id('sidebar-overlay')?.classList.remove('active'); } } window.addEventListener('resize', checkResponsive); checkResponsive(); }