diff --git a/frontend/packages/dashboard/src/dashboard.css b/frontend/packages/dashboard/src/dashboard.css index a0d0e04c..dc5dd382 100644 --- a/frontend/packages/dashboard/src/dashboard.css +++ b/frontend/packages/dashboard/src/dashboard.css @@ -1181,6 +1181,14 @@ footer .banner-thin { display: inline-block; padding: 0 4px 1px; } +/* Column for an agent no longer in the live roster — its past schedules + linger but the agent is gone. Greyed + struck through so the operator + can still see (and cancel) the stale entries rather than be surprised. */ +.schedules-table-agent-th--gone > div { + color: var(--muted); + text-decoration: line-through; + opacity: 0.75; +} .schedules-table-row-cancelled td { opacity: 0.55; } .schedules-table-body-cell { max-width: 30em; diff --git a/frontend/packages/dashboard/src/schedules.js b/frontend/packages/dashboard/src/schedules.js index 7b2eabb3..b396f4d8 100644 --- a/frontend/packages/dashboard/src/schedules.js +++ b/frontend/packages/dashboard/src/schedules.js @@ -619,9 +619,16 @@ function renderSchedulesTableHead(agents) { el('th', {}, 'owner'), el('th', { class: 'schedules-table-body-th' }, 'body'), ); + // 'operator' is always a valid target; every other column maps to a + // container, so flag any column whose agent is no longer in the live + // roster — its past schedules linger in the table but the agent is gone. + const liveNames = new Set(Array.from(containersState.values()).map((c) => c.name)); for (const a of agents) { - headerRow.append(el('th', { class: 'schedules-table-agent-th', title: a }, - el('div', {}, el('span', {}, a)))); + const gone = a !== 'operator' && !liveNames.has(a); + headerRow.append(el('th', { + class: 'schedules-table-agent-th' + (gone ? ' schedules-table-agent-th--gone' : ''), + title: gone ? a + ' — no longer exists (gone)' : a, + }, el('div', {}, el('span', {}, a)))); } headerRow.append(el('th', { class: 'schedules-table-actions-th' }, '')); thead.append(headerRow);