16 Commits

Author SHA1 Message Date
librelad
7db2a707b2 refactor(overview): turn the fleet Overview tab into an action board
Replace the stat-tile grid with a needs-attention board: a health hero on
top (one big status circle — green all-clear / amber something-to-do /
neutral pre-scan — matching the admin dashboard's dot language) over one
status row per area (updates, security, improvements, backups). Rows that
want a decision are tinted with their area hue and carry their action
buttons inline (Review / Update all / Open Backups); healthy areas
collapse to a quiet neutral one-liner. An Everything / Needs action chip
pair filters the board down to just the actionable rows.

Board rows deep-link with intent: Security lands on the Updates tab
pre-filtered to the affected apps via a new data-filter hop on the goto
action. backupSummary() now splits never-backed-up from week-stale apps
so the backup row can say which it is.

Signed-off-by: librelad <librelad@digitalangels.vip>
2026-06-12 22:33:23 +01:00
librelad
fa47e16cab feat(updater): automatic background scan for versions, CVEs & improvements
Replace the click-to-scan-only flow with a self-throttled auto-scan that
rides the existing task-processor idle poll (the same shape as the
network-drift check — no new daemon, unit, or endpoint):

- 'libreportal updater check auto' gates on the age of the generated
  updates.json vs CFG_UPDATER_SCAN_INTERVAL (minutes, default 30,
  0 disables); a fresh file makes the 60s tick a single stat() + return.
  Manual checks and post-update rescans reset the clock for free, and a
  missing file means the first scan runs ~a minute after install.
- Eligible signed hotfixes keep flowing through artifactApplyAuto, which
  only enqueues ordinary tasks — mutations stay on the task path.
- Open updater surfaces (standalone /updater and the fleet Overview's
  headless UpdaterPage) follow along with a 60s static-JSON re-read that
  repaints only when a generated_at stamp changed; timer released via
  dispose() on unmount, ticks skipped while hidden.
- Empty states now say the first scan happens automatically; Check now
  stays as the immediate manual override.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-06-12 22:07:42 +01:00
librelad
a06b6cd1d8 feat(overview): match fleet tab content to the app-detail tab layout
Every fleet Overview tab now follows the per-app detail tab idiom the
rest of the app uses: title + description on the left, action buttons
on the right, a divider underneath, and the body inside the recessed
dark container (.tasks-container recipe).

- renderHeader() gains an action slot; Check/Check now/Update all move
  out of in-body toolbars into the header (Updates keeps its filter
  chips in the body; the Apps-tracked stat card drops its duplicate
  Check button; UpdaterPage.renderImprovements can skip its toolbar).
- String tabs wrap their body in .ov-tab-body — margin/padding 16px,
  rgba(bg,.2) panel — mirroring backup/tasks/updater containers.
- The Backups tab's embedded nested strip (Dashboard/Backups/Locations/
  Configuration) now sits on the same surface as every other tab strip:
  added to the nebula sidebar-bg anchor rule (it was stuck on the
  lighter --hover-bg) and its buttons use .main-tab-button type.

Signed-off-by: librelad <librelad@digitalangels.vip>

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-12 18:55:10 +01:00
librelad
25dc51d63e fix(webui): make Overview sub-tab areas match the app-detail layout
Migrate and Backups detached their sub-tab strip from the content (16px gap)
and Backups had no top-level header and a transparent (card-less) body — so
both read as different from the per-app Config sub-tabs.

- Migrate: nest .tabs-content inside the same .tabs-wrapper as .tabs-list (the
  canonical app-detail structure) and drop the gap, so the strip joins the
  content card with no space and clean corners.
- Backups: inject the shared "Backups" .config-title header above the embedded
  BackupPage; drop the sub-tab strip's bottom gap; give the content (.main) the
  connected .tabs-content surface (card bg + rounded bottom) and round the
  strip's outer top corners — so it reads as one tabs-within-tabs unit.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 00:23:10 +01:00
librelad
4d54d6a9b0 feat(webui): unify Overview tab headers as in-content, app-detail style
Drop the floating fleet header; every Overview tab now renders its heading
inside the pane, under the tab strip, in the per-app detail .config-title
format (emoji + title + description) — matching the Migrate tab.

Introduces a small modular system so the area can't drift:
- OV_TAB_META is the single source of truth for each tab's icon/title/blurb.
- renderHeader(id) is the only thing that turns it into markup; renderTab()
  prepends it for the string tabs and mountMigrate() injects it once for the
  static Migrate pane. Body renderers now only ever produce the body.

Retires the now-dead floating-header plumbing: updateHeader(), the
ov-backups-active/ov-migrate-active hide toggles + CSS, and the .overview-header
rules. The tabbed interface owns the top padding the header used to provide.

Backups is the documented exception — it embeds the full BackupPage, which
supplies its own header.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-02 23:29:16 +01:00
librelad
f6f29bf68b feat(webui): match Migrate tab to app-detail tab design
Give the fleet Overview › Migrate tab the same in-content header + sub-tab
styling the per-app detail tabs use:

- Add a .config-title header (icon + title + description) inside the Migrate
  pane, above the Restore/Peers sub-tabs, and hide the generic floating fleet
  header for Migrate (mirrors how Backups already supplies its own heading).
- Make the .tab-button icon↔label gap explicit (flex + 6px gap) so sub-tabs
  render identically whether or not the markup has whitespace between the
  emoji and name spans; align the Backups sub-tab strip gap to match.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-02 23:04:44 +01:00
librelad
164f782a95 fix(webui): address Migrate-refactor review findings
- Restore empty-state 'Open Locations' now deep-links to the backup center's
  Locations sub-tab: the embedded center honors /overview/backups/<sub> and
  switchTab()s to it after mount (was landing on Dashboard).
