2026-03-14 21:08:57 -04:00
<!DOCTYPE html>
<!--
2026-03-26 18:22:53 -04:00
LOTUSGUILD TERMINAL DESIGN SYSTEM v1.2 — base.html
2026-03-14 21:08:57 -04:00
Reference template showing every component and layout pattern.
This file is a STATIC DEMO. Framework-specific wiring is in:
php/ → PHP / Tinker Tickets
python/ → Flask / Jinja2 / GANDALF
node/ → Express / EJS / PULSE
To build a new app, copy one of the framework skeletons from those
sub-directories and reference base.css + base.js.
-->
< html lang = "en" >
< head >
< meta charset = "UTF-8" >
2026-03-25 18:47:26 -04:00
< meta name = "viewport" content = "width=device-width, initial-scale=1.0, viewport-fit=cover" >
< meta name = "theme-color" content = "#030508" >
< meta name = "mobile-web-app-capable" content = "yes" >
< meta name = "apple-mobile-web-app-capable" content = "yes" >
< meta name = "apple-mobile-web-app-status-bar-style" content = "black-translucent" >
2026-03-14 21:08:57 -04:00
< title > MY APP — LotusGuild</ title >
< meta name = "description" content = "LotusGuild infrastructure application" >
2026-03-26 18:22:53 -04:00
< meta name = "robots" content = "noindex, nofollow" >
2026-03-14 21:08:57 -04:00
<!-- =========================================================
Security headers are set server-side. CSP nonce is injected
by SecurityHeadersMiddleware (PHP) / helmet (Node) / Flask.
All <script> tags need: nonce="NONCE_PLACEHOLDER"
========================================================= -->
2026-04-01 16:50:46 -04:00
<!-- Fonts: JetBrains Mono (UI) + VT323 (CRT display accent) -->
2026-03-25 18:47:26 -04:00
< link rel = "preconnect" href = "https://fonts.googleapis.com" >
< link rel = "preconnect" href = "https://fonts.gstatic.com" crossorigin >
2026-04-01 16:50:46 -04:00
< link href = "https://fonts.googleapis.com/css2?family=JetBrains+Mono:ital,wght@0,400;0,600;0,700;1,400&family=VT323&display=swap" rel = "stylesheet" >
2026-03-25 18:47:26 -04:00
<!-- Hacker template design system CSS -->
2026-03-14 21:08:57 -04:00
< link rel = "stylesheet" href = "/web_template/base.css" >
<!--
App-specific CSS (override or extend after base.css):
<link rel="stylesheet" href="/assets/css/app.css">
-->
< link rel = "icon" href = "/assets/images/favicon.png" type = "image/png" >
</ head >
< body >
2026-03-26 18:22:53 -04:00
< a class = "lt-skip-link" href = "#main-content" > Skip to main content</ a >
2026-03-14 21:08:57 -04:00
<!-- ===========================================================
BOOT SEQUENCE OVERLAY
Displays once per session. Remove if not desired.
data-app-name → used in the boot text banner.
=========================================================== -->
< div id = "lt-boot" class = "lt-boot-overlay" data-app-name = "MY APP" style = "display:none" >
< pre id = "lt-boot-text" class = "lt-boot-text" ></ pre >
</ div >
<!-- ===========================================================
HEADER
=========================================================== -->
2026-03-25 18:47:26 -04:00
<!-- ===========================================================
MOBILE NAV DRAWER (hidden on desktop, slides in on mobile)
=========================================================== -->
2026-03-26 18:22:53 -04:00
< div id = "lt-nav-drawer" class = "lt-nav-drawer" aria-hidden = "true" role = "dialog" aria-modal = "true" aria-label = "Navigation menu" >
2026-03-25 18:47:26 -04:00
< div class = "lt-nav-drawer-header" >
< span class = "lt-brand-title" > MY APP</ span >
2026-03-27 13:49:23 -04:00
< button type = "button" class = "lt-nav-drawer-close" id = "lt-nav-drawer-close" aria-label = "Close menu" > ✕</ button >
2026-03-25 18:47:26 -04:00
</ div >
< nav class = "lt-nav-drawer-links" aria-label = "Mobile navigation" >
< a href = "/" class = "lt-nav-drawer-link active" aria-current = "page" > Dashboard</ a >
< a href = "/tickets" class = "lt-nav-drawer-link" > Tickets</ a >
< a href = "/workers" class = "lt-nav-drawer-link" > Workers</ a >
< div class = "lt-nav-drawer-section" > Admin</ div >
< a href = "/admin/templates" class = "lt-nav-drawer-link lt-nav-drawer-link--indent" > Templates</ a >
< a href = "/admin/workflow" class = "lt-nav-drawer-link lt-nav-drawer-link--indent" > Workflow</ a >
< a href = "/admin/audit-log" class = "lt-nav-drawer-link lt-nav-drawer-link--indent" > Audit Log</ a >
< a href = "/admin/api-keys" class = "lt-nav-drawer-link lt-nav-drawer-link--indent" > API Keys</ a >
</ nav >
</ div >
< div id = "lt-nav-overlay" class = "lt-nav-drawer-overlay" ></ div >
2026-03-14 21:08:57 -04:00
< header class = "lt-header" >
< div class = "lt-header-left" >
2026-03-25 18:47:26 -04:00
<!-- Hamburger — visible only on tablet/mobile (CSS hides on desktop) -->
2026-03-27 13:49:23 -04:00
< button type = "button" class = "lt-menu-btn" id = "lt-menu-btn" aria-label = "Open navigation menu" aria-expanded = "false" aria-controls = "lt-nav-drawer" >
2026-03-25 18:47:26 -04:00
< span ></ span >< span ></ span >< span ></ span >
</ button >
2026-03-14 21:08:57 -04:00
<!-- Brand -->
< div class = "lt-brand" >
2026-03-25 18:47:26 -04:00
< span class = "lt-brand-title lt-glitch" data-text = "MY APP" > MY APP</ span >
2026-03-14 21:08:57 -04:00
< span class = "lt-brand-subtitle" > LotusGuild Infrastructure</ span >
</ div >
2026-03-25 18:47:26 -04:00
<!-- Horizontal nav links (desktop) -->
2026-03-14 21:08:57 -04:00
< nav class = "lt-nav" aria-label = "Main navigation" >
< a href = "/" class = "lt-nav-link active" > Dashboard</ a >
< a href = "/tickets" class = "lt-nav-link" > Tickets</ a >
< a href = "/workers" class = "lt-nav-link" > Workers</ a >
<!-- Dropdown example (admin menu) -->
< div class = "lt-nav-dropdown" >
< a href = "#" class = "lt-nav-link" > Admin ▾</ a >
< ul class = "lt-nav-dropdown-menu" >
< li >< a href = "/admin/templates" > Templates</ a ></ li >
< li >< a href = "/admin/workflow" > Workflow</ a ></ li >
< li >< a href = "/admin/audit-log" > Audit Log</ a ></ li >
< li >< a href = "/admin/api-keys" > API Keys</ a ></ li >
</ ul >
</ div >
</ nav >
</ div >
< div class = "lt-header-right" >
2026-03-25 22:29:55 -04:00
<!-- WebSocket status indicator -->
< div class = "lt-ws-status" id = "lt-ws-indicator" data-state = "disconnected" aria-label = "WebSocket status" >
< span class = "lt-dot" ></ span >< span > Offline</ span >
</ div >
<!-- Theme toggle -->
2026-03-27 13:49:23 -04:00
< button type = "button" class = "lt-theme-btn" id = "lt-theme-btn" aria-label = "Switch to light mode" title = "Switch to light mode" > ☀</ button >
2026-03-25 23:36:29 -04:00
<!-- Notifications with badge + dropdown -->
< div class = "lt-notif-dropdown-wrap" id = "lt-notif-bell" >
2026-03-27 13:49:23 -04:00
< button type = "button" class = "lt-btn lt-btn-sm lt-notif-bell-btn" id = "lt-notif-bell-btn" aria-label = "Open notifications" aria-expanded = "false" aria-haspopup = "true" style = "padding:0 0.6rem;" > 🔔</ button >
2026-03-26 19:51:21 -04:00
< div class = "lt-notif-panel" id = "lt-notif-panel" role = "region" aria-label = "Notifications" aria-hidden = "true" >
2026-03-25 23:36:29 -04:00
< div class = "lt-notif-panel-header" >
< span > Notifications</ span >
2026-03-26 20:16:12 -04:00
< button type = "button" class = "lt-notif-panel-clear" id = "lt-notif-clear-all" > Mark all read</ button >
2026-03-25 23:36:29 -04:00
</ div >
< div class = "lt-notif-panel-list" >
2026-03-26 20:16:12 -04:00
< div class = "lt-notif-item lt-notif-item--unread" role = "button" tabindex = "0" aria-label = "Unread: P1 alert: storage link-down, 5 min ago. Press Enter to dismiss." >
< span class = "lt-notif-dot" aria-hidden = "true" ></ span >
2026-03-25 23:36:29 -04:00
< div class = "lt-notif-item-body" >
< div class = "lt-notif-item-title" > P1 alert: storage link-down</ div >
< div class = "lt-notif-item-time" > 5 min ago</ div >
</ div >
</ div >
2026-03-26 20:16:12 -04:00
< div class = "lt-notif-item lt-notif-item--unread" role = "button" tabindex = "0" aria-label = "Unread: Worker node-03 reconnected, 12 min ago. Press Enter to dismiss." >
< span class = "lt-notif-dot" aria-hidden = "true" ></ span >
2026-03-25 23:36:29 -04:00
< div class = "lt-notif-item-body" >
< div class = "lt-notif-item-title" > Worker node-03 reconnected</ div >
< div class = "lt-notif-item-time" > 12 min ago</ div >
</ div >
</ div >
2026-03-26 20:16:12 -04:00
< div class = "lt-notif-item lt-notif-item--unread" role = "button" tabindex = "0" aria-label = "Unread: Export CSV complete — 42 rows, 1 hr ago. Press Enter to dismiss." >
< span class = "lt-notif-dot" aria-hidden = "true" ></ span >
2026-03-25 23:36:29 -04:00
< div class = "lt-notif-item-body" >
< div class = "lt-notif-item-title" > Export CSV complete — 42 rows</ div >
< div class = "lt-notif-item-time" > 1 hr ago</ div >
</ div >
</ div >
2026-03-26 20:16:12 -04:00
< div class = "lt-notif-item" role = "button" tabindex = "0" aria-label = "Scheduled maintenance completed, 3 hr ago. Press Enter to dismiss." >
< span class = "lt-notif-dot lt-notif-dot--read" aria-hidden = "true" ></ span >
2026-03-25 23:36:29 -04:00
< div class = "lt-notif-item-body" >
< div class = "lt-notif-item-title" > Scheduled maintenance completed</ div >
< div class = "lt-notif-item-time" > 3 hr ago</ div >
</ div >
</ div >
</ div >
< div class = "lt-notif-panel-footer" >
2026-03-26 20:46:31 -04:00
< button type = "button" class = "lt-btn lt-btn-sm lt-btn-ghost" style = "width:100%;font-size:0.72rem" > View all notifications</ button >
2026-03-25 23:36:29 -04:00
</ div >
</ div >
</ div >
2026-03-14 21:08:57 -04:00
<!-- Current user -->
< span class = "lt-header-user" > operator</ span >
<!-- Admin badge (only show for admins) -->
< span class = "lt-badge-admin" > admin</ span >
</ div >
</ header >
2026-03-25 22:29:55 -04:00
<!-- Right-side detail drawer -->
2026-03-26 18:22:53 -04:00
< div id = "lt-detail-drawer" class = "lt-drawer-right" aria-hidden = "true" role = "dialog" aria-modal = "true" aria-label = "Detail panel" data-overlay = "lt-detail-overlay" >
2026-03-25 22:29:55 -04:00
< div class = "lt-drawer-right-header" >
< span class = "lt-drawer-right-title" > // TICKET DETAIL</ span >
2026-03-27 13:49:23 -04:00
< button type = "button" class = "lt-drawer-right-close" data-drawer-close aria-label = "Close detail panel" > ✕</ button >
2026-03-25 22:29:55 -04:00
</ div >
< div class = "lt-drawer-right-body" >
2026-03-25 23:36:29 -04:00
<!-- Read-only meta row -->
< div class = "lt-kv-grid" style = "margin-bottom:1rem" >
2026-03-25 22:29:55 -04:00
< div class = "lt-kv-row" >< span class = "lt-kv-label" > ID</ span >< span class = "lt-kv-value lt-text-cyan" > #123456789</ span ></ div >
2026-03-25 23:36:29 -04:00
< div class = "lt-kv-row" >< span class = "lt-kv-label" > Created</ span >< span class = "lt-kv-value lt-text-muted" > 2026-03-10 09:14 UTC</ span ></ div >
2026-03-25 22:29:55 -04:00
</ div >
2026-03-25 23:36:29 -04:00
<!-- Editable fields -->
< div class = "lt-form-group" >
< label class = "lt-label" for = "td-title" > Title</ label >
< input id = "td-title" class = "lt-input" type = "text" value = "Storage array link-down on compute-storage-01" >
</ div >
2026-03-25 23:51:21 -04:00
< div class = "lt-drawer-form-grid" style = "display:grid;grid-template-columns:1fr 1fr;gap:0.75rem" >
2026-03-25 23:36:29 -04:00
< div class = "lt-form-group" >
< label class = "lt-label" for = "td-status" > Status</ label >
< select id = "td-status" class = "lt-select" >
< option selected > Open</ option >
< option > In Progress</ option >
< option > Pending</ option >
< option > Closed</ option >
</ select >
</ div >
< div class = "lt-form-group" >
< label class = "lt-label" for = "td-priority" > Priority</ label >
< select id = "td-priority" class = "lt-select" >
< option selected > P1 Critical</ option >
< option > P2 High</ option >
< option > P3 Medium</ option >
< option > P4 Low</ option >
</ select >
</ div >
</ div >
< div class = "lt-form-group" >
< label class = "lt-label" for = "td-assignee" > Assignee</ label >
< select id = "td-assignee" class = "lt-select" >
< option > Unassigned</ option >
< option selected > jdoe</ option >
< option > operator</ option >
< option > admin</ option >
</ select >
</ div >
< div class = "lt-form-group" >
< label class = "lt-label" for = "td-desc" > Description</ label >
< textarea id = "td-desc" class = "lt-input lt-textarea" rows = "3" style = "resize:vertical" > Storage array link-down on compute-storage-01. Affects prod write path. Investigate RAID controller firmware.</ textarea >
</ div >
<!-- Add a comment -->
< div class = "lt-divider-label" style = "margin:1rem 0 0.75rem" > Add Comment</ div >
< div class = "lt-form-group" style = "margin-bottom:0.5rem" >
2026-03-26 20:46:31 -04:00
< textarea id = "td-comment" class = "lt-input lt-textarea" rows = "2" placeholder = "Leave a comment…" style = "resize:vertical" aria-label = "Add comment" ></ textarea >
2026-03-25 23:36:29 -04:00
</ div >
2026-03-26 18:22:53 -04:00
< button type = "button" class = "lt-btn lt-btn-sm" onclick = "
2026-03-25 23:36:29 -04:00
const c=document.getElementById('td-comment');
if(c.value.trim()){lt.toast.success('Comment posted');c.value='';}
else lt.toast.warning('Comment is empty');
" > Post Comment</ button >
<!-- Activity timeline -->
< div class = "lt-divider-label" style = "margin:1rem 0 0.75rem" > Activity</ div >
2026-03-25 22:29:55 -04:00
< div class = "lt-timeline" >
< div class = "lt-timeline-item lt-timeline-item--orange" >
< div class = "lt-timeline-meta" >< span class = "lt-timeline-actor" > jdoe</ span > assigned ticket< span class = "lt-timeline-time" > 2h ago</ span ></ div >
< div class = "lt-timeline-body" > Assigned to self, escalated to P1.</ div >
</ div >
< div class = "lt-timeline-item" >
< div class = "lt-timeline-meta" >< span class = "lt-timeline-actor" > sysbot</ span > auto-created< span class = "lt-timeline-time" > 3h ago</ span ></ div >
< div class = "lt-timeline-body" > Alert triggered: < code > node_network_up = 0</ code ></ div >
</ div >
< div class = "lt-timeline-item lt-timeline-item--dim" >
< div class = "lt-timeline-meta" >< span class = "lt-timeline-actor" > monitor</ span > detected< span class = "lt-timeline-time" > 3h ago</ span ></ div >
< div class = "lt-timeline-body" > NIC link-down detected on large1:enp35s0.</ div >
</ div >
</ div >
</ div >
< div class = "lt-drawer-right-footer" >
2026-03-26 18:22:53 -04:00
< button type = "button" class = "lt-btn lt-btn-sm" onclick = "lt.rightDrawer.close('lt-detail-drawer')" > Cancel</ button >
< button type = "button" class = "lt-btn lt-btn-primary lt-btn-sm" onclick = "lt.toast.success('Ticket #123456789 updated')" > Save Changes</ button >
2026-03-25 22:29:55 -04:00
</ div >
</ div >
< div id = "lt-detail-overlay" class = "lt-drawer-right-overlay" ></ div >
2026-03-14 21:08:57 -04:00
<!-- ===========================================================
MAIN CONTENT AREA
=========================================================== -->
2026-03-26 18:22:53 -04:00
< main class = "lt-main lt-container" id = "main-content" >
2026-03-14 21:08:57 -04:00
<!-- Page title bar -->
< div class = "lt-page-header" >
< h1 class = "lt-page-title" > Dashboard</ h1 >
< div class = "lt-btn-group" >
< a href = "/create" class = "lt-btn lt-btn-primary" > New Ticket</ a >
2026-03-26 20:46:31 -04:00
< button type = "button" class = "lt-btn lt-btn-sm" data-modal-open = "export-modal" > Export</ button >
2026-03-14 21:08:57 -04:00
</ div >
</ div >
<!-- ==========================================================
STATS WIDGETS
data-filter-key / data-filter-val → wired by lt.statsFilter
========================================================== -->
< div class = "lt-stats-grid" >
2026-03-26 20:02:15 -04:00
< div class = "lt-stat-card active" role = "button" tabindex = "0" data-filter-key = "status" data-filter-val = "Open" aria-label = "Open tickets: 42" >
< span class = "lt-stat-icon" aria-hidden = "true" > 📋</ span >
2026-03-14 21:08:57 -04:00
< div class = "lt-stat-info" >
< span class = "lt-stat-value" > 42</ span >
< span class = "lt-stat-label" > Open</ span >
</ div >
</ div >
2026-03-26 20:02:15 -04:00
< div class = "lt-stat-card" role = "button" tabindex = "0" data-filter-key = "priority" data-filter-val = "1" aria-label = "Critical tickets: 3" >
< span class = "lt-stat-icon" aria-hidden = "true" > 🔴</ span >
2026-03-14 21:08:57 -04:00
< div class = "lt-stat-info" >
< span class = "lt-stat-value" > 3</ span >
< span class = "lt-stat-label" > Critical</ span >
</ div >
</ div >
2026-03-26 20:02:15 -04:00
< div class = "lt-stat-card" role = "button" tabindex = "0" data-filter-key = "assigned_to" data-filter-val = "0" aria-label = "Unassigned tickets: 11" >
< span class = "lt-stat-icon" aria-hidden = "true" > 👤</ span >
2026-03-14 21:08:57 -04:00
< div class = "lt-stat-info" >
< span class = "lt-stat-value" > 11</ span >
< span class = "lt-stat-label" > Unassigned</ span >
</ div >
</ div >
2026-03-26 20:02:15 -04:00
< div class = "lt-stat-card" role = "button" tabindex = "0" data-filter-key = "created" data-filter-val = "today" aria-label = "Today's tickets: 7" >
< span class = "lt-stat-icon" aria-hidden = "true" > 📅</ span >
2026-03-14 21:08:57 -04:00
< div class = "lt-stat-info" >
< span class = "lt-stat-value" > 7</ span >
< span class = "lt-stat-label" > Today</ span >
</ div >
</ div >
</ div >
<!-- ==========================================================
TAB NAVIGATION
========================================================== -->
2026-03-26 18:22:53 -04:00
< div class = "lt-tabs" role = "tablist" aria-label = "Main views" >
2026-03-27 13:49:23 -04:00
< button type = "button" class = "lt-tab active" role = "tab" data-tab = "tab-table" aria-selected = "true" aria-controls = "tab-table" id = "tab-btn-table" > Table View</ button >
< button type = "button" class = "lt-tab" role = "tab" data-tab = "tab-kanban" aria-selected = "false" aria-controls = "tab-kanban" id = "tab-btn-kanban" > Kanban</ button >
< button type = "button" class = "lt-tab" role = "tab" data-tab = "tab-workers" aria-selected = "false" aria-controls = "tab-workers" id = "tab-btn-workers" > Workers</ button >
2026-03-14 21:08:57 -04:00
</ div >
<!-- ==========================================================
SIDEBAR + CONTENT LAYOUT
========================================================== -->
< div class = "lt-layout" >
<!-- Sidebar filter panel -->
< aside class = "lt-sidebar" id = "lt-sidebar" >
< div class = "lt-sidebar-header" >
Filters
2026-03-27 13:49:23 -04:00
< button type = "button" class = "lt-sidebar-toggle" data-sidebar-toggle = "lt-sidebar"
2026-03-14 21:08:57 -04:00
aria-label = "Collapse filters" > ◀</ button >
</ div >
< div class = "lt-sidebar-body" >
2026-03-26 18:22:53 -04:00
< fieldset class = "lt-filter-group" >
< legend class = "lt-filter-label" > Status</ legend >
2026-03-14 21:08:57 -04:00
< label class = "lt-filter-option" >
< input type = "checkbox" class = "lt-checkbox" checked > Open
</ label >
< label class = "lt-filter-option" >
< input type = "checkbox" class = "lt-checkbox" > Pending
</ label >
< label class = "lt-filter-option" >
< input type = "checkbox" class = "lt-checkbox" > In Progress
</ label >
< label class = "lt-filter-option" >
< input type = "checkbox" class = "lt-checkbox" > Closed
</ label >
2026-03-26 18:22:53 -04:00
</ fieldset >
2026-03-14 21:08:57 -04:00
2026-03-26 18:22:53 -04:00
< fieldset class = "lt-filter-group" >
< legend class = "lt-filter-label" > Priority</ legend >
2026-03-14 21:08:57 -04:00
< label class = "lt-filter-option" >
< input type = "checkbox" class = "lt-checkbox" > P1 Critical
</ label >
< label class = "lt-filter-option" >
< input type = "checkbox" class = "lt-checkbox" > P2 High
</ label >
< label class = "lt-filter-option" >
< input type = "checkbox" class = "lt-checkbox" > P3 Medium
</ label >
2026-03-26 18:22:53 -04:00
</ fieldset >
2026-03-14 21:08:57 -04:00
< div class = "lt-filter-group" >
2026-03-26 19:51:21 -04:00
< label class = "lt-filter-label" for = "filter-assigned-to" > Assigned To</ label >
2026-03-14 21:08:57 -04:00
< div class = "lt-form-group" >
2026-03-26 19:51:21 -04:00
< select class = "lt-select lt-btn-sm" id = "filter-assigned-to" >
2026-03-14 21:08:57 -04:00
< option value = "" > All users</ option >
< option > operator</ option >
< option > admin</ option >
</ select >
</ div >
</ div >
< div class = "lt-btn-group" >
2026-03-26 20:46:31 -04:00
< button type = "button" class = "lt-btn lt-btn-sm" > Apply</ button >
< button type = "button" class = "lt-btn lt-btn-sm lt-btn-ghost" > Reset</ button >
2026-03-14 21:08:57 -04:00
</ div >
</ div >
</ aside >
<!-- Main content -->
< div class = "lt-content" >
<!-- Toolbar: search + actions -->
< div class = "lt-toolbar" >
< div class = "lt-toolbar-left" >
< div class = "lt-search" >
2026-03-25 23:36:29 -04:00
< input type = "search" class = "lt-input lt-search-input" id = "ticket-search"
2026-03-26 18:22:53 -04:00
placeholder = "Search tickets..." aria-label = "Search" autocomplete = "off" >
2026-03-14 21:08:57 -04:00
</ div >
2026-03-25 23:36:29 -04:00
<!-- Advanced filter dropdown -->
< div class = "lt-dropdown-wrap" id = "adv-filter-wrap" >
2026-03-26 20:46:31 -04:00
< button type = "button" class = "lt-btn lt-btn-sm lt-dropdown-trigger" id = "adv-filter-btn" aria-expanded = "false" aria-haspopup = "true" > Advanced ▾</ button >
2026-03-25 23:36:29 -04:00
< div class = "lt-dropdown-panel" id = "adv-filter-panel" aria-hidden = "true" >
2026-03-25 23:51:21 -04:00
< div style = "padding:0.75rem;display:grid;gap:0.5rem;width:clamp(200px,60vw,260px)" >
2026-03-25 23:36:29 -04:00
< div class = "lt-form-group" style = "margin:0" >
2026-03-26 20:02:15 -04:00
< label class = "lt-label" style = "font-size:0.75rem" for = "adv-filter-status" > Status</ label >
< select id = "adv-filter-status" class = "lt-select" style = "font-size:0.8rem" >
2026-03-25 23:36:29 -04:00
< option value = "" > All</ option >
< option > Open</ option >
< option > In Progress</ option >
< option > Pending</ option >
< option > Closed</ option >
</ select >
</ div >
< div class = "lt-form-group" style = "margin:0" >
2026-03-26 20:02:15 -04:00
< label class = "lt-label" style = "font-size:0.75rem" for = "adv-filter-priority" > Priority</ label >
< select id = "adv-filter-priority" class = "lt-select" style = "font-size:0.8rem" >
2026-03-25 23:36:29 -04:00
< option value = "" > All</ option >
< option > P1 Critical</ option >
< option > P2 High</ option >
< option > P3 Medium</ option >
< option > P4 Low</ option >
</ select >
</ div >
< div class = "lt-form-group" style = "margin:0" >
2026-03-26 20:02:15 -04:00
< label class = "lt-label" style = "font-size:0.75rem" for = "adv-filter-assignee" > Assignee</ label >
< select id = "adv-filter-assignee" class = "lt-select" style = "font-size:0.8rem" >
2026-03-25 23:36:29 -04:00
< option value = "" > Anyone</ option >
< option > Unassigned</ option >
< option > jdoe</ option >
< option > operator</ option >
< option > admin</ option >
</ select >
</ div >
< div style = "display:flex;gap:0.5rem;margin-top:0.25rem" >
2026-03-26 18:22:53 -04:00
< button type = "button" class = "lt-btn lt-btn-sm lt-btn-primary" style = "flex:1" onclick = "lt.toast.info('Filters applied');document.getElementById('adv-filter-panel').setAttribute('aria-hidden','true');document.getElementById('adv-filter-btn').setAttribute('aria-expanded','false')" > Apply</ button >
< button type = "button" class = "lt-btn lt-btn-sm lt-btn-ghost" onclick = "lt.toast.info('Filters cleared')" > Reset</ button >
2026-03-25 23:36:29 -04:00
</ div >
</ div >
</ div >
</ div >
2026-03-14 21:08:57 -04:00
</ div >
< div class = "lt-toolbar-right" >
2026-03-25 23:51:21 -04:00
< span class = "lt-text-muted lt-text-xs lt-hide-xs" id = "ticket-result-count" > 42 results</ span >
2026-03-25 23:36:29 -04:00
<!-- Bulk actions dropdown -->
< div class = "lt-dropdown-wrap" id = "bulk-action-wrap" >
2026-03-26 20:46:31 -04:00
< button type = "button" class = "lt-btn lt-btn-sm lt-btn-ghost lt-dropdown-trigger" id = "bulk-action-btn" aria-expanded = "false" aria-haspopup = "true" > Bulk Actions ▾</ button >
2026-03-25 23:36:29 -04:00
< div class = "lt-dropdown-panel lt-dropdown-panel--right" id = "bulk-action-panel" aria-hidden = "true" >
2026-03-26 18:22:53 -04:00
< button type = "button" class = "lt-dropdown-item" onclick = "lt.toast.success('Closed selected tickets');this.closest('.lt-dropdown-panel').setAttribute('aria-hidden','true')" > ✓ Close Selected</ button >
< button type = "button" class = "lt-dropdown-item" onclick = "lt.toast.info('Reassign dialog…');this.closest('.lt-dropdown-panel').setAttribute('aria-hidden','true')" > ↩ Reassign…</ button >
< button type = "button" class = "lt-dropdown-item" onclick = "lt.toast.info('Exporting selected…');this.closest('.lt-dropdown-panel').setAttribute('aria-hidden','true')" > ⤓ Export Selected</ button >
2026-03-25 23:36:29 -04:00
< div class = "lt-dropdown-divider" ></ div >
2026-03-26 18:22:53 -04:00
< button type = "button" class = "lt-dropdown-item lt-dropdown-item--danger" onclick = "lt.toast.error('Deleted selected');this.closest('.lt-dropdown-panel').setAttribute('aria-hidden','true')" > 🗑 Delete Selected</ button >
2026-03-25 23:36:29 -04:00
</ div >
</ div >
2026-03-14 21:08:57 -04:00
</ div >
</ div >
<!-- ==================================================
TAB PANEL: TABLE VIEW
================================================== -->
2026-03-26 18:22:53 -04:00
< div id = "tab-table" class = "lt-tab-panel active" role = "tabpanel" aria-labelledby = "tab-btn-table" >
2026-03-14 21:08:57 -04:00
<!-- Outer ASCII frame wrapping the table -->
< div class = "lt-frame" >
< span class = "lt-frame-bl" > ╚</ span >
< span class = "lt-frame-br" > ╝</ span >
< div class = "lt-section-header" > Ticket Queue</ div >
< div class = "lt-table-wrap" >
2026-03-26 00:12:04 -04:00
< table class = "lt-table lt-table-responsive" id = "ticket-table" aria-label = "Ticket queue" >
< caption class = "lt-sr-only" > Ticket queue — sorted by priority</ caption >
2026-03-14 21:08:57 -04:00
< thead >
< tr >
2026-03-26 18:22:53 -04:00
< th scope = "col" >< input type = "checkbox" class = "lt-checkbox" aria-label = "Select all" ></ th >
< th scope = "col" data-sort-key = "id" aria-sort = "none" > ID</ th >
< th scope = "col" data-sort-key = "priority" aria-sort = "none" > Priority</ th >
< th scope = "col" data-sort-key = "title" aria-sort = "none" > Title</ th >
< th scope = "col" data-sort-key = "status" aria-sort = "none" > Status</ th >
< th scope = "col" data-sort-key = "assignee" aria-sort = "none" > Assignee</ th >
< th scope = "col" data-sort-key = "created" aria-sort = "none" > Created</ th >
< th scope = "col" > Actions</ th >
2026-03-14 21:08:57 -04:00
</ tr >
</ thead >
< tbody >
<!-- P1 Critical row -->
< tr class = "lt-row-p1 lt-row-critical" >
2026-03-26 18:22:53 -04:00
< td data-label = "Select" >< input type = "checkbox" class = "lt-checkbox" aria-label = "Select ticket #123456789" ></ td >
2026-03-25 18:47:26 -04:00
< td data-label = "ID" >< a href = "/ticket/123456789" > #123456789</ a ></ td >
< td data-label = "Priority" >< span class = "lt-p1" > P1 Critical</ span ></ td >
< td data-label = "Title" > Storage array link-down on compute-storage-01</ td >
< td data-label = "Status" >< span class = "lt-status lt-status-open" > Open</ span ></ td >
< td data-label = "Assignee" class = "lt-text-muted" > Unassigned</ td >
< td data-label = "Created" class = "lt-text-xs lt-text-muted" > 5m ago</ td >
< td data-label = "Actions" >
2026-03-14 21:08:57 -04:00
< div class = "lt-btn-group" >
< a href = "/ticket/123456789" class = "lt-btn lt-btn-sm" > View</ a >
2026-03-26 20:46:31 -04:00
< button type = "button" class = "lt-btn lt-btn-sm lt-btn-danger" > Close</ button >
2026-03-14 21:08:57 -04:00
</ div >
</ td >
</ tr >
<!-- P2 High row -->
< tr class = "lt-row-p2 lt-row-warning" >
2026-03-26 18:22:53 -04:00
< td data-label = "Select" >< input type = "checkbox" class = "lt-checkbox" aria-label = "Select ticket #987654321" ></ td >
2026-03-25 18:47:26 -04:00
< td data-label = "ID" >< a href = "/ticket/987654321" > #987654321</ a ></ td >
< td data-label = "Priority" >< span class = "lt-p2" > P2 High</ span ></ td >
< td data-label = "Title" > Switch port flapping on USW-Pro-24</ td >
< td data-label = "Status" >< span class = "lt-status lt-status-in-progress" > In Progress</ span ></ td >
< td data-label = "Assignee" > operator</ td >
< td data-label = "Created" class = "lt-text-xs lt-text-muted" > 2h ago</ td >
< td data-label = "Actions" >
2026-03-14 21:08:57 -04:00
< div class = "lt-btn-group" >
< a href = "/ticket/987654321" class = "lt-btn lt-btn-sm" > View</ a >
</ div >
</ td >
</ tr >
<!-- P3 Medium row -->
< tr class = "lt-row-p3" >
2026-03-26 18:22:53 -04:00
< td data-label = "Select" >< input type = "checkbox" class = "lt-checkbox" aria-label = "Select ticket #111222333" ></ td >
2026-03-25 18:47:26 -04:00
< td data-label = "ID" >< a href = "/ticket/111222333" > #111222333</ a ></ td >
< td data-label = "Priority" >< span class = "lt-p3" > P3 Med</ span ></ td >
< td data-label = "Title" > Scheduled maintenance: replace SFP+ on large1</ td >
< td data-label = "Status" >< span class = "lt-status lt-status-pending" > Pending</ span ></ td >
< td data-label = "Assignee" > admin</ td >
< td data-label = "Created" class = "lt-text-xs lt-text-muted" > 1d ago</ td >
< td data-label = "Actions" >
2026-03-14 21:08:57 -04:00
< div class = "lt-btn-group" >
< a href = "/ticket/111222333" class = "lt-btn lt-btn-sm" > View</ a >
</ div >
</ td >
</ tr >
<!-- P4 closed -->
< tr class = "lt-row-p4" >
2026-03-26 18:22:53 -04:00
< td data-label = "Select" >< input type = "checkbox" class = "lt-checkbox" aria-label = "Select ticket #444555666" ></ td >
2026-03-25 18:47:26 -04:00
< td data-label = "ID" >< a href = "/ticket/444555666" > #444555666</ a ></ td >
< td data-label = "Priority" >< span class = "lt-p4" > P4 Low</ span ></ td >
< td data-label = "Title" > Update SSL cert on wiki.lotusguild.org</ td >
< td data-label = "Status" >< span class = "lt-status lt-status-closed" > Closed</ span ></ td >
< td data-label = "Assignee" > operator</ td >
< td data-label = "Created" class = "lt-text-xs lt-text-muted" > 3d ago</ td >
< td data-label = "Actions" >
2026-03-14 21:08:57 -04:00
< div class = "lt-btn-group" >
< a href = "/ticket/444555666" class = "lt-btn lt-btn-sm" > View</ a >
</ div >
</ td >
</ tr >
</ tbody >
</ table >
</ div >
</ div > <!-- /.lt-frame -->
</ div > <!-- /#tab-table -->
<!-- ==================================================
TAB PANEL: KANBAN
================================================== -->
2026-03-26 18:22:53 -04:00
< div id = "tab-kanban" class = "lt-tab-panel" role = "tabpanel" aria-labelledby = "tab-btn-kanban" >
2026-03-14 21:08:57 -04:00
< div class = "lt-grid-4" >
<!-- Kanban column: Open -->
< div class = "lt-frame" >
< span class = "lt-frame-bl" > ╚</ span >
< span class = "lt-frame-br" > ╝</ span >
< div class = "lt-section-header" > Open</ div >
2026-03-25 23:19:49 -04:00
< div class = "lt-section-body" id = "kanban-col-open" style = "min-height:60px" >
2026-03-14 21:08:57 -04:00
2026-03-26 20:09:29 -04:00
< div class = "lt-card lt-mb-md lt-row-p1" role = "article" aria-label = "P1 — Storage array link-down, 5m ago, Unassigned" >
2026-03-14 21:08:57 -04:00
< div class = "lt-flex lt-flex-between lt-mb-md" >
2026-03-26 19:51:21 -04:00
< span class = "lt-p1" aria-hidden = "true" > P1</ span >
< span class = "lt-dot lt-dot-up" aria-hidden = "true" ></ span >
2026-03-14 21:08:57 -04:00
</ div >
< div class = "lt-text-sm" > Storage array link-down</ div >
< div class = "lt-text-xs lt-text-muted lt-mt-sm" > 5m ago · Unassigned</ div >
</ div >
2026-03-26 20:09:29 -04:00
< div class = "lt-card lt-row-p3" role = "article" aria-label = "P3 — Update node_exporter on micro1, 1d ago, operator" >
2026-03-14 21:08:57 -04:00
< div class = "lt-flex lt-flex-between lt-mb-md" >
2026-03-26 19:51:21 -04:00
< span class = "lt-p3" aria-hidden = "true" > P3</ span >
< span class = "lt-dot lt-dot-up" aria-hidden = "true" ></ span >
2026-03-14 21:08:57 -04:00
</ div >
< div class = "lt-text-sm" > Update node_exporter on micro1</ div >
< div class = "lt-text-xs lt-text-muted lt-mt-sm" > 1d ago · operator</ div >
</ div >
</ div >
</ div >
<!-- Kanban column: Pending -->
< div class = "lt-frame" >
< span class = "lt-frame-bl" > ╚</ span >
< span class = "lt-frame-br" > ╝</ span >
< div class = "lt-section-header" > Pending</ div >
2026-03-25 23:19:49 -04:00
< div class = "lt-section-body" id = "kanban-col-pending" style = "min-height:60px" >
2026-03-26 20:09:29 -04:00
< div class = "lt-card lt-row-p2" role = "article" aria-label = "P2 — Scheduled maintenance window, 2d ago, admin" >
2026-03-14 21:08:57 -04:00
< div class = "lt-flex lt-flex-between lt-mb-md" >
2026-03-26 19:51:21 -04:00
< span class = "lt-p2" aria-hidden = "true" > P2</ span >
< span class = "lt-dot lt-dot-warn" aria-hidden = "true" ></ span >
2026-03-14 21:08:57 -04:00
</ div >
< div class = "lt-text-sm" > Scheduled maintenance window</ div >
< div class = "lt-text-xs lt-text-muted lt-mt-sm" > 2d ago · admin</ div >
</ div >
</ div >
</ div >
<!-- Kanban column: In Progress -->
< div class = "lt-frame" >
< span class = "lt-frame-bl" > ╚</ span >
< span class = "lt-frame-br" > ╝</ span >
< div class = "lt-section-header" > In Progress</ div >
2026-03-25 23:19:49 -04:00
< div class = "lt-section-body" id = "kanban-col-inprogress" style = "min-height:60px" >
2026-03-26 20:09:29 -04:00
< div class = "lt-card lt-row-p2 lt-item-running" role = "article" aria-label = "P2 — Switch port flapping on USW-Pro, 2h ago, operator" >
2026-03-14 21:08:57 -04:00
< div class = "lt-flex lt-flex-between lt-mb-md" >
2026-03-26 19:51:21 -04:00
< span class = "lt-p2" aria-hidden = "true" > P2</ span >
< span class = "lt-dot lt-dot-warn" aria-hidden = "true" ></ span >
2026-03-14 21:08:57 -04:00
</ div >
< div class = "lt-text-sm" > Switch port flapping on USW-Pro</ div >
< div class = "lt-text-xs lt-text-muted lt-mt-sm" > 2h ago · operator</ div >
</ div >
</ div >
</ div >
<!-- Kanban column: Closed -->
< div class = "lt-frame" >
< span class = "lt-frame-bl" > ╚</ span >
< span class = "lt-frame-br" > ╝</ span >
< div class = "lt-section-header" > Closed</ div >
2026-03-25 23:19:49 -04:00
< div class = "lt-section-body" id = "kanban-col-closed" style = "min-height:60px" >
2026-03-26 20:09:29 -04:00
< div class = "lt-card lt-row-p4" style = "opacity:0.6" role = "article" aria-label = "P4 — Update SSL cert on wiki, 3d ago, operator" >
2026-03-14 21:08:57 -04:00
< div class = "lt-flex lt-flex-between lt-mb-md" >
2026-03-26 19:51:21 -04:00
< span class = "lt-p4" aria-hidden = "true" > P4</ span >
< span class = "lt-dot lt-dot-idle" aria-hidden = "true" ></ span >
2026-03-14 21:08:57 -04:00
</ div >
< div class = "lt-text-sm" > Update SSL cert on wiki</ div >
< div class = "lt-text-xs lt-text-muted lt-mt-sm" > 3d ago · operator</ div >
</ div >
</ div >
</ div >
</ div > <!-- /.lt-grid-4 -->
</ div > <!-- /#tab-kanban -->
<!-- ==================================================
TAB PANEL: WORKERS
================================================== -->
2026-03-26 18:22:53 -04:00
< div id = "tab-workers" class = "lt-tab-panel" role = "tabpanel" aria-labelledby = "tab-btn-workers" >
2026-03-14 21:08:57 -04:00
< div class = "lt-grid-3" >
< div class = "lt-card" >
< div class = "lt-flex lt-flex-between lt-mb-md" >
< span class = "lt-text-amber lt-text-upper" > pulse-worker-01</ span >
< span class = "lt-status lt-status-online" > Online</ span >
</ div >
< div class = "lt-data-table-wrapper" >
2026-03-26 20:09:29 -04:00
< table class = "lt-data-table" aria-label = "pulse-worker-01 metrics" >
2026-03-26 18:22:53 -04:00
< tbody >
2026-03-26 20:09:29 -04:00
< tr >< th scope = "row" class = "lt-text-muted" > CPU</ th > < td > 12%</ td ></ tr >
< tr >< th scope = "row" class = "lt-text-muted" > Memory</ th > < td > 2.1 GB / 8 GB</ td ></ tr >
< tr >< th scope = "row" class = "lt-text-muted" > Load</ th > < td > 0.42 / 0.51 / 0.48</ td ></ tr >
< tr >< th scope = "row" class = "lt-text-muted" > Uptime</ th > < td > 14d 6h</ td ></ tr >
< tr >< th scope = "row" class = "lt-text-muted" > Tasks</ th > < td > 2 / 5</ td ></ tr >
2026-03-26 18:22:53 -04:00
</ tbody >
2026-03-14 21:08:57 -04:00
</ table >
</ div >
</ div >
< div class = "lt-card" >
< div class = "lt-flex lt-flex-between lt-mb-md" >
< span class = "lt-text-amber lt-text-upper" > pulse-worker-02</ span >
< span class = "lt-status lt-status-offline" > Offline</ span >
</ div >
< div class = "lt-msg lt-msg-warning" > Last seen 14m ago</ div >
</ div >
< div class = "lt-card" >
< div class = "lt-empty" > No more workers registered.</ div >
</ div >
</ div >
</ div > <!-- /#tab-workers -->
</ div > <!-- /.lt-content -->
</ div > <!-- /.lt-layout -->
<!-- ==========================================================
COMPONENT SHOWCASE (remove in production)
========================================================== -->
< div class = "lt-divider" ></ div >
<!-- Inner frame + subsection example -->
< div class = "lt-frame" >
< span class = "lt-frame-bl" > ╚</ span >
< span class = "lt-frame-br" > ╝</ span >
< div class = "lt-section-header" > Component Reference</ div >
< div class = "lt-frame-inner" >
< div class = "lt-subsection-header" > Buttons</ div >
< div class = "lt-section-body" >
< div class = "lt-btn-group" >
2026-03-26 20:46:31 -04:00
< button type = "button" class = "lt-btn" > Default</ button >
< button type = "button" class = "lt-btn lt-btn-primary" > Primary</ button >
< button type = "button" class = "lt-btn lt-btn-danger" > Danger</ button >
< button type = "button" class = "lt-btn lt-btn-sm" > Small</ button >
< button type = "button" class = "lt-btn lt-btn-ghost" > Ghost</ button >
< button type = "button" class = "lt-btn" disabled > Disabled</ button >
2026-03-14 21:08:57 -04:00
</ div >
</ div >
< div class = "lt-subsection-header" > Status Badges</ div >
< div class = "lt-section-body lt-flex lt-flex-wrap lt-gap-md" >
< span class = "lt-status lt-status-open" > Open</ span >
< span class = "lt-status lt-status-pending" > Pending</ span >
< span class = "lt-status lt-status-in-progress" > In Progress</ span >
< span class = "lt-status lt-status-closed" > Closed</ span >
< span class = "lt-status lt-status-online" > Online</ span >
< span class = "lt-status lt-status-offline" > Offline</ span >
< span class = "lt-status lt-status-running" > Running</ span >
< span class = "lt-status lt-status-completed" > Completed</ span >
< span class = "lt-status lt-status-failed" > Failed</ span >
</ div >
< div class = "lt-subsection-header" > Priority Badges</ div >
< div class = "lt-section-body lt-flex lt-flex-wrap lt-gap-md" >
< span class = "lt-priority lt-p1" > P1 Critical</ span >
< span class = "lt-priority lt-p2" > P2 High</ span >
< span class = "lt-priority lt-p3" > P3 Med</ span >
< span class = "lt-priority lt-p4" > P4 Low</ span >
< span class = "lt-priority lt-p5" > P5 Min</ span >
</ div >
< div class = "lt-subsection-header" > Chips & Badges</ div >
< div class = "lt-section-body lt-flex lt-flex-wrap lt-gap-sm" >
< span class = "lt-chip lt-chip-ok" > OK</ span >
< span class = "lt-chip lt-chip-warn" > Warning</ span >
< span class = "lt-chip lt-chip-critical" > Critical</ span >
< span class = "lt-chip lt-chip-info" > Info</ span >
< span class = "lt-badge lt-badge-green" > v1.0</ span >
< span class = "lt-badge lt-badge-amber" > Beta</ span >
< span class = "lt-badge lt-badge-red" > Deprecated</ span >
< span class = "lt-badge-admin" > admin</ span >
</ div >
< div class = "lt-subsection-header" > Status Dots</ div >
< div class = "lt-section-body lt-flex lt-gap-md" >
< span class = "lt-flex lt-gap-sm" >< span class = "lt-dot lt-dot-up" ></ span > Up</ span >
< span class = "lt-flex lt-gap-sm" >< span class = "lt-dot lt-dot-down" ></ span > Down</ span >
< span class = "lt-flex lt-gap-sm" >< span class = "lt-dot lt-dot-warn" ></ span > Degraded</ span >
< span class = "lt-flex lt-gap-sm" >< span class = "lt-dot lt-dot-idle" ></ span > Idle</ span >
</ div >
< div class = "lt-subsection-header" > Inline Messages</ div >
< div class = "lt-section-body" >
< div class = "lt-msg lt-msg-error" > Database connection refused on 10.10.10.50</ div >
< div class = "lt-msg lt-msg-success" > Ticket #123456789 updated successfully</ div >
< div class = "lt-msg lt-msg-warning" > Rate limit: 80% consumed (40/50 req/min)</ div >
< div class = "lt-msg lt-msg-info" > Auto-refresh active — updates every 30s</ div >
</ div >
< div class = "lt-subsection-header" > Forms</ div >
< div class = "lt-section-body" >
< div class = "lt-grid-2" >
< div >
< div class = "lt-form-group" >
< label class = "lt-label lt-label-required" for = "eg-title" > Ticket Title</ label >
< input id = "eg-title" type = "text" class = "lt-input" placeholder = "Describe the issue" >
</ div >
< div class = "lt-form-group" >
< label class = "lt-label" for = "eg-priority" > Priority</ label >
< select id = "eg-priority" class = "lt-select" >
< option > P1 — Critical</ option >
< option > P2 — High</ option >
< option selected > P3 — Medium</ option >
< option > P4 — Low</ option >
< option > P5 — Minimal</ option >
</ select >
</ div >
< div class = "lt-form-group" >
< label class = "lt-label" for = "eg-desc" > Description</ label >
< textarea id = "eg-desc" class = "lt-textarea" placeholder = "Markdown supported..." ></ textarea >
< span class = "lt-form-hint" > Markdown supported. Use #123456789 to link tickets.</ span >
</ div >
< div class = "lt-form-group" >
< label class = "lt-label" >
< input type = "checkbox" class = "lt-checkbox" > Confidential ticket
</ label >
</ div >
< div class = "lt-btn-group" >
2026-03-26 20:46:31 -04:00
< button type = "button" class = "lt-btn lt-btn-primary" > Submit</ button >
< button type = "button" class = "lt-btn lt-btn-ghost" > Cancel</ button >
2026-03-14 21:08:57 -04:00
</ div >
</ div >
< div >
< div class = "lt-search lt-form-group" >
< label class = "lt-label" for = "eg-search" > Search</ label >
< input id = "eg-search" type = "search" class = "lt-input lt-search-input"
2026-03-26 18:22:53 -04:00
placeholder = "Ctrl+K to focus" autocomplete = "off" >
2026-03-14 21:08:57 -04:00
</ div >
< div class = "lt-form-group" >
< label class = "lt-label" > Loading state (skeleton)</ label >
< div class = "lt-skeleton lt-p-md" style = "height:40px" ></ div >
</ div >
2026-04-01 16:50:46 -04:00
</ div >
2026-03-14 21:08:57 -04:00
< div class = "lt-form-group" >
< label class = "lt-label" > Empty state</ label >
< div class = "lt-empty" style = "padding:1rem" > No results found</ div >
</ div >
< div class = "lt-form-group" >
< label class = "lt-label" > Loading indicator</ label >
< div class = "lt-loading" style = "padding:1rem" ></ div >
</ div >
2026-04-01 16:50:46 -04:00
< div class = "lt-form-group" >
< label class = "lt-label" > Display-only fields < code style = "font-size:0.7rem" > .lt-display-field</ code ></ label >
< select class = "lt-select lt-display-field" style = "margin-bottom:0.5rem" >
< option > P3 — Medium</ option >
</ select >
< input type = "text" class = "lt-input lt-display-field" value = "Read-only value" >
</ div >
2026-03-14 21:08:57 -04:00
</ div >
</ div >
</ div >
< div class = "lt-subsection-header" > Log / Timeline Entries</ div >
< div class = "lt-section-body" >
< div class = "lt-log-entry success" >
< div class = "lt-log-ts" > 2026-03-14 09:12:33 EDT</ div >
< strong > Status changed:</ strong > Open → In Progress by operator
</ div >
< div class = "lt-log-entry warning" >
< div class = "lt-log-ts" > 2026-03-14 09:10:11 EDT</ div >
< strong > Priority escalated:</ strong > P3 → P1 by GANDALF auto-alert
</ div >
< div class = "lt-log-entry error" >
< div class = "lt-log-ts" > 2026-03-14 09:09:55 EDT</ div >
< strong > Alert triggered:</ strong > NIC link-down on large1:enp35s0
< div class = "lt-log-output" > node_exporter metric: node_network_up{interface="enp35s0"} = 0</ div >
</ div >
</ div >
</ div > <!-- /.lt-frame-inner -->
</ div > <!-- /.lt-frame (component showcase) -->
2026-03-25 18:47:26 -04:00
<!-- ===========================================================
COMPONENT SHOWCASE — v1.2 ADDITIONS
=========================================================== -->
< div class = "lt-frame lt-mt-lg" >
< div class = "lt-frame-inner" >
<!-- PROGRESS BARS -->
< div class = "lt-section-header" > Progress Bars</ div >
< div class = "lt-section-body" >
< div style = "display:flex;flex-direction:column;gap:var(--space-md)" >
< div >
< div class = "lt-progress-label" >< span > CPU LOAD</ span >< span > 72%</ span ></ div >
2026-03-26 18:22:53 -04:00
< div class = "lt-progress" role = "progressbar" aria-valuenow = "72" aria-valuemin = "0" aria-valuemax = "100" aria-label = "CPU Load" >< div class = "lt-progress-bar" style = "width:72%" data-width = "72%" ></ div ></ div >
2026-03-25 18:47:26 -04:00
</ div >
< div >
< div class = "lt-progress-label" >< span > MEMORY</ span >< span > 45%</ span ></ div >
2026-03-26 18:22:53 -04:00
< div class = "lt-progress lt-progress--cyan" role = "progressbar" aria-valuenow = "45" aria-valuemin = "0" aria-valuemax = "100" aria-label = "Memory" >< div class = "lt-progress-bar" style = "width:45%" data-width = "45%" ></ div ></ div >
2026-03-25 18:47:26 -04:00
</ div >
< div >
< div class = "lt-progress-label" >< span > DISK I/O</ span >< span > 89%</ span ></ div >
2026-03-26 18:22:53 -04:00
< div class = "lt-progress lt-progress--red lt-progress--lg" role = "progressbar" aria-valuenow = "89" aria-valuemin = "0" aria-valuemax = "100" aria-label = "Disk I/O" >< div class = "lt-progress-bar" style = "width:89%" data-width = "89%" ></ div ></ div >
2026-03-25 18:47:26 -04:00
</ div >
< div >
< div class = "lt-progress-label" >< span > UPTIME</ span >< span > 100%</ span ></ div >
2026-03-26 18:22:53 -04:00
< div class = "lt-progress lt-progress--green lt-progress--striped" role = "progressbar" aria-valuenow = "100" aria-valuemin = "0" aria-valuemax = "100" aria-label = "Uptime" >< div class = "lt-progress-bar" style = "width:100%" data-width = "100%" ></ div ></ div >
2026-03-25 18:47:26 -04:00
</ div >
</ div >
</ div >
<!-- BREADCRUMBS & PAGINATION -->
< div class = "lt-section-header" > Breadcrumbs / Pagination</ div >
< div class = "lt-section-body" style = "display:flex;flex-direction:column;gap:var(--space-md)" >
< nav class = "lt-breadcrumb" aria-label = "breadcrumb" >
< div class = "lt-breadcrumb-item" >< a href = "#" > ROOT</ a ></ div >
< div class = "lt-breadcrumb-sep" ></ div >
< div class = "lt-breadcrumb-item" >< a href = "#" > SYSTEMS</ a ></ div >
< div class = "lt-breadcrumb-sep" ></ div >
< div class = "lt-breadcrumb-item" >< a href = "#" > NETWORK</ a ></ div >
< div class = "lt-breadcrumb-sep" ></ div >
< div class = "lt-breadcrumb-item active" > NODE-07</ div >
</ nav >
2026-03-25 23:19:49 -04:00
< nav class = "lt-pagination" id = "demo-pagination" aria-label = "pagination" ></ nav >
2026-03-25 18:47:26 -04:00
</ div >
<!-- ACCORDION -->
< div class = "lt-section-header" > Accordion</ div >
< div class = "lt-section-body" >
< div >
< div class = "lt-accordion" >
2026-03-26 20:46:31 -04:00
< button type = "button" class = "lt-accordion-header" aria-expanded = "false" aria-controls = "acc-body-1" data-accordion >
2026-03-25 18:47:26 -04:00
SYSTEM OVERVIEW
2026-03-26 20:02:15 -04:00
< svg class = "lt-accordion-icon" viewBox = "0 0 10 6" fill = "none" stroke = "currentColor" stroke-width = "1.5" aria-hidden = "true" >< path d = "M1 1l4 4 4-4" /></ svg >
2026-03-25 18:47:26 -04:00
</ button >
2026-03-26 18:22:53 -04:00
< div class = "lt-accordion-body" id = "acc-body-1" >< div class = "lt-accordion-content" > Node running at 72% CPU. 12 active processes. Last restart: 3d ago.</ div ></ div >
2026-03-25 18:47:26 -04:00
</ div >
< div class = "lt-accordion" >
2026-03-26 20:46:31 -04:00
< button type = "button" class = "lt-accordion-header" aria-expanded = "false" aria-controls = "acc-body-2" data-accordion >
2026-03-25 18:47:26 -04:00
NETWORK CONFIG
2026-03-26 20:02:15 -04:00
< svg class = "lt-accordion-icon" viewBox = "0 0 10 6" fill = "none" stroke = "currentColor" stroke-width = "1.5" aria-hidden = "true" >< path d = "M1 1l4 4 4-4" /></ svg >
2026-03-25 18:47:26 -04:00
</ button >
2026-03-26 18:22:53 -04:00
< div class = "lt-accordion-body" id = "acc-body-2" >< div class = "lt-accordion-content" > eth0: 10.0.0.7 — MTU 1500 — RX 4.2 GB — TX 1.1 GB</ div ></ div >
2026-03-25 18:47:26 -04:00
</ div >
< div class = "lt-accordion" >
2026-03-26 20:46:31 -04:00
< button type = "button" class = "lt-accordion-header" aria-expanded = "false" aria-controls = "acc-body-3" data-accordion >
2026-03-25 18:47:26 -04:00
FIREWALL RULES
2026-03-26 20:02:15 -04:00
< svg class = "lt-accordion-icon" viewBox = "0 0 10 6" fill = "none" stroke = "currentColor" stroke-width = "1.5" aria-hidden = "true" >< path d = "M1 1l4 4 4-4" /></ svg >
2026-03-25 18:47:26 -04:00
</ button >
2026-03-26 18:22:53 -04:00
< div class = "lt-accordion-body" id = "acc-body-3" >< div class = "lt-accordion-content" > 22/tcp ALLOW — 80/tcp ALLOW — 443/tcp ALLOW — */* DENY</ div ></ div >
2026-03-25 18:47:26 -04:00
</ div >
</ div >
</ div >
<!-- ALERTS -->
< div class = "lt-section-header" > Alert Banners</ div >
< div class = "lt-section-body" style = "display:flex;flex-direction:column;gap:var(--space-sm)" >
< div class = "lt-alert" >
< span class = "lt-alert-icon" > ℹ </ span >
< div class = "lt-alert-body" >< div class = "lt-alert-title" > Information</ div >< div class = "lt-alert-msg" > Scheduled maintenance window: Sunday 02:00– 04:00 UTC.</ div ></ div >
2026-03-27 13:49:23 -04:00
< button type = "button" class = "lt-alert-close" data-alert-close aria-label = "Dismiss" > ✕</ button >
2026-03-25 18:47:26 -04:00
</ div >
< div class = "lt-alert lt-alert--warning" >
< span class = "lt-alert-icon" > ⚠</ span >
< div class = "lt-alert-body" >< div class = "lt-alert-title" > Warning</ div >< div class = "lt-alert-msg" > Disk usage on NODE-03 at 87%. Consider cleanup.</ div ></ div >
2026-03-27 13:49:23 -04:00
< button type = "button" class = "lt-alert-close" data-alert-close aria-label = "Dismiss" > ✕</ button >
2026-03-25 18:47:26 -04:00
</ div >
< div class = "lt-alert lt-alert--error" >
< span class = "lt-alert-icon" > ✕</ span >
< div class = "lt-alert-body" >< div class = "lt-alert-title" > Critical</ div >< div class = "lt-alert-msg" > NODE-11 unreachable. Last seen 14m ago.</ div ></ div >
2026-03-27 13:49:23 -04:00
< button type = "button" class = "lt-alert-close" data-alert-close aria-label = "Dismiss" > ✕</ button >
2026-03-25 18:47:26 -04:00
</ div >
< div class = "lt-alert lt-alert--success" >
< span class = "lt-alert-icon" > ✓</ span >
< div class = "lt-alert-body" >< div class = "lt-alert-title" > Success</ div >< div class = "lt-alert-msg" > Deployment v2.4.1 completed successfully.</ div ></ div >
2026-03-27 13:49:23 -04:00
< button type = "button" class = "lt-alert-close" data-alert-close aria-label = "Dismiss" > ✕</ button >
2026-03-25 18:47:26 -04:00
</ div >
</ div >
2026-04-29 17:18:33 -04:00
<!-- SLA BANNERS -->
< div class = "lt-section-header" > SLA Banners</ div >
< div class = "lt-section-body" style = "display:flex;flex-direction:column;gap:var(--space-sm)" >
2026-04-29 17:27:10 -04:00
< div class = "lt-sla-p1" role = "alert" data-sla-id = "demo-p1" >
< span class = "lt-sla-icon" aria-hidden = "true" > [ ! ]</ span >
2026-04-29 17:18:33 -04:00
< div class = "lt-sla-info" >
2026-04-29 17:27:10 -04:00
< div class = "lt-sla-title" > P1 Critical — SLA: 6h 42m elapsed of 8h limit</ div >
2026-04-29 17:18:33 -04:00
< div class = "lt-sla-bar" >< div class = "lt-sla-fill" style = "width:84%" ></ div ></ div >
</ div >
< div class = "lt-sla-meta" > Storage array link-down · #123456789</ div >
< button type = "button" class = "lt-sla-dismiss" aria-label = "Dismiss" > ✕</ button >
</ div >
2026-04-29 17:27:10 -04:00
< div class = "lt-sla-p2" role = "alert" data-sla-id = "demo-p2" >
< span class = "lt-sla-icon" aria-hidden = "true" > [ ~ ]</ span >
2026-04-29 17:18:33 -04:00
< div class = "lt-sla-info" >
< div class = "lt-sla-title" > P2 High — SLA: 9h 37m elapsed of 24h limit</ div >
< div class = "lt-sla-bar" >< div class = "lt-sla-fill" style = "width:40%" ></ div ></ div >
</ div >
< div class = "lt-sla-meta" > Switch port flapping · #987654321</ div >
< button type = "button" class = "lt-sla-dismiss" aria-label = "Dismiss" > ✕</ button >
</ div >
</ div >
2026-03-25 18:47:26 -04:00
<!-- TOGGLES, RANGE, TAGS -->
< div class = "lt-section-header" > Toggles / Range / Tags</ div >
< div class = "lt-section-body" style = "display:flex;flex-direction:column;gap:var(--space-lg)" >
< div style = "display:flex;gap:var(--space-lg);flex-wrap:wrap" >
< label class = "lt-toggle" >
< input type = "checkbox" checked >
< div class = "lt-toggle-track" >< div class = "lt-toggle-thumb" ></ div ></ div >
< span class = "lt-toggle-label" > Auto-refresh</ span >
</ label >
< label class = "lt-toggle" >
< input type = "checkbox" >
< div class = "lt-toggle-track" >< div class = "lt-toggle-thumb" ></ div ></ div >
< span class = "lt-toggle-label" > Dark alerts</ span >
</ label >
< label class = "lt-toggle" >
< input type = "checkbox" checked >
< div class = "lt-toggle-track" >< div class = "lt-toggle-thumb" ></ div ></ div >
< span class = "lt-toggle-label" > Notifications</ span >
</ label >
</ div >
< div class = "lt-range-wrap" style = "max-width:320px" >
< div class = "lt-range-header" >
< span class = "lt-range-label" > Refresh Interval (s)</ span >
< span class = "lt-range-value" > 30</ span >
</ div >
2026-03-26 20:02:15 -04:00
< input type = "range" class = "lt-range" min = "5" max = "60" value = "30" aria-label = "Refresh Interval in seconds" aria-valuemin = "5" aria-valuemax = "60" aria-valuenow = "30" >
2026-03-25 18:47:26 -04:00
</ div >
< div class = "lt-tags" >
< span class = "lt-tag lt-tag--orange" > CRITICAL</ span >
< span class = "lt-tag lt-tag--cyan" > NETWORK</ span >
< span class = "lt-tag lt-tag--green" > ONLINE</ span >
< span class = "lt-tag lt-tag--red" > OFFLINE</ span >
< span class = "lt-tag lt-tag--purple" > ADMIN</ span >
< span class = "lt-tag" > UNTAGGED</ span >
</ div >
2026-04-01 16:50:46 -04:00
< div style = "margin-top:var(--space-md);display:flex;flex-wrap:wrap;gap:var(--space-md);align-items:center" >
< span class = "lt-cursor" style = "font-size:1.1rem;font-family:var(--font-mono)" > SYSTEM STATUS</ span >
< span class = "lt-cursor lt-cursor--cyan" style = "font-size:1.1rem;font-family:var(--font-mono)" > SCANNING</ span >
< span class = "lt-cursor lt-cursor--orange" style = "font-size:1.1rem;font-family:var(--font-mono)" > AWAITING INPUT</ span >
< span style = "font-family:var(--font-crt);font-size:2rem;color:var(--accent-green)" > 42 NODES</ span >
< span class = "lt-tag lt-tag--red" style = "font-family:var(--font-crt);font-size:1rem;letter-spacing:0.05em" > CRITICAL</ span >
</ div >
2026-03-25 18:47:26 -04:00
</ div >
<!-- CODE BLOCK -->
< div class = "lt-section-header" > Code Block</ div >
< div class = "lt-section-body" >
< div class = "lt-code-block" >
< div class = "lt-code-header" >
< span class = "lt-code-lang" > bash</ span >
2026-03-27 13:49:23 -04:00
< button type = "button" class = "lt-code-copy" data-copy = "systemctl restart nginx && systemctl status nginx" > COPY</ button >
2026-03-25 18:47:26 -04:00
</ div >
< pre >< code >< span class = "tok-cmt" > # Restart and verify nginx</ span >
< span class = "tok-kw" > systemctl</ span > restart nginx < span class = "tok-kw" > && </ span > < span class = "tok-kw" > systemctl</ span > status nginx
< span class = "tok-cmt" > # Check active connections</ span >
< span class = "tok-kw" > ss</ span > -tlnp | < span class = "tok-kw" > grep</ span > < span class = "tok-str" > ':80\|:443'</ span ></ code ></ pre >
</ div >
</ div >
<!-- KEY-VALUE + STEPPER -->
< div class = "lt-section-header" > Data Grid / Stepper</ div >
< div class = "lt-section-body" style = "display:grid;grid-template-columns:1fr 1fr;gap:var(--space-lg)" >
< div >
< div class = "lt-kv-grid" >
< div class = "lt-kv-key" > Hostname</ div > < div class = "lt-kv-val lt-kv-val--cyan" > node-07.prod</ div >
< div class = "lt-kv-key" > IP Address</ div > < div class = "lt-kv-val" > 10.0.0.7</ div >
< div class = "lt-kv-key" > Status</ div > < div class = "lt-kv-val lt-kv-val--green" > ONLINE</ div >
< div class = "lt-kv-key" > Uptime</ div > < div class = "lt-kv-val" > 3d 14h 22m</ div >
< div class = "lt-kv-key" > Load Avg</ div > < div class = "lt-kv-val lt-kv-val--orange" > 2.14 / 1.87 / 1.60</ div >
< div class = "lt-kv-key" > Region</ div > < div class = "lt-kv-val" > US-EAST-1</ div >
</ div >
</ div >
< div >
< div class = "lt-stepper" >
< div class = "lt-step complete" >
< div class = "lt-step-num" > ✓</ div >
< div class = "lt-step-label" > INIT</ div >
</ div >
< div class = "lt-step complete" >
< div class = "lt-step-num" > ✓</ div >
< div class = "lt-step-label" > BUILD</ div >
</ div >
< div class = "lt-step active" >
< div class = "lt-step-num" > 3</ div >
< div class = "lt-step-label" > DEPLOY</ div >
</ div >
< div class = "lt-step" >
< div class = "lt-step-num" > 4</ div >
< div class = "lt-step-label" > VERIFY</ div >
</ div >
</ div >
</ div >
</ div >
<!-- LIST GROUP + BADGES -->
< div class = "lt-section-header" > List Group / Badges</ div >
< div class = "lt-section-body" style = "display:grid;grid-template-columns:1fr 1fr;gap:var(--space-lg)" >
< div class = "lt-list-group" >
< div class = "lt-list-item lt-list-item--active" >
< span class = "lt-dot lt-dot--green" ></ span >
< a href = "#" > node-01.prod</ a >
< span class = "lt-list-item-meta lt-kv-val--green" > ONLINE</ span >
</ div >
< div class = "lt-list-item" >
< span class = "lt-dot lt-dot--green" ></ span >
< a href = "#" > node-02.prod</ a >
< span class = "lt-list-item-meta lt-kv-val--green" > ONLINE</ span >
</ div >
< div class = "lt-list-item" >
< span class = "lt-dot lt-dot--orange" ></ span >
< a href = "#" > node-03.prod</ a >
< span class = "lt-list-item-meta lt-kv-val--orange" > DEGRADED</ span >
</ div >
< div class = "lt-list-item" >
< span class = "lt-dot lt-dot--red" ></ span >
< a href = "#" > node-11.prod</ a >
< span class = "lt-list-item-meta lt-kv-val--red" > OFFLINE</ span >
</ div >
</ div >
< div style = "display:flex;flex-direction:column;gap:var(--space-md)" >
< div style = "display:flex;gap:var(--space-md);align-items:center;flex-wrap:wrap" >
< div class = "lt-badge-wrap" >
2026-03-26 20:46:31 -04:00
< button type = "button" class = "lt-btn lt-btn-sm" > Alerts</ button >
2026-03-25 18:47:26 -04:00
< span class = "lt-badge" > 3</ span >
</ div >
< div class = "lt-badge-wrap" >
2026-03-26 20:46:31 -04:00
< button type = "button" class = "lt-btn lt-btn-sm" > Messages</ button >
2026-03-25 18:47:26 -04:00
< span class = "lt-badge lt-badge--orange" > 12</ span >
</ div >
< div class = "lt-badge-wrap" >
2026-03-26 20:46:31 -04:00
< button type = "button" class = "lt-btn lt-btn-sm" > Updates</ button >
2026-03-25 18:47:26 -04:00
< span class = "lt-badge lt-badge--cyan" > 5</ span >
</ div >
</ div >
< div style = "display:flex;gap:var(--space-sm);align-items:center;flex-wrap:wrap" >
< div class = "lt-bar-loader" >< span ></ span >< span ></ span >< span ></ span >< span ></ span ></ div >
< div class = "lt-spinner" ></ div >
< div class = "lt-spinner lt-spinner--cyan lt-spinner--sm" ></ div >
2026-04-01 16:50:46 -04:00
< div class = "lt-radar lt-radar--sm" ></ div >
< div class = "lt-radar" ></ div >
< div class = "lt-radar lt-radar--green" ></ div >
2026-03-25 18:47:26 -04:00
< div class = "lt-pulse-dot" ></ div >
</ div >
</ div >
</ div >
<!-- TAB BAR -->
< div class = "lt-section-header" > Tab Bar</ div >
< div class = "lt-section-body" >
< div class = "lt-tab-bar" role = "tablist" >
2026-03-27 13:49:23 -04:00
< button type = "button" class = "lt-tab active" role = "tab" id = "tab2-btn-overview" data-tab-target = "tab-overview" aria-selected = "true" aria-controls = "tab-overview" > Overview</ button >
< button type = "button" class = "lt-tab" role = "tab" id = "tab2-btn-logs" data-tab-target = "tab-logs" aria-selected = "false" aria-controls = "tab-logs" > Logs</ button >
< button type = "button" class = "lt-tab" role = "tab" id = "tab2-btn-metrics" data-tab-target = "tab-metrics" aria-selected = "false" aria-controls = "tab-metrics" > Metrics</ button >
< button type = "button" class = "lt-tab" role = "tab" id = "tab2-btn-config" data-tab-target = "tab-config" aria-selected = "false" aria-controls = "tab-config" > Config</ button >
2026-03-25 18:47:26 -04:00
</ div >
< div class = "lt-tab-panels" style = "padding:var(--space-md) 0" >
2026-03-26 18:22:53 -04:00
< div id = "tab-overview" class = "lt-tab-panel active" role = "tabpanel" aria-labelledby = "tab2-btn-overview" > System overview: all nodes nominal. 14 active sessions.</ div >
< div id = "tab-logs" class = "lt-tab-panel" role = "tabpanel" aria-labelledby = "tab2-btn-logs" >
2026-03-25 18:47:26 -04:00
< pre style = "font-family:var(--font-mono);font-size:0.75rem;color:var(--text-secondary);margin:0" > [03:41:22] nginx: 200 GET /api/nodes (12ms)
[03:41:23] cron: heartbeat OK
[03:41:24] alert: disk 87% on node-03</ pre >
</ div >
2026-03-26 18:22:53 -04:00
< div id = "tab-metrics" class = "lt-tab-panel" role = "tabpanel" aria-labelledby = "tab2-btn-metrics" > CPU avg: 34% — Mem avg: 61% — Net TX: 4.2 Mbps</ div >
< div id = "tab-config" class = "lt-tab-panel" role = "tabpanel" aria-labelledby = "tab2-btn-config" > Config last updated: 2026-03-20 by admin@lotusguild.io</ div >
2026-03-25 18:47:26 -04:00
</ div >
</ div >
<!-- TOOLTIP & DIVIDER DEMO -->
< div class = "lt-section-header" > Tooltips / Divider</ div >
< div class = "lt-section-body" style = "display:flex;flex-direction:column;gap:var(--space-md)" >
< div style = "display:flex;gap:var(--space-lg);flex-wrap:wrap;padding-top:var(--space-md)" >
< span data-tooltip = "Primary accent — use for actions" class = "lt-tag lt-tag--orange" > ORANGE</ span >
< span data-tooltip = "Info / border color" data-tooltip-pos = "bottom" class = "lt-tag lt-tag--cyan" > CYAN (bottom)</ span >
< span data-tooltip = "Success states" class = "lt-tag lt-tag--green" > GREEN</ span >
< span data-tooltip = "Critical / error" class = "lt-tag lt-tag--red" > RED</ span >
</ div >
< div class = "lt-divider" >< span class = "lt-divider-label" > SECTION BREAK</ span ></ div >
< div class = "lt-divider lt-divider--orange" >< span class = "lt-divider-label" > CRITICAL ZONE</ span ></ div >
</ div >
<!-- DROPZONE -->
< div class = "lt-section-header" > File Dropzone</ div >
< div class = "lt-section-body" >
< div class = "lt-dropzone" data-dropzone data-accept = ".json,.csv,.log" data-max-size = "10485760" >
2026-03-26 20:16:12 -04:00
< input type = "file" accept = ".json,.csv,.log" multiple aria-label = "Upload JSON, CSV, or log files (max 10 MB each)" >
2026-03-25 18:47:26 -04:00
< div class = "lt-dropzone-icon" > ⤓</ div >
< div class = "lt-dropzone-text" > Drop files here or < strong > click to browse</ strong ></ div >
< div class = "lt-dropzone-hint" > Accepts .json, .csv, .log — max 10 MB each</ div >
</ div >
< div class = "lt-file-list" id = "demo-file-list" ></ div >
</ div >
<!-- COMMAND PALETTE TRIGGER -->
< div class = "lt-section-header" > Command Palette</ div >
< div class = "lt-section-body" >
< p style = "font-family:var(--font-mono);font-size:0.8rem;color:var(--text-secondary);margin:0 0 var(--space-sm)" >
Press < kbd style = "background:var(--bg-tertiary);border:1px solid var(--border-dim);padding:2px 6px;font-family:var(--font-mono)" > Ctrl+K</ kbd > or click the button below to open the command palette.
</ p >
2026-03-26 18:22:53 -04:00
< button type = "button" class = "lt-btn lt-btn-ghost" onclick = "lt.cmdPalette.open()" > ⌘ Open Command Palette</ button >
2026-03-25 18:47:26 -04:00
</ div >
</ div > <!-- /.lt-frame-inner (v1.2 additions) -->
</ div > <!-- /.lt-frame (v1.2 additions) -->
2026-03-25 22:29:55 -04:00
<!-- ═══════════════════════════════════════════════════════════
v1.1 COMPONENT SHOWCASE
═══════════════════════════════════════════════════════════ -->
<!-- Theme Toggle -->
< div class = "lt-section" >
< div class = "lt-section-header" >
< span class = "lt-section-title" > // THEME TOGGLE</ span >
</ div >
< div class = "lt-section-body" >
< p style = "font-size:0.78rem;color:var(--text-secondary);margin-bottom:1rem" > Dark/light mode with OS preference detection and localStorage persistence.</ p >
< div class = "lt-flex lt-gap-sm lt-align-center lt-wrap" >
2026-03-26 18:22:53 -04:00
< button type = "button" class = "lt-btn lt-btn-primary" onclick = "lt.theme.toggle()" > Toggle Theme</ button >
< button type = "button" class = "lt-btn" onclick = "lt.theme.set('dark')" > Force Dark</ button >
< button type = "button" class = "lt-btn" onclick = "lt.theme.set('light')" > Force Light</ button >
2026-03-25 22:29:55 -04:00
< code style = "font-size:0.72rem;color:var(--accent-cyan)" > lt.theme.toggle() | .set('light') | .get()</ code >
</ div >
</ div >
</ div >
<!-- Skeleton Loaders -->
< div class = "lt-section" >
< div class = "lt-section-header" >
< span class = "lt-section-title" > // SKELETON LOADERS</ span >
</ div >
< div class = "lt-section-body" >
< div class = "lt-grid lt-grid-3" style = "gap:1rem;align-items:start" >
< div class = "lt-skeleton-card" >
< div class = "lt-skeleton-card-header" >
< span class = "lt-skeleton lt-skeleton-avatar" ></ span >
< div style = "flex:1" >< span class = "lt-skeleton lt-skeleton-title" ></ span >< span class = "lt-skeleton lt-skeleton-line-sm" ></ span ></ div >
</ div >
< span class = "lt-skeleton lt-skeleton-text" ></ span >
< span class = "lt-skeleton lt-skeleton-line-lg" ></ span >
< span class = "lt-skeleton lt-skeleton-text" style = "width:70%" ></ span >
< div class = "lt-flex lt-gap-sm" style = "margin-top:0.25rem" >
< span class = "lt-skeleton lt-skeleton-btn" ></ span >
< span class = "lt-skeleton lt-skeleton-badge" ></ span >
</ div >
</ div >
< div style = "grid-column:span 2" >
< div class = "lt-skeleton-row" >< span class = "lt-skeleton" style = "height:1rem" ></ span >< span class = "lt-skeleton lt-skeleton-text" ></ span >< span class = "lt-skeleton" style = "height:0.8rem;width:80%" ></ span >< span class = "lt-skeleton lt-skeleton-badge" ></ span >< span class = "lt-skeleton" style = "height:0.8rem;width:60%" ></ span >< span class = "lt-skeleton lt-skeleton-btn" ></ span ></ div >
< div class = "lt-skeleton-row" style = "opacity:0.65" >< span class = "lt-skeleton" style = "height:1rem" ></ span >< span class = "lt-skeleton lt-skeleton-text" ></ span >< span class = "lt-skeleton" style = "height:0.8rem;width:65%" ></ span >< span class = "lt-skeleton lt-skeleton-badge" ></ span >< span class = "lt-skeleton" style = "height:0.8rem;width:50%" ></ span >< span class = "lt-skeleton lt-skeleton-btn" ></ span ></ div >
< div class = "lt-skeleton-row" style = "opacity:0.35" >< span class = "lt-skeleton" style = "height:1rem" ></ span >< span class = "lt-skeleton lt-skeleton-text" ></ span >< span class = "lt-skeleton" style = "height:0.8rem;width:55%" ></ span >< span class = "lt-skeleton lt-skeleton-badge" ></ span >< span class = "lt-skeleton" style = "height:0.8rem;width:40%" ></ span >< span class = "lt-skeleton lt-skeleton-btn" ></ span ></ div >
</ div >
</ div >
</ div >
</ div >
<!-- Empty States -->
< div class = "lt-section" >
< div class = "lt-section-header" >
< span class = "lt-section-title" > // EMPTY STATES</ span >
</ div >
< div class = "lt-section-body" >
< div class = "lt-grid lt-grid-3" style = "gap:1rem" >
2026-03-26 20:46:31 -04:00
< div class = "lt-card" >< div class = "lt-empty-state" >< div class = "lt-empty-state-icon" > 📭</ div >< div class = "lt-empty-state-title" > No Tickets Found</ div >< div class = "lt-empty-state-body" > No tickets match your current filters.</ div >< button type = "button" class = "lt-btn lt-btn-sm lt-btn-primary" > Clear Filters</ button ></ div ></ div >
2026-03-26 18:22:53 -04:00
< div class = "lt-card" >< div class = "lt-empty-state" >< div class = "lt-empty-state-icon" > 🔌</ div >< div class = "lt-empty-state-title" > No Workers Online</ div >< div class = "lt-empty-state-body" > All workers are offline or unreachable.</ div >< button type = "button" class = "lt-btn lt-btn-sm" onclick = "lt.toast.warning('Checking workers…')" > Retry</ button ></ div ></ div >
2026-03-25 22:29:55 -04:00
< div class = "lt-card" >< div class = "lt-empty-state lt-empty-state--sm" >< div class = "lt-empty-state-icon" > 🗂</ div >< div class = "lt-empty-state-title" > No Results</ div >< div class = "lt-empty-state-body" > Try a different search term.</ div ></ div ></ div >
</ div >
</ div >
</ div >
<!-- Avatars & Notification Badges -->
< div class = "lt-section" >
< div class = "lt-section-header" >
< span class = "lt-section-title" > // AVATARS & NOTIFICATION BADGES</ span >
</ div >
< div class = "lt-section-body" >
< div class = "lt-flex lt-gap-lg lt-wrap lt-align-center" style = "margin-bottom:1rem" >
< span class = "lt-avatar lt-avatar--xs lt-avatar--orange" > AB</ span >
< span class = "lt-avatar lt-avatar--sm lt-avatar--green" > CD</ span >
< span class = "lt-avatar lt-avatar--purple" > EF</ span >
< span class = "lt-avatar lt-avatar--lg lt-avatar--red" > GH</ span >
< div class = "lt-avatar-wrap" >< span class = "lt-avatar lt-avatar--orange" > JD</ span >< span class = "lt-avatar-status lt-avatar-status--online" ></ span ></ div >
< div class = "lt-avatar-wrap" >< span class = "lt-avatar" > SK</ span >< span class = "lt-avatar-status lt-avatar-status--away" ></ span ></ div >
< div class = "lt-avatar-wrap" >< span class = "lt-avatar lt-avatar--red" > MR</ span >< span class = "lt-avatar-status lt-avatar-status--busy" ></ span ></ div >
< div class = "lt-avatar-group" >
< span class = "lt-avatar lt-avatar--sm lt-avatar--orange" > AA</ span >
< span class = "lt-avatar lt-avatar--sm lt-avatar--green" > BB</ span >
< span class = "lt-avatar lt-avatar--sm lt-avatar--purple" > CC</ span >
< span class = "lt-avatar lt-avatar--sm" style = "background:var(--bg-tertiary);color:var(--text-muted);font-size:0.6rem" > +4</ span >
</ div >
</ div >
< div class = "lt-flex lt-gap-md lt-align-center lt-wrap" >
2026-03-26 20:46:31 -04:00
< span class = "lt-notif-wrap" id = "demo-notif-btn" >< button type = "button" class = "lt-btn lt-btn-sm" > 🔔 Alerts</ button ></ span >
2026-03-26 18:22:53 -04:00
< button type = "button" class = "lt-btn lt-btn-sm" onclick = "lt.notif.inc('#demo-notif-btn')" > +1 Badge</ button >
< button type = "button" class = "lt-btn lt-btn-sm" onclick = "lt.notif.clear('#demo-notif-btn')" > Clear</ button >
< button type = "button" class = "lt-btn lt-btn-sm" onclick = "lt.notif.inc('#lt-notif-bell')" > +1 Header Bell</ button >
2026-03-25 22:29:55 -04:00
</ div >
</ div >
</ div >
<!-- Timeline -->
< div class = "lt-section" >
< div class = "lt-section-header" >
< span class = "lt-section-title" > // TIMELINE / ACTIVITY FEED</ span >
</ div >
< div class = "lt-section-body" >
< div class = "lt-grid lt-grid-2" style = "gap:1.5rem" >
< div class = "lt-timeline" >
< div class = "lt-timeline-item lt-timeline-item--red" >
< div class = "lt-timeline-meta" >< span class = "lt-timeline-actor" > alertmanager</ span > fired< span class = "lt-timeline-time lt-num" > 14:22:05</ span ></ div >
< div class = "lt-timeline-body" > CRITICAL: Storage array link-down on < code > compute-storage-01</ code > . Ticket auto-created.</ div >
</ div >
< div class = "lt-timeline-item lt-timeline-item--orange" >
< div class = "lt-timeline-meta" >< span class = "lt-timeline-actor" > jdoe</ span > assigned< span class = "lt-timeline-time lt-num" > 14:24:11</ span ></ div >
< div class = "lt-timeline-body" > Escalated to P1 Critical. Paged on-call team.</ div >
</ div >
< div class = "lt-timeline-item" >
< div class = "lt-timeline-meta" >< span class = "lt-timeline-actor" > jdoe</ span > commented< span class = "lt-timeline-time lt-num" > 14:31:44</ span ></ div >
< div class = "lt-timeline-body" > Confirmed NIC failure. Ordered replacement hardware. ETA 2h.</ div >
</ div >
< div class = "lt-timeline-item lt-timeline-item--green" >
< div class = "lt-timeline-meta" >< span class = "lt-timeline-actor" > jdoe</ span > resolved< span class = "lt-timeline-time lt-num" > 16:55:00</ span ></ div >
< div class = "lt-timeline-body" > Hardware replaced. Link restored. Monitoring for 30 min.</ div >
</ div >
</ div >
< div style = "display:flex;flex-direction:column;gap:0.75rem" >
< div class = "lt-stat-card" >< div class = "lt-stat-label" > Resolution Time</ div >< div class = "lt-stat-value lt-text-green lt-num" > 2h 33m</ div ></ div >
< div class = "lt-stat-card" >< div class = "lt-stat-label" > Events</ div >< div class = "lt-stat-value lt-num" > 4</ div ></ div >
< div class = "lt-stat-card" >< div class = "lt-stat-label" > SLA Status</ div >< div class = "lt-stat-value lt-text-orange" > Within SLA</ div ></ div >
</ div >
</ div >
</ div >
</ div >
<!-- Right Drawer -->
< div class = "lt-section" >
< div class = "lt-section-header" >
< span class = "lt-section-title" > // RIGHT-SIDE DRAWER</ span >
</ div >
< div class = "lt-section-body" >
< p style = "font-size:0.78rem;color:var(--text-secondary);margin-bottom:1rem" > Detail/inspect panel from the right. Focus trap, ESC close, overlay backdrop, return-focus.</ p >
< div class = "lt-flex lt-gap-sm lt-wrap" >
2026-03-27 13:49:23 -04:00
< button type = "button" class = "lt-btn lt-btn-primary" data-drawer-open = "lt-detail-drawer" > Open Detail Panel</ button >
2026-03-25 22:29:55 -04:00
< code style = "font-size:0.72rem;color:var(--accent-cyan)" > lt.rightDrawer.open('id') | .close() | .toggle()</ code >
</ div >
</ div >
</ div >
<!-- Context Menu -->
< div class = "lt-section" >
< div class = "lt-section-header" >
< span class = "lt-section-title" > // CONTEXT MENU</ span >
</ div >
< div class = "lt-section-body" >
< p style = "font-size:0.78rem;color:var(--text-secondary);margin-bottom:1rem" > Right-click any element with < code > data-context-menu</ code > or trigger programmatically.</ p >
< div class = "lt-flex lt-gap-sm lt-wrap lt-align-center" >
< div data-context-menu = "demo-ctx" class = "lt-card" style = "padding:0.75rem 1.25rem;cursor:context-menu;border-style:dashed;display:inline-block" >
Right-click this card ›
</ div >
2026-03-26 20:46:31 -04:00
< button type = "button" class = "lt-btn lt-btn-sm" id = "demo-ctx-btn" > Show Context Menu</ button >
2026-03-25 22:29:55 -04:00
</ div >
</ div >
</ div >
<!-- Combobox + Typeahead -->
< div class = "lt-section" >
< div class = "lt-section-header" >
< span class = "lt-section-title" > // COMBOBOX & TYPEAHEAD</ span >
</ div >
< div class = "lt-section-body" >
< div class = "lt-grid lt-grid-2" style = "gap:1.5rem" >
< div >
2026-03-26 20:16:12 -04:00
< label class = "lt-label" for = "demo-combobox-input" > Assign Workers (multi-select)</ label >
2026-03-25 22:29:55 -04:00
< div class = "lt-combobox" id = "demo-combobox" >
< div class = "lt-combobox-input-wrap" >
< input type = "text" class = "lt-combobox-input" id = "demo-combobox-input" placeholder = "Search workers…" autocomplete = "off" >
</ div >
< div class = "lt-combobox-dropdown" ></ div >
</ div >
</ div >
< div >
2026-03-26 20:16:12 -04:00
< label class = "lt-label" for = "demo-typeahead-input" > Search Tickets (typeahead)</ label >
2026-03-25 22:29:55 -04:00
< div class = "lt-typeahead" id = "demo-typeahead" >
< input type = "text" class = "lt-input lt-w-full" id = "demo-typeahead-input" placeholder = "Type to search…" autocomplete = "off" >
< div class = "lt-typeahead-dropdown" ></ div >
</ div >
</ div >
</ div >
</ div >
</ div >
<!-- Sticky Table -->
< div class = "lt-section" >
< div class = "lt-section-header" >
< span class = "lt-section-title" > // STICKY TABLE HEADERS</ span >
</ div >
< div class = "lt-section-body" >
< div class = "lt-table-sticky-wrap" >
< table class = "lt-table lt-table-responsive" >
2026-03-26 20:09:29 -04:00
< thead >< tr >< th scope = "col" > ID</ th >< th scope = "col" > Priority</ th >< th scope = "col" > Title</ th >< th scope = "col" > Status</ th >< th scope = "col" > Assignee</ th ></ tr ></ thead >
2026-03-25 22:29:55 -04:00
< tbody >
< tr >< td data-label = "ID" >< a href = "#" class = "lt-text-cyan" > #001</ a ></ td >< td data-label = "Priority" >< span class = "lt-badge lt-badge-p1" > P1</ span ></ td >< td data-label = "Title" > Link-down on compute-storage-01</ td >< td data-label = "Status" >< span class = "lt-badge lt-badge-open" > Open</ span ></ td >< td data-label = "Assignee" > jdoe</ td ></ tr >
< tr >< td data-label = "ID" >< a href = "#" class = "lt-text-cyan" > #002</ a ></ td >< td data-label = "Priority" >< span class = "lt-badge lt-badge-p2" > P2</ span ></ td >< td data-label = "Title" > Switch port flapping USW-Pro-24</ td >< td data-label = "Status" >< span class = "lt-badge lt-badge-in-progress" > In Progress</ span ></ td >< td data-label = "Assignee" > smith</ td ></ tr >
< tr >< td data-label = "ID" >< a href = "#" class = "lt-text-cyan" > #003</ a ></ td >< td data-label = "Priority" >< span class = "lt-badge lt-badge-p3" > P3</ span ></ td >< td data-label = "Title" > Scheduled SFP+ replacement</ td >< td data-label = "Status" >< span class = "lt-badge lt-badge-pending" > Pending</ span ></ td >< td data-label = "Assignee" > ops-bot</ td ></ tr >
< tr >< td data-label = "ID" >< a href = "#" class = "lt-text-cyan" > #004</ a ></ td >< td data-label = "Priority" >< span class = "lt-badge lt-badge-p4" > P4</ span ></ td >< td data-label = "Title" > SSL cert renewal wiki</ td >< td data-label = "Status" >< span class = "lt-badge lt-badge-open" > Open</ span ></ td >< td data-label = "Assignee" > admin</ td ></ tr >
< tr >< td data-label = "ID" >< a href = "#" class = "lt-text-cyan" > #005</ a ></ td >< td data-label = "Priority" >< span class = "lt-badge lt-badge-p1" > P1</ span ></ td >< td data-label = "Title" > RAID controller firmware</ td >< td data-label = "Status" >< span class = "lt-badge lt-badge-open" > Open</ span ></ td >< td data-label = "Assignee" > jdoe</ td ></ tr >
< tr >< td data-label = "ID" >< a href = "#" class = "lt-text-cyan" > #006</ a ></ td >< td data-label = "Priority" >< span class = "lt-badge lt-badge-p2" > P2</ span ></ td >< td data-label = "Title" > Backup job failure nas-01</ td >< td data-label = "Status" >< span class = "lt-badge lt-badge-closed" > Closed</ span ></ td >< td data-label = "Assignee" > backup-bot</ td ></ tr >
< tr >< td data-label = "ID" >< a href = "#" class = "lt-text-cyan" > #007</ a ></ td >< td data-label = "Priority" >< span class = "lt-badge lt-badge-p3" > P3</ span ></ td >< td data-label = "Title" > Prometheus alert rule tuning</ td >< td data-label = "Status" >< span class = "lt-badge lt-badge-in-progress" > In Progress</ span ></ td >< td data-label = "Assignee" > ops-team</ td ></ tr >
</ tbody >
</ table >
</ div >
</ div >
</ div >
<!-- Chart Containers -->
< div class = "lt-section" >
< div class = "lt-section-header" >
< span class = "lt-section-title" > // CHART CONTAINERS</ span >
</ div >
< div class = "lt-section-body" >
2026-03-25 23:51:21 -04:00
< div class = "lt-grid lt-grid-2" style = "gap:1rem;grid-template-columns:repeat(auto-fit,minmax(240px,1fr))" >
2026-03-25 22:29:55 -04:00
< div class = "lt-chart-wrap" >
< div class = "lt-chart-header" >
< span class = "lt-chart-title" > Ticket Volume (7d)</ span >
< div class = "lt-chart-legend" >
< span class = "lt-chart-legend-item" >< span class = "lt-chart-legend-dot" style = "background:var(--accent-cyan)" ></ span > Open</ span >
< span class = "lt-chart-legend-item" >< span class = "lt-chart-legend-dot" style = "background:var(--accent-green)" ></ span > Closed</ span >
</ div >
</ div >
< div class = "lt-chart-body" style = "min-height:120px;display:flex;align-items:center;justify-content:center;color:var(--text-muted);font-size:0.72rem;letter-spacing:0.1em" > [ Plug in Chart.js / D3 here ]</ div >
< div class = "lt-chart-axis" >< span > Mon</ span >< span > Tue</ span >< span > Wed</ span >< span > Thu</ span >< span > Fri</ span >< span > Sat</ span >< span > Sun</ span ></ div >
</ div >
< div class = "lt-chart-wrap is-loading" >
< div class = "lt-chart-header" >< span class = "lt-chart-title" > Worker Uptime</ span ></ div >
< div class = "lt-chart-body" ></ div >
</ div >
</ div >
</ div >
</ div >
<!-- Split Pane -->
< div class = "lt-section" >
< div class = "lt-section-header" >
< span class = "lt-section-title" > // SPLIT PANE</ span >
</ div >
< div class = "lt-section-body" >
2026-03-25 23:51:21 -04:00
< div class = "lt-split" id = "demo-split" style = "height:clamp(160px,30vh,240px);border:1px solid var(--border-dim)" >
2026-03-25 22:29:55 -04:00
< div class = "lt-split-pane" style = "padding:1rem;overflow:auto" >
< div class = "lt-section-label" style = "margin-bottom:0.5rem" > Panel A</ div >
< p style = "font-size:0.78rem;color:var(--text-secondary)" > Drag the divider to resize. Stacks vertically on mobile.</ p >
</ div >
< div class = "lt-split-divider" title = "Drag to resize" ></ div >
< div class = "lt-split-pane" style = "padding:1rem;overflow:auto" >
< div class = "lt-section-label" style = "margin-bottom:0.5rem" > Panel B</ div >
< p style = "font-size:0.78rem;color:var(--text-secondary)" > Both panels maintain independent scrolling.</ p >
</ div >
</ div >
< p style = "font-size:0.72rem;color:var(--text-muted);margin-top:0.5rem" >< code > lt.splitPane.init(el, { initial: 0.4, minA: 120 } )</ code ></ p >
</ div >
</ div >
<!-- WebSocket & Offline -->
< div class = "lt-section" >
< div class = "lt-section-header" >
< span class = "lt-section-title" > // WEBSOCKET & OFFLINE DETECTION</ span >
</ div >
< div class = "lt-section-body" >
< div class = "lt-flex lt-gap-md lt-wrap lt-align-center" style = "margin-bottom:1rem" >
< div class = "lt-ws-status" data-state = "connected" >< span class = "lt-dot" ></ span >< span > Connected</ span ></ div >
< div class = "lt-ws-status" data-state = "connecting" >< span class = "lt-dot" ></ span >< span > Connecting…</ span ></ div >
< div class = "lt-ws-status" data-state = "disconnected" >< span class = "lt-dot" ></ span >< span > Disconnected</ span ></ div >
</ div >
< div class = "lt-flex lt-gap-sm lt-wrap" >
2026-03-26 18:22:53 -04:00
< button type = "button" class = "lt-btn lt-btn-sm" onclick = "lt.offline.isOnline() ? lt.toast.success('Online ✓') : lt.toast.error('Offline ✗')" > Check Online Status</ button >
< button type = "button" class = "lt-btn lt-btn-sm" onclick = "lt.toast.info('lt.ws.connect(url, { reconnect:true, onMessage: fn })')" > WS API Hint</ button >
2026-03-25 22:29:55 -04:00
</ div >
< p style = "font-size:0.72rem;color:var(--text-muted);margin-top:0.75rem" > Offline banner + body class auto-applied on < code > navigator.onLine</ code > change. WS manager has exponential backoff, event emitter, status indicator binding.</ p >
</ div >
</ div >
2026-03-25 22:42:16 -04:00
<!-- Wizard / Multi-Step Form -->
< div class = "lt-section" >
< div class = "lt-section-header" >
< span class = "lt-section-title" > // WIZARD / MULTI-STEP FORM</ span >
</ div >
< div class = "lt-section-body" >
< div id = "demo-wizard" >
<!-- Step indicators -->
< div class = "lt-wizard-steps" >
2026-03-26 18:22:53 -04:00
< div class = "lt-wizard-step is-active" data-wizard-indicator aria-current = "step" >
2026-03-25 22:42:16 -04:00
< div class = "lt-wizard-num" > 1</ div >
< div class = "lt-wizard-label" > Details</ div >
</ div >
< div class = "lt-wizard-step" data-wizard-indicator >
< div class = "lt-wizard-num" > 2</ div >
< div class = "lt-wizard-label" > Assign</ div >
</ div >
< div class = "lt-wizard-step" data-wizard-indicator >
< div class = "lt-wizard-num" > 3</ div >
< div class = "lt-wizard-label" > Review</ div >
</ div >
</ div >
<!-- Counter -->
< p class = "lt-wizard-counter" > Step < strong data-wizard-current > 1</ strong > of < strong data-wizard-total > 3</ strong ></ p >
<!-- Steps -->
2026-03-26 19:51:21 -04:00
< div data-wizard-step = "1" class = "is-active" >
2026-03-25 22:42:16 -04:00
< div class = "lt-grid lt-grid-2" style = "gap:1rem;margin-top:1rem" >
2026-03-26 20:02:15 -04:00
< div class = "lt-form-group" >< label class = "lt-label" for = "wizard-title" > Ticket Title</ label >< input id = "wizard-title" type = "text" name = "title" class = "lt-input lt-w-full" placeholder = "Brief description…" ></ div >
< div class = "lt-form-group" >< label class = "lt-label" for = "wizard-priority" > Priority</ label >< select id = "wizard-priority" name = "priority" class = "lt-select lt-w-full" >< option value = "p1" > P1 Critical</ option >< option value = "p2" > P2 High</ option >< option value = "p3" selected > P3 Medium</ option >< option value = "p4" > P4 Low</ option ></ select ></ div >
2026-03-25 22:42:16 -04:00
</ div >
</ div >
< div data-wizard-step = "2" aria-hidden = "true" >
2026-03-26 20:02:15 -04:00
< div class = "lt-form-group" style = "margin-top:1rem" >< label class = "lt-label" for = "wizard-assignee" > Assign To</ label >< input id = "wizard-assignee" type = "text" name = "assignee" class = "lt-input lt-w-full" placeholder = "Username…" ></ div >
< div class = "lt-form-group" >< label class = "lt-label" for = "wizard-due" > Due Date</ label >< input id = "wizard-due" type = "date" name = "due" class = "lt-input" ></ div >
2026-03-25 22:42:16 -04:00
</ div >
< div data-wizard-step = "3" aria-hidden = "true" >
< div class = "lt-empty-state lt-empty-state--sm" >< div class = "lt-empty-state-icon" > ✅</ div >< div class = "lt-empty-state-title" > Review & Submit</ div >< div class = "lt-empty-state-body" > Check the details above and click Submit when ready.</ div ></ div >
</ div >
<!-- Nav -->
< div class = "lt-wizard-nav" >
2026-03-26 20:46:31 -04:00
< button type = "button" class = "lt-btn lt-btn-sm" data-wizard-prev disabled > ← Back</ button >
< button type = "button" class = "lt-btn lt-btn-primary lt-btn-sm" data-wizard-next > Next →</ button >
< button type = "button" class = "lt-btn lt-btn-primary lt-btn-sm" data-wizard-done style = "display:none" > Submit ✓</ button >
2026-03-25 22:42:16 -04:00
</ div >
</ div >
</ div >
</ div >
<!-- Sortable List -->
< div class = "lt-section" >
< div class = "lt-section-header" >
< span class = "lt-section-title" > // DRAG-TO-REORDER (SORTABLE)</ span >
</ div >
< div class = "lt-section-body" >
2026-03-25 23:51:21 -04:00
< div class = "lt-grid lt-grid-2" style = "gap:1.5rem;grid-template-columns:repeat(auto-fit,minmax(220px,1fr))" >
2026-03-25 22:42:16 -04:00
< div >
2026-03-25 23:51:21 -04:00
< p style = "font-size:0.75rem;color:var(--text-muted);margin-bottom:0.75rem" > Drag items to reorder. Uses native HTML5 drag API with pointer-events fallback.</ p >
2026-03-25 22:42:16 -04:00
< ul id = "demo-sortable" style = "list-style:none;padding:0;display:flex;flex-direction:column;gap:4px" >
< li data-id = "p1" style = "padding:0.5rem 0.75rem;background:var(--bg-card);border:1px solid var(--border-dim);display:flex;align-items:center;gap:0.5rem" >< span style = "color:var(--text-muted);cursor:grab" > ⠿</ span > P1 — Storage link-down</ li >
< li data-id = "p2" style = "padding:0.5rem 0.75rem;background:var(--bg-card);border:1px solid var(--border-dim);display:flex;align-items:center;gap:0.5rem" >< span style = "color:var(--text-muted);cursor:grab" > ⠿</ span > P2 — Switch port flapping</ li >
< li data-id = "p3" style = "padding:0.5rem 0.75rem;background:var(--bg-card);border:1px solid var(--border-dim);display:flex;align-items:center;gap:0.5rem" >< span style = "color:var(--text-muted);cursor:grab" > ⠿</ span > P3 — SFP+ replacement</ li >
< li data-id = "p4" style = "padding:0.5rem 0.75rem;background:var(--bg-card);border:1px solid var(--border-dim);display:flex;align-items:center;gap:0.5rem" >< span style = "color:var(--text-muted);cursor:grab" > ⠿</ span > P4 — SSL cert renewal</ li >
</ ul >
< p id = "demo-sort-order" style = "font-size:0.68rem;color:var(--text-muted);margin-top:0.5rem" > Order: p1, p2, p3, p4</ p >
</ div >
< div >
< p style = "font-size:0.72rem;color:var(--text-muted);margin-bottom:0.75rem" > API:</ p >
< pre class = "lt-code-block" style = "font-size:0.7rem" > const s = lt.sortable.init(listEl, {
onSort: (items) => console.log(s.getOrder())
});</ pre >
</ div >
</ div >
</ div >
</ div >
<!-- Countdown / Timer -->
< div class = "lt-section" >
< div class = "lt-section-header" >
< span class = "lt-section-title" > // COUNTDOWN & TIMER</ span >
</ div >
< div class = "lt-section-body" >
2026-03-25 23:51:21 -04:00
< div class = "lt-grid lt-grid-3" style = "gap:1rem;grid-template-columns:repeat(auto-fit,minmax(160px,1fr))" >
2026-03-25 22:42:16 -04:00
< div class = "lt-stat-card" >
< div class = "lt-stat-label" > SLA Countdown</ div >
< div class = "lt-countdown lt-num" id = "demo-countdown" > --:--:--</ div >
< div style = "font-size:0.68rem;color:var(--text-muted);margin-top:0.25rem" > Goes urgent (red) at < 5 min</ div >
</ div >
< div class = "lt-stat-card" >
< div class = "lt-stat-label" > Stopwatch</ div >
< div class = "lt-countdown lt-num" id = "demo-stopwatch" > 00:00:00</ div >
< div class = "lt-flex lt-gap-xs" style = "margin-top:0.5rem" >
2026-03-26 20:46:31 -04:00
< button type = "button" class = "lt-btn lt-btn-sm" id = "sw-pause" > Pause</ button >
< button type = "button" class = "lt-btn lt-btn-sm" id = "sw-reset" > Reset</ button >
2026-03-25 22:42:16 -04:00
</ div >
</ div >
< div class = "lt-stat-card" >
< div class = "lt-stat-label" > API</ div >
< pre class = "lt-code-block" style = "font-size:0.62rem;margin:0" > lt.timer.countdown(el, date, {
urgent: 300,
onExpire: () => {}
});
lt.timer.stopwatch(el);</ pre >
</ div >
</ div >
</ div >
</ div >
<!-- Image Lightbox -->
< div class = "lt-section" >
< div class = "lt-section-header" >
< span class = "lt-section-title" > // IMAGE LIGHTBOX</ span >
</ div >
< div class = "lt-section-body" >
< p style = "font-size:0.78rem;color:var(--text-secondary);margin-bottom:1rem" > Click any image to open full-screen viewer. Keyboard: ←/→ navigate, Esc closes.</ p >
< div class = "lt-flex lt-gap-md lt-wrap" >
< img class = "lt-lightbox-demo" src = "https://picsum.photos/seed/lt1/320/180" alt = "Server rack overview" style = "width:160px;height:90px;object-fit:cover;border:1px solid var(--border-dim)" loading = "lazy" >
< img class = "lt-lightbox-demo" src = "https://picsum.photos/seed/lt2/320/180" alt = "Network switch closeup" style = "width:160px;height:90px;object-fit:cover;border:1px solid var(--border-dim)" loading = "lazy" >
< img class = "lt-lightbox-demo" src = "https://picsum.photos/seed/lt3/320/180" alt = "Datacenter floor view" style = "width:160px;height:90px;object-fit:cover;border:1px solid var(--border-dim)" loading = "lazy" >
</ div >
< p style = "font-size:0.72rem;color:var(--text-muted);margin-top:0.5rem" >< code > lt.lightbox.init('.lt-lightbox-demo', { caption: 'alt', loop: true })</ code ></ p >
</ div >
</ div >
<!-- Sidebar Submenus -->
< div class = "lt-section" >
< div class = "lt-section-header" >
< span class = "lt-section-title" > // SIDEBAR SUBMENUS</ span >
</ div >
< div class = "lt-section-body" >
< div style = "width:220px;background:var(--bg-secondary);border:1px solid var(--border-dim);padding:0.5rem" >
< a href = "#" class = "lt-sidebar-link active" style = "display:flex;align-items:center;gap:0.5rem;padding:0.4rem 0.75rem;font-size:0.78rem;color:var(--accent-orange);text-decoration:none" > ⊞ Dashboard</ a >
< a href = "#" class = "lt-sidebar-link" style = "display:flex;align-items:center;gap:0.5rem;padding:0.4rem 0.75rem;font-size:0.78rem;color:var(--text-secondary);text-decoration:none" > 🎫 Tickets</ a >
< div class = "lt-sidebar-group is-open" >
< div class = "lt-sidebar-group-label" > Admin < span class = "chevron" > ▶</ span ></ div >
< div class = "lt-sidebar-submenu" >
< a href = "#" class = "lt-sidebar-sub-link active" aria-current = "page" > ⚙ Templates</ a >
< a href = "#" class = "lt-sidebar-sub-link" > 🔀 Workflow</ a >
< a href = "#" class = "lt-sidebar-sub-link" > 📋 Audit Log</ a >
< a href = "#" class = "lt-sidebar-sub-link" > 🔑 API Keys</ a >
</ div >
</ div >
< div class = "lt-sidebar-group" >
< div class = "lt-sidebar-group-label" > Reports < span class = "chevron" > ▶</ span ></ div >
< div class = "lt-sidebar-submenu" >
< a href = "#" class = "lt-sidebar-sub-link" > 📊 SLA Report</ a >
< a href = "#" class = "lt-sidebar-sub-link" > 📈 Volume Trends</ a >
</ div >
</ div >
</ div >
</ div >
</ div >
<!-- Markdown Renderer -->
< div class = "lt-section" >
< div class = "lt-section-header" >
< span class = "lt-section-title" > // MARKDOWN RENDERER</ span >
</ div >
< div class = "lt-section-body" >
< div class = "lt-grid lt-grid-2" style = "gap:1.5rem" >
< div >
< div class = "lt-section-label" style = "margin-bottom:0.5rem" > Source</ div >
< pre class = "lt-code-block" style = "font-size:0.7rem" > # Incident Report
**Severity**: P1 Critical
## Summary
Storage array link-down on `compute-storage-01`.
- Affected: 3 production services
- Duration: 2h 33m
- Root cause: NIC hardware failure
> Resolved. Monitoring continues.
[View Ticket](#001)</ pre >
</ div >
< div >
< div class = "lt-section-label" style = "margin-bottom:0.5rem" > Rendered</ div >
< div id = "demo-markdown" class = "lt-markdown" style = "background:var(--bg-card);border:1px solid var(--border-dim);padding:1rem" data-markdown = "# Incident Report **Severity**: P1 Critical ## Summary Storage array link-down on `compute-storage-01`. - Affected: 3 production services - Duration: 2h 33m - Root cause: NIC hardware failure > Resolved. Monitoring continues. [View Ticket](#001)" ></ div >
</ div >
</ div >
< p style = "font-size:0.72rem;color:var(--text-muted);margin-top:0.75rem" > Built-in micro-renderer (no deps). Drops in < code > window.marked</ code > or < code > window.markdownit</ code > automatically if present.</ p >
</ div >
</ div >
2026-03-14 21:08:57 -04:00
</ main > <!-- /.lt-main -->
2026-03-26 18:22:53 -04:00
<!-- ================================================================
FOOTER LANDMARK
================================================================ -->
< footer class = "lt-footer" role = "contentinfo" aria-label = "Application footer" >
< span class = "lt-text-muted lt-font-mono lt-text-xs" >
LotusGuild Terminal Design System v1.2 — Internal Use Only
</ span >
< span class = "lt-text-muted lt-font-mono lt-text-xs" >
© < span id = "footer-year" ></ span > LotusGuild. All rights reserved.
</ span >
</ footer >
2026-03-14 21:08:57 -04:00
<!-- ===========================================================
TOAST DEMO BUTTONS (remove in production)
=========================================================== -->
< div style = "position:fixed;bottom:1rem;left:1rem;display:flex;flex-direction:column;gap:0.5rem;z-index:900" >
2026-03-26 18:22:53 -04:00
< button type = "button" class = "lt-btn lt-btn-sm" onclick = "lt.toast.success('Ticket saved successfully')" > ✓ Toast</ button >
< button type = "button" class = "lt-btn lt-btn-sm lt-btn-danger" onclick = "lt.toast.error('Network error — retry in 5s', 5000)" > ✗ Error</ button >
< button type = "button" class = "lt-btn lt-btn-sm" onclick = "lt.toast.warning('Rate limit 80% used')" > ! Warn</ button >
< button type = "button" class = "lt-btn lt-btn-sm lt-btn-ghost" onclick = "lt.toast.info('Auto-refresh triggered')" > i Info</ button >
2026-03-14 21:08:57 -04:00
</ div >
<!-- ===========================================================
MODAL EXAMPLE: Export
=========================================================== -->
< div id = "export-modal" class = "lt-modal-overlay" aria-hidden = "true" >
2026-03-26 18:22:53 -04:00
< div class = "lt-modal" role = "dialog" aria-modal = "true" aria-labelledby = "export-modal-title" >
2026-03-14 21:08:57 -04:00
< div class = "lt-modal-header" >
2026-03-26 18:22:53 -04:00
< span class = "lt-modal-title" id = "export-modal-title" > Export Tickets</ span >
2026-03-27 13:49:23 -04:00
< button type = "button" class = "lt-modal-close" data-modal-close aria-label = "Close" > ✕</ button >
2026-03-14 21:08:57 -04:00
</ div >
< div class = "lt-modal-body" >
< div class = "lt-form-group" >
< label class = "lt-label" for = "export-fmt" > Format</ label >
< select id = "export-fmt" class = "lt-select" >
< option > CSV</ option >
< option > JSON</ option >
</ select >
</ div >
< div class = "lt-form-group" >
< label class = "lt-label" >
< input type = "checkbox" class = "lt-checkbox" checked > Selected tickets only
</ label >
</ div >
< div class = "lt-msg lt-msg-info" > Exports include all visible columns.</ div >
</ div >
< div class = "lt-modal-footer" >
2026-03-26 18:22:53 -04:00
< button type = "button" class = "lt-btn lt-btn-ghost" data-modal-close > Cancel</ button >
< button type = "button" class = "lt-btn lt-btn-primary" onclick = "lt.toast.success('Export started'); lt.modal.close('export-modal')" > Export</ button >
2026-03-14 21:08:57 -04:00
</ div >
</ div >
</ div >
<!-- ===========================================================
MODAL EXAMPLE: Keyboard shortcuts help
=========================================================== -->
< div id = "lt-keys-help" class = "lt-modal-overlay" aria-hidden = "true" >
2026-03-26 18:22:53 -04:00
< div class = "lt-modal" role = "dialog" aria-modal = "true" aria-labelledby = "keys-help-title" >
2026-03-14 21:08:57 -04:00
< div class = "lt-modal-header" >
2026-03-26 18:22:53 -04:00
< span class = "lt-modal-title" id = "keys-help-title" > Keyboard Shortcuts</ span >
2026-03-27 13:49:23 -04:00
< button type = "button" class = "lt-modal-close" data-modal-close aria-label = "Close" > ✕</ button >
2026-03-14 21:08:57 -04:00
</ div >
< div class = "lt-modal-body" >
< table class = "lt-data-table" style = "width:100%" >
< thead >
2026-03-26 20:09:29 -04:00
< tr >< th scope = "col" > Shortcut</ th >< th scope = "col" > Action</ th ></ tr >
2026-03-14 21:08:57 -04:00
</ thead >
< tbody >
< tr >< td > Ctrl / ⌘ + K</ td >< td > Focus search box</ td ></ tr >
< tr >< td > Ctrl / ⌘ + E</ td >< td > Toggle edit mode (ticket page)</ td ></ tr >
< tr >< td > Ctrl / ⌘ + S</ td >< td > Save changes (ticket page)</ td ></ tr >
< tr >< td > j / ↓</ td >< td > Select next row</ td ></ tr >
< tr >< td > k / ↑</ td >< td > Select previous row</ td ></ tr >
< tr >< td > Enter</ td >< td > Open selected ticket</ td ></ tr >
< tr >< td > n</ td >< td > New ticket</ td ></ tr >
< tr >< td > ?</ td >< td > Show this help</ td ></ tr >
< tr >< td > ESC</ td >< td > Close modal / cancel</ td ></ tr >
</ tbody >
</ table >
</ div >
< div class = "lt-modal-footer" >
2026-03-27 13:49:23 -04:00
< button type = "button" class = "lt-btn" data-modal-close > Close</ button >
2026-03-14 21:08:57 -04:00
</ div >
</ div >
</ div >
2026-03-25 18:47:26 -04:00
<!-- ===========================================================
COMMAND PALETTE OVERLAY
=========================================================== -->
< div id = "lt-cmd-overlay" class = "lt-cmd-overlay" role = "dialog" aria-modal = "true" aria-label = "Command palette" >
< div class = "lt-cmd-palette" id = "lt-cmd-palette" >
< div class = "lt-cmd-input-wrap" >
< span class = "lt-cmd-prompt" > > </ span >
2026-03-27 13:49:23 -04:00
< input id = "lt-cmd-input" class = "lt-cmd-input" type = "text" placeholder = "Search commands…" autocomplete = "off" spellcheck = "false" aria-label = "Search commands" >
2026-03-25 18:47:26 -04:00
</ div >
< div class = "lt-cmd-results" id = "lt-cmd-results" >
< div class = "lt-cmd-empty" > Start typing to search…</ div >
</ div >
< div class = "lt-cmd-footer" >
< span >< kbd > ↑</ kbd >< kbd > ↓</ kbd > Navigate</ span >
< span >< kbd > Enter</ kbd > Select</ span >
< span >< kbd > Esc</ kbd > Close</ span >
</ div >
</ div >
</ div >
2026-03-14 21:08:57 -04:00
<!-- ===========================================================
SCRIPTS
=========================================================== -->
<!--
PHP apps inject CSRF token + config here (with CSP nonce):
<script nonce="<?php echo $nonce; ?>">
window.CSRF_TOKEN = '<?php echo CsrfMiddleware::getToken(); ?>';
window.APP_TIMEZONE = '<?php echo $config['TIMEZONE']; ?>';
</script>
Node/Express (EJS):
<script nonce="<%= nonce %>">
window.CSRF_TOKEN = '<%= csrfToken %>';
</script>
Flask/Jinja2:
<script nonce="{{ nonce }}">
window.CSRF_TOKEN = '{{ csrf_token() }}';
</script>
-->
2026-03-25 18:47:26 -04:00
<!-- LotusGuild Terminal Design System JS v1.2 -->
2026-03-14 21:08:57 -04:00
< script src = "/web_template/base.js" ></ script >
2026-03-25 18:47:26 -04:00
<!-- Init v1.2 modules -->
< script >
document . addEventListener ( 'DOMContentLoaded' , () => {
2026-03-26 00:12:04 -04:00
// One call initializes accordion, tooltip, alerts, clipboard, sidebar, submenus + boot.
// Pass { boot: false } to skip the terminal boot animation.
lt . init ({ bootName : 'MY APP' });
2026-03-25 18:47:26 -04:00
// Command palette demo commands
lt . cmdPalette . init ([
{
group : 'Navigation' ,
items : [
{ icon : '⌂' , label : 'Dashboard' , kbd : 'G D' , action : () => lt . toast . info ( '→ Dashboard' ) },
{ icon : '⊞' , label : 'All Tickets' , kbd : 'G T' , action : () => lt . toast . info ( '→ Tickets' ) },
{ icon : '★' , label : 'Saved Filters' , action : () => lt . toast . info ( '→ Filters' ) },
]
},
{
group : 'Actions' ,
items : [
{ icon : '+' , label : 'New Ticket' , kbd : 'N' , action : () => lt . toast . success ( 'New ticket form' ) },
{ icon : '⤓' , label : 'Export CSV' , action : () => lt . modal . open ( 'export-modal' ) },
{ icon : '⟳' , label : 'Refresh Data' , kbd : 'R' , action : () => lt . toast . info ( 'Refreshing...' ) },
]
},
{
group : 'Help' ,
items : [
{ icon : '?' , label : 'Keyboard Shortcuts' , kbd : '?' , action : () => lt . modal . open ( 'lt-keys-help' ) },
]
},
]);
// Range slider live value display
document . querySelectorAll ( 'input[type="range"].lt-range' ). forEach ( r => {
const wrap = r . closest ( '.lt-range-wrap' );
const label = wrap && wrap . querySelector ( '.lt-range-value' );
if ( label ) {
label . textContent = r . value ;
2026-03-26 20:16:12 -04:00
r . addEventListener ( 'input' , () => { label . textContent = r . value ; r . setAttribute ( 'aria-valuenow' , r . value ); });
2026-03-25 18:47:26 -04:00
}
});
// Animate the demo progress bars on load
document . querySelectorAll ( '.lt-progress-bar[data-width]' ). forEach ( bar => {
requestAnimationFrame (() => { bar . style . width = bar . dataset . width ; });
});
2026-03-25 22:29:55 -04:00
// Theme toggle button
document . getElementById ( 'lt-theme-btn' ). addEventListener ( 'click' , () => lt . theme . toggle ());
// Context menu: register demo menu items
lt . contextMenu . register ( 'demo-ctx' , [
{ icon : '📋' , label : 'Copy ID' , kbd : 'C' , action : () => lt . toast . info ( 'ID copied' ) },
{ icon : '👁' , label : 'View Details' , action : () => lt . rightDrawer . open ( 'lt-detail-drawer' ) },
{ icon : '✏️' , label : 'Edit Ticket' , kbd : 'E' , action : () => lt . toast . info ( 'Edit…' ) },
{ divider : true },
{ icon : '🗑' , label : 'Delete' , danger : true , action : () => lt . toast . error ( 'Deleted' ) },
]);
// Programmatic context menu button
const demoCtxBtn = document . getElementById ( 'demo-ctx-btn' );
if ( demoCtxBtn ) demoCtxBtn . addEventListener ( 'click' , e => {
const r = demoCtxBtn . getBoundingClientRect ();
lt . contextMenu . show ( r . left , r . bottom + 4 , [
{ icon : '📋' , label : 'Copy ID' , action : () => lt . toast . info ( 'Copied' ) },
{ icon : '✏️' , label : 'Edit' , action : () => lt . toast . info ( 'Edit' ) },
{ divider : true },
{ icon : '🗑' , label : 'Delete' , danger : true , action : () => lt . toast . error ( 'Deleted' ) },
]);
});
// Combobox demo
lt . combobox . init (
document . getElementById ( 'demo-combobox-input' ),
[
{ value : 'worker-01' , label : 'worker-01' , icon : '🖥' },
{ value : 'worker-02' , label : 'worker-02' , icon : '🖥' },
{ value : 'worker-03' , label : 'worker-03' , icon : '🖥' },
{ value : 'gpu-01' , label : 'gpu-01' , icon : '⚡' },
{ value : 'gpu-02' , label : 'gpu-02' , icon : '⚡' },
{ value : 'storage-01' , label : 'storage-01' , icon : '💾' },
],
{ onChange : vals => console . log ( '[combobox]' , vals ) }
);
// Typeahead demo
lt . typeahead . init (
document . getElementById ( 'demo-typeahead-input' ),
[
{ value : '001' , label : 'Link-down on compute-storage-01' , icon : '🔴' , meta : 'P1' },
{ value : '002' , label : 'Switch port flapping USW-Pro-24' , icon : '🟠' , meta : 'P2' },
{ value : '003' , label : 'Scheduled SFP+ replacement large1' , icon : '🔵' , meta : 'P3' },
{ value : '004' , label : 'SSL cert renewal wiki.lotusguild.org' , icon : '🟢' , meta : 'P4' },
{ value : '005' , label : 'RAID controller firmware update' , icon : '🔴' , meta : 'P1' },
],
{ onSelect : item => lt . toast . info ( `Selected: ${ item . label } ` ) }
);
// Split pane demo
lt . splitPane . init ( document . getElementById ( 'demo-split' ), { initial : 0.4 , minA : 100 , minB : 100 });
// Demo notification badge initial count
lt . notif . set ( '#lt-notif-bell' , 3 );
2026-03-25 22:42:16 -04:00
// Wizard demo
lt . wizard . init ( document . getElementById ( 'demo-wizard' ), {
onComplete : data => lt . toast . success ( 'Ticket submitted: ' + ( data . title || 'untitled' )),
validate : ( step , data ) => {
if ( step === 1 && ! data . title ? . trim ()) { lt . toast . error ( 'Title is required' ); return false ; }
return true ;
},
});
// Sortable demo
const sortableList = lt . sortable . init ( document . getElementById ( 'demo-sortable' ), {
onSort : ( items ) => {
document . getElementById ( 'demo-sort-order' ). textContent = 'Order: ' + items . map ( el => el . dataset . id ). join ( ', ' );
},
});
2026-03-25 23:19:49 -04:00
// Kanban drag-and-drop — all four columns share group "kanban"
[ 'kanban-col-open' , 'kanban-col-pending' , 'kanban-col-inprogress' , 'kanban-col-closed' ]. forEach ( id => {
const col = document . getElementById ( id );
if ( col ) lt . sortable . init ( col , { group : 'kanban' , onSort : () => {} });
});
// Pagination demo (50 items, 10 per page)
lt . pagination . init ( '#demo-pagination' , {
total : 50 , perPage : 10 , page : 2 ,
onChange : p => lt . toast . info ( 'Page ' + p ),
});
2026-03-25 22:42:16 -04:00
// Countdown demo — SLA expires 2 hours from now
const slaTarget = new Date ( Date . now () + 2 * 60 * 60 * 1000 );
lt . timer . countdown ( document . getElementById ( 'demo-countdown' ), slaTarget , {
urgent : 300 ,
urgentClass : 'lt-text-red lt-countdown-urgent' ,
onExpire : () => lt . toast . error ( 'SLA BREACHED' ),
});
// Stopwatch demo
const sw = lt . timer . stopwatch ( document . getElementById ( 'demo-stopwatch' ));
let swRunning = true ;
document . getElementById ( 'sw-pause' ). addEventListener ( 'click' , function () {
if ( swRunning ) { sw . pause (); this . textContent = 'Resume' ; } else { sw . resume (); this . textContent = 'Pause' ; }
swRunning = ! swRunning ;
});
document . getElementById ( 'sw-reset' ). addEventListener ( 'click' , () => { sw . reset (); swRunning = true ; document . getElementById ( 'sw-pause' ). textContent = 'Pause' ; });
// Lightbox demo
lt . lightbox . init ( '.lt-lightbox-demo' );
// Markdown demo
lt . markdown . init ( '#demo-markdown' );
// Sidebar submenus (re-init for demo sidebar)
lt . sidebarSubmenus . init ( document . querySelector ( '.lt-section-body' ));
2026-03-25 23:36:29 -04:00
// ----- Notification bell dropdown -----
( function () {
const btn = document . getElementById ( 'lt-notif-bell-btn' );
const panel = document . getElementById ( 'lt-notif-panel' );
if ( ! btn || ! panel ) return ;
function open () {
2026-03-26 19:51:21 -04:00
panel . removeAttribute ( 'aria-hidden' );
2026-03-25 23:36:29 -04:00
btn . setAttribute ( 'aria-expanded' , 'true' );
// Mark all as read visually
}
2026-03-27 13:49:23 -04:00
function close ( restoreFocus ) {
2026-03-25 23:36:29 -04:00
panel . setAttribute ( 'aria-hidden' , 'true' );
btn . setAttribute ( 'aria-expanded' , 'false' );
2026-03-27 13:49:23 -04:00
if ( restoreFocus ) btn . focus ();
2026-03-25 23:36:29 -04:00
}
btn . addEventListener ( 'click' , e => {
e . stopPropagation ();
2026-03-26 19:51:21 -04:00
panel . hasAttribute ( 'aria-hidden' ) ? open () : close ();
2026-03-25 23:36:29 -04:00
});
// "Mark all read" button
const clearBtn = document . getElementById ( 'lt-notif-clear-all' );
if ( clearBtn ) clearBtn . addEventListener ( 'click' , () => {
panel . querySelectorAll ( '.lt-notif-item--unread' ). forEach ( el => el . classList . remove ( 'lt-notif-item--unread' ));
panel . querySelectorAll ( '.lt-notif-dot' ). forEach ( el => { el . classList . remove ( 'lt-notif-dot' ); el . classList . add ( 'lt-notif-dot' , 'lt-notif-dot--read' ); });
lt . notif . clear ( '#lt-notif-bell' );
lt . toast . info ( 'All notifications marked as read' );
});
2026-03-26 19:51:21 -04:00
// Individual item click + keyboard activation
2026-03-25 23:36:29 -04:00
panel . querySelectorAll ( '.lt-notif-item' ). forEach ( item => {
2026-03-26 19:51:21 -04:00
const activate = () => {
2026-03-25 23:36:29 -04:00
item . classList . remove ( 'lt-notif-item--unread' );
const dot = item . querySelector ( '.lt-notif-dot' );
if ( dot ) { dot . classList . add ( 'lt-notif-dot--read' ); }
close ();
2026-03-26 19:51:21 -04:00
};
item . addEventListener ( 'click' , activate );
item . addEventListener ( 'keydown' , e => { if ( e . key === 'Enter' || e . key === ' ' ) { e . preventDefault (); activate (); } });
2026-03-25 23:36:29 -04:00
});
// Close on outside click
document . addEventListener ( 'click' , e => {
if ( ! document . getElementById ( 'lt-notif-bell' ). contains ( e . target )) close ();
});
2026-03-27 13:49:23 -04:00
// Esc close — restore focus to bell button
document . addEventListener ( 'keydown' , e => { if ( e . key === 'Escape' && ! panel . hasAttribute ( 'aria-hidden' )) close ( true ); });
2026-03-25 23:36:29 -04:00
}());
// ----- Generic dropdown toggle (Advanced filter + Bulk Actions) -----
document . querySelectorAll ( '.lt-dropdown-trigger' ). forEach ( btn => {
const wrap = btn . closest ( '.lt-dropdown-wrap' );
const panel = wrap && wrap . querySelector ( '.lt-dropdown-panel' );
if ( ! panel ) return ;
btn . addEventListener ( 'click' , e => {
e . stopPropagation ();
2026-03-26 19:51:21 -04:00
const isOpen = ! panel . hasAttribute ( 'aria-hidden' );
2026-03-25 23:36:29 -04:00
// Close all other dropdowns first
2026-03-26 19:51:21 -04:00
document . querySelectorAll ( '.lt-dropdown-panel:not([aria-hidden])' ). forEach ( p => {
2026-03-25 23:36:29 -04:00
p . setAttribute ( 'aria-hidden' , 'true' );
const t = p . closest ( '.lt-dropdown-wrap' ). querySelector ( '.lt-dropdown-trigger' );
if ( t ) t . setAttribute ( 'aria-expanded' , 'false' );
});
if ( ! isOpen ) {
2026-03-26 19:51:21 -04:00
panel . removeAttribute ( 'aria-hidden' );
2026-03-25 23:36:29 -04:00
btn . setAttribute ( 'aria-expanded' , 'true' );
}
});
});
// Close dropdowns on outside click / Esc
document . addEventListener ( 'click' , () => {
2026-03-26 19:51:21 -04:00
document . querySelectorAll ( '.lt-dropdown-panel:not([aria-hidden])' ). forEach ( p => {
2026-03-25 23:36:29 -04:00
p . setAttribute ( 'aria-hidden' , 'true' );
const t = p . closest ( '.lt-dropdown-wrap' ). querySelector ( '.lt-dropdown-trigger' );
if ( t ) t . setAttribute ( 'aria-expanded' , 'false' );
});
});
2026-03-27 13:49:23 -04:00
document . addEventListener ( 'keydown' , e => {
if ( e . key !== 'Escape' ) return ;
document . querySelectorAll ( '.lt-dropdown-panel:not([aria-hidden])' ). forEach ( p => {
p . setAttribute ( 'aria-hidden' , 'true' );
const t = p . closest ( '.lt-dropdown-wrap' ). querySelector ( '.lt-dropdown-trigger' );
if ( t ) { t . setAttribute ( 'aria-expanded' , 'false' ); t . focus (); }
});
});
2026-03-25 23:36:29 -04:00
2026-04-29 17:27:10 -04:00
// SLA dismiss buttons — hide and persist per session
2026-04-29 17:18:33 -04:00
document . querySelectorAll ( '.lt-sla-dismiss' ). forEach ( btn => {
2026-04-29 17:27:10 -04:00
const banner = btn . closest ( '.lt-sla-p1, .lt-sla-p2' );
if ( ! banner ) return ;
const id = banner . dataset . slaId ;
try { if ( id && sessionStorage . getItem ( 'lt_sla_dismissed_' + id )) banner . hidden = true ; } catch ( _ ) {}
2026-04-29 17:18:33 -04:00
btn . addEventListener ( 'click' , () => {
2026-04-29 17:27:10 -04:00
banner . hidden = true ;
try { if ( id ) sessionStorage . setItem ( 'lt_sla_dismissed_' + id , '1' ); } catch ( _ ) {}
2026-04-29 17:18:33 -04:00
});
});
2026-03-26 18:22:53 -04:00
// Footer year
const footerYear = document . getElementById ( 'footer-year' );
if ( footerYear ) footerYear . textContent = new Date (). getFullYear ();
2026-03-25 18:47:26 -04:00
// Tab bar switching
document . querySelectorAll ( '.lt-tab-bar' ). forEach ( bar => {
bar . addEventListener ( 'click' , e => {
const tab = e . target . closest ( '.lt-tab' );
if ( ! tab ) return ;
const targetId = tab . dataset . tabTarget ;
if ( ! targetId ) return ;
const panels = bar . nextElementSibling ;
bar . querySelectorAll ( '.lt-tab' ). forEach ( t => { t . classList . remove ( 'active' ); t . setAttribute ( 'aria-selected' , 'false' ); });
tab . classList . add ( 'active' );
tab . setAttribute ( 'aria-selected' , 'true' );
if ( panels ) {
panels . querySelectorAll ( '.lt-tab-panel' ). forEach ( p => p . classList . remove ( 'active' ));
const target = panels . querySelector ( '#' + targetId );
if ( target ) target . classList . add ( 'active' );
}
});
});
});
</ script >
2026-03-14 21:08:57 -04:00
</ body >
</ html >