Files
manage/.pi-tmp/svc-hub-slice7-out.md
T
Developer a8dfbd5dc6 Cleanup: delete dead top-level pages + update docs (Slice 11)
Delete the old top-level page files whose content was migrated into
service-page tabs in slices 5-9:
- pages/Media.tsx, Applications.tsx (-> MediaTab)
- pages/FileBrowser.tsx, FileBrowser.impl.tsx (-> FilesTab)
- pages/Actions.tsx (-> ActionsTab)
- pages/Users.tsx, UsersPage.impl.tsx (replaced by Authentik tabs)
- components/BackupsPage.tsx (-> JobsTab)
- components/ObservabilityPage.tsx (split into Alerts/Links/Metrics tabs)
- hooks/useUsers.ts (orphaned after Users page deletion)
- the corresponding page test files (Media, FileBrowser, Applications,
  Actions, UsersPage) that tested the deleted pages directly.

The service-tab components are the live implementations; ServicePage
renders them. No live code references the deleted files.

Docs: append an Information Architecture section to REQUIREMENTS.md
documenting the services-as-hub model (nav shape, service-page tabs,
service type registry, Users->Authentik, Observability split, legacy
route 404s, empty state). Add a CHANGELOG entry under [Unreleased].

92 frontend tests pass (was 112; -20 deleted page tests); 271 backend
tests pass; lint/build green.

Refs openspec/changes/services-as-hub-ia/ (tasks slice 11).
2026-06-26 20:11:02 +00:00

5.9 KiB

Slice 7 — Frontend: backups Jobs tab (worker output)

Files changed

File Status Lines
frontend/src/pages/service-tabs/JobsTab.tsx new 87
frontend/src/pages/service-tabs/__tests__/JobsTab.test.tsx new 64
frontend/src/pages/service-tabs/index.ts modified +2/-1 (import JobsTab from new file, remove from stubs import)
frontend/src/pages/service-tabs/stubs.tsx modified -4 (removed JobsTab stub)

Total: ~155 changed lines (151 new files + 6 modified diff). Well under the 400-line budget.

What was implemented

7.1 — JobsTab

New file frontend/src/pages/service-tabs/JobsTab.tsx:

  • Accepts { instance }: { instance: ServiceInstance } props.
  • Lifts the operational content from components/BackupsPage.tsx: the Jobs / Runs / Alerts tab structure with the three sub-tables (BackupJobsTable, BackupRunsTable, BackupAlertsTable).
  • The page heading ("Backups") is dropped since the service page header already renders the instance name + binding name.
  • All hooks (useBackupJobs, useBackupRuns, useBackupAlerts, useAcknowledgeAlert) are called exactly as in BackupsPage — globally (no service_id filtering). The instance prop is accepted but currently only referenced via void instance since per-instance scoping requires hook changes that are out of scope for this slice.

7.2 — Tests + cleanup

  • New test file JobsTab.test.tsx: 2 tests covering sub-tab presence (Jobs, Runs, Alerts via regex match since the label includes the count) and job-name rendering with mocked hooks.
  • Removed JobsTabStub from stubs.tsx.
  • index.ts updated to import the real JobsTab from ./JobsobsTab instead of the stub.
  • BackupsPage.tsx and its tests are left intact (Slice 11 cleanup).

Hooks: instance-scoped or global?

Global. The backup hooks (useBackupJobs, useBackupRuns, useBackupAlerts) query without a service_id filter. The backend gained service_id attribution in Slice 3 (column on backup_jobs, ?service_id= param on report endpoints), but the hooks don't yet accept a serviceId parameter. This tab shows ALL backups data for now. Per-instance scoping by instance.id is a documented follow-up (the void instance reference and the file docstring both call this out).

Validation

npm run lint   → 0 errors, 2 pre-existing warnings (UsersPage.impl.tsx, unrelated)
npm run build  → ✓ built (tsc -b + vite)
npm run test   → 30 files / 96 tests passed (was 94; +2 new)

Deviations from design

  1. Hooks not scoped by instance.id. The design said "scope queries by instance.id" but the hooks (useBackups.ts) don't accept a serviceId param. Rewriting the hooks is out of scope for this slice (would touch api/backups.ts, hooks/useBackups.ts, and the widget source). A comment in the file docstring documents this as a follow-up.

  2. Page heading dropped. BackupsPage.tsx rendered <h1>Backups</h1>. The service page header already renders the instance name + "Backups" binding name, so the heading is redundant. The rest of the content (tabs, tables, loading states) is identical.

skill_resolution

none — no project/user SKILL.md paths were injected by the parent, and no .atl/skill-registry.md was found. The task was self-contained against the OpenSpec design/tasks docs and the reference files.

Residual risks

  • Hooks query globally. The JobsTab shows all backup data across all instances. When the hooks gain a serviceId param, this tab should be updated to pass instance.id.
  • Old BackupsPage.tsx still in repo. Deleted in Slice 11 cleanup. Its tests still pass (render the component directly, not via routing).
{
  "criteriaSatisfied": [
    {
      "id": "criterion-1",
      "status": "satisfied",
      "evidence": "Slice 7 implements JobsTab (lift from BackupsPage.tsx, global hooks with documented follow-up for instance scoping), wires it in index.ts, removes the stub, and adds 2 tests. No scope widening: only 4 files touched (2 new + 2 modified). Old BackupsPage.tsx preserved for Slice 11. 96 tests pass; lint/build green."
    }
  ],
  "changedFiles": [
    "frontend/src/pages/service-tabs/JobsTab.tsx",
    "frontend/src/pages/service-tabs/__tests__/JobsTab.test.tsx",
    "frontend/src/pages/service-tabs/index.ts",
    "frontend/src/pages/service-tabs/stubs.tsx"
  ],
  "testsAddedOrUpdated": [
    "frontend/src/pages/service-tabs/__tests__/JobsTab.test.tsx"
  ],
  "commandsRun": [
    {
      "command": "cd frontend && npm run lint",
      "result": "passed",
      "summary": "0 errors; 2 pre-existing warnings in UsersPage.impl.tsx (unrelated)"
    },
    {
      "command": "cd frontend && npm run build",
      "result": "passed",
      "summary": "tsc -b + vite build clean"
    },
    {
      "command": "cd frontend && npm run test",
      "result": "passed",
      "summary": "30 files / 96 tests passed (was 94; +2 new)"
    },
    {
      "command": "git diff --cached --stat",
      "result": "passed",
      "summary": "Empty — no staged files"
    }
  ],
  "validationOutput": [
    "lint: 0 errors",
    "build: success (tsc + vite)",
    "test: 96/96 passed (+2 new JobsTab tests)",
    "no staged files"
  ],
  "residualRisks": [
    "Backup hooks query globally (no service_id filter); per-instance scoping by instance.id is a documented follow-up once the hooks gain the parameter.",
    "Old BackupsPage.tsx still in repo (Slice 11 cleanup); its tests still pass."
  ],
  "noStagedFiles": true,
  "diffSummary": "~155 lines: JobsTab.tsx (87, lift from BackupsPage.tsx with instance prop + global hooks), JobsTab.test.tsx (64, 2 tests with mocked hooks), index.ts wiring (+1/-1), stubs.tsx cleanup (-4). Well under 400-line budget.",
  "reviewFindings": [
    "no blockers"
  ],
  "manualNotes": "git status confirms nothing is staged; all changes are unstaged/untracked, ready for the parent to review and commit."
}