Compare commits
2 Commits
6ced7c4c71
...
e694900ca8
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e694900ca8 | ||
|
|
8006ddba75 |
@ -589,27 +589,36 @@ class AppTabbedManager {
|
||||
section.innerHTML = '<div class="updater-empty">Loading…</div>';
|
||||
await this.appUpdater.refreshAll();
|
||||
const app = (this.appUpdater.apps || []).find((x) => x.name === this.currentApp);
|
||||
if (!app) {
|
||||
section.innerHTML = `<div class="updater-empty">No update data for this app yet. <button class="updater-btn updater-btn-primary" data-updater-action="check">Check now</button></div>`;
|
||||
return;
|
||||
}
|
||||
section.innerHTML = this.renderAppUpdaterHead(app) + this.appUpdater.renderAppDetail(app);
|
||||
// Always lead with the title block + recessed dark container, so the tab
|
||||
// matches the Config / Backups / Tasks tabs whether or not there's data.
|
||||
const body = app
|
||||
? this.appUpdater.renderAppDetail(app, { includeVersion: true })
|
||||
: `<div class="updater-empty">No update data for this app yet. <button class="updater-btn updater-btn-primary" data-updater-action="check">Check now</button></div>`;
|
||||
section.innerHTML = this.renderAppUpdaterHead(app) +
|
||||
`<div class="updater-detail-container">${body}</div>`;
|
||||
}
|
||||
|
||||
// Title block for the per-app Updates tab — emoji + title + description on the
|
||||
// left, Check/Update actions on the right. Mirrors .backup-title (.config-title
|
||||
// family) so every app-detail tab shares one header idiom. `a` may be null
|
||||
// (no data yet); the Update button only appears when an update is available.
|
||||
renderAppUpdaterHead(a) {
|
||||
const up = this.appUpdater;
|
||||
const esc = (s) => up.escape(s);
|
||||
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 available</span>`
|
||||
: (a.scanned ? `<span class="updater-badge updater-badge-ok">up to date</span>` : `<span class="updater-badge updater-badge-unknown">unscanned</span>`);
|
||||
const updBtn = a.update_available
|
||||
const esc = (s) => (this.appUpdater ? this.appUpdater.escape(s) : String(s == null ? '' : s));
|
||||
const name = this.currentApp
|
||||
? (window.getAppDisplayName ? window.getAppDisplayName(this.currentApp) : this.currentApp)
|
||||
: 'this app';
|
||||
const updBtn = (a && a.update_available)
|
||||
? `<button class="updater-btn updater-btn-primary" data-updater-action="update" data-app="${esc(a.name)}">Update</button>`
|
||||
: '';
|
||||
return `<div class="app-updater-head">
|
||||
<div class="app-updater-head-main">${badge} <span class="updater-row-ver">${cur}${avail ? ` <span class="updater-arrow">→</span> <strong>${avail}</strong>` : ''}</span></div>
|
||||
<div class="app-updater-head-actions"><button class="updater-btn" data-updater-action="check">↻ Check</button> ${updBtn}</div>
|
||||
return `<div class="updater-title">
|
||||
<div class="updater-title-main">
|
||||
<h3>⬆️ Updates</h3>
|
||||
<p>Version, security and recovery for ${esc(name)}.</p>
|
||||
</div>
|
||||
<div class="updater-title-actions">
|
||||
<button class="updater-btn" data-updater-action="check">↻ Check</button>
|
||||
${updBtn}
|
||||
</div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
|
||||
@ -199,19 +199,53 @@
|
||||
#overview-view .ov-subtabs-content .tab-panel { display: none; }
|
||||
#overview-view .ov-subtabs-content .tab-panel.active { display: block; }
|
||||
|
||||
/* ---- per-app Updates tab header ----------------------------------------- */
|
||||
.app-updater-section { padding: 4px 0; }
|
||||
.app-updater-head {
|
||||
/* ---- per-app Updates tab (app-detail) ----------------------------------- */
|
||||
/* Same idiom as the Config / Backups / Tasks tabs: a title block (icon + h3 +
|
||||
description, with a bottom-border divider) followed by a recessed dark
|
||||
container holding the body. Mirrors .backup-section / .backup-title /
|
||||
.backup-snapshots-container. */
|
||||
.app-updater-section {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
flex-wrap: wrap;
|
||||
padding: 12px 14px;
|
||||
border: 1px solid var(--border-color, rgba(255, 255, 255, .12));
|
||||
border-radius: 10px;
|
||||
background: var(--input-bg, rgba(255, 255, 255, .03));
|
||||
margin-bottom: 4px;
|
||||
flex-direction: column;
|
||||
padding: 0;
|
||||
}
|
||||
.updater-title {
|
||||
padding: 20px;
|
||||
background: transparent;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
gap: 16px;
|
||||
}
|
||||
.updater-title-main { flex: 1; min-width: 0; }
|
||||
.updater-title h3 {
|
||||
margin: 0 0 8px 0;
|
||||
color: var(--text-primary, #fff);
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
}
|
||||
.updater-title p {
|
||||
margin: 0;
|
||||
color: var(--text-secondary, #ccc);
|
||||
font-size: 13px;
|
||||
}
|
||||
.updater-title-actions { display: flex; gap: 10px; align-items: center; flex-shrink: 0; }
|
||||
|
||||
/* Recessed dark panel wrapping the body — same recipe as .backup-snapshots-
|
||||
container / .tasks-container. */
|
||||
.updater-detail-container {
|
||||
padding: 4px 16px;
|
||||
margin: 16px;
|
||||
background: rgba(var(--bg-rgb), 0.2);
|
||||
border-radius: 8px;
|
||||
}
|
||||
/* Inside the container the Version / Security / Recovery / History sections are
|
||||
separated by divider lines (the fleet row-details, not in this container, keep
|
||||
their gap-only spacing). */
|
||||
.updater-detail-container .updater-detail { gap: 0; padding-top: 0; }
|
||||
.updater-detail-container .updater-detail-section { padding: 16px 0; }
|
||||
.updater-detail-container .updater-detail-section:first-child { padding-top: 0; }
|
||||
.updater-detail-container .updater-detail-section + .updater-detail-section {
|
||||
border-top: 1px solid var(--border-color);
|
||||
}
|
||||
.app-updater-head-main { display: flex; align-items: center; gap: 10px; }
|
||||
.app-updater-head-actions { display: flex; gap: 8px; }
|
||||
|
||||
@ -386,8 +386,21 @@ class UpdaterPage {
|
||||
// verbatim as the fleet Updates expander body (overview-manager.js) AND the
|
||||
// per-app Updater tab. Action buttons keep the data-updater-action/data-app
|
||||
// contract, so whichever delegated handler is in scope drives them.
|
||||
renderAppDetail(app) {
|
||||
renderAppDetail(app, opts = {}) {
|
||||
const a = app || {};
|
||||
// Optional leading "Version" section — the per-app Updates tab shows the
|
||||
// current/available version + status badge as a section in the panel. The
|
||||
// fleet rows omit it (the row head already shows the version).
|
||||
let versionSection = '';
|
||||
if (opts.includeVersion) {
|
||||
const cur = this.escape(a.current_version || a.current_image || '—');
|
||||
const avail = a.update_available ? this.escape(a.available_version || a.available_image || 'newer') : null;
|
||||
const badge = a.update_available
|
||||
? `<span class="updater-badge updater-badge-update">update available</span>`
|
||||
: (a.scanned ? `<span class="updater-badge updater-badge-ok">up to date</span>` : `<span class="updater-badge updater-badge-unknown">unscanned</span>`);
|
||||
versionSection = `<div class="updater-detail-section"><h4>Version</h4>
|
||||
<div class="updater-detail-row">${badge} <span class="updater-row-ver">${cur}${avail ? ` <span class="updater-arrow">→</span> <strong>${avail}</strong>` : ''}</span></div></div>`;
|
||||
}
|
||||
const cves = a.cves || [];
|
||||
const cveItems = cves.map((c) => `
|
||||
<div class="updater-cve sev-${(c.severity || 'low').toLowerCase()}">
|
||||
@ -423,7 +436,7 @@ class UpdaterPage {
|
||||
<span class="updater-detail-meta">${this.escape(e.from || '')}${e.to ? ` → ${this.escape(e.to)}` : ''}</span>
|
||||
<span class="updater-detail-meta">${this.fmtRel(e.ts)}</span></div>`).join('')}</div>` : '';
|
||||
|
||||
return `<div class="updater-detail">${security}${recovery}${history}</div>`;
|
||||
return `<div class="updater-detail">${versionSection}${security}${recovery}${history}</div>`;
|
||||
}
|
||||
|
||||
empty(msg, withCheck) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user