ui: split /hosts/{id} into host page + /runs/{runID} run page
Host page owns host metadata, full runs table with per-row stage strip, in-flight banner, and empty-state CTA. Run page owns pipeline, active step, logs, sub-steps, spec diffs, and hold banner with a breadcrumb back to the host. Dashboard tile reverts to host-only. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
+121
-92
@@ -822,59 +822,6 @@ body.bare main { max-width: none; }
|
||||
.log-line.log-debug { opacity: .6; }
|
||||
.log-line.log-hit { background: rgba(228,169,75,.08); }
|
||||
|
||||
.runs-sidebar {
|
||||
background: var(--bg-elev);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius);
|
||||
padding: 12px;
|
||||
position: sticky;
|
||||
top: 16px;
|
||||
max-height: calc(100vh - 32px);
|
||||
overflow-y: auto;
|
||||
}
|
||||
.runs-sidebar-heading {
|
||||
margin: 0 0 10px;
|
||||
font-size: 12px;
|
||||
color: var(--text-dim);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: .5px;
|
||||
font-weight: 600;
|
||||
}
|
||||
.runs-sidebar-empty { color: var(--text-dim); font-size: 13px; margin: 0; }
|
||||
.runs-sidebar-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 2px; }
|
||||
.runs-sidebar-item a {
|
||||
display: grid;
|
||||
grid-template-columns: 16px auto 1fr auto;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 6px 8px;
|
||||
border-radius: 6px;
|
||||
color: var(--text);
|
||||
text-decoration: none;
|
||||
font-size: 12px;
|
||||
}
|
||||
.runs-sidebar-item a:hover { background: var(--bg-elev-2); text-decoration: none; }
|
||||
.runs-sidebar-active a { background: rgba(60,130,246,.12); border: 1px solid rgba(60,130,246,.5); }
|
||||
.runs-sidebar-dot {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
border-radius: 50%;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 9px;
|
||||
font-weight: 700;
|
||||
background: var(--bg-elev-2);
|
||||
border: 1px solid var(--border);
|
||||
color: var(--text-dim);
|
||||
}
|
||||
.runs-sidebar-dot-pass { background: var(--success); border-color: var(--success); color: #0b0d12; }
|
||||
.runs-sidebar-dot-fail { background: var(--danger); border-color: var(--danger); color: #fff; }
|
||||
.runs-sidebar-dot-active { background: var(--accent-strong); border-color: var(--accent); color: #fff; }
|
||||
.runs-sidebar-id { font-family: var(--mono); font-weight: 600; }
|
||||
.runs-sidebar-started { color: var(--text-dim); }
|
||||
.runs-sidebar-duration { font-family: var(--mono); color: var(--text-dim); font-size: 11px; }
|
||||
|
||||
.btn-primary {
|
||||
background: var(--accent-strong);
|
||||
border-color: var(--accent-strong);
|
||||
@@ -888,55 +835,137 @@ body.bare main { max-width: none; }
|
||||
}
|
||||
.btn-danger:hover { background: rgba(229,100,102,.1); }
|
||||
|
||||
/* ---------- Dashboard tile mini run-view (Phase 3) ---------------- */
|
||||
/* ---------- Host page (/hosts/{id}) ------------------------------- */
|
||||
|
||||
/* Small variant of stage-dot for the compact step list. Same colour
|
||||
rules as the full-size pipeline dot so operators read one language
|
||||
everywhere; only the geometry shrinks. */
|
||||
/* Small variant of stage-dot, reused by the runs-table stage-strip so
|
||||
per-row progress reads with the same visual language as the pipeline
|
||||
on the run page. Kept lean — no borders, no glyphs, just colour. */
|
||||
.stage-dot-sm {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
font-size: 9px;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
font-size: 0;
|
||||
border-width: 1px;
|
||||
flex-shrink: 0;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.tile-meta-row {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: baseline;
|
||||
font-size: 12px;
|
||||
color: var(--text-dim);
|
||||
padding: 4px 0 6px;
|
||||
}
|
||||
.tile-run-id { font-variant-numeric: tabular-nums; }
|
||||
.tile-run-duration { margin-left: auto; font-variant-numeric: tabular-nums; }
|
||||
.host-page { display: flex; flex-direction: column; gap: 12px; }
|
||||
|
||||
.tile-steplist {
|
||||
list-style: none;
|
||||
margin: 0 0 8px;
|
||||
padding: 0;
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 2px 10px;
|
||||
.host-summary {
|
||||
background: var(--bg-elev);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius);
|
||||
padding: 14px 16px;
|
||||
}
|
||||
.tile-steplist .tile-step {
|
||||
.host-summary-head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
font-size: 11px;
|
||||
line-height: 1.4;
|
||||
color: var(--text-dim);
|
||||
min-width: 0;
|
||||
gap: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.tile-steplist .tile-step-name {
|
||||
.host-summary-name { margin: 0; font-size: 22px; font-weight: 600; }
|
||||
.host-summary-meta {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
|
||||
gap: 6px 20px;
|
||||
margin: 0 0 6px;
|
||||
font-size: 13px;
|
||||
}
|
||||
.host-summary-meta dt { color: var(--text-dim); font-size: 11px; text-transform: uppercase; letter-spacing: .4px; }
|
||||
.host-summary-meta dd { margin: 0; font-family: var(--mono); }
|
||||
.host-summary-notes { margin-top: 8px; }
|
||||
.host-summary-notes h3 { margin: 0 0 4px; font-size: 12px; color: var(--text-dim); text-transform: uppercase; letter-spacing: .4px; }
|
||||
.host-summary-spec summary { cursor: pointer; color: var(--text-dim); font-size: 12px; }
|
||||
.host-summary-spec-yaml {
|
||||
background: var(--bg);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 6px;
|
||||
padding: 8px 10px;
|
||||
font-family: var(--mono);
|
||||
font-size: 12px;
|
||||
overflow-x: auto;
|
||||
margin: 6px 0 0;
|
||||
}
|
||||
|
||||
.host-actions { padding: 0; }
|
||||
.host-actions-row { display: flex; gap: 10px; flex-wrap: wrap; align-items: center; }
|
||||
.host-nd-toggle { display: inline-flex; gap: 6px; align-items: center; color: var(--text-dim); font-size: 13px; }
|
||||
|
||||
.in-flight-banner-wrap { display: contents; }
|
||||
.in-flight-banner {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
background: rgba(60,130,246,.12);
|
||||
border: 1px solid rgba(60,130,246,.5);
|
||||
border-radius: var(--radius);
|
||||
padding: 10px 14px;
|
||||
color: var(--text);
|
||||
text-decoration: none;
|
||||
font-size: 14px;
|
||||
}
|
||||
.in-flight-banner:hover { background: rgba(60,130,246,.20); text-decoration: none; }
|
||||
.in-flight-label { font-weight: 600; }
|
||||
.in-flight-state { color: var(--text-dim); font-family: var(--mono); }
|
||||
.in-flight-open { margin-left: auto; color: var(--accent); }
|
||||
|
||||
.host-empty-state {
|
||||
text-align: center;
|
||||
background: var(--bg-elev);
|
||||
border: 1px dashed var(--border);
|
||||
border-radius: var(--radius);
|
||||
padding: 40px 20px;
|
||||
}
|
||||
.host-empty-title { font-size: 18px; font-weight: 600; margin: 0 0 4px; }
|
||||
.host-empty-sub { color: var(--text-dim); margin: 0 0 16px; font-size: 13px; }
|
||||
.btn-primary.big { font-size: 15px; padding: 10px 20px; }
|
||||
|
||||
.host-runs { }
|
||||
.host-runs h2 { font-size: 14px; color: var(--text-dim); text-transform: uppercase; letter-spacing: .4px; margin: 0 0 8px; }
|
||||
|
||||
.runs-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
background: var(--bg-elev);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius);
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
font-size: 13px;
|
||||
}
|
||||
/* Passed/failed/running steps keep full-strength text so the eye jumps
|
||||
to active work; pending/skipped fade back into the background. */
|
||||
.tile-step-passed .tile-step-name,
|
||||
.tile-step-failed .tile-step-name,
|
||||
.tile-step-running .tile-step-name { color: var(--text); }
|
||||
.tile-step-skipped { opacity: .5; }
|
||||
.runs-table thead th {
|
||||
text-align: left;
|
||||
padding: 8px 10px;
|
||||
color: var(--text-dim);
|
||||
font-size: 11px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: .4px;
|
||||
font-weight: 600;
|
||||
border-bottom: 1px solid var(--border);
|
||||
background: var(--bg-elev-2);
|
||||
}
|
||||
.runs-table tbody td {
|
||||
padding: 8px 10px;
|
||||
border-top: 1px solid var(--border);
|
||||
vertical-align: middle;
|
||||
}
|
||||
.runs-table tbody tr:first-child td { border-top: none; }
|
||||
.runs-table tbody tr:hover { background: var(--bg-elev-2); }
|
||||
.runs-row-live { background: rgba(60,130,246,.08); }
|
||||
.runs-row-live:hover { background: rgba(60,130,246,.14); }
|
||||
.runs-col-id a { font-family: var(--mono); font-weight: 600; color: var(--text); text-decoration: none; }
|
||||
.runs-col-id a:hover { color: var(--accent); }
|
||||
.runs-col-started, .runs-col-duration { color: var(--text-dim); font-family: var(--mono); white-space: nowrap; }
|
||||
.runs-open-link { color: var(--accent); text-decoration: none; font-size: 12px; white-space: nowrap; }
|
||||
.runs-open-link:hover { text-decoration: underline; }
|
||||
|
||||
.stage-strip {
|
||||
display: inline-flex;
|
||||
gap: 3px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
/* ---------- Run page (/runs/{runID}) ------------------------------ */
|
||||
|
||||
.run-page { display: flex; flex-direction: column; gap: 12px; }
|
||||
.run-body { display: flex; flex-direction: column; gap: 10px; }
|
||||
.run-header-name { margin: 0; font-size: 20px; font-weight: 600; }
|
||||
|
||||
Reference in New Issue
Block a user