-
Current host
-
{dashboardSnapshot.system.publicHost}
+
Status
+
{dashboardSnapshot.service.status}
-
-
{dashboardSnapshot.service.status}
-
{dashboardSnapshot.service.versionLabel}
+
+ Version
+ {dashboardSnapshot.service.versionLabel}
-
+
+ Users
+ {dashboardSnapshot.users.total}
+
+
+
- {activeTab === 'dashboard' ?
: null}
- {activeTab === 'users' ?
: null}
- {activeTab === 'system' ?
: null}
-
+
+ {tabs.map((tab) => (
+ setActiveTab(tab.id)}
+ >
+ {tab.label}
+
+ ))}
+
+
+ {activeTab === 'dashboard' ?
: null}
+ {activeTab === 'users' ?
: null}
+ {activeTab === 'system' ?
: null}
);
}
diff --git a/src/app.css b/src/app.css
index dcfc563..5f4609b 100644
--- a/src/app.css
+++ b/src/app.css
@@ -1,40 +1,44 @@
:root {
- font-family: "Segoe UI", Arial, sans-serif;
- line-height: 1.5;
- font-weight: 400;
- color: #18212b;
- background: #f4f6f8;
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Arial, sans-serif;
+ color: #111827;
+ background: #f3f4f6;
font-synthesis: none;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
- --bg: #f4f6f8;
+ --page-bg: #f3f4f6;
--surface: #ffffff;
- --surface-soft: #f8fafb;
- --border: #dde4ea;
- --border-strong: #cbd5dd;
- --text: #18212b;
- --muted: #667380;
- --accent: #1f6feb;
- --accent-soft: #edf4ff;
- --success: #16794c;
- --warn: #9a6700;
- --danger: #b42318;
- --idle: #667380;
- --shadow: 0 1px 2px rgba(16, 24, 40, 0.04);
+ --surface-muted: #f9fafb;
+ --border: #e5e7eb;
+ --border-strong: #d1d5db;
+ --text: #111827;
+ --muted: #6b7280;
+ --accent: #2563eb;
+ --accent-muted: #eff6ff;
+ --success: #15803d;
+ --warning: #a16207;
+ --danger: #b91c1c;
+ --shadow: 0 1px 2px rgba(17, 24, 39, 0.04);
}
* {
box-sizing: border-box;
}
+html,
+body,
+#app {
+ min-height: 100%;
+}
+
body {
margin: 0;
min-width: 320px;
- min-height: 100vh;
- background: var(--bg);
+ background: var(--page-bg);
color: var(--text);
+ font-size: 14px;
+ line-height: 1.5;
}
button,
@@ -46,13 +50,8 @@ button {
cursor: pointer;
}
-#app,
-.login-shell,
-.app-shell {
- min-height: 100vh;
-}
-
.login-shell {
+ min-height: 100vh;
display: grid;
place-items: center;
padding: 24px;
@@ -60,49 +59,41 @@ button {
.login-card,
.panel-card,
-.sidebar,
-.topbar {
+.shell-header,
+.tabbar {
background: var(--surface);
border: 1px solid var(--border);
box-shadow: var(--shadow);
}
.login-card {
- width: min(100%, 420px);
- padding: 28px;
- border-radius: 16px;
+ width: min(100%, 360px);
+ padding: 24px;
+ border-radius: 12px;
display: grid;
- gap: 18px;
+ gap: 20px;
}
-.eyebrow,
-.section-label {
- margin: 0;
- font-size: 0.75rem;
- font-weight: 700;
- letter-spacing: 0.08em;
- text-transform: uppercase;
- color: var(--muted);
+.login-copy {
+ display: grid;
+ gap: 4px;
}
-.login-card h1,
-.brand-block h1,
-.panel-card h2,
-.topbar h2 {
+.login-copy h1,
+.shell-title h1,
+.card-header h2,
+.modal-header h2 {
margin: 0;
- color: var(--text);
- font-size: 1.375rem;
- line-height: 1.25;
+ font-size: 20px;
+ line-height: 1.2;
font-weight: 600;
+ color: var(--text);
}
-.lede,
-.muted,
-.attention-item p,
-.sidebar-foot p,
-.service-row span,
-.user-cell span,
-.table-foot {
+.login-copy p,
+.toolbar-title p,
+.service-row p,
+.event-row p {
margin: 0;
color: var(--muted);
}
@@ -110,351 +101,367 @@ button {
.login-form,
.modal-form {
display: grid;
- gap: 14px;
+ gap: 12px;
}
.login-form label,
.modal-form label {
display: grid;
gap: 6px;
- color: var(--text);
- font-size: 0.94rem;
+ font-weight: 500;
}
.login-form input,
.modal-form input {
width: 100%;
- padding: 10px 12px;
- border-radius: 10px;
+ height: 40px;
+ padding: 0 12px;
border: 1px solid var(--border-strong);
+ border-radius: 8px;
background: var(--surface);
color: var(--text);
}
.login-form input:focus,
.modal-form input:focus {
- outline: 2px solid rgba(31, 111, 235, 0.18);
+ outline: 2px solid rgba(37, 99, 235, 0.12);
border-color: var(--accent);
}
-.login-form button,
-.action-row button,
-.modal-form button,
-.copy-link,
-.danger-link,
-.nav-item,
-.ghost-button {
- transition:
- background-color 0.15s ease,
- border-color 0.15s ease,
- color 0.15s ease;
-}
-
-.login-form button,
-.action-row button,
-.modal-form button,
-.header-actions button {
- padding: 10px 14px;
- border-radius: 10px;
+button,
+.button-secondary,
+.copy-button {
+ height: 36px;
+ padding: 0 12px;
+ border-radius: 8px;
border: 1px solid var(--accent);
background: var(--accent);
color: #ffffff;
font-weight: 600;
}
-.secondary {
- border-color: var(--border-strong) !important;
- background: var(--surface) !important;
- color: var(--text) !important;
+.button-secondary,
+.copy-button {
+ border-color: var(--border-strong);
+ background: var(--surface);
+ color: var(--text);
}
-.login-form button:hover,
-.action-row button:hover,
-.modal-form button:hover,
-.header-actions button:hover,
-.ghost-button:hover,
-.copy-link:hover,
-.danger-link:hover {
- filter: brightness(0.98);
+.copy-button.danger {
+ color: var(--danger);
}
.form-error {
margin: 0;
color: var(--danger);
- font-size: 0.92rem;
}
-.login-note {
- display: grid;
- gap: 4px;
- padding-top: 6px;
- border-top: 1px solid var(--border);
- color: var(--muted);
- font-size: 0.9rem;
-}
-
-.app-shell {
- display: grid;
- grid-template-columns: 220px minmax(0, 1fr);
- gap: 16px;
- padding: 16px;
-}
-
-.sidebar {
- border-radius: 14px;
- padding: 20px;
- display: grid;
- gap: 20px;
- align-self: start;
- position: sticky;
- top: 16px;
-}
-
-.brand-block {
- display: grid;
- gap: 4px;
-}
-
-.brand-block h1 {
- font-size: 1.12rem;
-}
-
-.nav-list {
- display: grid;
- gap: 8px;
-}
-
-.nav-item {
- padding: 10px 12px;
- border-radius: 8px;
- text-align: left;
- border: 1px solid transparent;
- background: transparent;
- color: var(--text);
- display: block;
- font-weight: 500;
-}
-
-.nav-item.active {
- background: var(--accent-soft);
- border-color: #cfe0ff;
-}
-
-.sidebar-foot {
- display: grid;
- gap: 8px;
- padding-top: 16px;
- border-top: 1px solid var(--border);
-}
-
-.content-shell {
+.shell {
+ width: min(1280px, calc(100vw - 32px));
+ margin: 16px auto;
display: grid;
gap: 16px;
}
-.topbar {
- padding: 14px 18px;
- border-radius: 14px;
+.shell-header {
+ border-radius: 12px;
+ padding: 18px 20px;
display: flex;
justify-content: space-between;
+ align-items: center;
gap: 16px;
+}
+
+.shell-title {
+ display: grid;
+ gap: 4px;
+}
+
+.shell-title p {
+ margin: 0;
+ color: var(--muted);
+}
+
+.header-meta {
+ display: grid;
+ grid-auto-flow: column;
+ gap: 24px;
align-items: center;
}
-.topbar-meta {
- display: flex;
- gap: 10px;
- flex-wrap: wrap;
- color: var(--muted);
- font-size: 0.88rem;
+.header-meta div {
+ display: grid;
+ gap: 2px;
}
-.tab-grid {
+.header-meta span {
+ color: var(--muted);
+ font-size: 12px;
+}
+
+.header-meta strong {
+ font-size: 14px;
+ font-weight: 600;
+}
+
+.tabbar {
+ display: inline-flex;
+ width: fit-content;
+ gap: 4px;
+ padding: 4px;
+ border-radius: 10px;
+}
+
+.tab-button {
+ height: 34px;
+ padding: 0 14px;
+ border: 0;
+ border-radius: 8px;
+ background: transparent;
+ color: var(--muted);
+ font-weight: 600;
+}
+
+.tab-button.active {
+ background: var(--accent-muted);
+ color: var(--accent);
+}
+
+.page-grid {
display: grid;
gap: 16px;
grid-template-columns: repeat(2, minmax(0, 1fr));
}
-.users-grid {
+.page-grid.single-column {
grid-template-columns: 1fr;
}
-.system-grid {
- grid-template-columns: minmax(280px, 1fr) minmax(0, 2fr);
+.system-grid .wide-card {
+ grid-column: 1 / -1;
}
.panel-card {
- border-radius: 14px;
+ border-radius: 12px;
padding: 18px;
display: grid;
gap: 14px;
min-width: 0;
}
-.hero-card {
- background: var(--surface);
-}
-
-.hero-status,
-.action-row,
-.stat-stack,
-.attention-item,
+.card-header,
+.table-toolbar,
+.toolbar-actions,
+.actions-row,
+.modal-header,
+.modal-actions,
.service-row,
-.system-stats {
+.service-meta {
display: flex;
- gap: 10px;
- flex-wrap: wrap;
align-items: center;
+ justify-content: space-between;
+ gap: 12px;
}
-.metric {
- font-size: 2rem;
- line-height: 1;
- color: var(--text);
- font-weight: 600;
+.card-header {
+ align-items: baseline;
}
-.sparkline-list,
-.attention-list,
+.stats-strip {
+ display: grid;
+ grid-template-columns: repeat(3, minmax(0, 1fr));
+ gap: 12px;
+}
+
+.stats-strip div {
+ display: grid;
+ gap: 4px;
+ padding: 12px;
+ border: 1px solid var(--border);
+ border-radius: 10px;
+ background: var(--surface-muted);
+}
+
+.stats-strip span,
+.kv-list dt {
+ color: var(--muted);
+}
+
+.stats-strip strong {
+ font-size: 18px;
+ line-height: 1.2;
+}
+
+.kv-list {
+ margin: 0;
+ display: grid;
+ gap: 10px;
+}
+
+.kv-list div {
+ display: grid;
+ grid-template-columns: 120px minmax(0, 1fr);
+ gap: 12px;
+}
+
+.kv-list dt,
+.kv-list dd {
+ margin: 0;
+}
+
+.usage-list,
+.event-list,
.service-list {
display: grid;
gap: 10px;
}
-.sparkline-row {
+.usage-row {
display: grid;
- grid-template-columns: 56px minmax(0, 1fr) 88px;
- gap: 10px;
+ grid-template-columns: 40px minmax(0, 1fr) 96px;
+ gap: 12px;
align-items: center;
- font-size: 0.92rem;
+}
+
+.usage-row span {
color: var(--muted);
}
-.sparkline-track {
+.usage-row strong {
+ font-size: 13px;
+ text-align: right;
+}
+
+.usage-bar {
height: 8px;
border-radius: 999px;
- background: #edf1f4;
+ background: #eceff3;
overflow: hidden;
}
-.sparkline-track div {
+.usage-bar div {
height: 100%;
- border-radius: inherit;
- background: #7aa7e8;
+ background: #94a3b8;
}
-.stat-stack {
- justify-content: space-between;
-}
-
-.stat-stack div,
-.system-stats div {
- flex: 1 1 120px;
- display: grid;
- gap: 4px;
+.event-row,
+.service-row {
padding: 12px;
border: 1px solid var(--border);
- border-radius: 12px;
- background: var(--surface-soft);
+ border-radius: 10px;
+ background: var(--surface-muted);
}
-.attention-item,
-.service-row {
- padding: 12px 14px;
- border-radius: 12px;
- border: 1px solid var(--border);
- background: var(--surface-soft);
-}
-
-.dot {
- width: 8px;
- height: 8px;
- border-radius: 999px;
- flex: none;
-}
-
-.dot.warn {
- background: #d4a72c;
-}
-
-.dot.live {
- background: #2da66a;
-}
-
-.dot.fail {
- background: #d95040;
-}
-
-.table-card,
-.config-card {
- min-width: 0;
-}
-
-.table-header {
- display: flex;
- justify-content: space-between;
+.event-row {
+ display: grid;
+ grid-template-columns: 10px minmax(0, 1fr);
gap: 12px;
align-items: start;
}
-.header-actions {
+.event-marker {
+ width: 8px;
+ height: 8px;
+ border-radius: 999px;
+ margin-top: 6px;
+}
+
+.event-marker.live {
+ background: var(--success);
+}
+
+.event-marker.warn {
+ background: var(--warning);
+}
+
+.event-marker.fail {
+ background: var(--danger);
+}
+
+.toolbar-title {
+ display: grid;
+ gap: 4px;
+}
+
+.toolbar-title h2 {
+ margin: 0;
+ font-size: 18px;
+ line-height: 1.2;
+}
+
+.toolbar-actions {
+ flex-wrap: wrap;
+}
+
+.summary-pills {
display: flex;
- gap: 10px;
+ gap: 8px;
+ flex-wrap: wrap;
+}
+
+.summary-pills span {
+ display: inline-flex;
align-items: center;
+ height: 30px;
+ padding: 0 10px;
+ border: 1px solid var(--border);
+ border-radius: 999px;
+ background: var(--surface-muted);
+ color: var(--muted);
}
.table-wrap {
overflow: auto;
border: 1px solid var(--border);
- border-radius: 12px;
+ border-radius: 10px;
}
table {
width: 100%;
+ min-width: 760px;
border-collapse: collapse;
- min-width: 700px;
background: var(--surface);
}
th,
td {
- padding: 13px 14px;
- text-align: left;
+ padding: 12px 14px;
border-bottom: 1px solid var(--border);
- font-size: 0.94rem;
+ text-align: left;
+ white-space: nowrap;
}
th {
+ background: var(--surface-muted);
color: var(--muted);
- font-size: 0.78rem;
- font-weight: 700;
+ font-size: 12px;
+ font-weight: 600;
text-transform: uppercase;
- letter-spacing: 0.05em;
- background: var(--surface-soft);
+ letter-spacing: 0.04em;
}
tbody tr:last-child td {
- border-bottom: none;
+ border-bottom: 0;
}
-.user-cell {
- display: grid;
- gap: 2px;
+.user-cell strong {
+ font-size: 14px;
}
.status-pill {
display: inline-flex;
align-items: center;
justify-content: center;
- min-height: 28px;
- padding: 4px 10px;
+ min-width: 72px;
+ height: 28px;
+ padding: 0 10px;
+ border: 1px solid currentColor;
border-radius: 999px;
- font-size: 0.72rem;
+ font-size: 11px;
font-weight: 700;
text-transform: uppercase;
- letter-spacing: 0.06em;
- border: 1px solid currentColor;
- background: #ffffff;
+ letter-spacing: 0.04em;
+ background: var(--surface);
}
.status-pill.live {
@@ -462,7 +469,7 @@ tbody tr:last-child td {
}
.status-pill.warn {
- color: var(--warn);
+ color: var(--warning);
}
.status-pill.fail {
@@ -470,140 +477,115 @@ tbody tr:last-child td {
}
.status-pill.idle {
- color: var(--idle);
-}
-
-.copy-link,
-.danger-link {
- padding: 8px 12px;
- border-radius: 10px;
- border: 1px solid var(--border-strong);
- background: var(--surface);
- color: var(--text);
-}
-
-.danger-link {
- color: var(--danger);
-}
-
-.service-row,
-.service-meta {
- justify-content: space-between;
-}
-
-.config-card {
- grid-column: 1 / -1;
-}
-
-.table-foot {
- font-size: 0.88rem;
+ color: var(--muted);
}
pre {
margin: 0;
padding: 14px;
- border-radius: 12px;
overflow: auto;
border: 1px solid var(--border);
- background: #fbfcfd;
- color: #24303c;
- font-family: Consolas, "Courier New", monospace;
- font-size: 0.88rem;
- line-height: 1.6;
+ border-radius: 10px;
+ background: #fbfbfc;
+ color: #1f2937;
+ font: 13px/1.55 Consolas, "Courier New", monospace;
}
.modal-backdrop {
position: fixed;
inset: 0;
- background: rgba(15, 23, 42, 0.18);
display: grid;
place-items: center;
- padding: 16px;
+ padding: 24px;
+ background: rgba(17, 24, 39, 0.2);
}
.modal-card {
- width: min(100%, 480px);
+ width: min(100%, 440px);
background: var(--surface);
border: 1px solid var(--border);
- border-radius: 14px;
- box-shadow: 0 12px 30px rgba(15, 23, 42, 0.12);
+ border-radius: 12px;
+ box-shadow: 0 12px 32px rgba(17, 24, 39, 0.12);
padding: 18px;
display: grid;
gap: 16px;
}
-.modal-header,
-.modal-actions {
- display: flex;
- justify-content: space-between;
- align-items: center;
- gap: 10px;
-}
-
.modal-form {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.modal-actions {
grid-column: 1 / -1;
- justify-content: end;
+ justify-content: flex-end;
}
-.ghost-button {
- padding: 8px 12px;
- border-radius: 10px;
- border: 1px solid var(--border-strong);
- background: var(--surface);
- color: var(--text);
-}
+@media (max-width: 960px) {
+ .shell {
+ width: calc(100vw - 24px);
+ margin: 12px auto;
+ gap: 12px;
+ }
-@media (max-width: 1120px) {
- .app-shell {
+ .shell-header,
+ .table-toolbar {
+ flex-direction: column;
+ align-items: flex-start;
+ }
+
+ .header-meta {
+ grid-auto-flow: row;
+ grid-template-columns: repeat(3, minmax(0, 1fr));
+ width: 100%;
+ gap: 12px;
+ }
+
+ .page-grid,
+ .stats-strip,
+ .modal-form {
grid-template-columns: 1fr;
}
- .sidebar {
- position: static;
- }
-
- .tab-grid,
- .users-grid,
- .system-grid {
- grid-template-columns: 1fr;
+ .modal-actions {
+ justify-content: stretch;
}
}
-@media (max-width: 720px) {
- .login-shell,
- .app-shell {
- padding: 12px;
- }
-
- .login-card,
- .sidebar,
- .panel-card,
- .topbar {
+@media (max-width: 640px) {
+ .login-shell {
padding: 16px;
}
- .topbar {
- flex-direction: column;
- align-items: start;
+ .shell {
+ width: calc(100vw - 16px);
+ margin: 8px auto;
}
- .header-actions,
- .modal-form,
- .modal-actions {
+ .shell-header,
+ .panel-card,
+ .login-card,
+ .modal-card {
+ padding: 16px;
+ }
+
+ .tabbar {
+ width: 100%;
+ }
+
+ .tab-button {
+ flex: 1 1 0;
+ }
+
+ .header-meta {
grid-template-columns: 1fr;
- flex-direction: column;
- align-items: stretch;
}
- .sparkline-row {
- grid-template-columns: 52px minmax(0, 1fr);
+ .usage-row {
+ grid-template-columns: 40px minmax(0, 1fr);
}
- .sparkline-row span:last-child {
+ .usage-row strong {
grid-column: 2;
+ text-align: left;
}
}