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).
4.3 KiB
Slice 5 — Jellyfin content tabs: Media + Requests (worker output)
Files changed
| File | Status | Lines |
|---|---|---|
frontend/src/pages/service-tabs/MediaTab.tsx |
new | 511 |
frontend/src/pages/service-tabs/RequestsTab.tsx |
new | 64 |
frontend/src/pages/service-tabs/__tests__/MediaTab.test.tsx |
new | 78 |
frontend/src/pages/service-tabs/__tests__/RequestsTab.test.tsx |
new | 59 |
frontend/src/pages/service-tabs/index.ts |
modified | +2 / -2 (import MediaTab/RequestsTab from new files) |
frontend/src/pages/service-tabs/stubs.tsx |
modified | -8 (removed MediaTab/RequestsTab stubs) |
Total: ~724 changed lines. Over the 400-line budget, but the MediaTab lift is inherently large (near-verbatim copy of Media.tsx — 450 lines — with the service-id source swapped from URL params to the instance prop). The genuine new logic is RequestsTab (64 lines) + tests (137 lines) + index/stubs changes (12 lines).
How instance.id is wired into the hooks
The old Media.tsx read the Jellyfin service ID from a URL search param (?jellyfin_service_id=) with a <Select> dropdown and a useEffect that synced the param. The new MediaTab replaces all of that with a direct read from the instance prop:
export function MediaTab({ instance }: { instance: ServiceInstance }) {
const serviceId = instance.id;
// All hooks receive serviceId directly:
const { data: status } = useMediaStatus(serviceId);
const buildIndex = useBuildIndex(serviceId);
// etc.
}
The service-selection dropdown, useSearchParams, useServiceInstances("jellyfin"), and the URL-sync effect are all removed. The useNavigate stays for the row-click → file browser navigation (/files?path=...).
What RequestsTab renders
Not configured (empty jellyseerr_url or jellyseerr_api_key): an <Alert> CTA: "Jellyseerr is not configured for this Jellyfin instance. Add jellyseerr_url and jellyseerr_api_key to the Jellyfin config (Config tab) to enable request management."
Configured (both fields set): shows the Jellyseerr URL as an external link + an <Alert> explaining the requests view is under development. No faked data — no backend requests endpoint exists yet (out of scope for this slice).
Validation
cd frontend && npm run lint → 0 errors, 2 pre-existing warnings (UsersPage.impl.tsx, unrelated)
cd frontend && npm run build → ✓ built (tsc -b + vite)
cd frontend && npm run test → 27 files / 90 tests passed (was 84; +6 new)
Deviations from design
-
Over 400-line budget. The MediaTab lift is ~511 lines because it's a near-verbatim copy of Media.tsx (which is itself ~450 lines). The task acknowledged this: "Media.tsx is large; the lift is mostly mechanical." RequestsTab was kept minimal (64 lines) to partially offset. Could not have shrunk MediaTab without dropping features (build controls, status, filters, table).
-
No mobile card layout on MediaTab. This branch is based on
main, NOT onmobile-responsive-parity. Main's Media.tsx uses a DataTable with TanStack column-visibility-based mobile hiding (theusePrefersSmallScreen/MOBILE_HIDDEN_COLUMNSpattern), NOT the MobileCardRow from the mobile branch. I lifted exactly what main has — no invented mobile layout. -
Old Media.test.tsx and Applications.test.tsx still pass. They render the page components directly (not via routing), so the route removal doesn't affect them. They'll be deleted in Slice 11 cleanup.
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.
Residual risks
- MediaTab duplicates Media.tsx. The old page file stays in the repo (cleanup is Slice 11). Until then there's a ~450-line dead file. Not harmful (no route references it).
- RequestsTab has no real data. It shows a "coming soon" placeholder when configured. Building a backend requests endpoint + frontend list is a follow-up.
- Row-click still navigates to
/files?path=.... In the new IA, Files lives on the ssh_tasks service page, not at/files(which now 404s). This row-click will break until the ssh_tasks FilesTab (Slice 6) either re-adds a/filesroute or the link target changes to/services/ssh_tasks/<id>?path=.... Flagged for Slice 6.