Files
manage/.pi-tmp/slice1-worker-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.9 KiB

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.