docs(PBI-11): canoniek plan voor mobile-shell met drie architectuur-beslissingen

Vorige planlocatie (~/.claude/plans/twinkly-plotting-wombat.md) was overschreven
met ST-1209-plan; deze doc neemt het over.

Drie aanbevelingen verwerkt na evaluatie tegen huidige codebase:
- A. Gedeelde entityDialogContentClasses muteren (dekt ST-1133 + ST-1138 in één edit)
- B. Eigen route group app/(mobile)/ — nested layout kan parent-NavBar niet onderdrukken
- C. Gescheiden SplitPane cookie-key voor mobile (backlog-3-mobile)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Janpeter Visser 2026-05-04 09:40:03 +02:00
parent f7a425a5db
commit 3887e07af2
2 changed files with 199 additions and 0 deletions

View file

@ -44,6 +44,7 @@ Auto-generated on 2026-05-04 from front-matter and headings.
| [M10 — Password-loze inlog via QR-pairing](./plans/M10-qr-pairing-login.md) | active | 2026-05-03 |
| [M11 — Claude vraagt, gebruiker antwoordt](./plans/M11-claude-questions.md) | active | 2026-05-03 |
| [M9 — Actief Product Backlog](./plans/M9-active-product-backlog.md) | active | 2026-05-03 |
| [PBI-11 — Mobile-shell met landscape-lock (settings + backlog + solo)](./plans/PBI-11-mobile-shell.md) | — | — |
| [ST-1109 — PBI krijgt een status (Ready / Blocked / Done)](./plans/ST-1109-pbi-status.md) | active | 2026-05-03 |
| [ST-1110 — Demo gebruiker read-only](./plans/ST-1110-demo-readonly.md) | active | 2026-05-03 |
| [ST-1111 — Voer uit-knop met Claude Code job queue](./plans/ST-1111-claude-job-trigger.md) | active | 2026-05-03 |

View file

