|
5 | 5 | <script src="/static/dzarlax.js" defer></script> |
6 | 6 | <script src="/static/htmx.min.js" defer></script> |
7 | 7 | <style> |
8 | | - body { padding: 1.5rem 2rem; margin: 0; max-width: none; } |
9 | | - h1 { margin: 0; } |
10 | | - .admin-header { display: flex; align-items: baseline; justify-content: space-between; gap: 1rem; margin-bottom: 0.75rem; } |
11 | | - .admin-header__brand { font-size: 1.35rem; font-weight: 600; letter-spacing: -0.01em; } |
12 | | - .admin-tabs { display: flex; gap: 0.25rem; margin-bottom: 1.5rem; border-bottom: 1px solid rgba(0,0,0,0.1); } |
13 | | - .admin-tabs__tab { padding: 0.5rem 1rem; color: var(--text-secondary, rgba(0,0,0,0.6)); text-decoration: none; border-bottom: 2px solid transparent; margin-bottom: -1px; } |
14 | | - .admin-tabs__tab:hover { color: var(--text-primary, #000); } |
15 | | - .admin-tabs__tab--active { color: var(--text-primary, #000); border-bottom-color: var(--accent, #3b82f6); font-weight: 600; } |
| 8 | + /* Admin UI overrides on top of the dzarlax design system. Anything that |
| 9 | + duplicates DS is deliberately absent — we rely on DS components for: |
| 10 | + • .navbar / .navbar__brand (page header) |
| 11 | + • .tab-nav / .tab-nav a.active (page tabs) |
| 12 | + • .kpi-bar / .kpi-item (metric row on Analytics) |
| 13 | + • .divider--labeled / .divider__label (chat session breaks) |
| 14 | + • .card / .card__body / .card__header (content panels) |
| 15 | + • [hidden] !important + input 16px (iOS zoom fix) |
| 16 | + Admin pages are full-width — design system navbar/container padding is |
| 17 | + intentionally bypassed so data-dense tables use every pixel. */ |
| 18 | + body { padding: 0 1.5rem 2rem; margin: 0; max-width: none; } |
| 19 | + |
16 | 20 | .admin-section { margin-bottom: 2rem; } |
17 | 21 | .filters { display: flex; gap: 0.75rem; flex-wrap: wrap; align-items: center; margin-bottom: 1rem; } |
18 | 22 | .filters label { display: inline-flex; gap: 0.25rem; align-items: center; } |
|
21 | 25 | .assign-buttons { margin-top: 0.25rem; } |
22 | 26 | .assign-buttons .btn { margin-right: 0.25rem; margin-bottom: 0.25rem; } |
23 | 27 | .price-cell { white-space: nowrap; font-variant-numeric: tabular-nums; } |
24 | | - .preset-banner { padding: 0.75rem 1rem; border-radius: var(--radius, 8px); background: var(--surface-2, #E8E6E3); margin-bottom: 1rem; display: flex; justify-content: space-between; align-items: center; gap: 1rem; flex-wrap: wrap; } |
25 | | - .preset-banner .preset-meta { color: var(--text-secondary, rgba(0,0,0,0.6)); font-size: 0.875rem; margin-top: 0.25rem; } |
| 28 | + .preset-banner { padding: 0.75rem 1rem; border-radius: var(--radius); background: var(--surface-2); margin-bottom: 1rem; display: flex; justify-content: space-between; align-items: center; gap: 1rem; flex-wrap: wrap; } |
| 29 | + .preset-banner .preset-meta { color: var(--text-secondary); font-size: 0.875rem; margin-top: 0.25rem; } |
26 | 30 | .preset-banner details { margin-top: 0.5rem; font-size: 0.85em; } |
27 | | - .preset-banner details > summary { cursor: pointer; color: var(--text-secondary, rgba(0,0,0,0.6)); user-select: none; list-style: none; } |
| 31 | + .preset-banner details > summary { cursor: pointer; color: var(--text-secondary); user-select: none; list-style: none; } |
28 | 32 | .preset-banner details > summary::-webkit-details-marker { display: none; } |
29 | 33 | .preset-banner details > summary::before { content: "▸ "; display: inline-block; transition: transform 0.15s; } |
30 | 34 | .preset-banner details[open] > summary::before { content: "▾ "; } |
31 | | - .preset-banner details .formula { margin-top: 0.5rem; padding: 0.5rem 0.75rem; background: rgba(0,0,0,0.04); border-left: 3px solid var(--accent, #888); border-radius: 4px; line-height: 1.45; font-size: 0.9em; } |
| 35 | + .preset-banner details .formula { margin-top: 0.5rem; padding: 0.5rem 0.75rem; background: rgba(0,0,0,0.04); border-left: 3px solid var(--accent); border-radius: 4px; line-height: 1.45; font-size: 0.9em; } |
32 | 36 | .preset-banner details .formula code { background: rgba(0,0,0,0.06); padding: 0 0.25rem; border-radius: 3px; } |
33 | 37 | .suggest-btn { margin-left: 0.5rem; } |
34 | 38 | .table-wrap { overflow-x: auto; } |
35 | 39 | .table-wrap table { width: 100%; } |
36 | 40 | .usage-toolbar { display: flex; align-items: center; gap: 0.5rem; margin-bottom: 1rem; flex-wrap: wrap; } |
37 | | - .usage-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 0.75rem; margin-bottom: 1.25rem; } |
38 | | - .usage-card { padding: 0.75rem 1rem; border-radius: var(--radius, 8px); background: var(--surface-2, #E8E6E3); } |
39 | | - .usage-card__label { font-size: 0.8em; color: var(--text-secondary, rgba(0,0,0,0.6)); text-transform: uppercase; letter-spacing: 0.05em; } |
40 | | - .usage-card__value { font-size: 1.5em; font-weight: 600; font-variant-numeric: tabular-nums; margin-top: 0.25rem; } |
41 | | - .usage-card__sub { font-size: 0.8em; color: var(--text-secondary, rgba(0,0,0,0.6)); margin-top: 0.25rem; } |
42 | | - .usage-chart-wrap { margin-bottom: 1.5rem; padding: 0.75rem; background: rgba(0,0,0,0.03); border-radius: var(--radius, 8px); max-width: 640px; } |
43 | | - .usage-chart-title { font-size: 0.85em; margin-bottom: 0.5rem; color: var(--text-secondary, rgba(0,0,0,0.7)); } |
| 41 | + .usage-chart-wrap { margin-bottom: 1.5rem; padding: 0.75rem; background: rgba(0,0,0,0.03); border-radius: var(--radius); max-width: 640px; } |
| 42 | + .usage-chart-title { font-size: 0.85em; margin-bottom: 0.5rem; color: var(--text-secondary); } |
44 | 43 | .usage-chart { width: 100%; height: auto; display: block; max-height: 140px; } |
45 | 44 | .usage-chart + .usage-chart { margin-top: 0.25rem; } |
46 | 45 | .usage-chart rect:hover { opacity: 1 !important; } |
47 | 46 | .usage-section-h { font-size: 0.95em; margin-top: 1.5rem; margin-bottom: 0.5rem; font-weight: 600; } |
48 | 47 | .usage-turns td { vertical-align: top; } |
49 | | - .usage-turn__q { color: var(--text-primary, #000); margin-bottom: 0.25rem; font-size: 0.9em; } |
50 | | - .usage-turn__a { color: var(--text-secondary, rgba(0,0,0,0.7)); font-size: 0.9em; } |
51 | | - |
52 | | - /* Form input sizing — 16px minimum on iOS stops the page zooming in on |
53 | | - focus. Applied generously so every tab's inputs behave the same. */ |
54 | | - input, select, textarea { font-size: max(16px, 0.95rem); } |
55 | | - |
56 | | - /* The dzarlax design-system assigns display: inline-flex to .btn, which |
57 | | - outranks the browser's default [hidden]{display:none}. Without this, |
58 | | - hidden toggles on buttons (e.g. Stop in the chat form) do nothing. */ |
59 | | - [hidden] { display: none !important; } |
| 48 | + .usage-turn__q { color: var(--text); margin-bottom: 0.25rem; font-size: 0.9em; } |
| 49 | + .usage-turn__a { color: var(--text-secondary); font-size: 0.9em; } |
60 | 50 |
|
61 | | - /* Responsive base — tuned for phone portrait (<=640). Tabs with many |
62 | | - entries become horizontally scrollable; body padding shrinks so |
63 | | - content uses the full viewport. Tables inherit scrolling via |
64 | | - .table-wrap (already wrapped where needed). */ |
65 | 51 | @media (max-width: 640px) { |
66 | | - body { padding: 0.75rem 0.6rem; } |
67 | | - h1 { font-size: 1.35rem; margin-bottom: 0.5rem; } |
68 | | - .admin-tabs { overflow-x: auto; overflow-y: hidden; flex-wrap: nowrap; -webkit-overflow-scrolling: touch; margin-bottom: 1rem; } |
69 | | - .admin-tabs::-webkit-scrollbar { height: 3px; } |
70 | | - .admin-tabs__tab { white-space: nowrap; padding: 0.45rem 0.7rem; font-size: 0.92em; } |
| 52 | + body { padding: 0 0.75rem 1.5rem; } |
71 | 53 | .admin-section { margin-bottom: 1.25rem; } |
72 | 54 | .filters { gap: 0.5rem; } |
73 | 55 | .filters input[type="text"], .filters input[type="search"] { flex: 1 1 100%; min-width: 0; } |
74 | | - .usage-grid { gap: 0.5rem; } |
75 | | - .usage-card { padding: 0.6rem 0.75rem; } |
76 | | - .usage-card__value { font-size: 1.25em; } |
77 | | - .usage-card__label { font-size: 0.72em; } |
78 | 56 | .usage-chart-wrap { padding: 0.5rem; } |
79 | 57 | .preset-banner { padding: 0.6rem 0.75rem; flex-direction: column; align-items: flex-start; } |
80 | | - /* Min 40px touch target on primary actions. */ |
| 58 | + /* Min 40px touch target. */ |
81 | 59 | .btn { min-height: 40px; } |
82 | | - /* Tables that didn't opt into .table-wrap still scroll safely. */ |
| 60 | + /* Tables that didn't opt into .table-wrap scroll safely. */ |
83 | 61 | table { display: block; overflow-x: auto; max-width: 100%; } |
84 | | - |
85 | | - /* Settings form: stack label/input/button vertically. Same grid but |
86 | | - each cell full-width. Save button becomes full-width action too. */ |
| 62 | + /* Settings form stacks label/input/button vertically. */ |
87 | 63 | .settings-row { grid-template-columns: 1fr; gap: 0.4rem; padding: 0.6rem 0; } |
88 | 64 | .settings-row > :nth-child(2) { width: 100%; } |
89 | 65 | .settings-input { width: 100% !important; } |
90 | | - |
91 | | - /* MCP server cards: stack label above field so 9rem labels don't |
92 | | - squeeze mobile inputs; tighter padding. */ |
| 66 | + /* MCP server cards: labels become small captions above fields. */ |
93 | 67 | .mcp-card { padding: 0.75rem; } |
94 | 68 | .mcp-field { grid-template-columns: 1fr; gap: 0.2rem; } |
95 | 69 | .mcp-field label { padding-top: 0; font-size: 0.78em; text-transform: uppercase; letter-spacing: 0.03em; } |
|
98 | 72 | {{end}} |
99 | 73 |
|
100 | 74 | {{define "admin_header"}} |
101 | | -<header class="admin-header"> |
102 | | - <h1 class="admin-header__brand">Personal Assistant</h1> |
103 | | -</header> |
| 75 | +<nav class="navbar"> |
| 76 | + <a class="navbar__brand" href="/">Personal Assistant</a> |
| 77 | +</nav> |
104 | 78 | {{template "admin_tabs" .}} |
105 | 79 | {{end}} |
106 | 80 |
|
107 | 81 | {{define "admin_tabs"}} |
108 | | -<nav class="admin-tabs"> |
109 | | - <a class="admin-tabs__tab {{if eq .ActiveTab "chat"}}admin-tabs__tab--active{{end}}" href="/chat">Chat</a> |
110 | | - <a class="admin-tabs__tab {{if eq .ActiveTab "routing"}}admin-tabs__tab--active{{end}}" href="/">Routing & Models</a> |
111 | | - <a class="admin-tabs__tab {{if eq .ActiveTab "analytics"}}admin-tabs__tab--active{{end}}" href="/analytics">Analytics</a> |
112 | | - <a class="admin-tabs__tab {{if eq .ActiveTab "prompts"}}admin-tabs__tab--active{{end}}" href="/prompts">Prompts</a> |
113 | | - <a class="admin-tabs__tab {{if eq .ActiveTab "mcp"}}admin-tabs__tab--active{{end}}" href="/mcp">MCP</a> |
114 | | - <a class="admin-tabs__tab {{if eq .ActiveTab "settings"}}admin-tabs__tab--active{{end}}" href="/settings">Settings</a> |
| 82 | +<nav class="tab-nav"> |
| 83 | + <a class="{{if eq .ActiveTab "chat"}}active{{end}}" href="/chat">Chat</a> |
| 84 | + <a class="{{if eq .ActiveTab "routing"}}active{{end}}" href="/">Routing & Models</a> |
| 85 | + <a class="{{if eq .ActiveTab "analytics"}}active{{end}}" href="/analytics">Analytics</a> |
| 86 | + <a class="{{if eq .ActiveTab "prompts"}}active{{end}}" href="/prompts">Prompts</a> |
| 87 | + <a class="{{if eq .ActiveTab "mcp"}}active{{end}}" href="/mcp">MCP</a> |
| 88 | + <a class="{{if eq .ActiveTab "settings"}}active{{end}}" href="/settings">Settings</a> |
115 | 89 | </nav> |
116 | 90 | {{end}} |
0 commit comments