fix(dashboard): hover infobulles, full-width layout, UX polish
- Fix doc tooltips: split CSS into <style type='text/tailwindcss'> for @apply directives + raw CSS for reliable doc panel rendering - Convert doc panels from click-toggle to hover-based infobulles with arrow pointer, fade-in animation, and auto-dismiss on mobile - Replace '?' icons with 'ⓘ' across all 11 templates (51 tooltips) - Full-width layout: reduce padding on mobile (px-3), scale up on desktop (lg:px-5, xl:px-6) for maximum screen utilization - Auto-collapse sidebar on narrow screens (<1024px) - Keyboard shortcuts: Alt+1–9 for page navigation, Alt+B toggle sidebar - Add LEGITIMATE_BROWSER filter button to detections page - Sticky header with stronger blur (backdrop-blur-md) - All 46 routes pass tests Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@ -23,7 +23,7 @@
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
<style type="text/tailwindcss">
|
||||
body { font-family: 'Inter', system-ui, sans-serif; }
|
||||
/* ── Threat badges ── */
|
||||
.threat-critical { color: #ef4444; font-weight: 700; }
|
||||
@ -56,21 +56,51 @@
|
||||
.section-title { @apply text-sm font-semibold text-gray-200 flex items-center gap-2; }
|
||||
.section-body { @apply p-5; }
|
||||
/* ── Sidebar ── */
|
||||
.nav-item { @apply flex items-center gap-3 px-3 py-2 rounded-lg text-gray-400 hover:text-white hover:bg-gray-800/60 transition-colors text-sm cursor-pointer; }
|
||||
.nav-item.active { @apply bg-brand-600/20 text-brand-400 border-l-2 border-brand-500; }
|
||||
.nav-group-title { @apply text-[10px] uppercase tracking-widest text-gray-600 px-3 pt-4 pb-1; }
|
||||
</style>
|
||||
<style>
|
||||
/* ── Raw CSS (no @apply) for reliable rendering ── */
|
||||
.sidebar { width: 220px; transition: width 0.2s ease; }
|
||||
.sidebar.collapsed { width: 56px; }
|
||||
.sidebar.collapsed .nav-text { display: none; }
|
||||
.sidebar.collapsed .nav-group-title { display: none; }
|
||||
.sidebar.collapsed .sidebar-logo-text { display: none; }
|
||||
.nav-item { @apply flex items-center gap-3 px-3 py-2 rounded-lg text-gray-400 hover:text-white hover:bg-gray-800/60 transition-colors text-sm cursor-pointer; }
|
||||
.nav-item.active { @apply bg-brand-600/20 text-brand-400 border-l-2 border-brand-500; }
|
||||
.nav-group-title { @apply text-[10px] uppercase tracking-widest text-gray-600 px-3 pt-4 pb-1; }
|
||||
/* ── Doc tooltips ── */
|
||||
.doc-btn { @apply inline-flex items-center justify-center w-5 h-5 rounded-full text-gray-500 hover:text-gray-300 hover:bg-gray-700 transition-colors cursor-help text-xs; }
|
||||
.doc-panel { @apply hidden absolute z-50 w-80 p-4 bg-gray-800 border border-gray-700 rounded-xl shadow-2xl text-xs text-gray-300 leading-relaxed; }
|
||||
.doc-panel.show { @apply block; }
|
||||
.doc-panel h4 { @apply text-white font-semibold text-sm mb-2; }
|
||||
.doc-panel p { @apply mb-2; }
|
||||
.doc-panel .doc-source { @apply text-gray-500 italic mt-2 pt-2 border-t border-gray-700; }
|
||||
|
||||
/* ── Infobulles (hover tooltips) ── */
|
||||
.doc-btn {
|
||||
display: inline-flex; align-items: center; justify-content: center;
|
||||
width: 16px; height: 16px; border-radius: 50%; font-size: 10px;
|
||||
color: #4b5563; cursor: help; transition: all 0.15s;
|
||||
vertical-align: middle; margin-left: 4px; flex-shrink: 0;
|
||||
}
|
||||
.doc-btn:hover { color: #d1d5db; background: #374151; }
|
||||
.doc-panel {
|
||||
display: none; position: absolute; z-index: 60;
|
||||
top: calc(100% + 10px); right: -8px; width: 300px;
|
||||
padding: 12px 14px; background: #111827; border: 1px solid #374151;
|
||||
border-radius: 10px; box-shadow: 0 16px 48px rgba(0,0,0,0.6);
|
||||
font-size: 11px; line-height: 1.6; color: #d1d5db;
|
||||
pointer-events: auto;
|
||||
}
|
||||
.doc-panel::before {
|
||||
content: ''; position: absolute; top: -5px; right: 14px;
|
||||
width: 10px; height: 10px; background: #111827;
|
||||
border-left: 1px solid #374151; border-top: 1px solid #374151;
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
.doc-panel h4 { color: white; font-weight: 600; font-size: 12px; margin: 0 0 6px; }
|
||||
.doc-panel p { margin: 0 0 5px; }
|
||||
.doc-panel .doc-source { color: #6b7280; font-style: italic; margin-top: 6px; padding-top: 6px; border-top: 1px solid #1f2937; font-size: 10px; }
|
||||
/* Hover: show on parent hover */
|
||||
.relative:has(> .doc-btn):hover > .doc-panel,
|
||||
.doc-panel.show {
|
||||
display: block; animation: ttIn 0.12s ease-out;
|
||||
}
|
||||
/* Mobile: keep tap toggle via JS */
|
||||
@keyframes ttIn { from { opacity:0; transform:translateY(-3px); } to { opacity:1; transform:translateY(0); } }
|
||||
|
||||
/* ── Animations ── */
|
||||
@keyframes fadeUp { from { opacity:0; transform:translateY(8px); } to { opacity:1; transform:translateY(0); } }
|
||||
.animate-in { animation: fadeUp 0.3s ease-out both; }
|
||||
@ -156,14 +186,14 @@
|
||||
<!-- ═══ Main Content ═══ -->
|
||||
<div id="main-wrap" class="flex-1 min-h-screen" style="margin-left:220px; transition: margin-left 0.2s ease;">
|
||||
<!-- Page header -->
|
||||
<header class="sticky top-0 z-40 bg-gray-950/80 backdrop-blur border-b border-gray-800">
|
||||
<div class="flex items-center h-12 px-6">
|
||||
<h1 class="text-base font-semibold text-gray-100">{% block page_title %}{% endblock %}</h1>
|
||||
<header class="sticky top-0 z-40 bg-gray-950/90 backdrop-blur-md border-b border-gray-800">
|
||||
<div class="flex items-center h-12 px-4 lg:px-6">
|
||||
<h1 class="text-sm lg:text-base font-semibold text-gray-100 truncate">{% block page_title %}{% endblock %}</h1>
|
||||
<div class="flex-1"></div>
|
||||
{% block header_actions %}{% endblock %}
|
||||
</div>
|
||||
</header>
|
||||
<main class="px-6 py-5">
|
||||
<main class="px-3 py-4 lg:px-5 lg:py-5 xl:px-6">
|
||||
{% block content %}{% endblock %}
|
||||
</main>
|
||||
</div>
|
||||
@ -176,6 +206,17 @@
|
||||
sb.classList.toggle('collapsed');
|
||||
mw.style.marginLeft = sb.classList.contains('collapsed') ? '56px' : '220px';
|
||||
}
|
||||
// Auto-collapse on narrow screens
|
||||
if (window.innerWidth < 1024) toggleSidebar();
|
||||
|
||||
// ── Keyboard shortcuts ──
|
||||
document.addEventListener('keydown', e => {
|
||||
if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA' || e.target.tagName === 'SELECT') return;
|
||||
const routes = {'1':'/', '2':'/detections', '3':'/scores', '4':'/campaigns',
|
||||
'5':'/traffic', '6':'/network', '7':'/features', '8':'/models', '9':'/classify'};
|
||||
if (e.altKey && routes[e.key]) { e.preventDefault(); window.location = routes[e.key]; }
|
||||
if (e.key === 'b' && e.altKey) { e.preventDefault(); toggleSidebar(); }
|
||||
});
|
||||
|
||||
// ── Clock ──
|
||||
function updateClock() {
|
||||
@ -184,11 +225,16 @@
|
||||
}
|
||||
updateClock(); setInterval(updateClock, 1000);
|
||||
|
||||
// ── Doc tooltip system ──
|
||||
// ── Doc tooltip (mobile tap fallback) ──
|
||||
function docToggle(btn) {
|
||||
const panel = btn.nextElementSibling;
|
||||
document.querySelectorAll('.doc-panel.show').forEach(p => { if (p !== panel) p.classList.remove('show'); });
|
||||
panel.classList.toggle('show');
|
||||
// Auto-dismiss after 8s on mobile
|
||||
if (panel.classList.contains('show')) {
|
||||
clearTimeout(panel._timer);
|
||||
panel._timer = setTimeout(() => panel.classList.remove('show'), 8000);
|
||||
}
|
||||
}
|
||||
document.addEventListener('click', e => {
|
||||
if (!e.target.closest('.doc-btn') && !e.target.closest('.doc-panel'))
|
||||
@ -314,9 +360,9 @@
|
||||
).join('');
|
||||
}
|
||||
|
||||
// ── Doc helper: generates a (?) button + panel ──
|
||||
// ── Doc helper: generates an ⓘ tooltip button + panel ──
|
||||
function docHTML(title, body, source) {
|
||||
return `<span class="relative inline-block ml-1"><button onclick="docToggle(this)" class="doc-btn" aria-label="Aide">?</button><div class="doc-panel"><h4>${escapeHtml(title)}</h4>${body}<p class="doc-source">Source : ${escapeHtml(source)}</p></div></span>`;
|
||||
return `<span class="relative inline-block ml-1"><button onclick="docToggle(this)" class="doc-btn" aria-label="Aide">ⓘ</button><div class="doc-panel"><h4>${escapeHtml(title)}</h4>${body}<p class="doc-source">Source : ${escapeHtml(source)}</p></div></span>`;
|
||||
}
|
||||
</script>
|
||||
{% block scripts %}{% endblock %}
|
||||
|
||||
Reference in New Issue
Block a user