@ -0,0 +1,198 @@
# PBI-11 — Mobile-shell met landscape-lock (settings + backlog + solo)
> **Status:** READY · priority 3 · sort_order 8
> **Stories:** ST-1133 (TaskDialog full-screen) · ST-1134 (foundation) · ST-1135 (UA-redirect) · ST-1136 (settings) · ST-1137 (backlog) · ST-1138 (solo) · ST-1139 (docs + E2E)
## Doel
Scrum4Me bruikbaar maken op een mobiele telefoon, beperkt tot drie schermen — Settings (account + product-selector + QR-pairing-instructie + logout), Product Backlog (PBI/Story/Task aanmaken), Solo Paneel (voortgang vastleggen). Landscape-orientatie afgedwongen via PWA-manifest + CSS-overlay. App-naam en -icoon onderdrukken op `/m/*`. Desktop-app blijft ongewijzigd.
## Drie architectuur-beslissingen
### Beslissing A — gedeelde dialog-classes (raakt ST-1133 + ST-1138)
Alle entity-dialogen (PbiDialog, StoryDialog, TaskDialog, TaskDetailDialog) delen dezelfde class-string in [components/shared/entity-dialog-layout.ts](../../components/shared/entity-dialog-layout.ts):
```ts
export const entityDialogContentClasses = cn(
'flex flex-col p-0 gap-0',
'max-h-[90vh] w-full max-w-[calc(100%-2rem)]',
'sm:max-w-[90vw] sm:max-h-[85vh]',
'lg:max-w-[50vw] lg:min-w-[480px]',
)
```
→ Mobile-fullscreen wordt via één edit op deze constant geregeld:
```ts
'max-sm:w-screen max-sm:h-screen max-sm:max-w-none max-sm:rounded-none'
```
**Gevolg voor stories:**
- ST-1133 T-317 muteert `entity-dialog-layout.ts`, niet `task-dialog.tsx` rechtstreeks
- ST-1138 T-332 vervalt als file-edit — wordt verify-only (controleer dat TaskDetailDialog mee-erft)
- PBI/Story-dialogen krijgen mobile-fullscreen "voor niets" (handig voor ST-1137)
### Beslissing B — eigen route group `app/(mobile)/`
Parent layout `app/(app)/layout.tsx` rendert NavBar, MinWidthBanner, StatusBar, SoloRealtimeBridge, NotificationsBridge. Een nested layout in `(app)/m/` kan deze parent-output **niet** verwijderen (Next.js layouts erven naar binnen, niet vervangen).
**Keuze:** verplaats `/m/*` naar een eigen route group `app/(mobile)/m/{settings,pair,products}/...` met eigen `app/(mobile)/layout.tsx`.
**Auth-guard duplicatie voorkomen** door `getSession()`-check te extraheren naar `lib/auth-guard.ts`:
```ts
// lib/auth-guard.ts
export async function requireSession() {
const session = await getSession()
if (!session.userId) redirect('/login')
return session
}
```
Beide layouts (`(app)/layout.tsx` en `(mobile)/layout.tsx`) roepen deze helper aan. Bestaande `/m/pair/page.tsx` (M10 QR-pairing) verhuist mee naar `app/(mobile)/m/pair/page.tsx` — geen URL-wijziging, alleen filesystem-move.
**Gevolg voor stories:**
- ST-1134 T-321 schrijft `app/(mobile)/layout.tsx`, niet `app/(app)/m/layout.tsx`
- ST-1136 page wordt `app/(mobile)/m/settings/page.tsx`
- ST-1137 page wordt `app/(mobile)/m/products/[id]/page.tsx`
- ST-1138 page wordt `app/(mobile)/m/products/[id]/solo/page.tsx`
- M10's `/m/pair` verhuist naar `app/(mobile)/m/pair/` — URL ongewijzigd, geen redirect-migratie nodig
### Beslissing C — gescheiden SplitPane cookie-key
ST-1137 hergebruikt `BacklogSplitPane` (drie panelen). Op mobile rendert die in tab-mode (auto-switch + back-button uit ST-1116). De SplitPane bewaart split-percentages in een cookie.
**Keuze:** gescheiden cookie-key voor mobile — `split-pane:backlog-3-mobile:<id>` — zodat mobile-gebruikers (die in tab-mode geen split-percentages bewerken maar wel terug kunnen schakelen) de desktop-split niet beïnvloeden.
**Gevolg voor stories:**
- ST-1137 T-328 geeft expliciete `cookieKey`-prop aan `BacklogSplitPane` op de mobile-route
## Hergebruik (al aanwezig)
| Wat | Bron |
|---|---|
| Mobile tab-mode in `SplitPane` (incl. `tabLabels`, `mobileBreakpoint`, `activeTab`) | ST-1116 — [components/split-pane/split-pane.tsx](../../components/split-pane/split-pane.tsx) |
| Click-cascade auto-switch in `BacklogSplitPane` | ST-1116 commit `3e86a8d` |
| QR-pairing route `/m/pair` | M10 commit `625221f` |
| `/m/pair` confirmation page | bestaand |
| Functional-spec mobile-tabs sectie | `docs/specs/functional.md:234-235` |
## Stories
### ST-1133 — TaskDialog full-screen op mobile (verifieer en fix)
**Doel:** entity-dialogen renderen 100vw × 100vh op viewport `<640px`.
**Acceptance:**
- `entityDialogContentClasses` in `components/shared/entity-dialog-layout.ts` bevat `max-sm:w-screen max-sm:h-screen max-sm:max-w-none max-sm:rounded-none`
- Sticky header en footer blijven bereikbaar; body scrollt
- Werkt voor TaskDialog, TaskDetailDialog, PbiDialog, StoryDialog (alle gebruiken de constant)
- Tests dekken mobile-render via `window.innerWidth`-mock voor minstens TaskDialog en TaskDetailDialog
- Geen regressie op desktop (`sm:max-w-[90vw]` blijft op `>=640px`)
**Tasks:**
- T-316 inventariseer huidige render
- T-317 fix de gedeelde constant
- T-318 tests
### ST-1134 — Mobile shell foundation (route group + landscape-guard + tab-bar + manifest)
**Doel:** route group `(mobile)`, landscape-overlay, bottom tab-bar, PWA-manifest.
**Acceptance:**
- `app/(mobile)/layout.tsx` rendert zonder NavBar / AppIcon / MinWidthBanner / StatusBar
- Auth-guard via gedeelde `lib/auth-guard.ts` helper; `(app)/layout.tsx` gebruikt dezelfde helper
- `<LandscapeGuard>` toont rotate-overlay in portrait (window.matchMedia)
- `<MobileTabBar>` bottom-fixed met 3 lucide-iconen (ListTree, Activity, Settings); tap-targets ≥44×44 px
- `public/manifest.json` bevat `"orientation": "landscape"`
- M10 `/m/pair` verhuist filesystem-only naar `app/(mobile)/m/pair/` — URL onveranderd
- Tests: LandscapeGuard render-states, TabBar route-active, auth-guard helper
**Tasks:**
- T-319 LandscapeGuard
- T-320 MobileTabBar
- T-321 `(mobile)/layout.tsx` + manifest + auth-guard extractie + filesystem-move van `/m/pair`
### ST-1135 — Mobile UA-redirect bij login
**Acceptance:**
- `lib/user-agent.ts` exporteert `isPhoneUA(ua: string | null): boolean` op basis van `Mobi`-substring
- `actions/auth.ts` `loginAction` redirect bij phone-UA naar `/m/products/[active]/solo`; zonder actief product naar `/m/settings`
- Tablet-UA en desktop-UA blijven op `/dashboard`
- Demo-user volgt zelfde routing
- Tests dekken alle paden (phone met/zonder product, tablet, desktop, null UA, demo)
**Tasks:** T-322 helper · T-323 loginAction integratie · T-324 tests
### ST-1136 — Mobile Settings-pagina
**Acceptance:**
- `app/(mobile)/m/settings/page.tsx`
- Toont username, isDemo-badge, actief-product-naam
- Product-selector — klik → `setActiveProductAction` + redirect `/m/products/[id]/solo`
- QR-pairing-instructie — link "Open scrum4me.app/login op je desktop om in te loggen via QR"
- Logout-knop met AlertDialog "Uitloggen?" → `logoutAction`
- Geen avatar-upload, geen bio-edit
- Tests render-states + logout-flow
**Tasks:** T-325 layout · T-326 logout-flow · T-327 tests
### ST-1137 — Mobile Product Backlog-pagina
**Acceptance:**
- `app/(mobile)/m/products/[id]/page.tsx` hergebruikt PbiList/StoryPanel/TaskPanel + backlog-store
- `BacklogSplitPane` rendert in tab-mode op `<1024px`; auto-switch op selectie blijft werken
- TaskDialog-searchParams wiring (`?newTask=`, `?editTask=`, `?storyId=`) werkt
- Cookie-key gescheiden: `split-pane:backlog-3-mobile:<id>`
- + knoppen voor PBI/Story/Task werken; demo blijft read-only
- Tests: page-rendering met initial state, tab-mode, click-cascade-flow
**Tasks:** T-328 page wrapper + cookie-key · T-329 TaskDialog wiring · T-330 tests
### ST-1138 — Mobile Solo Paneel
**Acceptance:**
- `app/(mobile)/m/products/[id]/solo/page.tsx` hergebruikt SoloBoard
- 3 kanban-kolommen blijven; horizontal scroll
- TaskDetailDialog rendert 100vw × 100vh op `<640px`**gedekt door beslissing A** (entityDialogContentClasses)
- "Voer uit"-knop bereikbaar
- SSE-stream blijft werken
- Tests: solo-page rendert, TaskDetailDialog erft mobile-classes (zonder eigen file-edit)
**Tasks:**
- T-331 page wrapper
- T-332 verify-only (geen file-edit; controleer dat shared constant uit ST-1133 doorwerkt)
- T-333 tests
### ST-1139 — Docs sync + end-to-end verificatie
**Acceptance:**
- `docs/specs/functional.md` heeft "Mobile shell"-sectie; desktop-first-clausule herzien
- `docs/architecture.md` beschrijft route group `(mobile)`, manifest landscape, UA-redirect, gedeelde auth-guard
- `npm run lint && npm test && npm run build` slagen
- E2E checklist (11 punten — zie hieronder)
- Bekende limiet: iOS Safari PWA-orientation-lock werkt niet 100% — CSS-overlay als fallback
**Tasks:** T-334 functional-spec · T-335 architecture-doc · T-336 E2E-verificatie
## Verificatie (E2E checklist uit T-336)
1. `npm run lint && npm test && npm run build` slagen
2. DevTools mobile-emulatie iPhone 12 landscape: `/m/products/[id]` rendert tab-mode, geen NavBar, tab-bar onderaan
3. Portrait → rotate-overlay zichtbaar; landscape → overlay verdwijnt
4. Tab-bar 3 iconen werken (Backlog/Solo/Settings)
5. Login phone-UA → redirect `/m/products/[id]/solo`; desktop-UA → `/dashboard`
6. Backlog-flow: + PBI, + Story, + Task in TaskDialog
7. Solo-flow: tap task → TaskDetailDialog full-screen, "Voer uit"-knop bereikbaar
8. TaskDialog full-screen op `<640px` (via shared constant)
9. PWA-installatie test op echte mobile (Android of iOS)
10. `/m/pair` QR-flow intact na route-group-verhuizing
11. Demo op mobile read-only; logout via `/m/settings` werkt; geen Scrum4Me-tekst of AppIcon op `/m/*`
## Out of scope
- Tablets (geen Mobi-UA) blijven desktop-flow gebruiken
- iOS PWA full-orientation-lock (CSS-overlay is fallback)
- Avatar/bio editor op mobile-settings
- 1-koloms-kanban (3-koloms blijft, swipe horizontaal)