Files
manage/openspec/changes/mobile-responsive-parity/tasks.md
T
Developer 18ee77a4e4 Plan mobile responsive parity (OpenSpec change)
Add proposal/spec/design/tasks for full mobile parity across all 9 routes.
Decisions: hybrid tables (cards below md for big four), Sheet-based forms,
always-visible edit affordance, 44px touch targets, single-column dashboard
with anchors, responsive web only (no PWA), phone portrait at md:768px cut.
Polling unchanged (risk flagged). Delivery: 10 chained PRs, primitives first.
2026-06-26 11:49:47 +00:00

227 lines
7.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Tasks — Mobile responsive parity
**Change:** `mobile-responsive-parity`
**Phase:** tasks
**Date:** 2026-06-26
## Review workload forecast
| Field | Value |
|-------|-------|
| Estimated changed lines | ~22002800 |
| Chained PRs recommended | Yes (10 slices) |
| Chain strategy | stacked-to-main |
| Slice order | 1 (primitives) → 2 (Dashboard) → 35 (tables) → 68 (forms) → 9 (touch audit) → 10 (docs + verify) |
Each slice is committed separately (user pref). Every slice must leave
`cd frontend && npm run lint && npm run build && npm run test` green. Every
touched page gains a Vitest case asserting behavior at <768px and ≥768px.
---
## Slice 1 — Shared primitives
**Goal:** Land the four building blocks every later slice depends on. No
page-level behavior changes yet.
- [ ] **1.1 `useIsMobile()` hook**
- Files: `frontend/src/hooks/useIsMobile.ts` (new)
- Lines: ~20
- Details: SSR-safe `matchMedia("(max-width: 768px)")` listener per design.
- [ ] **1.2 `MobileCardRow` component**
- Files: `frontend/src/components/ui/mobile-card.tsx` (new), plus a Vitest
spec `frontend/src/components/ui/__tests__/mobile-card.test.tsx`.
- Lines: ~80 + ~60 test
- Details: generic `<T,>`, fields list, `primary` field, optional `onRowClick`
and `actions` slot per design. 44px min card height.
- [ ] **1.3 `SheetForm` component**
- Files: `frontend/src/components/ui/sheet-form.tsx` (new), plus spec.
- Lines: ~70 + ~50 test
- Details: wraps shadcn `Sheet`; sticky header + sticky footer; `h-[100dvh]`;
props per design. Dirty-state confirm on outside click.
- [ ] **1.4 `EditActionButton` — extend `HoverEditButton`**
- Files: `frontend/src/components/HoverEditButton.tsx`
- Lines: ~15
- Details: add `mobile="always" | "hover"` (default `always`). Tailwind:
always visible below `md`, hover-revealed at `md:` and up.
- [ ] **1.5 `mobile-touch-target` utility**
- Files: `frontend/src/index.css` (add utility)
- Lines: ~10
- Details: media-gated 44×44 min hit area per design.
- [ ] **1.6 Replace inline `matchMedia` in `App.tsx`**
- Files: `frontend/src/App.tsx`
- Lines: ~10 removed, ~3 added
- Details: use `useIsMobile()`; preserve current shell behavior exactly.
---
## Slice 2 — Dashboard (R7)
**Goal:** Dashboard collapses to single column + section anchor bar on mobile.
- [ ] **2.1 Single-column grid below `md`**
- Files: `frontend/src/pages/Dashboard.tsx`
- Lines: ~20
- Details: widget list uses `grid grid-cols-1 md:grid-cols-*` (match existing
desktop column count). Respect configured sort order.
- [ ] **2.2 Section anchor bar**
- Files: `frontend/src/pages/Dashboard.tsx`
- Lines: ~40
- Details: group widgets (Observability / Media / Backups / Custom). Anchor
bar `md:hidden`, horizontal scroll of pills, jumps to section by id.
- [ ] **2.3 Tests**
- Files: `frontend/src/pages/__tests__/Dashboard.test.tsx`
- Lines: ~40
- Details: assert single column at 375px, grid at 1280px, anchor bar visible
only at <768px.
---
## Slice 3 — Media table (R3.1, R3.5)
- [ ] **3.1 Mobile fields + card render**
- Files: `frontend/src/pages/Media.tsx`
- Lines: ~60
- Details: card primary = title; fields = size, HDR flag, library, year.
Hide column-visibility toggle below `md`. Preserve pagination controls.
- [ ] **3.2 Tests**
- Files: `frontend/src/pages/__tests__/Media.test.tsx`
- Lines: ~40
---
## Slice 4 — FileBrowser table (R3.1)
- [ ] **4.1 Mobile fields + card render**
- Files: `frontend/src/pages/FileBrowser.impl.tsx`
- Lines: ~60
- Details: card primary = name; fields = size, mtime, type. Preserve
directory-navigation tap target (whole card). Preserve ffprobe/job affordances.
- [ ] **4.2 Tests**
- Files: `frontend/src/pages/__tests__/FileBrowser.test.tsx`
- Lines: ~30
---
## Slice 5 — Users + Backups tables (R3.1)
- [ ] **5.1 UsersPage card**
- Files: `frontend/src/pages/UsersPage.impl.tsx`
- Lines: ~70
- Details: card primary = display name; fields = username, activity badge,
email (if present). Preserve selection checkboxes (44px) and drawer open.
- [ ] **5.2 Backups cards (3 tables)**
- Files: `frontend/src/components/BackupAlertsTable.tsx`,
`frontend/src/components/BackupJobsTable.tsx`,
`frontend/src/components/BackupRunsTable.tsx`
- Lines: ~120 (3 × ~40)
- Details: per-table primary + 3 fields; preserve acknowledge/run actions on
the card.
- [ ] **5.3 Tests**
- Files: existing component test files
- Lines: ~90
---
## Slice 6 — ServicePage form (R4)
- [ ] **6.1 Sheet form below `md`**
- Files: `frontend/src/pages/ServicePage.tsx`
- Lines: ~60
- Details: branch on `useIsMobile()`; reuse form body inside `SheetForm`.
Single-column fields. Preserve save semantics.
- [ ] **6.2 Tests**
- Files: `frontend/src/pages/__tests__/ServicePage.test.tsx` (new or extend)
- Lines: ~50
---
## Slice 7 — Settings form (R4)
- [ ] **7.1 Machines + SSH-key editors in Sheet**
- Files: `frontend/src/pages/Settings.tsx`
- Lines: ~100
- Details: both machine editor and SSH-key editor open in `SheetForm` below
`md`. Validate-on-save preserved.
- [ ] **7.2 Tests**
- Files: `frontend/src/pages/__tests__/Settings.test.tsx`
- Lines: ~40
---
## Slice 8 — Message compose + WidgetConfigDialog (R4)
- [ ] **8.1 Message compose Sheet**
- Files: `frontend/src/pages/UsersPage.impl.tsx`
- Lines: ~60
- Details: compose dialog → `SheetForm` below `md`. HTML body textarea + iOS
Safari attachment upload verified manually.
- [ ] **8.2 WidgetConfigDialog Sheet**
- Files: `frontend/src/components/WidgetConfigDialog.tsx`
- Lines: ~60
- Details: reorder list and per-widget config render inside `SheetForm` below
`md`. Sticky save bar.
- [ ] **8.3 Tests**
- Files: extend existing
- Lines: ~60
---
## Slice 9 — Touch-target audit (R6)
- [ ] **9.1 Apply `mobile-touch-target` across routes**
- Files: all 9 pages + shared components (`SessionActivityPanel`,
`ObservabilityPage`, etc.)
- Lines: ~150 (sprinkled)
- Details: icon buttons, checkboxes, switches, badges-as-buttons, row taps.
Manual device-mode pass at 375px logging violations; fix each.
- [ ] **9.2 Audit log**
- Files: this PR description
- Details: list every element touched with before/after hit-area size.
---
## Slice 10 — Docs + verify
- [ ] **10.1 Update `docs/REQUIREMENTS.md`**
- Files: `docs/REQUIREMENTS.md`
- Lines: ~20
- Details: add a Mobile section documenting the breakpoint, card/Sheet
behavior, 44px policy, and the polling follow-up risk.
- [ ] **10.2 Cross-route manual pass**
- Details: walk all 9 routes at 375px (iPhone 12 Pro preset) and at 1280px.
Confirm no regressions; file follow-ups for any iOS Safari quirks found.
- [ ] **10.3 Verify report**
- Files: `openspec/changes/mobile-responsive-parity/verify-report.md`
- Lines: ~80
- Details: per-AC evidence (AC1AC8), tool versions, manual test notes.
---
## Notes
- Each slice's diff should stay well under 400 changed lines. If a slice (e.g.
Settings at ~100 + 40 test) approaches the budget, split along the natural
sub-section boundary.
- Slices 35 (tables) and 68 (forms) can be reordered or parallelized across
branches if helpful, but each must merge green.
- No slice touches the backend.