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
+63
View File
@@ -0,0 +1,63 @@
# Slice 1 — Shared primitives (worker output)
Implemented all 6 sub-tasks of Slice 1 (`mobile-responsive-parity`). No page-level files touched.
## Files changed
| File | Status | Lines |
|------|--------|------|
| `frontend/src/hooks/useIsMobile.ts` | new | 31 |
| `frontend/src/components/ui/mobile-card.tsx` | new | 112 |
| `frontend/src/components/ui/__tests__/mobile-card.test.tsx` | new | 73 |
| `frontend/src/components/ui/sheet-form.tsx` | new | 101 |
| `frontend/src/components/ui/__tests__/sheet-form.test.tsx` | new | 96 |
| `frontend/src/components/HoverEditButton.tsx` | modified | +24 / -8 |
| `frontend/src/components/__tests__/HoverEditButton.test.tsx` | modified | +19 |
| `frontend/src/index.css` | modified | +16 |
| `frontend/src/App.tsx` | modified | +2 / -13 |
**Total: ~492 lines** (79 tracked diff + 413 new files). Slightly over the 400-line budget; the overrun is entirely test coverage (mobile-card 73 + sheet-form 96 + hover-edit 19 = 188 test lines) which is the safety net for all 9 downstream page slices. Implementation-only lines are ~304, within budget. Flagging for parent decision; I did not trim tests since they guard later slices.
## Validation
```
cd frontend && npm run lint → 0 errors, 2 pre-existing warnings (UsersPage.impl.tsx, unrelated)
cd frontend && npm run build → ✓ built in 3.49s (tsc -b + vite)
cd frontend && npm run test → 83 passed (25 files)
```
- `useIsMobile` — no dedicated test (it's a thin matchMedia wrapper exercised by App.tsx integration); the page-slice tests will assert <768px/≥768px behavior.
- `MobileCardRow` — 4 tests (primary+fields render, onRowClick fires, actions slot, non-interactive mode).
- `SheetForm` — 5 tests (title+children, onSave, onCancel, isPending disables+labels, close-X calls onCancel).
- `HoverEditButton` — 4 tests (existing 2 + default mobile=always tokens + legacy mobile=hover tokens).
## Deviations from design
1. **`MobileCardRow` key strategy**: design pseudocode used `MobileCardRowProps<T>` with `rows: TData[]` (a typo — `TData` undefined). Implemented as `rows: T[]` (correct generic). Also added an optional `className` prop on the outer container — minor additive convenience, not a behavior change.
2. **`MobileCardRow` field rendering**: design said "key/value stack"; I used a `<dl>` with `grid-cols-[auto_1fr]` so labels align across rows. Same semantics, cleaner alignment.
3. **`HoverEditButton` default class**: added `mobile-touch-target` to the button so it meets 44px below md out of the box (consistent with spec R6). Design did not name this class explicitly here but R6/R9 require it on all interactive elements; this primitive is reused by later slices so it should be compliant by default.
4. **`SheetForm` side**: used `side="bottom"` with `h-[100dvh]` for a true full-screen mobile form. Design said "side=bottom or side=right, full screen"; bottom is the more native mobile form factor and avoids the `sm:max-w-sm` cap on side=right from the Sheet primitive.
5. **`SheetForm` close button**: design said "title + close X"; I render the X via the Cancel handler (X = cancel) rather than Radix's `onOpenChange(false)`, so dirty-state confirm logic (spec R4.5) can be centralized in the consumer's `onCancel`. Documented in the component docstring.
No other deviations. All shadcn primitives, `cn()`, lucide-react icons, and existing code style (tabs, `data-slot` where relevant) matched.
## skill_resolution
`none` — no project/user SKILL.md paths were injected by the parent, and no `.atl/skill-registry.md` was provided. The task was self-contained against the OpenSpec design/tasks docs.
## Residual risks
- **jsdom doesn't honor `@media`**: the `useIsMobile` hook returns whatever `window.matchMedia` reports in jsdom (default false). Per-page breakpoint tests in later slices will need to mock `matchMedia` or use the existing resize pattern. Not a Slice 1 blocker.
- **`h-[100dvh]` iOS Safari**: per design risk note; needs manual verification on a real iOS device during Slice 10 (manual cross-route pass). The flex-column layout avoids the sticky-inside-transform pitfall.
- **`HoverEditButton` consuming pages** (Actions, Settings) use `.rail-edit` hover CSS (`&:hover .rail-edit { opacity: 1 }`). With the new default (`mobile="always"`), the button is visible below md and hover-revealed at md+ — desktop behavior unchanged because the md:-prefixed classes take over at ≥768px. No migration needed on those pages for Slice 1; they keep working as-is.
## Review findings
No blockers. One item for the parent reviewer to confirm:
- Total diff ~492 lines exceeds the 400-line slice budget by ~92 lines, entirely due to additive tests. Acceptable for a foundational primitives slice, but the parent may prefer to split or trim test prose.
## Manual notes
- `git status` confirms nothing is staged; all changes are unstaged/untracked, ready for the parent to review and commit.
- The `swap-pane` untracked file at repo root is pre-existing and unrelated; not touched.