| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8" /> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0" /> |
| <title>{% block title %}Lab Portal{% endblock %}</title> |
| <link rel="icon" href="{{ url_for('static', filename='favicon.svg') }}" type="image/svg+xml" /> |
| <style> |
| |
| :root { |
| --bg: #0e0e10; |
| --surface: #1a1a2e; |
| --surface2: #22223a; |
| --border: #2a2a4a; |
| --text: #e2e2e8; |
| --text-dim: #9090a8; |
| --accent: #6c63ff; |
| --accent-h: #8b83ff; |
| --danger: #ff6b6b; |
| --success: #51cf66; |
| --sidebar-w: 220px; |
| --topbar-h: 0px; |
| } |
| |
| |
| *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; } |
| html, body { height: 100%; } |
| body { |
| font-family: "Segoe UI", system-ui, -apple-system, sans-serif; |
| background: var(--bg); |
| color: var(--text); |
| display: flex; |
| } |
| |
| |
| .sidebar { |
| width: var(--sidebar-w); |
| min-height: 100vh; |
| background: var(--surface); |
| border-right: 1px solid var(--border); |
| display: flex; |
| flex-direction: column; |
| padding: 1.25rem 0; |
| flex-shrink: 0; |
| } |
| .sidebar-brand { |
| padding: 0 1.25rem 1.25rem; |
| font-size: 1.1rem; |
| font-weight: 700; |
| letter-spacing: .03em; |
| color: var(--accent); |
| border-bottom: 1px solid var(--border); |
| margin-bottom: .75rem; |
| } |
| .sidebar nav { flex: 1; display: flex; flex-direction: column; gap: 2px; padding: 0 .5rem; } |
| .sidebar a { |
| display: flex; |
| align-items: center; |
| gap: .65rem; |
| padding: .6rem .85rem; |
| border-radius: 8px; |
| color: var(--text-dim); |
| text-decoration: none; |
| font-size: .92rem; |
| transition: background .15s, color .15s; |
| } |
| .sidebar a:hover { background: var(--surface2); color: var(--text); } |
| .sidebar a.active { background: var(--accent); color: #fff; } |
| .sidebar a .icon { font-size: 1.15rem; } |
| .sidebar-footer { |
| padding: .75rem 1.25rem 0; |
| border-top: 1px solid var(--border); |
| margin-top: auto; |
| } |
| .sidebar-footer a { |
| color: var(--text-dim); |
| text-decoration: none; |
| font-size: .82rem; |
| } |
| .sidebar-footer a:hover { color: var(--danger); } |
| |
| |
| .main { |
| flex: 1; |
| display: flex; |
| flex-direction: column; |
| min-height: 100vh; |
| overflow: hidden; |
| } |
| .page-header { |
| padding: 1.25rem 2rem; |
| border-bottom: 1px solid var(--border); |
| background: var(--surface); |
| } |
| .page-header h1 { |
| font-size: 1.25rem; |
| font-weight: 600; |
| } |
| .page-header p { |
| color: var(--text-dim); |
| font-size: .85rem; |
| margin-top: .25rem; |
| } |
| .page-body { |
| flex: 1; |
| padding: 2rem; |
| overflow-y: auto; |
| } |
| |
| |
| .card { |
| background: var(--surface); |
| border: 1px solid var(--border); |
| border-radius: 12px; |
| padding: 1.5rem; |
| } |
| .btn { |
| display: inline-flex; |
| align-items: center; |
| gap: .5rem; |
| padding: .55rem 1.3rem; |
| border-radius: 8px; |
| font-size: .9rem; |
| font-weight: 600; |
| border: none; |
| cursor: pointer; |
| text-decoration: none; |
| transition: background .15s, transform .1s; |
| } |
| .btn:active { transform: scale(.97); } |
| .btn-primary { background: var(--accent); color: #fff; } |
| .btn-primary:hover { background: var(--accent-h); } |
| .btn-outline { |
| background: transparent; |
| border: 1px solid var(--border); |
| color: var(--text); |
| } |
| .btn-outline:hover { border-color: var(--accent); color: var(--accent); } |
| |
| |
| @media (max-width: 768px) { |
| .sidebar { width: 56px; } |
| .sidebar-brand, .sidebar a span, .sidebar-footer span { display: none; } |
| .sidebar a { justify-content: center; padding: .6rem; } |
| .page-body { padding: 1rem; } |
| } |
| |
| |
| {% block extra_css %}{% endblock %} |
| </style> |
| </head> |
| <body> |
|
|
| |
| <aside class="sidebar"> |
| <div class="sidebar-brand">Lab Portal</div> |
| <nav> |
| {% for endpoint, icon, label in sidebar_pages %} |
| <a href="{{ url_for(endpoint) }}" |
| class="{{ 'active' if active_page == endpoint else '' }}"> |
| <span class="icon">{{ icon }}</span> |
| <span>{{ label }}</span> |
| </a> |
| {% endfor %} |
| </nav> |
| <div class="sidebar-footer"> |
| <a href="{{ url_for('logout') }}"><span>Sign out</span></a> |
| </div> |
| </aside> |
|
|
| |
| <div class="main"> |
| {% block header %}{% endblock %} |
|
|
| <div class="page-body"> |
| {% block content %}{% endblock %} |
| </div> |
| </div> |
|
|
| {% block extra_js %}{% endblock %} |
| </body> |
| </html> |
|
|