Skip to content

feat: offline merchant dashboard — service worker, background sync & PWA install - #661

Open
micmusjnr20 wants to merge 1 commit into
Betta-Pay:mainfrom
micmusjnr20:feat/offline-merchant-dashboard
Open

feat: offline merchant dashboard — service worker, background sync & PWA install#661
micmusjnr20 wants to merge 1 commit into
Betta-Pay:mainfrom
micmusjnr20:feat/offline-merchant-dashboard

Conversation

@micmusjnr20

Copy link
Copy Markdown

Summary

The merchant dashboard was entirely network-dependent — no offline shell, no cached assets, no stale-while-revalidate for the app shell. This PR makes the dashboard a resilient, installable PWA:

  • Service worker (Workbox)scripts/sw-template.js is bundled by scripts/build-sw.mjs (rollup + workbox-build, wired into npm run build) into public/sw.js. It precaches the Next.js app shell (JS/CSS/fonts/icons), serves navigations network-first with a cached-shell fallback per route, and runtime-caches GET API list/detail responses stale-while-revalidate (same-origin /api/* and the NEXT_PUBLIC_API_URL origin), so list pages stay populated offline.
  • Offline banner — reuses offlineStore + useOnlineStatus: the banner reflects API reachability (healthz poll), not just navigator.onLine, and now also shows how many offline changes are waiting to sync. /healthz is never cached, so reachability is always live.
  • Background sync — payment links created and webhook test events sent while offline are queued in IndexedDB (lib/offline/syncQueue.ts, shared with the SW) and replayed automatically when connectivity returns (native sync + SW online event + client-side reconnect trigger). The payments page shows a pending-sync badge and refetches on SYNC_COMPLETE; the webhook tester marks queued deliveries "Queued (offline)" and flips them to delivered/failed after replay.
  • Installablepublic/manifest.webmanifest + generated icons (scripts/generate-icons.mjs) + components/layout/InstallPrompt.tsx capture beforeinstallprompt and let merchants install the dashboard. The middleware matcher now excludes sw.js, the manifest, and icons so they're never redirected to login.

How it works (walkthrough)

  1. First visit: the SW installs, precaches the shell, and SWR-caches API lists as the merchant browses (dashboard, payments).
  2. Go offline + reload: the SW serves the cached HTML/JS/CSS and answers API GETs from the stale-while-revalidate cache — the dashboard renders with data and the banner shows "You are offline / API server is unreachable".
  3. Create a payment link while offline: the POST is queued (toast + "N offline links waiting to sync" badge) instead of failing.
  4. Connectivity returns: the queued POST is replayed by the SW, the stale list is evicted so the new link appears immediately, and the badge clears.

Verification

  • npm run verify:offline — new script that drives a production build in headless Chromium through the full scenario. 13/13 checks passed:
✅ Service worker installed and controlling the page
✅ App shell precached (451 entries)
✅ GET API list response cached stale-while-revalidate
✅ HTML shell cached per route (offline navigation works)
✅ Browser reports offline after reload
✅ Dashboard renders offline with cached payment link
✅ Offline banner shown (API reachability via healthz)
✅ Offline-created payment link queued with confirmation toast
✅ Pending-sync badge appears in the header
✅ Queued payment-link POST replayed on reconnect — {"source":"Offline Invoice", ...}
✅ Background-synced link visible in the list
✅ Pending-sync badge cleared after sync
  • npm run build passes (451 precached assets, 15.7 MiB shell).
  • Jest: new lib/offline tests pass; the only failing suites are the same pre-existing ones that fail on main (verified via git stash comparison).
  • Playwright e2e (payments/auth/navigation): failure set is identical to main in this environment — no new regressions.

Notes for reviewers

  • The service worker is registered production-only (components/ui/service-worker-registration.tsx) so dev HMR is unaffected.
  • public/sw.js is build-generated and git-ignored — it embeds the build-time NEXT_PUBLIC_API_URL so the SW matches the same API origin the client uses.
  • GET/HEAD requests are no longer rejected when offline (lib/api/axios.ts); they pass through so the SW can answer from cache. Mutations still fail fast so callers can queue them for background sync.
  • React Query queries now use networkMode: 'offlineFirst' so they reach the SW when the browser reports offline.
  • On logout the SW's API cache is cleared (CLEAR_API_CACHE) so a different account never sees stale session/payment data.
  • Also fixed three pre-existing type errors in lib/api/hooks.ts (missing isFetching) that blocked next build.

Test plan

  1. npm run build — passes.
  2. NEXT_PUBLIC_API_URL=http://localhost:3000 npm run build && npm run verify:offline — 13/13 checks.
  3. Manual: install the app (install prompt in the merchant layout), toggle DevTools offline, reload /dashboard — shell + data render with banner; create a payment link offline; reconnect — it syncs and appears.

…WA install

Adds a Workbox service worker that precaches the app shell and serves GET
API list endpoints stale-while-revalidate, an API-reachability-aware offline
banner, background sync for payment links and webhook tests created while
offline, and a web manifest with icons plus an install prompt for the
merchant dashboard.

Verified end-to-end with `npm run verify:offline` (13/13 checks: the shell
renders offline with cached data and the banner; an offline-created payment
link is replayed on reconnect and appears in the list). `npm run build`
passes; jest shows only pre-existing failures.

Generated with Codebuff 🤖
Co-Authored-By: Codebuff <noreply@codebuff.com>
@vercel

vercel Bot commented Aug 29, 2026

Copy link
Copy Markdown

@micmusjnr20 is attempting to deploy a commit to the therealjhay's projects Team on Vercel.

A member of the Team first needs to authorize it.

@therealjhay

Copy link
Copy Markdown
Contributor

kindly resolve conflict

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants