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).
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
instanceprop is accepted but currently only referenced viavoid instancesince 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
JobsTabStubfromstubs.tsx. index.tsupdated to import the realJobsTabfrom./JobsobsTabinstead of the stub.BackupsPage.tsxand 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
-
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 touchapi/backups.ts,hooks/useBackups.ts, and the widget source). A comment in the file docstring documents this as a follow-up. -
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."
}