Files
manage/.pi-tmp/svc-hub-slice5-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

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

  1. 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).

  2. No mobile card layout on MediaTab. This branch is based on main, NOT on mobile-responsive-parity. Main's Media.tsx uses a DataTable with TanStack column-visibility-based mobile hiding (the usePrefersSmallScreen / MOBILE_HIDDEN_COLUMNS pattern), NOT the MobileCardRow from the mobile branch. I lifted exactly what main has — no invented mobile layout.

  3. 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 /files route or the link target changes to /services/ssh_tasks/<id>?path=.... Flagged for Slice 6.