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:
parent
f7a425a5db
commit
3887e07af2
2 changed files with 199 additions and 0 deletions
|
|
@ -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 |
|
| [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 |
|
| [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 |
|
| [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-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-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 |
|
| [ST-1111 — Voer uit-knop met Claude Code job queue](./plans/ST-1111-claude-job-trigger.md) | active | 2026-05-03 |
|
||||||
|
|
|
||||||
198
docs/plans/PBI-11-mobile-shell.md
Normal file
198
docs/plans/PBI-11-mobile-shell.md
Normal 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)
|
||||||
Loading…
Add table
Add a link
Reference in a new issue