# 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)
