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
/apiresponses 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.
localhostremains 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 toindex.htmlfor non-API routes. - Existing brand assets:
public/favicon.svg,#3b66fftheme color, AgendaMax name and current Spanish UI. - Existing entry points:
src/main.tsx,src/App.tsx,LoginPage,AppShell, andAdminShell. - 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.webmanifestwithnameandshort_nameset to AgendaMax,start_url/,scope/,displaystandalone, portrait orientation, brand colors, and PNG icons at 192x192 and 512x512.sw.jswith 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:
unsupported: no UI.available: show an install action backed bybeforeinstallprompt.ios-instructions: show a short dialog explaining Safari's Share then Add to Home Screen flow.installed: no UI, detected fromdisplay-mode: standaloneandnavigator.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
- Browser loads the Vite entry document.
- Production client registers
/sw.js; the worker installs and claims future navigations. - The install hook evaluates browser capability and standalone state.
- Android/Chromium emits
beforeinstallprompt; the hook stores the event and exposes the CTA. - User selects the CTA; the hook calls the native prompt and handles the
resulting
appinstalledor dismissal event. - iOS Safari is identified when not standalone; the CTA opens the instruction dialog and does not attempt an unsupported API.
- 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 typecheckpasses.npm run buildpasses and emits the manifest, worker, icons, and metadata indist/.- 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:visualand 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
/apior non-GET requests. - Production HTTPS serving works through the existing Express/Docker path.
- Typecheck, build, PWA behavior checks, and visual audit pass.