1108 lines
48 KiB
HTML
1108 lines
48 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>ResolutionFlow — Adaptive Workspaces Mockup</title>
|
|
<link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700;800&family=Inter:wght@400;500;600&family=Outfit:wght@400;500;600;700&display=swap" rel="stylesheet">
|
|
<style>
|
|
:root {
|
|
--gradient-from: #818cf8;
|
|
--gradient-to: #a78bfa;
|
|
--bg-app: #09090b;
|
|
--bg-sidebar: #0c0c0f;
|
|
--bg-card: #18181b;
|
|
--bg-surface: #111114;
|
|
--bg-hover: #1e1e23;
|
|
--bg-active: rgba(129,140,248,0.08);
|
|
--border: #27272a;
|
|
--border-subtle: #1f1f23;
|
|
--text-primary: #fafafa;
|
|
--text-secondary: #a1a1aa;
|
|
--text-muted: #52525b;
|
|
--text-dimmed: #3f3f46;
|
|
--success: #22c55e;
|
|
--warning: #f59e0b;
|
|
--error: #ef4444;
|
|
--info: #3b82f6;
|
|
--sidebar-w: 260px;
|
|
}
|
|
|
|
* { margin:0; padding:0; box-sizing:border-box; }
|
|
|
|
body {
|
|
font-family: 'Inter', system-ui, -apple-system, sans-serif;
|
|
background: var(--bg-app);
|
|
color: var(--text-primary);
|
|
height: 100vh;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.app-shell {
|
|
display: grid;
|
|
grid-template-columns: var(--sidebar-w) 1fr;
|
|
grid-template-rows: 56px 1fr;
|
|
height: 100vh;
|
|
}
|
|
|
|
/* ── Top Bar ── */
|
|
.topbar {
|
|
grid-column: 1 / -1;
|
|
display: flex;
|
|
align-items: center;
|
|
padding: 0 20px;
|
|
background: var(--bg-sidebar);
|
|
border-bottom: 1px solid var(--border);
|
|
gap: 16px;
|
|
z-index: 100;
|
|
}
|
|
|
|
.topbar-logo {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10px;
|
|
width: calc(var(--sidebar-w) - 40px);
|
|
flex-shrink: 0;
|
|
}
|
|
.topbar-logo svg { width: 32px; height: 32px; }
|
|
|
|
.topbar-brand {
|
|
font-family: 'Plus Jakarta Sans', sans-serif;
|
|
font-size: 1.1rem;
|
|
font-weight: 700;
|
|
letter-spacing: -0.02em;
|
|
}
|
|
.topbar-brand .res { color: var(--text-primary); }
|
|
.topbar-brand .flow {
|
|
background: linear-gradient(90deg, var(--gradient-from), var(--gradient-to));
|
|
-webkit-background-clip: text;
|
|
-webkit-text-fill-color: transparent;
|
|
}
|
|
|
|
.topbar-search {
|
|
flex: 1;
|
|
max-width: 480px;
|
|
position: relative;
|
|
}
|
|
.topbar-search input {
|
|
width: 100%;
|
|
background: var(--bg-card);
|
|
border: 1px solid var(--border);
|
|
border-radius: 8px;
|
|
padding: 8px 12px 8px 36px;
|
|
color: var(--text-primary);
|
|
font-family: inherit;
|
|
font-size: 0.8125rem;
|
|
outline: none;
|
|
transition: border-color 0.15s;
|
|
}
|
|
.topbar-search input::placeholder { color: var(--text-muted); }
|
|
.topbar-search input:focus { border-color: var(--gradient-from); }
|
|
.topbar-search .search-icon {
|
|
position: absolute; left: 11px; top: 50%; transform: translateY(-50%);
|
|
color: var(--text-muted); width: 15px; height: 15px;
|
|
}
|
|
.topbar-search .search-shortcut {
|
|
position: absolute; right: 10px; top: 50%; transform: translateY(-50%);
|
|
background: var(--bg-surface); border: 1px solid var(--border); border-radius: 4px;
|
|
padding: 1px 6px; font-size: 0.6875rem; color: var(--text-muted); font-family: 'Outfit', sans-serif;
|
|
}
|
|
|
|
.topbar-actions {
|
|
display: flex; align-items: center; gap: 6px; margin-left: auto;
|
|
}
|
|
.topbar-btn {
|
|
display: flex; align-items: center; justify-content: center;
|
|
width: 36px; height: 36px; border-radius: 8px; border: none;
|
|
background: transparent; color: var(--text-secondary); cursor: pointer;
|
|
transition: all 0.15s; position: relative;
|
|
}
|
|
.topbar-btn:hover { background: var(--bg-hover); color: var(--text-primary); }
|
|
.topbar-btn svg { width: 18px; height: 18px; }
|
|
.notification-badge {
|
|
position: absolute; top: 6px; right: 6px; width: 8px; height: 8px;
|
|
background: var(--error); border-radius: 50%; border: 2px solid var(--bg-sidebar);
|
|
}
|
|
.avatar-sm {
|
|
width: 32px; height: 32px; border-radius: 50%;
|
|
background: linear-gradient(135deg, var(--gradient-from), var(--gradient-to));
|
|
display: flex; align-items: center; justify-content: center;
|
|
font-family: 'Plus Jakarta Sans', sans-serif; font-size: 0.75rem;
|
|
font-weight: 700; color: white; margin-left: 4px; cursor: pointer;
|
|
}
|
|
|
|
/* ── Sidebar ── */
|
|
.sidebar {
|
|
background: var(--bg-sidebar);
|
|
border-right: 1px solid var(--border);
|
|
overflow-y: auto;
|
|
padding: 16px 12px;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 2px;
|
|
}
|
|
.sidebar::-webkit-scrollbar { width: 4px; }
|
|
.sidebar::-webkit-scrollbar-thumb { background: var(--border); border-radius: 4px; }
|
|
|
|
.nav-section { margin-bottom: 16px; }
|
|
|
|
.nav-section-label {
|
|
font-family: 'Outfit', sans-serif;
|
|
font-size: 0.6875rem; font-weight: 600;
|
|
text-transform: uppercase; letter-spacing: 0.06em;
|
|
color: var(--text-muted); padding: 0 12px; margin-bottom: 6px;
|
|
}
|
|
|
|
.nav-item {
|
|
display: flex; align-items: center; gap: 10px;
|
|
padding: 8px 12px; border-radius: 7px; cursor: pointer;
|
|
transition: all 0.12s; font-size: 0.8125rem; font-weight: 500;
|
|
color: var(--text-secondary); text-decoration: none; position: relative;
|
|
}
|
|
.nav-item:hover { background: var(--bg-hover); color: var(--text-primary); }
|
|
.nav-item.active { background: var(--bg-active); color: var(--text-primary); }
|
|
.nav-item.active::before {
|
|
content: ''; position: absolute; left: 0; top: 6px; bottom: 6px; width: 3px;
|
|
background: linear-gradient(180deg, var(--gradient-from), var(--gradient-to));
|
|
border-radius: 0 3px 3px 0;
|
|
}
|
|
.nav-item svg { width: 18px; height: 18px; flex-shrink: 0; opacity: 0.7; }
|
|
.nav-item.active svg { opacity: 1; }
|
|
|
|
.nav-item .badge {
|
|
margin-left: auto; background: var(--bg-card); border: 1px solid var(--border);
|
|
border-radius: 10px; padding: 0 7px; font-size: 0.6875rem;
|
|
font-family: 'Outfit', sans-serif; color: var(--text-muted); line-height: 1.7;
|
|
}
|
|
.nav-item.active .badge {
|
|
background: rgba(129,140,248,0.15); border-color: rgba(129,140,248,0.3); color: var(--gradient-from);
|
|
}
|
|
|
|
.nav-divider { height: 1px; background: var(--border-subtle); margin: 10px 12px; }
|
|
|
|
/* ── Workspace Switcher ── */
|
|
.workspace-switcher {
|
|
margin-bottom: 16px;
|
|
padding: 0 4px;
|
|
}
|
|
|
|
.workspace-current {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10px;
|
|
padding: 10px 12px;
|
|
background: var(--bg-card);
|
|
border: 1px solid var(--border);
|
|
border-radius: 8px;
|
|
cursor: pointer;
|
|
transition: all 0.15s;
|
|
position: relative;
|
|
}
|
|
.workspace-current:hover {
|
|
border-color: var(--gradient-from);
|
|
background: var(--bg-hover);
|
|
}
|
|
.workspace-current.open {
|
|
border-color: var(--gradient-from);
|
|
border-radius: 8px 8px 0 0;
|
|
border-bottom-color: var(--border-subtle);
|
|
}
|
|
|
|
.workspace-icon {
|
|
width: 32px; height: 32px; border-radius: 7px;
|
|
display: flex; align-items: center; justify-content: center;
|
|
font-size: 1rem; flex-shrink: 0;
|
|
transition: background 0.2s;
|
|
}
|
|
|
|
.workspace-label {
|
|
flex: 1;
|
|
}
|
|
.workspace-label .ws-name {
|
|
font-family: 'Plus Jakarta Sans', sans-serif;
|
|
font-size: 0.8125rem; font-weight: 700;
|
|
letter-spacing: -0.005em;
|
|
transition: color 0.2s;
|
|
}
|
|
.workspace-label .ws-desc {
|
|
font-size: 0.6875rem; color: var(--text-muted);
|
|
transition: color 0.2s;
|
|
}
|
|
|
|
.workspace-chevron {
|
|
color: var(--text-muted);
|
|
transition: transform 0.2s;
|
|
flex-shrink: 0;
|
|
}
|
|
.workspace-current.open .workspace-chevron { transform: rotate(180deg); }
|
|
.workspace-chevron svg { width: 14px; height: 14px; }
|
|
|
|
.workspace-dropdown {
|
|
display: none;
|
|
background: var(--bg-card);
|
|
border: 1px solid var(--gradient-from);
|
|
border-top: 1px solid var(--border-subtle);
|
|
border-radius: 0 0 8px 8px;
|
|
overflow: hidden;
|
|
animation: dropIn 0.15s ease;
|
|
}
|
|
.workspace-dropdown.show { display: block; }
|
|
|
|
@keyframes dropIn {
|
|
from { opacity: 0; transform: translateY(-4px); }
|
|
to { opacity: 1; transform: translateY(0); }
|
|
}
|
|
|
|
.workspace-option {
|
|
display: flex; align-items: center; gap: 10px;
|
|
padding: 10px 12px;
|
|
cursor: pointer; transition: background 0.1s;
|
|
border-bottom: 1px solid var(--border-subtle);
|
|
}
|
|
.workspace-option:last-child { border-bottom: none; }
|
|
.workspace-option:hover { background: var(--bg-hover); }
|
|
.workspace-option.active-ws { background: var(--bg-active); }
|
|
|
|
.workspace-option .ws-opt-icon {
|
|
width: 28px; height: 28px; border-radius: 6px;
|
|
display: flex; align-items: center; justify-content: center;
|
|
font-size: 0.875rem; flex-shrink: 0;
|
|
}
|
|
.workspace-option .ws-opt-name {
|
|
font-family: 'Plus Jakarta Sans', sans-serif;
|
|
font-size: 0.8125rem; font-weight: 600;
|
|
}
|
|
.workspace-option .ws-opt-count {
|
|
margin-left: auto; font-family: 'Outfit', sans-serif;
|
|
font-size: 0.6875rem; color: var(--text-dimmed);
|
|
}
|
|
|
|
.workspace-add {
|
|
display: flex; align-items: center; gap: 8px;
|
|
padding: 9px 12px; cursor: pointer;
|
|
font-size: 0.75rem; color: var(--text-muted);
|
|
transition: all 0.1s;
|
|
border-top: 1px solid var(--border-subtle);
|
|
}
|
|
.workspace-add:hover { background: var(--bg-hover); color: var(--gradient-from); }
|
|
.workspace-add svg { width: 14px; height: 14px; }
|
|
|
|
/* ── Category Tags ── */
|
|
.category-tag {
|
|
display: flex; align-items: center; gap: 8px;
|
|
padding: 6px 12px; border-radius: 6px; cursor: pointer;
|
|
transition: all 0.15s; font-size: 0.8125rem; color: var(--text-secondary);
|
|
}
|
|
.category-tag:hover { background: var(--bg-hover); color: var(--text-primary); }
|
|
.cat-dot { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; }
|
|
.category-tag .cat-count {
|
|
margin-left: auto; font-size: 0.6875rem; color: var(--text-dimmed);
|
|
font-family: 'Outfit', sans-serif;
|
|
}
|
|
|
|
.tag-cloud {
|
|
display: flex; flex-wrap: wrap; gap: 4px; padding: 0 8px;
|
|
}
|
|
.filter-chip {
|
|
display: inline-flex; align-items: center; gap: 5px;
|
|
padding: 3px 8px; background: var(--bg-card); border: 1px solid var(--border);
|
|
border-radius: 5px; font-size: 0.6875rem; color: var(--text-secondary);
|
|
cursor: pointer; transition: all 0.12s; font-family: 'Outfit', sans-serif;
|
|
}
|
|
.filter-chip:hover { border-color: var(--text-muted); color: var(--text-primary); }
|
|
|
|
.sidebar-footer {
|
|
margin-top: auto; padding-top: 12px; border-top: 1px solid var(--border-subtle);
|
|
}
|
|
|
|
/* ── Animated content transitions ── */
|
|
.sidebar-content { transition: opacity 0.2s; }
|
|
.sidebar-content.switching { opacity: 0; }
|
|
|
|
/* ── Main Content ── */
|
|
.main {
|
|
overflow-y: auto; padding: 24px 28px; background: var(--bg-app);
|
|
}
|
|
.main::-webkit-scrollbar { width: 6px; }
|
|
.main::-webkit-scrollbar-thumb { background: var(--border); border-radius: 4px; }
|
|
|
|
.page-header {
|
|
display: flex; align-items: center; justify-content: space-between; margin-bottom: 20px;
|
|
}
|
|
.page-header h1 {
|
|
font-family: 'Plus Jakarta Sans', sans-serif;
|
|
font-size: 1.375rem; font-weight: 700; letter-spacing: -0.01em;
|
|
}
|
|
.page-header-actions { display: flex; align-items: center; gap: 8px; }
|
|
|
|
.btn {
|
|
display: inline-flex; align-items: center; gap: 6px;
|
|
padding: 8px 16px; border-radius: 8px; border: none;
|
|
font-family: inherit; font-size: 0.8125rem; font-weight: 600;
|
|
cursor: pointer; transition: all 0.15s;
|
|
}
|
|
.btn svg { width: 16px; height: 16px; }
|
|
.btn-primary {
|
|
background: linear-gradient(135deg, var(--gradient-from), var(--gradient-to));
|
|
color: white; box-shadow: 0 2px 12px rgba(129,140,248,0.25);
|
|
}
|
|
.btn-primary:hover { box-shadow: 0 4px 20px rgba(129,140,248,0.35); transform: translateY(-1px); }
|
|
.btn-secondary {
|
|
background: var(--bg-card); border: 1px solid var(--border); color: var(--text-secondary);
|
|
}
|
|
.btn-secondary:hover { border-color: var(--text-muted); color: var(--text-primary); }
|
|
|
|
/* ── Quick Stats ── */
|
|
.quick-stats { display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; margin-bottom: 24px; }
|
|
.stat-card {
|
|
background: var(--bg-card); border: 1px solid var(--border-subtle);
|
|
border-radius: 10px; padding: 16px 18px; transition: border-color 0.15s;
|
|
}
|
|
.stat-card:hover { border-color: var(--border); }
|
|
.stat-label {
|
|
font-family: 'Outfit', sans-serif; font-size: 0.6875rem;
|
|
text-transform: uppercase; letter-spacing: 0.05em;
|
|
color: var(--text-muted); margin-bottom: 6px;
|
|
}
|
|
.stat-value {
|
|
font-family: 'Plus Jakarta Sans', sans-serif;
|
|
font-size: 1.5rem; font-weight: 700; letter-spacing: -0.02em;
|
|
}
|
|
.stat-meta { font-size: 0.6875rem; color: var(--text-dimmed); margin-top: 4px; }
|
|
.stat-value.gradient {
|
|
background: linear-gradient(90deg, var(--gradient-from), var(--gradient-to));
|
|
-webkit-background-clip: text; -webkit-text-fill-color: transparent;
|
|
}
|
|
|
|
/* ── Filters Bar ── */
|
|
.filters-bar {
|
|
display: flex; align-items: center; gap: 8px; margin-bottom: 20px; flex-wrap: wrap;
|
|
}
|
|
.main-filter-chip {
|
|
display: inline-flex; align-items: center; gap: 6px;
|
|
padding: 6px 12px; background: var(--bg-card); border: 1px solid var(--border);
|
|
border-radius: 6px; font-size: 0.75rem; color: var(--text-secondary);
|
|
cursor: pointer; transition: all 0.12s; font-family: 'Outfit', sans-serif;
|
|
}
|
|
.main-filter-chip:hover { border-color: var(--text-muted); color: var(--text-primary); }
|
|
.main-filter-chip.active {
|
|
background: rgba(129,140,248,0.1); border-color: rgba(129,140,248,0.3); color: var(--gradient-from);
|
|
}
|
|
.main-filter-chip svg { width: 14px; height: 14px; }
|
|
.filter-divider { width: 1px; height: 24px; background: var(--border); }
|
|
|
|
/* ── Section Groups ── */
|
|
.section-group { margin-bottom: 28px; }
|
|
.section-header {
|
|
display: flex; align-items: center; gap: 10px; margin-bottom: 12px;
|
|
padding-bottom: 10px; border-bottom: 1px solid var(--border-subtle);
|
|
}
|
|
.section-icon { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; }
|
|
.section-title {
|
|
font-family: 'Plus Jakarta Sans', sans-serif; font-size: 0.8125rem;
|
|
font-weight: 700; text-transform: uppercase; letter-spacing: 0.04em;
|
|
}
|
|
.section-count {
|
|
font-family: 'Outfit', sans-serif; font-size: 0.6875rem; color: var(--text-muted);
|
|
background: var(--bg-surface); border-radius: 10px; padding: 1px 8px;
|
|
}
|
|
|
|
/* ── Tree / Flow Items ── */
|
|
.tree-list { display: flex; flex-direction: column; gap: 4px; }
|
|
.tree-list-header {
|
|
display: grid; grid-template-columns: 40px 1fr 130px 80px 100px 40px;
|
|
align-items: center; gap: 12px; padding: 0 16px 8px;
|
|
font-family: 'Outfit', sans-serif; font-size: 0.6875rem;
|
|
text-transform: uppercase; letter-spacing: 0.05em; color: var(--text-dimmed);
|
|
}
|
|
.tree-item {
|
|
display: grid; grid-template-columns: 40px 1fr 130px 80px 100px 40px;
|
|
align-items: center; gap: 12px; padding: 12px 16px;
|
|
background: var(--bg-card); border: 1px solid transparent;
|
|
border-radius: 8px; cursor: pointer; transition: all 0.12s;
|
|
}
|
|
.tree-item:hover { border-color: var(--border); background: var(--bg-hover); }
|
|
|
|
.tree-icon-box {
|
|
width: 36px; height: 36px; border-radius: 8px;
|
|
display: flex; align-items: center; justify-content: center; font-size: 1rem;
|
|
}
|
|
.tree-info h3 {
|
|
font-family: 'Plus Jakarta Sans', sans-serif;
|
|
font-size: 0.875rem; font-weight: 600; margin-bottom: 3px;
|
|
}
|
|
.tree-meta {
|
|
display: flex; align-items: center; gap: 8px;
|
|
font-size: 0.6875rem; color: var(--text-muted);
|
|
}
|
|
.tree-meta .tag {
|
|
background: var(--bg-surface); border: 1px solid var(--border-subtle);
|
|
border-radius: 4px; padding: 1px 6px; font-family: 'Outfit', sans-serif;
|
|
font-size: 0.625rem; color: var(--text-muted);
|
|
}
|
|
.tree-category {
|
|
font-family: 'Outfit', sans-serif; font-size: 0.75rem; color: var(--text-secondary);
|
|
display: flex; align-items: center; gap: 6px;
|
|
}
|
|
.cat-indicator { width: 6px; height: 6px; border-radius: 50%; }
|
|
.tree-usage {
|
|
font-family: 'Outfit', sans-serif; font-size: 0.75rem; color: var(--text-muted); text-align: center;
|
|
}
|
|
.tree-updated { font-size: 0.6875rem; color: var(--text-dimmed); text-align: right; }
|
|
.tree-actions { display: flex; align-items: center; justify-content: center; }
|
|
.tree-actions button {
|
|
background: none; border: none; color: var(--text-muted); cursor: pointer;
|
|
padding: 4px; border-radius: 4px; display: flex; opacity: 0; transition: all 0.12s;
|
|
}
|
|
.tree-item:hover .tree-actions button { opacity: 1; }
|
|
|
|
/* ── Sessions Panel ── */
|
|
.sessions-panel {
|
|
background: var(--bg-card); border: 1px solid var(--border-subtle);
|
|
border-radius: 10px; overflow: hidden;
|
|
}
|
|
.sessions-panel-header {
|
|
display: flex; align-items: center; justify-content: space-between;
|
|
padding: 14px 18px; border-bottom: 1px solid var(--border-subtle);
|
|
}
|
|
.sessions-panel-header h2 {
|
|
font-family: 'Plus Jakarta Sans', sans-serif; font-size: 0.875rem; font-weight: 700;
|
|
}
|
|
.session-row {
|
|
display: grid; grid-template-columns: 8px 1fr 140px 80px 100px;
|
|
align-items: center; gap: 12px; padding: 11px 18px;
|
|
border-bottom: 1px solid var(--border-subtle); font-size: 0.8125rem;
|
|
transition: background 0.1s; cursor: pointer;
|
|
}
|
|
.session-row:last-child { border-bottom: none; }
|
|
.session-row:hover { background: var(--bg-hover); }
|
|
.session-status-dot { width: 8px; height: 8px; border-radius: 50%; }
|
|
.session-name { font-weight: 500; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
.session-tree { font-size: 0.6875rem; color: var(--text-muted); font-family: 'Outfit', sans-serif; }
|
|
.session-ticket { font-size: 0.6875rem; color: var(--text-muted); font-family: monospace; }
|
|
.session-time { font-size: 0.6875rem; color: var(--text-dimmed); text-align: right; }
|
|
|
|
.bg-success { background: var(--success); }
|
|
.bg-warning { background: var(--warning); }
|
|
.bg-gradient { background: linear-gradient(135deg, var(--gradient-from), var(--gradient-to)); }
|
|
|
|
/* ── Workspace color accents ── */
|
|
.ws-accent-troubleshooting { --ws-color: #ef4444; }
|
|
.ws-accent-procedures { --ws-color: #3b82f6; }
|
|
.ws-accent-policies { --ws-color: #8b5cf6; }
|
|
.ws-accent-finance { --ws-color: #22c55e; }
|
|
|
|
/* ── Content transition ── */
|
|
.main-content { transition: opacity 0.2s ease; }
|
|
.main-content.switching { opacity: 0; }
|
|
|
|
/* ── Toast notification ── */
|
|
.toast {
|
|
position: fixed; bottom: 60px; left: 50%; transform: translateX(-50%) translateY(20px);
|
|
background: var(--bg-card); border: 1px solid var(--gradient-from);
|
|
border-radius: 10px; padding: 12px 20px;
|
|
font-size: 0.8125rem; color: var(--text-primary);
|
|
display: flex; align-items: center; gap: 10px;
|
|
box-shadow: 0 8px 32px rgba(0,0,0,0.5);
|
|
opacity: 0; transition: all 0.3s ease; z-index: 1000;
|
|
pointer-events: none;
|
|
}
|
|
.toast.show { opacity: 1; transform: translateX(-50%) translateY(0); }
|
|
.toast-icon { font-size: 1rem; }
|
|
.toast-text strong { color: var(--gradient-from); }
|
|
|
|
.fade-in { animation: fadeIn 0.3s ease; }
|
|
@keyframes fadeIn { from { opacity:0; transform:translateY(6px); } to { opacity:1; transform:translateY(0); } }
|
|
|
|
.mockup-label {
|
|
position: fixed; bottom: 12px; right: 16px;
|
|
background: rgba(129,140,248,0.12); border: 1px solid rgba(129,140,248,0.25);
|
|
border-radius: 6px; padding: 4px 10px;
|
|
font-family: 'Outfit', sans-serif; font-size: 0.6875rem;
|
|
color: var(--gradient-from); z-index: 1000; backdrop-filter: blur(8px);
|
|
}
|
|
|
|
.mockup-hint {
|
|
position: fixed; bottom: 12px; left: 50%; transform: translateX(-50%);
|
|
background: rgba(129,140,248,0.12); border: 1px solid rgba(129,140,248,0.25);
|
|
border-radius: 8px; padding: 6px 14px;
|
|
font-family: 'Outfit', sans-serif; font-size: 0.75rem;
|
|
color: var(--gradient-from); z-index: 1000; backdrop-filter: blur(8px);
|
|
display: flex; align-items: center; gap: 8px;
|
|
animation: pulse 2s ease-in-out infinite;
|
|
}
|
|
@keyframes pulse {
|
|
0%, 100% { opacity: 0.8; }
|
|
50% { opacity: 1; }
|
|
}
|
|
.mockup-hint svg { width: 14px; height: 14px; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
|
|
<div class="app-shell">
|
|
|
|
<!-- ═══════════════ TOP BAR ═══════════════ -->
|
|
<header class="topbar">
|
|
<div class="topbar-logo">
|
|
<svg viewBox="0 0 80 80" fill="none">
|
|
<defs>
|
|
<linearGradient id="logo-g" x1="0%" y1="0%" x2="100%" y2="0%">
|
|
<stop offset="0%" stop-color="#818cf8"/>
|
|
<stop offset="100%" stop-color="#a78bfa"/>
|
|
</linearGradient>
|
|
</defs>
|
|
<circle cx="10" cy="14" r="5" fill="url(#logo-g)" opacity="0.25"/>
|
|
<circle cx="10" cy="30" r="5.5" fill="url(#logo-g)" opacity="0.4"/>
|
|
<circle cx="10" cy="50" r="5.5" fill="url(#logo-g)" opacity="0.4"/>
|
|
<circle cx="10" cy="66" r="5" fill="url(#logo-g)" opacity="0.25"/>
|
|
<path d="M15 14L28 34" stroke="url(#logo-g)" stroke-width="2" stroke-linecap="round" stroke-dasharray="2 3" opacity="0.35"/>
|
|
<path d="M15.5 30L28 38" stroke="url(#logo-g)" stroke-width="2" stroke-linecap="round" opacity="0.5"/>
|
|
<path d="M15.5 50L28 42" stroke="url(#logo-g)" stroke-width="2" stroke-linecap="round" opacity="0.5"/>
|
|
<path d="M15 66L28 46" stroke="url(#logo-g)" stroke-width="2" stroke-linecap="round" stroke-dasharray="2 3" opacity="0.35"/>
|
|
<circle cx="36" cy="40" r="10" fill="url(#logo-g)" opacity="0.12"/>
|
|
<circle cx="36" cy="40" r="7" fill="url(#logo-g)" opacity="0.9"/>
|
|
<path d="M43 40H70M70 40L60 30M70 40L60 50" stroke="url(#logo-g)" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
|
|
</svg>
|
|
<span class="topbar-brand"><span class="res">Resolution</span><span class="flow">Flow</span></span>
|
|
</div>
|
|
<div class="topbar-search">
|
|
<svg class="search-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="8"/><path d="m21 21-4.35-4.35"/></svg>
|
|
<input type="text" placeholder="Search trees, sessions, tags…" id="searchInput" />
|
|
<span class="search-shortcut">⌘K</span>
|
|
</div>
|
|
<div class="topbar-actions">
|
|
<button class="topbar-btn" title="Quick launch">
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"/></svg>
|
|
</button>
|
|
<button class="topbar-btn" title="Notifications">
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"/><path d="M13.73 21a2 2 0 0 1-3.46 0"/></svg>
|
|
<span class="notification-badge"></span>
|
|
</button>
|
|
<div class="avatar-sm" title="Michael C.">MC</div>
|
|
</div>
|
|
</header>
|
|
|
|
<!-- ═══════════════ SIDEBAR ═══════════════ -->
|
|
<nav class="sidebar">
|
|
|
|
<!-- Workspace Switcher -->
|
|
<div class="workspace-switcher">
|
|
<div class="workspace-current" id="wsCurrent" onclick="toggleDropdown()">
|
|
<div class="workspace-icon" id="wsIcon" style="background: rgba(239,68,68,0.12);">
|
|
<span id="wsEmoji">🔧</span>
|
|
</div>
|
|
<div class="workspace-label">
|
|
<div class="ws-name" id="wsName">Troubleshooting</div>
|
|
<div class="ws-desc" id="wsDesc">Break/fix decision trees</div>
|
|
</div>
|
|
<div class="workspace-chevron">
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="6 9 12 15 18 9"/></svg>
|
|
</div>
|
|
</div>
|
|
<div class="workspace-dropdown" id="wsDropdown">
|
|
<div class="workspace-option active-ws" data-ws="troubleshooting" onclick="switchWorkspace('troubleshooting')">
|
|
<div class="ws-opt-icon" style="background: rgba(239,68,68,0.12);">🔧</div>
|
|
<span class="ws-opt-name">Troubleshooting</span>
|
|
<span class="ws-opt-count">42 trees</span>
|
|
</div>
|
|
<div class="workspace-option" data-ws="procedures" onclick="switchWorkspace('procedures')">
|
|
<div class="ws-opt-icon" style="background: rgba(59,130,246,0.12);">📋</div>
|
|
<span class="ws-opt-name">Procedures</span>
|
|
<span class="ws-opt-count">18 flows</span>
|
|
</div>
|
|
<div class="workspace-option" data-ws="policies" onclick="switchWorkspace('policies')">
|
|
<div class="ws-opt-icon" style="background: rgba(139,92,246,0.12);">📜</div>
|
|
<span class="ws-opt-name">Policies</span>
|
|
<span class="ws-opt-count">7 flows</span>
|
|
</div>
|
|
<div class="workspace-option" data-ws="finance" onclick="switchWorkspace('finance')">
|
|
<div class="ws-opt-icon" style="background: rgba(34,197,94,0.12);">💰</div>
|
|
<span class="ws-opt-name">Finance</span>
|
|
<span class="ws-opt-count">4 flows</span>
|
|
</div>
|
|
<div class="workspace-add">
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>
|
|
Add workspace…
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Main Nav -->
|
|
<div class="nav-section">
|
|
<a class="nav-item active" href="#">
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="7" height="7" rx="1"/><rect x="14" y="3" width="7" height="7" rx="1"/><rect x="3" y="14" width="7" height="7" rx="1"/><rect x="14" y="14" width="7" height="7" rx="1"/></svg>
|
|
Dashboard
|
|
</a>
|
|
<a class="nav-item" href="#" id="navAllFlows">
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"/></svg>
|
|
<span id="navAllLabel">All Trees</span>
|
|
<span class="badge" id="navAllCount">42</span>
|
|
</a>
|
|
<a class="nav-item" href="#">
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 20h9"/><path d="M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4L16.5 3.5z"/></svg>
|
|
<span id="navEditorLabel">Tree Editor</span>
|
|
</a>
|
|
<a class="nav-item" href="#">
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg>
|
|
Sessions
|
|
<span class="badge">3</span>
|
|
</a>
|
|
<a class="nav-item" href="#">
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/></svg>
|
|
Exports
|
|
</a>
|
|
<a class="nav-item" href="#">
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M19 21l-7-5-7 5V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2z"/></svg>
|
|
Step Library
|
|
</a>
|
|
</div>
|
|
|
|
<div class="nav-divider"></div>
|
|
|
|
<!-- Dynamic Categories -->
|
|
<div class="sidebar-content" id="sidebarContent">
|
|
<div class="nav-section">
|
|
<div class="nav-section-label">Categories</div>
|
|
<div id="categoriesContainer"></div>
|
|
</div>
|
|
|
|
<div class="nav-divider"></div>
|
|
|
|
<div class="nav-section">
|
|
<div class="nav-section-label">Popular Tags</div>
|
|
<div class="tag-cloud" id="tagsContainer"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Footer -->
|
|
<div class="sidebar-footer">
|
|
<a class="nav-item" href="#">
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/></svg>
|
|
Team
|
|
</a>
|
|
<a class="nav-item" href="#">
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>
|
|
Settings
|
|
</a>
|
|
</div>
|
|
</nav>
|
|
|
|
<!-- ═══════════════ MAIN CONTENT ═══════════════ -->
|
|
<main class="main">
|
|
<div class="main-content" id="mainContent">
|
|
|
|
<div class="page-header fade-in">
|
|
<h1 id="pageTitle">Troubleshooting Dashboard</h1>
|
|
<div class="page-header-actions">
|
|
<button class="btn btn-secondary">
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="17 8 12 3 7 8"/><line x1="12" y1="3" x2="12" y2="15"/></svg>
|
|
Import
|
|
</button>
|
|
<button class="btn btn-primary" id="newBtn">
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>
|
|
<span id="newBtnLabel">New Tree</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Stats -->
|
|
<div class="quick-stats fade-in" id="statsRow"></div>
|
|
|
|
<!-- Filters -->
|
|
<div class="filters-bar fade-in" id="filtersRow"></div>
|
|
|
|
<!-- Tree/Flow Items -->
|
|
<div class="section-group fade-in" id="mainSection"></div>
|
|
|
|
<!-- Recent Sessions -->
|
|
<div class="section-group fade-in" id="sessionsSection"></div>
|
|
|
|
</div>
|
|
</main>
|
|
</div>
|
|
|
|
<div class="toast" id="toast">
|
|
<span class="toast-icon" id="toastIcon"></span>
|
|
<span class="toast-text" id="toastText"></span>
|
|
</div>
|
|
|
|
<div class="mockup-hint">
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="6 9 12 15 18 9"/></svg>
|
|
Click the workspace switcher in the sidebar to see it adapt
|
|
</div>
|
|
|
|
<div class="mockup-label">ResolutionFlow — Adaptive Workspaces Mockup</div>
|
|
|
|
<script>
|
|
// ═══════════════════════════════════════════════════════
|
|
// WORKSPACE DATA — each workspace defines its own context
|
|
// ═══════════════════════════════════════════════════════
|
|
const workspaces = {
|
|
troubleshooting: {
|
|
name: 'Troubleshooting',
|
|
desc: 'Break/fix decision trees',
|
|
emoji: '🔧',
|
|
color: '#ef4444',
|
|
bgColor: 'rgba(239,68,68,0.12)',
|
|
allLabel: 'All Trees',
|
|
allCount: '42',
|
|
editorLabel: 'Tree Editor',
|
|
newLabel: 'New Tree',
|
|
pageTitle: 'Troubleshooting Dashboard',
|
|
searchPlaceholder: 'Search trees, sessions, tags…',
|
|
categories: [
|
|
{ name: 'Networking', color: '#3b82f6', count: 12 },
|
|
{ name: 'Active Directory', color: '#22c55e', count: 8 },
|
|
{ name: 'Email / Exchange', color: '#f59e0b', count: 6 },
|
|
{ name: 'Server Issues', color: '#ef4444', count: 5 },
|
|
{ name: 'Citrix / RDS', color: '#8b5cf6', count: 4 },
|
|
{ name: 'VPN / Remote', color: '#06b6d4', count: 4 },
|
|
{ name: 'Printers / Peripherals', color: '#ec4899', count: 3 },
|
|
],
|
|
tags: ['tier-1', 'tier-2', 'tier-3', 'dns', 'o365', 'vpn', 'backup', 'critical'],
|
|
stats: [
|
|
{ label: 'Active Trees', value: '42', meta: '+3 this week' },
|
|
{ label: 'Sessions Today', value: '12', gradient: true, meta: '↑ 20% from yesterday' },
|
|
{ label: 'Open Sessions', value: '3', color: '#f59e0b', meta: 'Awaiting completion' },
|
|
{ label: 'Docs Generated', value: '847', meta: 'All time' },
|
|
],
|
|
filters: ['All', 'Recently Used', 'My Trees', 'Team Trees', 'Defaults'],
|
|
sectionTitle: 'Frequently Used',
|
|
sectionCount: 5,
|
|
items: [
|
|
{ icon: '🌐', iconBg: 'rgba(59,130,246,0.12)', iconColor: '#3b82f6', name: 'DNS Resolution Failure', tags: ['tier-1', 'dns'], steps: '12 steps · 4 solutions', cat: 'Networking', catColor: '#3b82f6', uses: 284, updated: '2 days ago' },
|
|
{ icon: '🔒', iconBg: 'rgba(34,197,94,0.12)', iconColor: '#22c55e', name: 'Account Lockout Investigation', tags: ['tier-2', 'security'], steps: '18 steps · 6 solutions', cat: 'Active Directory', catColor: '#22c55e', uses: 216, updated: '1 week ago' },
|
|
{ icon: '📧', iconBg: 'rgba(245,158,11,0.12)', iconColor: '#f59e0b', name: 'Mail Flow Troubleshooting', tags: ['tier-2', 'o365'], steps: '15 steps · 5 solutions', cat: 'Email / Exchange', catColor: '#f59e0b', uses: 189, updated: '3 days ago' },
|
|
{ icon: '🔌', iconBg: 'rgba(6,182,212,0.12)', iconColor: '#06b6d4', name: 'VPN Connection Failure', tags: ['tier-1', 'vpn'], steps: '10 steps · 3 solutions', cat: 'VPN / Remote', catColor: '#06b6d4', uses: 172, updated: '5 days ago' },
|
|
{ icon: '🖥️', iconBg: 'rgba(239,68,68,0.12)', iconColor: '#ef4444', name: 'Server Unresponsive', tags: ['tier-3', 'critical'], steps: '22 steps · 8 solutions', cat: 'Server Issues', catColor: '#ef4444', uses: 145, updated: '1 day ago' },
|
|
],
|
|
sessions: [
|
|
{ status: 'warning', name: 'DNS Resolution Failure', progress: '→ Checked DNS config · step 4/12', ticket: 'TKT-4821', time: '12 min ago' },
|
|
{ status: 'warning', name: 'Account Lockout Investigation', progress: '→ Reviewing event logs · step 6/18', ticket: 'TKT-4819', time: '45 min ago' },
|
|
{ status: 'success', name: 'Mail Flow Troubleshooting', progress: '✓ Resolved · MX record corrected', ticket: 'TKT-4815', time: 'Yesterday' },
|
|
]
|
|
},
|
|
|
|
procedures: {
|
|
name: 'Procedures',
|
|
desc: 'Step-by-step operational flows',
|
|
emoji: '📋',
|
|
color: '#3b82f6',
|
|
bgColor: 'rgba(59,130,246,0.12)',
|
|
allLabel: 'All Procedures',
|
|
allCount: '18',
|
|
editorLabel: 'Flow Editor',
|
|
newLabel: 'New Procedure',
|
|
pageTitle: 'Procedures Dashboard',
|
|
searchPlaceholder: 'Search procedures, runbooks, checklists…',
|
|
categories: [
|
|
{ name: 'Server Builds', color: '#3b82f6', count: 5 },
|
|
{ name: 'Onboarding', color: '#22c55e', count: 4 },
|
|
{ name: 'Offboarding', color: '#ef4444', count: 3 },
|
|
{ name: 'Backup & DR', color: '#f59e0b', count: 3 },
|
|
{ name: 'Patch Management', color: '#8b5cf6', count: 2 },
|
|
{ name: 'Security Audit', color: '#06b6d4', count: 1 },
|
|
],
|
|
tags: ['windows-server', 'hyper-v', 'new-hire', 'quarterly', 'azure', 'compliance', 'm365-setup'],
|
|
stats: [
|
|
{ label: 'Active Procedures', value: '18', meta: '+2 this month' },
|
|
{ label: 'Runs This Week', value: '8', gradient: true, meta: '↑ 33% from last week' },
|
|
{ label: 'In Progress', value: '2', color: '#3b82f6', meta: 'Being executed' },
|
|
{ label: 'Avg Completion', value: '94%', meta: 'Last 30 days' },
|
|
],
|
|
filters: ['All', 'Recently Run', 'My Procedures', 'Team', 'Templates'],
|
|
sectionTitle: 'Most Used Procedures',
|
|
sectionCount: 4,
|
|
items: [
|
|
{ icon: '🖥️', iconBg: 'rgba(59,130,246,0.12)', iconColor: '#3b82f6', name: 'Windows Server 2022 Build', tags: ['windows-server', 'hyper-v'], steps: '34 steps · 6 checkpoints', cat: 'Server Builds', catColor: '#3b82f6', uses: 89, updated: '1 week ago' },
|
|
{ icon: '👤', iconBg: 'rgba(34,197,94,0.12)', iconColor: '#22c55e', name: 'New Employee Onboarding', tags: ['new-hire', 'm365-setup'], steps: '28 steps · 4 checkpoints', cat: 'Onboarding', catColor: '#22c55e', uses: 67, updated: '3 days ago' },
|
|
{ icon: '🔄', iconBg: 'rgba(245,158,11,0.12)', iconColor: '#f59e0b', name: 'Monthly Backup Verification', tags: ['quarterly', 'compliance'], steps: '16 steps · 3 checkpoints', cat: 'Backup & DR', catColor: '#f59e0b', uses: 52, updated: '2 days ago' },
|
|
{ icon: '🚪', iconBg: 'rgba(239,68,68,0.12)', iconColor: '#ef4444', name: 'Employee Offboarding', tags: ['compliance', 'security'], steps: '22 steps · 5 checkpoints', cat: 'Offboarding', catColor: '#ef4444', uses: 41, updated: '5 days ago' },
|
|
],
|
|
sessions: [
|
|
{ status: 'warning', name: 'Windows Server 2022 Build', progress: '→ Installing roles · step 12/34', ticket: 'PRJ-102', time: '30 min ago' },
|
|
{ status: 'warning', name: 'New Employee Onboarding', progress: '→ M365 license assignment · step 8/28', ticket: 'HR-445', time: '2 hours ago' },
|
|
{ status: 'success', name: 'Monthly Backup Verification', progress: '✓ Complete · All systems verified', ticket: 'OPS-891', time: 'Yesterday' },
|
|
]
|
|
},
|
|
|
|
policies: {
|
|
name: 'Policies',
|
|
desc: 'Compliance & policy builders',
|
|
emoji: '📜',
|
|
color: '#8b5cf6',
|
|
bgColor: 'rgba(139,92,246,0.12)',
|
|
allLabel: 'All Policies',
|
|
allCount: '7',
|
|
editorLabel: 'Policy Editor',
|
|
newLabel: 'New Policy',
|
|
pageTitle: 'Policies Dashboard',
|
|
searchPlaceholder: 'Search policies, compliance, frameworks…',
|
|
categories: [
|
|
{ name: 'Security Policies', color: '#ef4444', count: 3 },
|
|
{ name: 'Acceptable Use', color: '#3b82f6', count: 2 },
|
|
{ name: 'Data Governance', color: '#8b5cf6', count: 1 },
|
|
{ name: 'Incident Response', color: '#f59e0b', count: 1 },
|
|
],
|
|
tags: ['nist', 'hipaa', 'soc2', 'gdpr', 'internal', 'client-facing', 'annual-review'],
|
|
stats: [
|
|
{ label: 'Active Policies', value: '7', meta: '+1 this quarter' },
|
|
{ label: 'Pending Review', value: '2', gradient: true, meta: 'Due this month' },
|
|
{ label: 'Compliance Score', value: '91%', color: '#22c55e', meta: 'Across frameworks' },
|
|
{ label: 'Last Audit', value: '14d', meta: 'Days since last review' },
|
|
],
|
|
filters: ['All', 'Needs Review', 'Active', 'Draft', 'Archived'],
|
|
sectionTitle: 'Policy Documents',
|
|
sectionCount: 4,
|
|
items: [
|
|
{ icon: '🔐', iconBg: 'rgba(239,68,68,0.12)', iconColor: '#ef4444', name: 'Password & Authentication Policy', tags: ['nist', 'soc2'], steps: '12 sections · NIST aligned', cat: 'Security Policies', catColor: '#ef4444', uses: 34, updated: '2 weeks ago' },
|
|
{ icon: '💻', iconBg: 'rgba(59,130,246,0.12)', iconColor: '#3b82f6', name: 'Acceptable Use Policy', tags: ['internal', 'client-facing'], steps: '8 sections · Template', cat: 'Acceptable Use', catColor: '#3b82f6', uses: 28, updated: '1 month ago' },
|
|
{ icon: '🚨', iconBg: 'rgba(245,158,11,0.12)', iconColor: '#f59e0b', name: 'Incident Response Plan', tags: ['nist', 'compliance'], steps: '15 sections · Runbook', cat: 'Incident Response', catColor: '#f59e0b', uses: 19, updated: '3 weeks ago' },
|
|
{ icon: '📊', iconBg: 'rgba(139,92,246,0.12)', iconColor: '#8b5cf6', name: 'Data Classification Standard', tags: ['gdpr', 'hipaa'], steps: '6 sections · Framework', cat: 'Data Governance', catColor: '#8b5cf6', uses: 12, updated: '1 month ago' },
|
|
],
|
|
sessions: [
|
|
{ status: 'warning', name: 'Password Policy Update', progress: '→ Review MFA requirements · section 4/12', ticket: 'POL-23', time: '1 day ago' },
|
|
{ status: 'success', name: 'Acceptable Use Policy', progress: '✓ Approved · v2.1 published', ticket: 'POL-21', time: '2 weeks ago' },
|
|
]
|
|
},
|
|
|
|
finance: {
|
|
name: 'Finance',
|
|
desc: 'Billing & procurement flows',
|
|
emoji: '💰',
|
|
color: '#22c55e',
|
|
bgColor: 'rgba(34,197,94,0.12)',
|
|
allLabel: 'All Finance Flows',
|
|
allCount: '4',
|
|
editorLabel: 'Flow Editor',
|
|
newLabel: 'New Flow',
|
|
pageTitle: 'Finance Dashboard',
|
|
searchPlaceholder: 'Search billing, procurement, approvals…',
|
|
categories: [
|
|
{ name: 'License Management', color: '#3b82f6', count: 2 },
|
|
{ name: 'Procurement', color: '#22c55e', count: 1 },
|
|
{ name: 'Billing Reviews', color: '#f59e0b', count: 1 },
|
|
],
|
|
tags: ['monthly', 'approval-required', 'vendor', 'license-audit', 'cost-savings'],
|
|
stats: [
|
|
{ label: 'Active Flows', value: '4', meta: 'All operational' },
|
|
{ label: 'Runs This Month', value: '6', gradient: true, meta: 'On schedule' },
|
|
{ label: 'Cost Saved', value: '$4.2K', color: '#22c55e', meta: 'From license audits' },
|
|
{ label: 'Pending Approvals', value: '1', meta: 'Awaiting sign-off' },
|
|
],
|
|
filters: ['All', 'Pending Approval', 'Recurring', 'One-time'],
|
|
sectionTitle: 'Finance Flows',
|
|
sectionCount: 3,
|
|
items: [
|
|
{ icon: '🔑', iconBg: 'rgba(59,130,246,0.12)', iconColor: '#3b82f6', name: 'M365 License Audit', tags: ['monthly', 'license-audit'], steps: '8 steps · 2 approvals', cat: 'License Management', catColor: '#3b82f6', uses: 24, updated: '1 week ago' },
|
|
{ icon: '🛒', iconBg: 'rgba(34,197,94,0.12)', iconColor: '#22c55e', name: 'Hardware Procurement', tags: ['approval-required', 'vendor'], steps: '12 steps · 3 approvals', cat: 'Procurement', catColor: '#22c55e', uses: 18, updated: '2 weeks ago' },
|
|
{ icon: '📄', iconBg: 'rgba(245,158,11,0.12)', iconColor: '#f59e0b', name: 'Quarterly Billing Review', tags: ['quarterly', 'cost-savings'], steps: '10 steps · Report', cat: 'Billing Reviews', catColor: '#f59e0b', uses: 12, updated: '1 month ago' },
|
|
],
|
|
sessions: [
|
|
{ status: 'warning', name: 'M365 License Audit', progress: '→ Reviewing unused licenses · step 5/8', ticket: 'FIN-67', time: '3 hours ago' },
|
|
{ status: 'success', name: 'Hardware Procurement', progress: '✓ Approved · PO submitted', ticket: 'FIN-64', time: 'Yesterday' },
|
|
]
|
|
}
|
|
};
|
|
|
|
// ═══════════════════════════════
|
|
// RENDER FUNCTIONS
|
|
// ═══════════════════════════════
|
|
|
|
let currentWs = 'troubleshooting';
|
|
let dropdownOpen = false;
|
|
|
|
function toggleDropdown() {
|
|
dropdownOpen = !dropdownOpen;
|
|
const dd = document.getElementById('wsDropdown');
|
|
const cur = document.getElementById('wsCurrent');
|
|
if (dropdownOpen) {
|
|
dd.classList.add('show');
|
|
cur.classList.add('open');
|
|
} else {
|
|
dd.classList.remove('show');
|
|
cur.classList.remove('open');
|
|
}
|
|
}
|
|
|
|
function switchWorkspace(wsKey) {
|
|
if (wsKey === currentWs) { toggleDropdown(); return; }
|
|
|
|
const ws = workspaces[wsKey];
|
|
currentWs = wsKey;
|
|
|
|
// Close dropdown
|
|
dropdownOpen = false;
|
|
document.getElementById('wsDropdown').classList.remove('show');
|
|
document.getElementById('wsCurrent').classList.remove('open');
|
|
|
|
// Update dropdown active states
|
|
document.querySelectorAll('.workspace-option').forEach(el => {
|
|
el.classList.toggle('active-ws', el.dataset.ws === wsKey);
|
|
});
|
|
|
|
// Animate out
|
|
document.getElementById('sidebarContent').classList.add('switching');
|
|
document.getElementById('mainContent').classList.add('switching');
|
|
|
|
setTimeout(() => {
|
|
// Update workspace switcher
|
|
document.getElementById('wsIcon').style.background = ws.bgColor;
|
|
document.getElementById('wsEmoji').textContent = ws.emoji;
|
|
document.getElementById('wsName').textContent = ws.name;
|
|
document.getElementById('wsDesc').textContent = ws.desc;
|
|
|
|
// Update nav
|
|
document.getElementById('navAllLabel').textContent = ws.allLabel;
|
|
document.getElementById('navAllCount').textContent = ws.allCount;
|
|
document.getElementById('navEditorLabel').textContent = ws.editorLabel;
|
|
document.getElementById('searchInput').placeholder = ws.searchPlaceholder;
|
|
|
|
// Update main content
|
|
document.getElementById('pageTitle').textContent = ws.pageTitle;
|
|
document.getElementById('newBtnLabel').textContent = ws.newLabel;
|
|
|
|
// Render categories
|
|
renderCategories(ws.categories);
|
|
renderTags(ws.tags);
|
|
renderStats(ws.stats);
|
|
renderFilters(ws.filters);
|
|
renderItems(ws);
|
|
renderSessions(ws.sessions);
|
|
|
|
// Animate in
|
|
document.getElementById('sidebarContent').classList.remove('switching');
|
|
document.getElementById('mainContent').classList.remove('switching');
|
|
|
|
// Show toast
|
|
showToast(ws.emoji, `Switched to <strong>${ws.name}</strong>`);
|
|
}, 200);
|
|
}
|
|
|
|
function renderCategories(cats) {
|
|
document.getElementById('categoriesContainer').innerHTML = cats.map(c => `
|
|
<div class="category-tag">
|
|
<span class="cat-dot" style="background: ${c.color};"></span>
|
|
${c.name}
|
|
<span class="cat-count">${c.count}</span>
|
|
</div>
|
|
`).join('');
|
|
}
|
|
|
|
function renderTags(tags) {
|
|
document.getElementById('tagsContainer').innerHTML = tags.map(t => `
|
|
<span class="filter-chip">${t}</span>
|
|
`).join('');
|
|
}
|
|
|
|
function renderStats(stats) {
|
|
document.getElementById('statsRow').innerHTML = stats.map(s => `
|
|
<div class="stat-card">
|
|
<div class="stat-label">${s.label}</div>
|
|
<div class="stat-value${s.gradient ? ' gradient' : ''}" ${s.color ? `style="color:${s.color}"` : ''}>${s.value}</div>
|
|
<div class="stat-meta">${s.meta}</div>
|
|
</div>
|
|
`).join('');
|
|
}
|
|
|
|
function renderFilters(filters) {
|
|
document.getElementById('filtersRow').innerHTML = filters.map((f, i) => `
|
|
<span class="main-filter-chip${i === 0 ? ' active' : ''}">${f}</span>
|
|
`).join('') + `
|
|
<div class="filter-divider"></div>
|
|
<span class="main-filter-chip">
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="width:14px;height:14px;"><polygon points="22 3 2 3 10 12.46 10 19 14 21 14 12.46 22 3"/></svg>
|
|
More Filters
|
|
</span>
|
|
`;
|
|
}
|
|
|
|
function renderItems(ws) {
|
|
const dots = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="1"/><circle cx="19" cy="12" r="1"/><circle cx="5" cy="12" r="1"/></svg>';
|
|
|
|
const header = `
|
|
<div class="section-header">
|
|
<span class="section-icon bg-gradient"></span>
|
|
<span class="section-title">${ws.sectionTitle}</span>
|
|
<span class="section-count">${ws.sectionCount || ws.items.length}</span>
|
|
</div>
|
|
<div class="tree-list-header">
|
|
<span></span><span>Name</span><span>Category</span><span style="text-align:center;">Uses</span><span style="text-align:right;">Updated</span><span></span>
|
|
</div>
|
|
`;
|
|
|
|
const items = ws.items.map(item => `
|
|
<div class="tree-item">
|
|
<div class="tree-icon-box" style="background:${item.iconBg};"><span style="color:${item.iconColor};">${item.icon}</span></div>
|
|
<div class="tree-info">
|
|
<h3>${item.name}</h3>
|
|
<div class="tree-meta">
|
|
${item.tags.map(t => `<span class="tag">${t}</span>`).join('')}
|
|
<span>${item.steps}</span>
|
|
</div>
|
|
</div>
|
|
<div class="tree-category"><span class="cat-indicator" style="background:${item.catColor};"></span> ${item.cat}</div>
|
|
<div class="tree-usage">${item.uses}</div>
|
|
<div class="tree-updated">${item.updated}</div>
|
|
<div class="tree-actions"><button>${dots}</button></div>
|
|
</div>
|
|
`).join('');
|
|
|
|
document.getElementById('mainSection').innerHTML = header + `<div class="tree-list">${items}</div>`;
|
|
}
|
|
|
|
function renderSessions(sessions) {
|
|
const rows = sessions.map(s => `
|
|
<div class="session-row">
|
|
<span class="session-status-dot bg-${s.status}"></span>
|
|
<span class="session-name">${s.name}</span>
|
|
<span class="session-tree">${s.progress}</span>
|
|
<span class="session-ticket">${s.ticket}</span>
|
|
<span class="session-time">${s.time}</span>
|
|
</div>
|
|
`).join('');
|
|
|
|
document.getElementById('sessionsSection').innerHTML = `
|
|
<div class="sessions-panel">
|
|
<div class="sessions-panel-header">
|
|
<h2>Recent Sessions</h2>
|
|
<button class="btn btn-secondary" style="padding:5px 12px; font-size:0.75rem;">View All</button>
|
|
</div>
|
|
${rows}
|
|
</div>
|
|
`;
|
|
}
|
|
|
|
function showToast(icon, html) {
|
|
const toast = document.getElementById('toast');
|
|
document.getElementById('toastIcon').textContent = icon;
|
|
document.getElementById('toastText').innerHTML = html;
|
|
toast.classList.add('show');
|
|
setTimeout(() => toast.classList.remove('show'), 2000);
|
|
}
|
|
|
|
// ═══════════════════════════════
|
|
// INIT
|
|
// ═══════════════════════════════
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
const ws = workspaces[currentWs];
|
|
renderCategories(ws.categories);
|
|
renderTags(ws.tags);
|
|
renderStats(ws.stats);
|
|
renderFilters(ws.filters);
|
|
renderItems(ws);
|
|
renderSessions(ws.sessions);
|
|
});
|
|
|
|
// Close dropdown on outside click
|
|
document.addEventListener('click', (e) => {
|
|
if (dropdownOpen && !e.target.closest('.workspace-switcher')) {
|
|
toggleDropdown();
|
|
}
|
|
});
|
|
</script>
|
|
|
|
</body>
|
|
</html>
|