0eb91f1937
JS modules added: - lt.theme — dark/light toggle, OS preference sync, localStorage persist - lt.notif — notification badge (set/inc/clear) on any element - lt.rightDrawer — right-side detail panel with focus trap + return focus - lt.contextMenu — right-click custom menu, keyboard nav, danger variant - lt.offline — navigator.onLine banner + event hooks - lt.ws — WebSocket manager with exponential backoff reconnect - lt.combobox — multi-select with search, tag chips, keyboard nav - lt.typeahead — async/sync autocomplete with match highlighting - lt.cookie — get/set/del with SameSite/Secure helpers - lt.splitPane — pointer-events resizable split pane (horizontal/vertical) - Toast queue: max-stack, progress bar drain animation, auto-drain CSS sections added (51–68): - Light theme (html[data-theme="light"]) with full variable overrides - Theme toggle button (.lt-theme-btn) - Skeleton loader variants (card, row, text, title, avatar, btn, badge) - Empty state component (.lt-empty-state, --sm variant) - Nav notification badge (.lt-notif-wrap / .lt-notif-badge) - Right-side drawer (.lt-drawer-right + overlay) - Sticky table header (.lt-table-sticky-wrap) - Multi-select combobox (.lt-combobox, tags, dropdown) - Context menu (.lt-context-menu, divider, label, danger) - Offline banner (.lt-offline-banner) - Timeline / activity feed (.lt-timeline, color variants) - Avatar + avatar group + status ring (.lt-avatar) - Split pane (.lt-split, .lt-split-divider with pointer drag) - Chart container (.lt-chart-wrap, legend, axis, loading state) - Toast queue stack + progress drain bar - Autocomplete / typeahead (.lt-typeahead-dropdown, match highlight) - WebSocket status indicator (.lt-ws-status, data-state variants) - Print enhancements (extended @media print rules) HTML demo sections for all new components added to base.html Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1516 lines
77 KiB
HTML
1516 lines
77 KiB
HTML
<!DOCTYPE html>
|
||
<!--
|
||
LOTUSGUILD TERMINAL DESIGN SYSTEM v2.0 — base.html
|
||
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">
|
||
<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">
|
||
<title>MY APP — LotusGuild</title>
|
||
<meta name="description" content="LotusGuild infrastructure application">
|
||
|
||
<!-- =========================================================
|
||
Security headers are set server-side. CSP nonce is injected
|
||
by SecurityHeadersMiddleware (PHP) / helmet (Node) / Flask.
|
||
All <script> tags need: nonce="NONCE_PLACEHOLDER"
|
||
========================================================= -->
|
||
|
||
<!-- Monospace font: JetBrains Mono -->
|
||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:ital,wght@0,400;0,600;0,700;1,400&display=swap" rel="stylesheet">
|
||
|
||
<!-- Hacker template design system CSS -->
|
||
<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>
|
||
|
||
<!-- ===========================================================
|
||
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
|
||
=========================================================== -->
|
||
<!-- ===========================================================
|
||
MOBILE NAV DRAWER (hidden on desktop, slides in on mobile)
|
||
=========================================================== -->
|
||
<div id="lt-nav-drawer" class="lt-nav-drawer" aria-hidden="true" role="dialog" aria-label="Navigation menu">
|
||
<div class="lt-nav-drawer-header">
|
||
<span class="lt-brand-title">MY APP</span>
|
||
<button class="lt-nav-drawer-close" id="lt-nav-drawer-close" aria-label="Close menu">✕</button>
|
||
</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>
|
||
|
||
<header class="lt-header">
|
||
<div class="lt-header-left">
|
||
|
||
<!-- Hamburger — visible only on tablet/mobile (CSS hides on desktop) -->
|
||
<button class="lt-menu-btn" id="lt-menu-btn" aria-label="Open navigation menu" aria-expanded="false" aria-controls="lt-nav-drawer">
|
||
<span></span><span></span><span></span>
|
||
</button>
|
||
|
||
<!-- Brand -->
|
||
<div class="lt-brand">
|
||
<span class="lt-brand-title lt-glitch" data-text="MY APP">MY APP</span>
|
||
<span class="lt-brand-subtitle">LotusGuild Infrastructure</span>
|
||
</div>
|
||
|
||
<!-- Horizontal nav links (desktop) -->
|
||
<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">
|
||
<!-- 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 -->
|
||
<button class="lt-theme-btn" id="lt-theme-btn" aria-label="Switch to light mode" title="Switch to light mode">☀</button>
|
||
<!-- Notifications with badge -->
|
||
<span class="lt-notif-wrap" id="lt-notif-bell" aria-label="Notifications">
|
||
<button class="lt-btn lt-btn-sm" style="padding:0 0.5rem;min-height:32px;" aria-label="Open notifications">🔔</button>
|
||
</span>
|
||
<!-- Current user -->
|
||
<span class="lt-header-user">operator</span>
|
||
<!-- Admin badge (only show for admins) -->
|
||
<span class="lt-badge-admin">admin</span>
|
||
</div>
|
||
</header>
|
||
|
||
<!-- Right-side detail drawer -->
|
||
<div id="lt-detail-drawer" class="lt-drawer-right" aria-hidden="true" role="dialog" aria-label="Detail panel" data-overlay="lt-detail-overlay">
|
||
<div class="lt-drawer-right-header">
|
||
<span class="lt-drawer-right-title">// TICKET DETAIL</span>
|
||
<button class="lt-drawer-right-close" data-drawer-close aria-label="Close detail panel">✕</button>
|
||
</div>
|
||
<div class="lt-drawer-right-body">
|
||
<div class="lt-kv-grid">
|
||
<div class="lt-kv-row"><span class="lt-kv-label">ID</span><span class="lt-kv-value lt-text-cyan">#123456789</span></div>
|
||
<div class="lt-kv-row"><span class="lt-kv-label">Status</span><span class="lt-kv-value"><span class="lt-badge lt-badge-open">Open</span></span></div>
|
||
<div class="lt-kv-row"><span class="lt-kv-label">Priority</span><span class="lt-kv-value"><span class="lt-badge lt-badge-p1">P1 Critical</span></span></div>
|
||
<div class="lt-kv-row"><span class="lt-kv-label">Assignee</span><span class="lt-kv-value" style="display:flex;align-items:center;gap:0.5rem"><span class="lt-avatar lt-avatar--sm lt-avatar--orange">JD</span>jdoe</span></div>
|
||
<div class="lt-kv-row"><span class="lt-kv-label">Created</span><span class="lt-kv-value">2026-03-10</span></div>
|
||
</div>
|
||
<div class="lt-divider-label" style="margin:1rem 0">Description</div>
|
||
<p style="font-size:0.78rem;color:var(--text-secondary);line-height:1.6">Storage array link-down on compute-storage-01. Affects prod write path. Investigate RAID controller firmware.</p>
|
||
<div class="lt-divider-label" style="margin:1rem 0">Activity</div>
|
||
<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">
|
||
<button class="lt-btn lt-btn-sm" onclick="lt.rightDrawer.close('lt-detail-drawer')">Close</button>
|
||
<button class="lt-btn lt-btn-primary lt-btn-sm">Save Changes</button>
|
||
</div>
|
||
</div>
|
||
<div id="lt-detail-overlay" class="lt-drawer-right-overlay"></div>
|
||
|
||
<!-- ===========================================================
|
||
MAIN CONTENT AREA
|
||
=========================================================== -->
|
||
<main class="lt-main lt-container">
|
||
|
||
<!-- 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>
|
||
<button class="lt-btn lt-btn-sm" data-modal-open="export-modal">Export</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ==========================================================
|
||
STATS WIDGETS
|
||
data-filter-key / data-filter-val → wired by lt.statsFilter
|
||
========================================================== -->
|
||
<div class="lt-stats-grid">
|
||
<div class="lt-stat-card active" data-filter-key="status" data-filter-val="Open">
|
||
<span class="lt-stat-icon">📋</span>
|
||
<div class="lt-stat-info">
|
||
<span class="lt-stat-value">42</span>
|
||
<span class="lt-stat-label">Open</span>
|
||
</div>
|
||
</div>
|
||
<div class="lt-stat-card" data-filter-key="priority" data-filter-val="1">
|
||
<span class="lt-stat-icon">🔴</span>
|
||
<div class="lt-stat-info">
|
||
<span class="lt-stat-value">3</span>
|
||
<span class="lt-stat-label">Critical</span>
|
||
</div>
|
||
</div>
|
||
<div class="lt-stat-card" data-filter-key="assigned_to" data-filter-val="0">
|
||
<span class="lt-stat-icon">👤</span>
|
||
<div class="lt-stat-info">
|
||
<span class="lt-stat-value">11</span>
|
||
<span class="lt-stat-label">Unassigned</span>
|
||
</div>
|
||
</div>
|
||
<div class="lt-stat-card" data-filter-key="created" data-filter-val="today">
|
||
<span class="lt-stat-icon">📅</span>
|
||
<div class="lt-stat-info">
|
||
<span class="lt-stat-value">7</span>
|
||
<span class="lt-stat-label">Today</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ==========================================================
|
||
TAB NAVIGATION
|
||
========================================================== -->
|
||
<div class="lt-tabs">
|
||
<button class="lt-tab active" data-tab="tab-table">Table View</button>
|
||
<button class="lt-tab" data-tab="tab-kanban">Kanban</button>
|
||
<button class="lt-tab" data-tab="tab-workers">Workers</button>
|
||
</div>
|
||
|
||
<!-- ==========================================================
|
||
SIDEBAR + CONTENT LAYOUT
|
||
========================================================== -->
|
||
<div class="lt-layout">
|
||
|
||
<!-- Sidebar filter panel -->
|
||
<aside class="lt-sidebar" id="lt-sidebar">
|
||
<div class="lt-sidebar-header">
|
||
Filters
|
||
<button class="lt-sidebar-toggle" data-sidebar-toggle="lt-sidebar"
|
||
aria-label="Collapse filters">◀</button>
|
||
</div>
|
||
<div class="lt-sidebar-body">
|
||
|
||
<div class="lt-filter-group">
|
||
<span class="lt-filter-label">Status</span>
|
||
<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>
|
||
</div>
|
||
|
||
<div class="lt-filter-group">
|
||
<span class="lt-filter-label">Priority</span>
|
||
<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>
|
||
</div>
|
||
|
||
<div class="lt-filter-group">
|
||
<span class="lt-filter-label">Assigned To</span>
|
||
<div class="lt-form-group">
|
||
<select class="lt-select lt-btn-sm">
|
||
<option value="">All users</option>
|
||
<option>operator</option>
|
||
<option>admin</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="lt-btn-group">
|
||
<button class="lt-btn lt-btn-sm">Apply</button>
|
||
<button class="lt-btn lt-btn-sm lt-btn-ghost">Reset</button>
|
||
</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">
|
||
<input type="search" class="lt-input lt-search-input"
|
||
placeholder="Search tickets..." aria-label="Search">
|
||
</div>
|
||
<button class="lt-btn lt-btn-sm">Advanced ▾</button>
|
||
</div>
|
||
<div class="lt-toolbar-right">
|
||
<span class="lt-text-muted lt-text-xs">42 results</span>
|
||
<button class="lt-btn lt-btn-sm lt-btn-ghost">Bulk Actions</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ==================================================
|
||
TAB PANEL: TABLE VIEW
|
||
================================================== -->
|
||
<div id="tab-table" class="lt-tab-panel active">
|
||
|
||
<!-- 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">
|
||
<table class="lt-table lt-table-responsive" id="ticket-table">
|
||
<thead>
|
||
<tr>
|
||
<th><input type="checkbox" class="lt-checkbox" aria-label="Select all"></th>
|
||
<th data-sort-key="id">ID</th>
|
||
<th data-sort-key="priority">Priority</th>
|
||
<th data-sort-key="title">Title</th>
|
||
<th data-sort-key="status">Status</th>
|
||
<th data-sort-key="assignee">Assignee</th>
|
||
<th data-sort-key="created">Created</th>
|
||
<th>Actions</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
|
||
<!-- P1 Critical row -->
|
||
<tr class="lt-row-p1 lt-row-critical">
|
||
<td data-label="Select"><input type="checkbox" class="lt-checkbox"></td>
|
||
<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">
|
||
<div class="lt-btn-group">
|
||
<a href="/ticket/123456789" class="lt-btn lt-btn-sm">View</a>
|
||
<button class="lt-btn lt-btn-sm lt-btn-danger">Close</button>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
|
||
<!-- P2 High row -->
|
||
<tr class="lt-row-p2 lt-row-warning">
|
||
<td data-label="Select"><input type="checkbox" class="lt-checkbox"></td>
|
||
<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">
|
||
<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">
|
||
<td data-label="Select"><input type="checkbox" class="lt-checkbox"></td>
|
||
<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">
|
||
<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">
|
||
<td data-label="Select"><input type="checkbox" class="lt-checkbox"></td>
|
||
<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">
|
||
<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
|
||
================================================== -->
|
||
<div id="tab-kanban" class="lt-tab-panel">
|
||
<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>
|
||
<div class="lt-section-body">
|
||
|
||
<div class="lt-card lt-mb-md lt-row-p1">
|
||
<div class="lt-flex lt-flex-between lt-mb-md">
|
||
<span class="lt-p1">P1</span>
|
||
<span class="lt-dot lt-dot-up"></span>
|
||
</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>
|
||
|
||
<div class="lt-card lt-row-p3">
|
||
<div class="lt-flex lt-flex-between lt-mb-md">
|
||
<span class="lt-p3">P3</span>
|
||
<span class="lt-dot lt-dot-up"></span>
|
||
</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>
|
||
<div class="lt-section-body">
|
||
<div class="lt-card lt-row-p2">
|
||
<div class="lt-flex lt-flex-between lt-mb-md">
|
||
<span class="lt-p2">P2</span>
|
||
<span class="lt-dot lt-dot-warn"></span>
|
||
</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>
|
||
<div class="lt-section-body">
|
||
<div class="lt-card lt-row-p2 lt-item-running">
|
||
<div class="lt-flex lt-flex-between lt-mb-md">
|
||
<span class="lt-p2">P2</span>
|
||
<span class="lt-dot lt-dot-warn"></span>
|
||
</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>
|
||
<div class="lt-section-body">
|
||
<div class="lt-card lt-row-p4" style="opacity:0.6">
|
||
<div class="lt-flex lt-flex-between lt-mb-md">
|
||
<span class="lt-p4">P4</span>
|
||
<span class="lt-dot lt-dot-idle"></span>
|
||
</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
|
||
================================================== -->
|
||
<div id="tab-workers" class="lt-tab-panel">
|
||
<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">
|
||
<table class="lt-data-table">
|
||
<tr><td class="lt-text-muted">CPU</td> <td>12%</td></tr>
|
||
<tr><td class="lt-text-muted">Memory</td> <td>2.1 GB / 8 GB</td></tr>
|
||
<tr><td class="lt-text-muted">Load</td> <td>0.42 / 0.51 / 0.48</td></tr>
|
||
<tr><td class="lt-text-muted">Uptime</td> <td>14d 6h</td></tr>
|
||
<tr><td class="lt-text-muted">Tasks</td> <td>2 / 5</td></tr>
|
||
</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">
|
||
<button class="lt-btn">Default</button>
|
||
<button class="lt-btn lt-btn-primary">Primary</button>
|
||
<button class="lt-btn lt-btn-danger">Danger</button>
|
||
<button class="lt-btn lt-btn-sm">Small</button>
|
||
<button class="lt-btn lt-btn-ghost">Ghost</button>
|
||
<button class="lt-btn" disabled>Disabled</button>
|
||
</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">
|
||
<button class="lt-btn lt-btn-primary">Submit</button>
|
||
<button class="lt-btn lt-btn-ghost">Cancel</button>
|
||
</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"
|
||
placeholder="Ctrl+K to focus">
|
||
</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>
|
||
<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>
|
||
</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) -->
|
||
|
||
|
||
<!-- ===========================================================
|
||
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>
|
||
<div class="lt-progress"><div class="lt-progress-bar" style="width:72%" data-width="72%"></div></div>
|
||
</div>
|
||
<div>
|
||
<div class="lt-progress-label"><span>MEMORY</span><span>45%</span></div>
|
||
<div class="lt-progress lt-progress--cyan"><div class="lt-progress-bar" style="width:45%" data-width="45%"></div></div>
|
||
</div>
|
||
<div>
|
||
<div class="lt-progress-label"><span>DISK I/O</span><span>89%</span></div>
|
||
<div class="lt-progress lt-progress--red lt-progress--lg"><div class="lt-progress-bar" style="width:89%" data-width="89%"></div></div>
|
||
</div>
|
||
<div>
|
||
<div class="lt-progress-label"><span>UPTIME</span><span>100%</span></div>
|
||
<div class="lt-progress lt-progress--green lt-progress--striped"><div class="lt-progress-bar" style="width:100%" data-width="100%"></div></div>
|
||
</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>
|
||
<nav class="lt-pagination" aria-label="pagination">
|
||
<button class="lt-page-btn" disabled>«</button>
|
||
<button class="lt-page-btn" disabled>1</button>
|
||
<button class="lt-page-btn active">2</button>
|
||
<button class="lt-page-btn">3</button>
|
||
<button class="lt-page-btn">4</button>
|
||
<button class="lt-page-btn">5</button>
|
||
<button class="lt-page-btn">»</button>
|
||
</nav>
|
||
</div>
|
||
|
||
<!-- ACCORDION -->
|
||
<div class="lt-section-header">Accordion</div>
|
||
<div class="lt-section-body">
|
||
<div>
|
||
<div class="lt-accordion">
|
||
<button class="lt-accordion-header" aria-expanded="false" data-accordion>
|
||
SYSTEM OVERVIEW
|
||
<svg class="lt-accordion-icon" viewBox="0 0 10 6" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M1 1l4 4 4-4"/></svg>
|
||
</button>
|
||
<div class="lt-accordion-body"><div class="lt-accordion-content">Node running at 72% CPU. 12 active processes. Last restart: 3d ago.</div></div>
|
||
</div>
|
||
<div class="lt-accordion">
|
||
<button class="lt-accordion-header" aria-expanded="false" data-accordion>
|
||
NETWORK CONFIG
|
||
<svg class="lt-accordion-icon" viewBox="0 0 10 6" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M1 1l4 4 4-4"/></svg>
|
||
</button>
|
||
<div class="lt-accordion-body"><div class="lt-accordion-content">eth0: 10.0.0.7 — MTU 1500 — RX 4.2 GB — TX 1.1 GB</div></div>
|
||
</div>
|
||
<div class="lt-accordion">
|
||
<button class="lt-accordion-header" aria-expanded="false" data-accordion>
|
||
FIREWALL RULES
|
||
<svg class="lt-accordion-icon" viewBox="0 0 10 6" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M1 1l4 4 4-4"/></svg>
|
||
</button>
|
||
<div class="lt-accordion-body"><div class="lt-accordion-content">22/tcp ALLOW — 80/tcp ALLOW — 443/tcp ALLOW — */* DENY</div></div>
|
||
</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>
|
||
<button class="lt-alert-close" data-alert-close aria-label="Dismiss">✕</button>
|
||
</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>
|
||
<button class="lt-alert-close" data-alert-close aria-label="Dismiss">✕</button>
|
||
</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>
|
||
<button class="lt-alert-close" data-alert-close aria-label="Dismiss">✕</button>
|
||
</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>
|
||
<button class="lt-alert-close" data-alert-close aria-label="Dismiss">✕</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 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>
|
||
<input type="range" class="lt-range" min="5" max="60" value="30">
|
||
</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>
|
||
</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>
|
||
<button class="lt-code-copy" data-copy="systemctl restart nginx && systemctl status nginx">COPY</button>
|
||
</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">
|
||
<button class="lt-btn lt-btn-sm">Alerts</button>
|
||
<span class="lt-badge">3</span>
|
||
</div>
|
||
<div class="lt-badge-wrap">
|
||
<button class="lt-btn lt-btn-sm">Messages</button>
|
||
<span class="lt-badge lt-badge--orange">12</span>
|
||
</div>
|
||
<div class="lt-badge-wrap">
|
||
<button class="lt-btn lt-btn-sm">Updates</button>
|
||
<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>
|
||
<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">
|
||
<button class="lt-tab active" role="tab" data-tab-target="tab-overview" aria-selected="true">Overview</button>
|
||
<button class="lt-tab" role="tab" data-tab-target="tab-logs" aria-selected="false">Logs</button>
|
||
<button class="lt-tab" role="tab" data-tab-target="tab-metrics" aria-selected="false">Metrics</button>
|
||
<button class="lt-tab" role="tab" data-tab-target="tab-config" aria-selected="false">Config</button>
|
||
</div>
|
||
<div class="lt-tab-panels" style="padding:var(--space-md) 0">
|
||
<div id="tab-overview" class="lt-tab-panel active">System overview: all nodes nominal. 14 active sessions.</div>
|
||
<div id="tab-logs" class="lt-tab-panel">
|
||
<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>
|
||
<div id="tab-metrics" class="lt-tab-panel">CPU avg: 34% — Mem avg: 61% — Net TX: 4.2 Mbps</div>
|
||
<div id="tab-config" class="lt-tab-panel">Config last updated: 2026-03-20 by admin@lotusguild.io</div>
|
||
</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">
|
||
<input type="file" accept=".json,.csv,.log" multiple>
|
||
<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>
|
||
<button class="lt-btn lt-btn-ghost" onclick="lt.cmdPalette.open()">⌘ Open Command Palette</button>
|
||
</div>
|
||
|
||
</div><!-- /.lt-frame-inner (v1.2 additions) -->
|
||
</div><!-- /.lt-frame (v1.2 additions) -->
|
||
|
||
|
||
<!-- ═══════════════════════════════════════════════════════════
|
||
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">
|
||
<button class="lt-btn lt-btn-primary" onclick="lt.theme.toggle()">Toggle Theme</button>
|
||
<button class="lt-btn" onclick="lt.theme.set('dark')">Force Dark</button>
|
||
<button class="lt-btn" onclick="lt.theme.set('light')">Force Light</button>
|
||
<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">
|
||
<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 class="lt-btn lt-btn-sm lt-btn-primary">Clear Filters</button></div></div>
|
||
<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 class="lt-btn lt-btn-sm" onclick="lt.toast.warning('Checking workers…')">Retry</button></div></div>
|
||
<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">
|
||
<span class="lt-notif-wrap" id="demo-notif-btn"><button class="lt-btn lt-btn-sm">🔔 Alerts</button></span>
|
||
<button class="lt-btn lt-btn-sm" onclick="lt.notif.inc('#demo-notif-btn')">+1 Badge</button>
|
||
<button class="lt-btn lt-btn-sm" onclick="lt.notif.clear('#demo-notif-btn')">Clear</button>
|
||
<button class="lt-btn lt-btn-sm" onclick="lt.notif.inc('#lt-notif-bell')">+1 Header Bell</button>
|
||
</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">
|
||
<button class="lt-btn lt-btn-primary" data-drawer-open="lt-detail-drawer">Open Detail Panel</button>
|
||
<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>
|
||
<button class="lt-btn lt-btn-sm" id="demo-ctx-btn">Show Context Menu</button>
|
||
</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>
|
||
<label class="lt-label">Assign Workers (multi-select)</label>
|
||
<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>
|
||
<label class="lt-label">Search Tickets (typeahead)</label>
|
||
<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">
|
||
<thead><tr><th>ID</th><th>Priority</th><th>Title</th><th>Status</th><th>Assignee</th></tr></thead>
|
||
<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">
|
||
<div class="lt-grid lt-grid-2" style="gap:1rem">
|
||
<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">
|
||
<div class="lt-split" id="demo-split" style="height:200px;border:1px solid var(--border-dim)">
|
||
<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">
|
||
<button class="lt-btn lt-btn-sm" onclick="lt.offline.isOnline() ? lt.toast.success('Online ✓') : lt.toast.error('Offline ✗')">Check Online Status</button>
|
||
<button class="lt-btn lt-btn-sm" onclick="lt.toast.info('lt.ws.connect(url, { reconnect:true, onMessage: fn })')">WS API Hint</button>
|
||
</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>
|
||
|
||
</main><!-- /.lt-main -->
|
||
|
||
|
||
<!-- ===========================================================
|
||
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">
|
||
<button class="lt-btn lt-btn-sm" onclick="lt.toast.success('Ticket saved successfully')">✓ Toast</button>
|
||
<button class="lt-btn lt-btn-sm lt-btn-danger" onclick="lt.toast.error('Network error — retry in 5s', 5000)">✗ Error</button>
|
||
<button class="lt-btn lt-btn-sm" onclick="lt.toast.warning('Rate limit 80% used')">! Warn</button>
|
||
<button class="lt-btn lt-btn-sm lt-btn-ghost" onclick="lt.toast.info('Auto-refresh triggered')">i Info</button>
|
||
</div>
|
||
|
||
<!-- ===========================================================
|
||
MODAL EXAMPLE: Export
|
||
=========================================================== -->
|
||
<div id="export-modal" class="lt-modal-overlay" aria-hidden="true">
|
||
<div class="lt-modal">
|
||
<div class="lt-modal-header">
|
||
<span class="lt-modal-title">Export Tickets</span>
|
||
<button class="lt-modal-close" data-modal-close aria-label="Close">✕</button>
|
||
</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">
|
||
<button class="lt-btn lt-btn-ghost" data-modal-close>Cancel</button>
|
||
<button class="lt-btn lt-btn-primary" onclick="lt.toast.success('Export started'); lt.modal.close('export-modal')">Export</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ===========================================================
|
||
MODAL EXAMPLE: Keyboard shortcuts help
|
||
=========================================================== -->
|
||
<div id="lt-keys-help" class="lt-modal-overlay" aria-hidden="true">
|
||
<div class="lt-modal">
|
||
<div class="lt-modal-header">
|
||
<span class="lt-modal-title">Keyboard Shortcuts</span>
|
||
<button class="lt-modal-close" data-modal-close aria-label="Close">✕</button>
|
||
</div>
|
||
<div class="lt-modal-body">
|
||
<table class="lt-data-table" style="width:100%">
|
||
<thead>
|
||
<tr><th>Shortcut</th><th>Action</th></tr>
|
||
</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">
|
||
<button class="lt-btn" data-modal-close>Close</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ===========================================================
|
||
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>
|
||
<input id="lt-cmd-input" class="lt-cmd-input" type="text" placeholder="Search commands…" autocomplete="off" spellcheck="false">
|
||
</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>
|
||
|
||
<!-- ===========================================================
|
||
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>
|
||
-->
|
||
|
||
<!-- LotusGuild Terminal Design System JS v1.2 -->
|
||
<script src="/web_template/base.js"></script>
|
||
|
||
<!-- Init v1.2 modules -->
|
||
<script>
|
||
document.addEventListener('DOMContentLoaded', () => {
|
||
// Accordion
|
||
lt.accordion.init();
|
||
|
||
// Tooltips (JS-positioned — optional, CSS tooltips work without this)
|
||
lt.tooltip.init();
|
||
|
||
// Clipboard copy buttons ([data-copy])
|
||
lt.clipboard.init();
|
||
|
||
// Alert dismiss buttons
|
||
lt.alerts.init();
|
||
|
||
// 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;
|
||
r.addEventListener('input', () => { label.textContent = r.value; });
|
||
}
|
||
});
|
||
|
||
// Animate the demo progress bars on load
|
||
document.querySelectorAll('.lt-progress-bar[data-width]').forEach(bar => {
|
||
requestAnimationFrame(() => { bar.style.width = bar.dataset.width; });
|
||
});
|
||
|
||
// 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);
|
||
|
||
// 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>
|
||
|
||
</body>
|
||
</html>
|