feat(overview): tasks-style app rows on the Updates tab
Redesign the Fleet Overview Updates list rows to match the Tasks page:
each app now shows its icon tile, a status pill ('✓ Up to date' /
'↑ Update available' / '• Unscanned'), and a dedicated 'Details' toggle
button (chevron) in the row actions instead of the bare leading chevron.
Resolve the app icon from the slug (/core/icons/apps/<slug>.svg, hidden on
error) and prefer window.getAppDisplayName for a prettier title.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
423f3163a9
commit
8adcb140e5
@ -195,17 +195,67 @@
|
||||
padding: 12px 14px;
|
||||
}
|
||||
.ov-row-head .updater-row-ver { margin-left: auto; }
|
||||
.ov-row-head .ov-row-actions { margin-left: 12px; }
|
||||
.ov-chevron {
|
||||
flex: 0 0 auto;
|
||||
display: inline-block;
|
||||
font-size: .8rem;
|
||||
opacity: .7;
|
||||
transition: transform .18s ease;
|
||||
}
|
||||
.ov-row-head[aria-expanded="true"] .ov-chevron { transform: rotate(90deg); }
|
||||
.ov-row-head .ov-row-actions { margin-left: 12px; display: flex; align-items: center; gap: 8px; }
|
||||
.ov-row-head:focus-visible { outline: 2px solid rgba(var(--page-updater-rgb), .7); outline-offset: -2px; }
|
||||
|
||||
/* Tasks-style app row: icon tile + status pill + Details toggle -------------- */
|
||||
.ov-row-icon {
|
||||
flex: 0 0 auto;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 8px;
|
||||
object-fit: contain;
|
||||
padding: 3px;
|
||||
border: 1px solid var(--border-color, rgba(255, 255, 255, .14));
|
||||
background: rgba(255, 255, 255, .04);
|
||||
}
|
||||
.ov-status {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
padding: 2px 9px;
|
||||
border-radius: 999px;
|
||||
font-size: .72rem;
|
||||
font-weight: 600;
|
||||
letter-spacing: .02em;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.ov-status-ok {
|
||||
background: rgba(var(--status-success-rgb), .18);
|
||||
border: 1px solid rgba(var(--status-success-rgb), .45);
|
||||
color: #86efac;
|
||||
}
|
||||
.ov-status-update {
|
||||
background: rgba(var(--status-warning-rgb), .18);
|
||||
border: 1px solid rgba(var(--status-warning-rgb), .5);
|
||||
color: #fcd34d;
|
||||
}
|
||||
.ov-status-unknown {
|
||||
background: rgba(255, 255, 255, .06);
|
||||
border: 1px solid rgba(255, 255, 255, .16);
|
||||
color: var(--text-muted, rgba(255, 255, 255, .6));
|
||||
}
|
||||
.ov-details-btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
padding: 4px 10px;
|
||||
border-radius: 6px;
|
||||
border: 1px solid var(--border-color, rgba(255, 255, 255, .14));
|
||||
background: rgba(255, 255, 255, .04);
|
||||
color: var(--text-secondary, rgba(255, 255, 255, .7));
|
||||
font-size: .72rem;
|
||||
cursor: pointer;
|
||||
transition: background .15s ease, border-color .15s ease, color .15s ease;
|
||||
}
|
||||
.ov-details-btn:hover {
|
||||
background: rgba(var(--page-updater-rgb), .16);
|
||||
border-color: rgba(var(--page-updater-rgb), .5);
|
||||
color: #fff;
|
||||
}
|
||||
.ov-details-btn svg { transition: transform .18s ease; }
|
||||
.ov-row-head[aria-expanded="true"] .ov-details-btn svg { transform: rotate(180deg); }
|
||||
|
||||
.ov-row-details { display: none; padding: 4px 16px 16px; border-top: 1px solid var(--border-color, rgba(255, 255, 255, .1)); }
|
||||
.ov-row-details.ov-open { display: block; }
|
||||
|
||||
|
||||
@ -538,23 +538,25 @@ class OverviewManager {
|
||||
|
||||
updateRow(a) {
|
||||
const esc = (s) => this.escape(s);
|
||||
const slug = esc(a.name);
|
||||
const name = esc((window.getAppDisplayName ? window.getAppDisplayName(a.name) : null) || a.displayName || a.name);
|
||||
const cur = esc(a.current_version || a.current_image || '—');
|
||||
const avail = a.update_available ? esc(a.available_version || a.available_image || 'newer') : null;
|
||||
const badge = a.update_available
|
||||
? `<span class="updater-badge updater-badge-update">update</span>`
|
||||
: (a.scanned ? `<span class="updater-badge updater-badge-ok">up to date</span>` : `<span class="updater-badge updater-badge-unknown">unscanned</span>`);
|
||||
const status = a.update_available
|
||||
? `<span class="ov-status ov-status-update">↑ Update available</span>`
|
||||
: (a.scanned ? `<span class="ov-status ov-status-ok">✓ Up to date</span>` : `<span class="ov-status ov-status-unknown">• Unscanned</span>`);
|
||||
const sev = a.worstSeverity ? `<span class="updater-badge sev-${a.worstSeverity}">${a.worstSeverity}</span>` : '';
|
||||
const updBtn = a.update_available
|
||||
? `<button class="updater-btn updater-btn-primary" data-updater-action="update" data-app="${esc(a.name)}">Update</button>`
|
||||
? `<button class="updater-btn updater-btn-primary" data-updater-action="update" data-app="${slug}">Update</button>`
|
||||
: '';
|
||||
return `<div class="updater-row ov-row" data-app="${esc(a.name)}">
|
||||
<div class="updater-row-head ov-row-head" data-overview-action="toggle" data-app="${esc(a.name)}" role="button" tabindex="0" aria-expanded="false" aria-controls="ov-detail-${esc(a.name)}">
|
||||
<span class="ov-chevron" aria-hidden="true">▸</span>
|
||||
<span class="updater-row-name">${esc(a.displayName)}</span> ${badge} ${sev}
|
||||
return `<div class="updater-row ov-row" data-app="${slug}">
|
||||
<div class="updater-row-head ov-row-head" data-overview-action="toggle" data-app="${slug}" role="button" tabindex="0" aria-expanded="false" aria-controls="ov-detail-${slug}">
|
||||
<img class="ov-row-icon" src="/core/icons/apps/${slug}.svg" alt="" onerror="this.style.display='none'">
|
||||
<span class="updater-row-name">${name}</span> ${status} ${sev}
|
||||
<span class="updater-row-ver">${cur}${avail ? ` <span class="updater-arrow">→</span> <strong>${avail}</strong>` : ''}</span>
|
||||
<span class="ov-row-actions">${updBtn}</span>
|
||||
<span class="ov-row-actions">${updBtn}<button class="ov-details-btn" data-overview-action="toggle" data-app="${slug}" tabindex="-1" aria-hidden="true"><svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="6,9 12,15 18,9"></polyline></svg><span>Details</span></button></span>
|
||||
</div>
|
||||
<div class="updater-row-details ov-row-details" id="ov-detail-${esc(a.name)}" role="region" aria-label="${esc(a.displayName)} details" hidden></div>
|
||||
<div class="updater-row-details ov-row-details" id="ov-detail-${slug}" role="region" aria-label="${name} details" hidden></div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user