feat: job-level Est. completion in drawer header (1.0.0-53)
The drawer's per-stage Finish chip is the stage's finish, not the
whole burn-in's. Added a right-aligned "Est. completion" pill in
the drawer-job-header that uses the server-weighted burnin.percent
to extrapolate the whole job's finish time (covers precheck + SMART
+ surface_validate + final_check).
Suppressed under 0.5% job progress to avoid the early-sample
overshoot we saw earlier ("Finish: Sep 22" on a fresh start).
Bind-mount only (templates + static); no rebuild needed. Running
container reports 1.0.0-52 until next rebuild; this commit just
catches the source version up.
This commit is contained in:
parent
c5a41d0260
commit
c906ab15f7
3 changed files with 59 additions and 2 deletions
|
|
@ -86,7 +86,7 @@ class Settings(BaseSettings):
|
||||||
ssh_key: str = "" # PEM private key content (paste full key including headers)
|
ssh_key: str = "" # PEM private key content (paste full key including headers)
|
||||||
|
|
||||||
# Application version — used by the /api/v1/updates/check endpoint
|
# Application version — used by the /api/v1/updates/check endpoint
|
||||||
app_version: str = "1.0.0-52"
|
app_version: str = "1.0.0-53"
|
||||||
|
|
||||||
# ---- Authentication (1.0.0-22) ----
|
# ---- Authentication (1.0.0-22) ----
|
||||||
# session_secret: HMAC key for signing session cookies. Empty = generate
|
# session_secret: HMAC key for signing session cookies. Empty = generate
|
||||||
|
|
|
||||||
|
|
@ -2939,3 +2939,37 @@ th.sort-desc::after {
|
||||||
.stage-reason-failed .stage-reason-text { color: var(--red, #f85149); }
|
.stage-reason-failed .stage-reason-text { color: var(--red, #f85149); }
|
||||||
.stage-reason-cancelled .stage-reason-text,
|
.stage-reason-cancelled .stage-reason-text,
|
||||||
.stage-reason-unknown .stage-reason-text { color: var(--yellow, #d29922); }
|
.stage-reason-unknown .stage-reason-text { color: var(--yellow, #d29922); }
|
||||||
|
|
||||||
|
/* -----------------------------------------------------------------------
|
||||||
|
Drawer job-level estimated completion (right-aligned in the header,
|
||||||
|
so it doesn't compete with the state chip + operator info).
|
||||||
|
----------------------------------------------------------------------- */
|
||||||
|
.drawer-job-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
.drawer-job-finish {
|
||||||
|
margin-left: auto;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: baseline;
|
||||||
|
gap: 8px;
|
||||||
|
padding: 4px 10px;
|
||||||
|
background: var(--bg-soft, #161b22);
|
||||||
|
border: 1px solid var(--border, #30363d);
|
||||||
|
border-radius: 5px;
|
||||||
|
font-family: "SF Mono", "Consolas", monospace;
|
||||||
|
}
|
||||||
|
.drawer-job-finish-label {
|
||||||
|
font-size: 9px;
|
||||||
|
color: var(--text-muted);
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: .04em;
|
||||||
|
}
|
||||||
|
.drawer-job-finish-value {
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--text-strong, #f0f6fc);
|
||||||
|
font-weight: 500;
|
||||||
|
font-variant-numeric: tabular-nums;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1544,7 +1544,30 @@
|
||||||
html += '<span class="drawer-job-meta">';
|
html += '<span class="drawer-job-meta">';
|
||||||
if (burnin.operator) html += 'by ' + _esc(burnin.operator);
|
if (burnin.operator) html += 'by ' + _esc(burnin.operator);
|
||||||
if (burnin.started_at) html += ' \u00b7 ' + _drawerFmtDt(burnin.started_at);
|
if (burnin.started_at) html += ' \u00b7 ' + _drawerFmtDt(burnin.started_at);
|
||||||
html += '</span></div>';
|
html += '</span>';
|
||||||
|
// Job-level estimated completion. Uses the weighted overall job %
|
||||||
|
// (recalculated server-side from stage progress) so it reflects
|
||||||
|
// every stage, not just the current one. Suppressed under 0.5%
|
||||||
|
// so the early sample doesn't paint a "Finish: Sep 22" stutter.
|
||||||
|
if (burnin.state === 'running' && burnin.started_at) {
|
||||||
|
var jobPct = parseFloat(burnin.percent || 0);
|
||||||
|
if (jobPct >= 0.5) {
|
||||||
|
var jobStartMs = Date.parse(burnin.started_at);
|
||||||
|
var jobElapsedSec = Math.max(0, (Date.now() - jobStartMs) / 1000);
|
||||||
|
var jobTotalSec = jobElapsedSec * (100 / jobPct);
|
||||||
|
var jobRemainSec = Math.max(0, jobTotalSec - jobElapsedSec);
|
||||||
|
var jobFinish = new Date(Date.now() + jobRemainSec * 1000);
|
||||||
|
var jobFinishStr = jobFinish.toLocaleString(undefined, {
|
||||||
|
weekday: 'short', month: 'short', day: 'numeric',
|
||||||
|
hour: 'numeric', minute: '2-digit',
|
||||||
|
});
|
||||||
|
html += '<span class="drawer-job-finish" title="Estimated completion of the entire burn-in (all stages)">';
|
||||||
|
html += '<span class="drawer-job-finish-label">Est. completion</span>';
|
||||||
|
html += '<span class="drawer-job-finish-value">' + jobFinishStr + '</span>';
|
||||||
|
html += '</span>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
html += '</div>';
|
||||||
|
|
||||||
html += '<div class="drawer-stages">';
|
html += '<div class="drawer-stages">';
|
||||||
var stages = burnin.stages || [];
|
var stages = burnin.stages || [];
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue