Files
AgendaPro/docs/superpowers/specs/2026-07-27-pwa-installability-design.md

7.5 KiB

AgendaMax PWA Installability

Goal

Make AgendaMax installable as a web app on mobile and desktop browsers, with a native installation prompt where the browser supports it and an explicit iOS installation guide where Apple does not expose that prompt API.

Decisions

  • Use a manually maintained manifest and service worker instead of adding vite-plugin-pwa. This keeps the caching policy visible and avoids a new build-time dependency for a small, already stable Vite app.
  • Use an online-first data model. The service worker may recover the application shell after a previous visit, but it must never cache /api responses or attempt offline writes for appointments, clients, cash, or settings.
  • Keep the existing AgendaMax visual language and Spanish copy. Installation is an optional secondary action, not a permanent mobile banner.
  • Require HTTPS in deployed environments. localhost remains valid for local testing because browsers treat it as a secure context.

Current Context

  • Frontend: React 18, TypeScript, Vite, Tailwind CSS, React Router.
  • Production serving: Express serves dist/ and falls back to index.html for non-API routes.
  • Existing brand assets: public/favicon.svg, #3b66ff theme color, AgendaMax name and current Spanish UI.
  • Existing entry points: src/main.tsx, src/App.tsx, LoginPage, AppShell, and AdminShell.
  • There is no manifest, service worker, install prompt handling, or iOS-specific home-screen metadata today.

Architecture

Static PWA assets

Add the following public assets, copied by Vite into dist/:

  • manifest.webmanifest with name and short_name set to AgendaMax, start_url /, scope /, display standalone, portrait orientation, brand colors, and PNG icons at 192x192 and 512x512.
  • sw.js with a versioned cache name.
  • PNG icons derived from the existing AgendaMax favicon mark. Keep the SVG favicon unchanged for browser tabs.

Update index.html with the manifest link, iOS home-screen metadata, an explicit mobile web app title, viewport-fit=cover, and the existing theme and description metadata.

Service worker

Register the worker from src/main.tsx only for production builds, using updateViaCache: "none" so a deployed worker is checked promptly.

The worker has these rules:

  • On install, cache only the stable shell/bootstrap assets and call skipWaiting.
  • On navigation requests, use network-first and fall back to the cached root document. This allows deployments to deliver fresh HTML while preserving a previously visited shell during a temporary outage.
  • On same-origin static GET requests, use cache-first for immutable or hashed assets and add successful responses to the runtime cache.
  • Bypass /api/, non-GET requests, cross-origin requests, and requests with credentials or other conditions that could expose tenant data.
  • On activation, remove caches from older versions and call clients.claim.

The worker is deliberately not a data-sync layer. If an API request fails, the existing application error/loading behavior remains authoritative.

Install state and UI

Create a small reusable install hook/component with these states:

  1. unsupported: no UI.
  2. available: show an install action backed by beforeinstallprompt.
  3. ios-instructions: show a short dialog explaining Safari's Share then Add to Home Screen flow.
  4. installed: no UI, detected from display-mode: standalone and navigator.standalone.

The hook must retain the deferred Android prompt only until it is used, handle appinstalled, and avoid showing the CTA repeatedly after a user dismisses it within the current browser session.

Render the reusable action in:

  • LoginPage, so a user can install before authentication;
  • the authenticated business mobile header/sidebar in AppShell;
  • the authenticated platform-admin mobile header/sidebar in AdminShell.

The action should remain a compact secondary button with a download icon. The iOS dialog is instructional and must not claim that a native prompt is available. It must not reserve a persistent bottom band or interfere with existing mobile navigation.

Use safe-area-aware spacing where the install dialog or mobile controls touch a screen edge.

Data Flow

  1. Browser loads the Vite entry document.
  2. Production client registers /sw.js; the worker installs and claims future navigations.
  3. The install hook evaluates browser capability and standalone state.
  4. Android/Chromium emits beforeinstallprompt; the hook stores the event and exposes the CTA.
  5. User selects the CTA; the hook calls the native prompt and handles the resulting appinstalled or dismissal event.
  6. iOS Safari is identified when not standalone; the CTA opens the instruction dialog and does not attempt an unsupported API.
  7. All authenticated API requests continue to use the existing fetch/auth path and always go to the network.

Error Handling and Compatibility

  • Failure to register the service worker is non-fatal and must not prevent the React application from rendering; log a concise warning in development only.
  • Missing or invalid install events result in hidden UI, not an exception.
  • Existing browsers that cannot install PWAs continue to work as normal web pages.
  • iOS installation is supported through Safari's system flow. Other iOS browsers may render the app but are not promised a direct install action.
  • The service worker must not intercept API errors, mutate auth tokens, or serve one user's tenant data to another user.

Testing and Verification

Automated

  • npm run typecheck passes.
  • npm run build passes and emits the manifest, worker, icons, and metadata in dist/.
  • Add a Playwright check for Chromium's deferred install event and prompt().
  • Add a Playwright check for iOS-style user-agent/standalone detection and the instructional dialog.
  • Add checks that unsupported and already-installed states do not render the CTA.
  • Verify the production Express server returns status 200 for the manifest, worker, and icon paths, while SPA fallback still serves client routes.
  • Run npm run audit:visual and confirm no horizontal overflow at 375, 768, 1280, or 1536 pixels.

Manual device acceptance

  • Android Chrome on HTTPS displays the native install action and opens AgendaMax in standalone mode with the correct icon/name.
  • iPhone Safari on HTTPS shows the two-step installation guide and the added home-screen app opens without browser chrome.
  • After a prior visit, temporarily disabling the network still allows the shell to load; API-backed data correctly remains unavailable rather than showing stale cached data.
  • A new deployment replaces the old worker/cache without requiring the user to clear browser storage manually.

Scope Exclusions

  • No offline appointment creation, edits, queued writes, conflict resolution, or background synchronization.
  • No native App Store/Play Store packaging.
  • No change to authentication, tenant isolation, API routes, or database schema.

Acceptance Criteria

  • AgendaMax has a valid install manifest and 192x192/512x512 icons.
  • Android/Chromium exposes an in-app install action when the native event is available.
  • iOS Safari exposes clear Share -> Add to Home Screen instructions.
  • Installed standalone mode hides the install action.
  • The service worker is registered in production, preserves the shell after a prior visit, and never caches /api or non-GET requests.
  • Production HTTPS serving works through the existing Express/Docker path.
  • Typecheck, build, PWA behavior checks, and visual audit pass.