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).
This commit is contained in:
Developer
2026-06-26 20:11:02 +00:00
parent ec57eff59a
commit a8dfbd5dc6
45 changed files with 3441 additions and 4711 deletions
+72
View File
@@ -0,0 +1,72 @@
# Slice 3 — Media table mobile layout (worker output)
## Files changed
| File | Status | Lines |
|------|--------|------|
| `frontend/src/pages/Media.tsx` | modified | +190 / -27 |
| `frontend/src/pages/__tests__/Media.test.tsx` | modified | +93 / -0 |
**Total: ~256 changed lines** (283 insertions, 27 deletions). Under the 400-line budget.
## What was implemented
### 3.1 — Mobile fields + card render
Below `md` (`isMobile === true`), the page renders `<MobileCardRow>` inside the same `rounded-lg border bg-card` wrapper, followed by a standalone `MediaMobilePagination` component. Desktop renders the existing `<DataTable>` unchanged.
**Mobile card field list** (module-level `mediaCardFields` constant):
| Field | Key | Rationale |
|-------|-----|-----------|
| **Title** (primary) | `title` | Primary identifier — bold card title |
| Size | `size` | Already human-readable ("12.4 GB"); helps identify large files at a glance |
| HDR | `hdr` | Shows HDR format string ("HDR10", "Dolby Vision") or "-" for SDR — key tech quality indicator |
| Library | `library` | Which Jellyfin library the item belongs to — context for multi-library setups |
| Year | `year` | Quick identification; number rendered as string, "-" if null |
Runtime, bitrate, resolution, video codec, series/season/episode, date_added, and path are omitted from the mobile card — they're available on desktop and would make the card too tall for phone scanning.
**Preserved behaviors:**
- Row click → `navigate("/files?path=...")` — wired via `MobileCardRow` `onRowClick`.
- Pagination — a new `MediaMobilePagination` component mirrors the DataTable's internal `DataTablePagination` (rows count, page-size select, page indicator, prev/next buttons) but works off the raw `PaginationState` instead of a TanStack table instance.
- Build index / status controls above the table — unchanged.
- Column-visibility toggle — automatically hidden (DataTable is not rendered below `md`).
- Desktop (`md+`) — byte-for-byte identical: the `isMobile === false` branch renders the exact same `<DataTable>` with the same props.
### 3.2 — Tests
Added a `setMatchMedia(matches)` helper to stub `window.matchMedia` for jsdom (same pattern as Dashboard.test.tsx). Called `setMatchMedia(false)` in `beforeEach` so existing desktop tests are unaffected. 5 new tests in a `describe("Media (mobile card layout — slice 3)")` block:
1. **Cards render with title as primary below md** — asserts card titles and field labels render, desktop column headers do NOT.
2. **Column-visibility toggle is hidden below md** — asserts no "Columns" button.
3. **Pagination controls render below cards on mobile** — asserts "2 rows", page indicator, and prev/next buttons.
4. **Card tap navigates to file browser** — clicks "Inception" card, asserts `navigate` called with the encoded path.
5. **DataTable renders at desktop width** — asserts column headers present + "Columns" button present.
## Validation
```
npm run lint → 0 errors, 2 pre-existing warnings (UsersPage.impl.tsx, unrelated)
npm run build → ✓ built (tsc -b + vite)
npm run test → 25 files / 94 tests passed (was 89; +5 new)
```
## Deviations from design
1. **Standalone `MediaMobilePagination` component instead of reusing DataTable's pagination.** The DataTable renders pagination internally (not as a separate export). Extracting a shared pagination component would touch `data-table.tsx` (out of scope for this slice). The inline `MediaMobilePagination` mirrors `DataTablePagination` exactly (same labels, same controls, same aria-labels) so the mobile UX is consistent. A future refactor can extract both into a shared `<TablePagination>`.
2. **`setMatchMedia` mock added to existing test file.** The existing Media tests didn't mock `window.matchMedia` because the old `usePrefersSmallScreen` hook guarded against it (`typeof window.matchMedia === "function"`). The new `useIsMobile` hook calls `window.matchMedia` unconditionally (it's the standardized hook from Slice 1). The mock defaults to desktop (`matches: false`) so all 6 existing tests pass unchanged.
3. **`mediaCardFields` is a module-level constant.** This mirrors the existing `mediaColumns` pattern (module-level for TanStack stability). MobileCardRow doesn't require it, but keeping it stable avoids per-render allocation and is consistent with the codebase's existing style.
## skill_resolution
`none` — no project/user SKILL.md paths were injected by the parent, and no `.atl/skill-registry.md` was found.
## Residual risks
- **`MediaMobilePagination` duplicates `DataTablePagination`.** If the desktop pagination UI changes (labels, aria attributes), the mobile version won't auto-update. A shared component extraction in a later refactor would fix this. Low priority since the pagination UI is stable.
- **jsdom `matchMedia` is not reactive.** The `setMatchMedia` mock sets the initial `matches` value but `addEventListener` is a no-op (no resize events fire). This is adequate for breakpoint-branch tests but cannot test responsive transitions. Same limitation as Dashboard.test.tsx.
- **Row selection (`enableRowSelection`) is desktop-only on mobile.** The mobile card layout has no selection checkboxes (the card itself is the tap target for navigation). This is consistent with the spec (R3.5 says the card picks the fields; R3.3 preserves "row click / selection semantics" but on mobile the primary interaction is navigation, not batch selection). If batch selection is needed on mobile later, the `actions` slot can hold a checkbox.