--- 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*