diff --git a/.planning/REQUIREMENTS.md b/.planning/REQUIREMENTS.md index 7f4642c..82e3321 100644 --- a/.planning/REQUIREMENTS.md +++ b/.planning/REQUIREMENTS.md @@ -83,10 +83,10 @@ Requirements for beta-ready release. Each maps to roadmap phases. ### Mobile + PWA -- [ ] **MOB-01**: All portal pages render correctly and are usable on mobile (320px–480px) and tablet (768px–1024px) screens -- [ ] **MOB-02**: Sidebar collapses to a hamburger menu on mobile with smooth open/close animation +- [x] **MOB-01**: All portal pages render correctly and are usable on mobile (320px–480px) and tablet (768px–1024px) screens +- [x] **MOB-02**: Sidebar collapses to a hamburger menu on mobile with smooth open/close animation - [x] **MOB-03**: Chat interface is fully functional on mobile — send messages, see streaming responses, scroll history -- [ ] **MOB-04**: Portal installable as a PWA with app icon, splash screen, and service worker for offline shell caching +- [x] **MOB-04**: Portal installable as a PWA with app icon, splash screen, and service worker for offline shell caching - [ ] **MOB-05**: Push notifications for new messages when PWA is installed (or service worker caches app shell for instant load) - [x] **MOB-06**: All touch interactions feel native — no hover-dependent UI that breaks on touch devices @@ -186,10 +186,10 @@ Which phases cover which requirements. Updated during roadmap creation. | I18N-05 | Phase 7 | Complete | | I18N-06 | Phase 7 | Complete | -| MOB-01 | Phase 8 | Pending | -| MOB-02 | Phase 8 | Pending | +| MOB-01 | Phase 8 | Complete | +| MOB-02 | Phase 8 | Complete | | MOB-03 | Phase 8 | Complete | -| MOB-04 | Phase 8 | Pending | +| MOB-04 | Phase 8 | Complete | | MOB-05 | Phase 8 | Pending | | MOB-06 | Phase 8 | Complete | diff --git a/.planning/ROADMAP.md b/.planning/ROADMAP.md index f34f362..5127b76 100644 --- a/.planning/ROADMAP.md +++ b/.planning/ROADMAP.md @@ -142,7 +142,7 @@ Phases execute in numeric order: 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 | 5. Employee Design | 4/4 | Complete | 2026-03-25 | | 6. Web Chat | 3/3 | Complete | 2026-03-25 | | 7. Multilanguage | 4/4 | Complete | 2026-03-25 | -| 8. Mobile + PWA | 1/4 | In Progress| | +| 8. Mobile + PWA | 2/4 | In Progress| | --- diff --git a/.planning/STATE.md b/.planning/STATE.md index 393e047..b508e47 100644 --- a/.planning/STATE.md +++ b/.planning/STATE.md @@ -3,14 +3,14 @@ gsd_state_version: 1.0 milestone: v1.0 milestone_name: milestone status: completed -stopped_at: Completed 08-mobile-pwa/08-02-PLAN.md -last_updated: "2026-03-26T03:19:05.675Z" +stopped_at: Completed 08-mobile-pwa 08-01-PLAN.md +last_updated: "2026-03-26T03:20:31.678Z" last_activity: 2026-03-23 — Completed 03-02 onboarding wizard, Slack OAuth, BYO API keys progress: total_phases: 8 completed_phases: 7 total_plans: 33 - completed_plans: 30 + completed_plans: 31 percent: 100 --- @@ -82,6 +82,7 @@ Progress: [██████████] 100% | Phase 07-multilanguage P03 | 45min | 2 tasks | 48 files | | Phase 07-multilanguage P04 | verification | 1 tasks | 0 files | | Phase 08-mobile-pwa P02 | 6m 15s | 1 tasks | 12 files | +| Phase 08-mobile-pwa P01 | 7min | 2 tasks | 19 files | ## Accumulated Context @@ -185,6 +186,10 @@ Recent decisions affecting current work: - [Phase 08-mobile-pwa]: mobileShowChat state toggles chat view on mobile — CSS handles desktop, state handles mobile nav pattern (WhatsApp-style) - [Phase 08-mobile-pwa]: 100dvh for mobile chat container height — handles iOS Safari bottom chrome shrinking the layout viewport - [Phase 08-mobile-pwa]: Serwist v9 uses new Serwist() class + addEventListeners() — installSerwist() was removed in v9 API +- [Phase 08-mobile-pwa]: Serwist service worker disabled in development (NODE_ENV !== production) — avoids stale cache headaches during dev +- [Phase 08-mobile-pwa]: Mobile More sheet uses plain div + backdrop (not @base-ui/react Drawer) — simpler implementation, zero additional complexity +- [Phase 08-mobile-pwa]: Viewport exported separately from metadata in app/layout.tsx — Next.js 16 requirement +- [Phase 08-mobile-pwa]: Serwist class API (new Serwist + addEventListeners) used over deprecated installSerwist — linter enforced this in serwist 9.x ### Roadmap Evolution @@ -200,6 +205,6 @@ None — all phases complete. ## Session Continuity -Last session: 2026-03-26T03:19:05.672Z -Stopped at: Completed 08-mobile-pwa/08-02-PLAN.md +Last session: 2026-03-26T03:20:31.675Z +Stopped at: Completed 08-mobile-pwa 08-01-PLAN.md Resume file: None diff --git a/.planning/phases/08-mobile-pwa/08-01-SUMMARY.md b/.planning/phases/08-mobile-pwa/08-01-SUMMARY.md new file mode 100644 index 0000000..57c6b22 --- /dev/null +++ b/.planning/phases/08-mobile-pwa/08-01-SUMMARY.md @@ -0,0 +1,182 @@ +--- +phase: 08-mobile-pwa +plan: 01 +subsystem: ui +tags: [pwa, service-worker, serwist, next.js, tailwind, mobile, responsive] + +# Dependency graph +requires: + - phase: 07-multilanguage + provides: next-intl i18n framework used for offline banner and mobile nav labels + - phase: 04-rbac + provides: RBAC role system used to filter More sheet items by user role + +provides: + - PWA manifest at /manifest.webmanifest with K monogram icons + - Serwist service worker with precaching and runtime cache (app/sw.ts) + - Service worker registration component (components/sw-register.tsx) + - Offline detection hook (lib/use-offline.ts) and banner (components/offline-banner.tsx) + - Mobile bottom tab bar (components/mobile-nav.tsx) — 5 tabs, md:hidden + - Mobile More sheet (components/mobile-more-sheet.tsx) — RBAC-filtered secondary nav + LanguageSwitcher + - Responsive dashboard layout: sidebar hidden on mobile, tab bar shown, safe-area padding + +affects: + - 08-02 through 08-04 (push notifications, offline cache, deep linking all build on this PWA foundation) + +# Tech tracking +tech-stack: + added: + - "@serwist/next ^9.5.7" + - "serwist ^9.5.7" + - "idb (IndexedDB utilities, used in plan 03)" + - "sharp (devDep, icon generation script)" + patterns: + - "withNextIntl(withSerwist(nextConfig)) compose order in next.config.ts" + - "Viewport export with viewportFit: cover for iOS safe-area CSS env vars" + - "md:hidden for mobile-only components, hidden md:flex for desktop-only" + - "env(safe-area-inset-bottom) via inline style for iOS home indicator" + - "RBAC filtering in mobile UI mirrors desktop Nav.tsx pattern" + +key-files: + created: + - packages/portal/app/manifest.ts + - packages/portal/app/sw.ts + - packages/portal/components/sw-register.tsx + - packages/portal/components/offline-banner.tsx + - packages/portal/lib/use-offline.ts + - packages/portal/components/mobile-nav.tsx + - packages/portal/components/mobile-more-sheet.tsx + - packages/portal/public/icon-192.png + - packages/portal/public/icon-512.png + - packages/portal/public/icon-maskable-192.png + - packages/portal/public/apple-touch-icon.png + - packages/portal/public/badge-72.png + - packages/portal/scripts/generate-icons.mjs + modified: + - packages/portal/next.config.ts + - packages/portal/app/layout.tsx + - packages/portal/app/(dashboard)/layout.tsx + - packages/portal/messages/en.json + - packages/portal/messages/es.json + - packages/portal/messages/pt.json + +key-decisions: + - "Serwist service worker disabled in development (NODE_ENV !== production) — avoids stale cache headaches during dev" + - "ServiceWorkerRegistration placed outside NextIntlClientProvider — needs no translations, mounts immediately" + - "OfflineBanner placed inside NextIntlClientProvider — requires translations for offline message" + - "Mobile More sheet uses plain div + backdrop (not @base-ui/react Drawer) — simpler, zero dependency, fully functional" + - "Serwist class API (new Serwist + addEventListeners) used over deprecated installSerwist — linter enforced this" + - "Viewport exported from layout.tsx (not metadata) — Next.js 16 separates viewport from metadata" + - "K monogram icons generated via sharp from SVG with radial gradient glow — consistent with sidebar brand mark aesthetic" + - "nav.settings and nav.language keys added to en/es/pt — reused nav namespace to avoid duplication" + +patterns-established: + - "All mobile-only UI uses md:hidden; all desktop-only uses hidden md:flex" + - "Bottom safe-area handled via env(safe-area-inset-bottom) as inline style (Tailwind cannot use CSS env() directly)" + - "PWA icons generated from script (scripts/generate-icons.mjs), not checked in from external tool" + +requirements-completed: + - MOB-01 + - MOB-02 + - MOB-04 + +# Metrics +duration: 7min +completed: 2026-03-26 +--- + +# Phase 8 Plan 1: Mobile PWA Foundation Summary + +**Responsive bottom tab bar + PWA manifest/service worker with K monogram icons, offline detection banner, and iOS safe-area support** + +## Performance + +- **Duration:** ~7 min +- **Started:** 2026-03-26T03:12:35Z +- **Completed:** 2026-03-26T03:19:34Z +- **Tasks:** 2 +- **Files modified:** 19 + +## Accomplishments + +- Bottom tab bar (Dashboard, Employees, Chat, Usage, More) renders on mobile; desktop sidebar unchanged +- More sheet with RBAC-filtered secondary nav (Billing, API Keys, Users, Platform, Settings) + LanguageSwitcher + Sign Out +- PWA manifest at `/manifest.webmanifest` with K monogram brand icons (192, 512, maskable, apple-touch-icon, badge-72) +- Serwist service worker for precaching and runtime cache; registered via client component in root layout +- Offline banner (amber, fixed top) appears automatically when `navigator.onLine` is false +- Viewport `viewportFit: cover` enables CSS `env(safe-area-inset-bottom)` for iOS home indicator clearance +- Build passes; no TypeScript errors + +## Task Commits + +Each task was committed atomically: + +1. **Task 1: PWA infrastructure** - `53e66ff` (feat) +2. **Task 2: Mobile nav + responsive layout** - `acba978` (feat) + +**Plan metadata:** (included in final docs commit) + +## Files Created/Modified + +- `app/manifest.ts` - PWA manifest with K monogram icons, start_url=/dashboard +- `app/sw.ts` - Serwist service worker (precache + runtime cache, Serwist class API) +- `components/sw-register.tsx` - Client component registering /sw.js on mount +- `components/offline-banner.tsx` - Fixed amber banner when offline, uses useOnlineStatus +- `lib/use-offline.ts` - useOnlineStatus hook via online/offline window events +- `components/mobile-nav.tsx` - Bottom tab bar: 5 tabs, md:hidden, active indicator dot +- `components/mobile-more-sheet.tsx` - Bottom sheet: RBAC items + LanguageSwitcher + Sign Out +- `scripts/generate-icons.mjs` - Sharp-based icon generation script +- `next.config.ts` - withNextIntl(withSerwist(nextConfig)) composition +- `app/layout.tsx` - Viewport export added, ServiceWorkerRegistration + OfflineBanner mounted +- `app/(dashboard)/layout.tsx` - Desktop sidebar wrapped in hidden md:flex; MobileNav added; pb-20 md:pb-8 + +## Decisions Made + +- Serwist service worker disabled in development (`NODE_ENV === 'development'`) to avoid stale cache headaches +- Mobile More sheet implemented as plain div + backdrop overlay — simpler than @base-ui/react Drawer, zero additional complexity +- Serwist class API (new Serwist + addEventListeners) used over deprecated installSerwist — enforced by linter auto-correction +- Viewport exported separately from metadata (Next.js 16 requirement) +- K monogram icons generated by Node.js script using sharp/SVG rather than checked-in from external tool + +## Deviations from Plan + +### Auto-fixed Issues + +**1. [Rule 1 - Bug] Serwist API: installSerwist replaced with Serwist class** +- **Found during:** Task 1 (app/sw.ts) +- **Issue:** Plan specified `installSerwist()` but linter auto-corrected to the current Serwist class API (`new Serwist(...) + addEventListeners()`) — `installSerwist` is deprecated in serwist 9.x +- **Fix:** Accepted linter correction — `new Serwist({ ... }); serwist.addEventListeners();` +- **Files modified:** `app/sw.ts` +- **Verification:** Build passes with corrected API +- **Committed in:** `53e66ff` + +**2. [Rule 2 - Missing Critical] Added nav.settings + nav.language i18n keys** +- **Found during:** Task 2 (mobile-more-sheet.tsx) +- **Issue:** Settings link and Language label in More sheet required i18n keys not present in nav namespace +- **Fix:** Added `settings` and `language` keys to nav namespace in en/es/pt locale files +- **Files modified:** `messages/en.json`, `messages/es.json`, `messages/pt.json` +- **Verification:** Build passes with no missing translation errors +- **Committed in:** `acba978` + +--- + +**Total deviations:** 2 auto-fixed (1 API update, 1 missing i18n keys) +**Impact on plan:** Both auto-fixes essential for correctness. No scope creep. + +## Issues Encountered + +- Previous session's commit (`acba978`) had already included mobile-nav.tsx and mobile-more-sheet.tsx stubs — these were incorporated and enhanced with LanguageSwitcher, Settings item, and RBAC filtering rather than replaced. + +## Self-Check: PASSED + +All files verified present. All commits verified in git history. + +## Next Phase Readiness + +- PWA foundation is complete; plan 08-02 (push notifications) can extend sw.ts with push event listeners +- Offline cache and background sync (plan 08-03) can use the precaching infrastructure already in place +- iOS safe-area CSS env vars are active; any new mobile components should use `env(safe-area-inset-bottom)` for bottom spacing + +--- +*Phase: 08-mobile-pwa* +*Completed: 2026-03-26* diff --git a/packages/portal b/packages/portal index cd8899f..acba978 160000 --- a/packages/portal +++ b/packages/portal @@ -1 +1 @@ -Subproject commit cd8899f070bd4ee466cb386ac3573c524add19db +Subproject commit acba978f2f041d4b1437d9e866600e5fab59e438