feat: jobs system with dedicated nav page and run history
Replaces ad-hoc Tailscale config tracking with a proper jobs system. Jobs get their own nav page (master/detail layout), a dedicated DB table, and full run history persisted forever. Tailscale connection settings move from the Settings modal into the Jobs page. Registry pattern makes adding future jobs straightforward. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
87
css/app.css
87
css/app.css
@@ -763,4 +763,91 @@ select:focus { border-color: var(--accent); }
|
||||
|
||||
/* Toast — stretch across bottom */
|
||||
.toast { right: 16px; left: 16px; bottom: 16px; }
|
||||
|
||||
/* Jobs — stack sidebar above detail */
|
||||
.jobs-layout { grid-template-columns: 1fr; }
|
||||
.jobs-sidebar { border-right: none; border-bottom: 1px solid var(--border); }
|
||||
}
|
||||
|
||||
/* ── JOBS PAGE ───────────────────────────────────────────────────────────────── */
|
||||
|
||||
.jobs-layout {
|
||||
display: grid;
|
||||
grid-template-columns: 220px 1fr;
|
||||
height: calc(100vh - 48px);
|
||||
}
|
||||
.jobs-sidebar {
|
||||
border-right: 1px solid var(--border);
|
||||
overflow-y: auto;
|
||||
}
|
||||
.jobs-sidebar-title {
|
||||
padding: 16px 16px 8px;
|
||||
font-size: 10px;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.1em;
|
||||
color: var(--text3);
|
||||
}
|
||||
.job-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding: 12px 16px;
|
||||
cursor: pointer;
|
||||
border-bottom: 1px solid var(--border);
|
||||
user-select: none;
|
||||
}
|
||||
.job-item:hover, .job-item.active { background: var(--bg2); }
|
||||
.job-item-name { font-size: 13px; color: var(--text); }
|
||||
.jobs-detail {
|
||||
padding: 28px 32px;
|
||||
overflow-y: auto;
|
||||
max-width: 600px;
|
||||
}
|
||||
.jobs-detail-hd { margin-bottom: 20px; }
|
||||
.jobs-detail-title { font-size: 17px; font-weight: 600; color: var(--text); }
|
||||
.jobs-detail-desc { font-size: 12px; color: var(--text2); margin-top: 4px; line-height: 1.6; }
|
||||
.job-actions { display: flex; gap: 8px; margin: 16px 0 0; }
|
||||
.jobs-placeholder { padding: 48px 32px; color: var(--text3); font-size: 13px; }
|
||||
|
||||
/* Shared job status dot */
|
||||
.job-dot {
|
||||
width: 7px;
|
||||
height: 7px;
|
||||
border-radius: 50%;
|
||||
flex-shrink: 0;
|
||||
display: inline-block;
|
||||
}
|
||||
.job-dot--success { background: var(--accent); }
|
||||
.job-dot--error { background: var(--red); }
|
||||
.job-dot--running { background: var(--amber); animation: pulse 2s ease-in-out infinite; }
|
||||
.job-dot--none { background: var(--border2); }
|
||||
|
||||
/* Run history list */
|
||||
.run-item {
|
||||
display: grid;
|
||||
grid-template-columns: 10px 1fr 60px 1fr;
|
||||
gap: 0 12px;
|
||||
padding: 7px 0;
|
||||
border-bottom: 1px solid var(--border);
|
||||
font-size: 12px;
|
||||
align-items: baseline;
|
||||
}
|
||||
.run-item:last-child { border-bottom: none; }
|
||||
.run-time { color: var(--text3); }
|
||||
.run-status { color: var(--text2); }
|
||||
.run-result { color: var(--text); }
|
||||
.run-empty { color: var(--text3); font-size: 12px; padding: 8px 0; }
|
||||
|
||||
/* Nav dot */
|
||||
.nav-job-dot {
|
||||
display: inline-block;
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-radius: 50%;
|
||||
margin-left: 5px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.nav-job-dot--success { background: var(--accent); }
|
||||
.nav-job-dot--error { background: var(--red); }
|
||||
.nav-job-dot--none { display: none; }
|
||||
|
||||
Reference in New Issue
Block a user