# 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:` — 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 - `` toont rotate-overlay in portrait (window.matchMedia) - `` 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:` - + 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)