- PeersPage.notify + MigratePage.notify use the real window.notificationSystem
  (were calling a never-defined window.showNotification → console-only).
- Remove the now-dead Admin config-manager peers branch (Peers left Admin).
- Trim the dead migrate.json/peers fetch + hostnameToPeerName from BackupPage
  (no consumer after the migrate removal).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-01 11:12:11 +01:00
librelad
25e25230fd fix(webui): drop deleted backup-migrate.js from the embedded center asset list
The embedded backup center's lazy bundle still listed backup-migrate.js, which
was just removed — its 404 failed the whole load chain. Drop it.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-01 10:39:36 +01:00
librelad
ebdae15838 feat(webui): deep-link the Migrate sub-tabs (/overview/migrate/{restore,peers})
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-01 10:28:58 +01:00
librelad
4a964c42a2 feat(webui): add Migrate fleet tab (Restore + Peers sub-tabs)
New 5th Overview tab 'Migrate' with a nested segmented sub-tab row reusing the
per-app Config-tab .tabs-list/.tab-button design:
- Restore: a standalone MigratePage (cross-host migrate moved out of BackupPage
  into its own controller + fragment + modal; own data fetch + task dispatch).
- Peers: reuses the existing PeersPage (container-parameterized) + its template.
Both lazy-loaded on first open and disposed on apps-feature unmount. Additive —
migrate is still in the backup center and Peers still in Admin until the next
commits remove the duplicates.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-01 10:24:54 +01:00
librelad
efdbed8e0c fix(webui): embedded backup-center review fixes
- BackupPage task-refresh run-guard is instance-relative now (window.backupPage
  OR window.overviewBackupPage), so the embedded center's own auto-refresh works
  instead of relying on OverviewManager's overlapping coverage.
- _ensureBackupAssets no longer memoizes a rejected promise — a transient
  script-load failure no longer bricks the backup center until reload; the next
  open retries.
- spaClean.loadScript removes the failed <script> element on error so the
  getElementById dedupe can't make a retry resolve without re-fetching.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-01 01:55:09 +01:00
librelad
f1f0cf7516 fix(webui): scope embedded backup-center mount check to the pane
The 'already mounted?' guard checked #backup-section, but the per-app Backups
tab also defines that id — detect the prior mount via the pane's own
.backup-layout instead so the check is correct, not coincidental.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-01 01:40:29 +01:00
librelad
c508a20605 feat(webui): embed the full backup center in the Overview Backups tab
Instead of a glance + 'Open backup center' button, the Backups tab now mounts
the real BackupPage (dashboard/snapshots/locations/migrate/configuration) inline,
with its sidebar restyled as a nested tab strip and its own header taking over.

- BackupPage gains an embedded mode (opts.embedded): no /backup URL coupling, so
  sub-tabs switch in-page under /overview/backups. Backward compatible.
- OverviewManager lazy-loads the backup bundle + fragment on first open, news a
  BackupPage({embedded:true}), and disposes it on apps-feature unmount. Colliding
  ids (#sidebar/#mobile-overlay) are stripped on inject.
- Revert the Admin backup-config surface — the embedded center (incl.
  Configuration) is now the single home for backup settings.
- The updater needs no equivalent: its sections were already unpacked into the
  Overview/Updates/Improvements tabs + the per-row expander.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-01 01:36:25 +01:00
librelad
d5e2375f38 fix(webui): address review findings on the fleet Overview build
- HIGH: renderAppDetail gated the Roll back button on last_snapshot* fields no
  generator emits, so it never rendered. Derive recoverability from update
  history (updater_apply always snapshots first) so the affordance is reachable.
- MED: per-app Updates tab now repaints on update/rollback/check/hotfix task
  completion (mirrors the backups card) instead of going stale until re-click.
- MED: in-page tab switches now sync spaClean.currentRoute, so the sidebar
  Overview entry no longer no-ops after switching tabs.
- LOW: keyboard activation (Enter/Space) for the role=button expander heads,
  backup tiles, and sidebar Overview entry.
- LOW: preserve ALL expanded Updates rows across a background repaint, not just
  the single ?app= deep-link (split toggle into _openDetail/_closeDetail).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-01 00:37:59 +01:00
librelad
dbc5e64505 feat(webui): deep-link auto-expand for Overview Updates rows
Open the per-app row named by ?app=<name> on load/repaint and write it back on
toggle, so an expanded Updates row is a shareable URL — mirrors the Tasks page's
?task=<id> pattern.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31 23:46:34 +01:00
librelad
8acf2d02c3 feat(webui): add fleet Overview area (Overview/Updates/Improvements/Backups tabs)
Introduce a per-fleet Overview area inside the apps shell, reachable from a new
'Overview' entry pinned above the apps sidebar search. Selecting it renders a
top-tabbed view in the main pane — Overview · Updates · Improvements · Backups —
mirroring the per-app tabbed layout, with the apps sidebar persistent.

- TabController: generic root-scoped show/hide tab host (core/ui-state).
- OverviewManager: drives the 4 tabs. Reuses a headless UpdaterPage for all
  update/CVE/improvement data + rendering (its renderX() are pure HTML
  producers) and reads backup/dashboard.json directly for backup health.
- Overview tab: combined update + backup health cards.
- Updates tab: per-app expander table (CVEs/recovery/history inline via the new
  UpdaterPage.renderAppDetail) + All/Updates/Security filter chips.
- Improvements tab: reuses the updater's signed-hotfix renderer.
- Backups tab: fleet backup-health tiles; actions deep-link per app.
- Additive only: /overview* routes on the apps feature; old /updater and
  /backup pages untouched. Cleanup (redirects, nav, Admin config move) is next.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31 23:37:35 +01:00