Deploy to Production / Build & Verify (push) Failing after 5m56s
Ping Search Engines / Notify Search Engines (push) Successful in 2s
Deploy to Production / Pre-Deploy Tests (push) Has been skipped
Deploy to Production / Deploy to Railway (push) Has been skipped
Deploy to Production / Deploy to Render (push) Has been skipped
Deploy to Production / Deploy to VPS (PM2) (push) Has been skipped
Deploy to Production / Deploy to Fly.io (push) Has been skipped
Deploy to Production / Post-Deploy Verification (push) Has been skipped
Deploy to Production / Notify on Failure (push) Successful in 2s
E2E Test Suite / Critical User Journeys (push) Has been skipped
E2E Test Suite / API Integration Tests (push) Has been skipped
E2E Test Suite / Smoke Tests (P0) (push) Failing after 11m26s
E2E Test Suite / Form Interaction Tests (push) Failing after 11m42s
E2E Test Suite / Destructive & Chaos Tests (push) Failing after 12m2s
E2E Test Suite / Cross-Browser Regression (chromium) (push) Failing after 16m14s
E2E Test Suite / Cross-Browser Regression (webkit) (push) Failing after 17m45s
E2E Test Suite / Cross-Browser Regression (firefox) (push) Failing after 25m23s
E2E Test Suite / Security Header Tests (push) Failing after 7m55s
E2E Test Suite / Test Report Summary (push) Failing after 20s
E2E Test Suite / Mobile Device Tests (push) Failing after 2h49m9s
Uptime Monitor / Health & Response Time (push) Failing after 2s
Uptime Monitor / SSL Certificate (push) Successful in 2s
Uptime Monitor / Send Alerts (push) Failing after 3s
Uptime Monitor / Record Uptime Success (push) Has been skipped
196 lines
6.7 KiB
Markdown
196 lines
6.7 KiB
Markdown
# PWA Implementation — WorkRoot IT Solutions
|
||
|
||
## Overview
|
||
|
||
Progressive Web App (PWA) features have been added to the WorkRoot website. The implementation follows a **progressive enhancement** approach — the site works fully without JavaScript; PWA features layer on top.
|
||
|
||
---
|
||
|
||
## Files Added / Modified
|
||
|
||
| File | Type | Purpose |
|
||
|------|------|---------|
|
||
| `public/manifest.json` | New | Web App Manifest (installability) |
|
||
| `public/sw.js` | New | Service Worker (offline + background sync) |
|
||
| `src/pages/offline.astro` | New | Offline fallback page |
|
||
| `src/components/PWAInstallPrompt.astro` | New | Install prompt banner + background sync helper |
|
||
| `src/layouts/BaseLayout.astro` | Modified | Links manifest, registers SW, includes install prompt |
|
||
| `src/middleware.ts` | Modified | CSP updated: `worker-src 'self'`, `manifest-src 'self'` |
|
||
|
||
---
|
||
|
||
## Features Implemented
|
||
|
||
### 1. Web App Manifest (`public/manifest.json`)
|
||
|
||
- **Display mode:** `standalone` (hides browser chrome when installed)
|
||
- **Theme color:** `#0891b2` (matches brand primary)
|
||
- **Background color:** `#0f172a` (dark splash screen)
|
||
- **Shortcuts:** Quick links to `/contact` and `/services` from the home screen
|
||
- **Icons:** Uses existing `apple-touch-icon.png` and `favicon.svg`
|
||
|
||
### 2. Service Worker (`public/sw.js`)
|
||
|
||
**Caching Strategies:**
|
||
|
||
| Request Type | Strategy | Details |
|
||
|---|---|---|
|
||
| Static assets (JS, CSS, images, fonts) | Cache-first | Served from cache; fetched and cached on miss |
|
||
| HTML pages | Stale-while-revalidate | Serve cache instantly, update in background |
|
||
| API routes (`/api/*`) | Network-only | Never cached — always fresh |
|
||
|
||
**Pre-cached on install:**
|
||
- `/` (home)
|
||
- `/offline` (fallback page)
|
||
- `/manifest.json`
|
||
- `/favicon.svg`
|
||
- `/apple-touch-icon.png`
|
||
|
||
**Cache names (versioned for easy invalidation):**
|
||
- `workroot-static-v1`
|
||
- `workroot-pages-v1`
|
||
|
||
To invalidate caches on next deploy, increment `CACHE_VERSION` in `public/sw.js`.
|
||
|
||
### 3. Offline Fallback (`src/pages/offline.astro`)
|
||
|
||
- Served at `/offline`
|
||
- Clean branded page with "Try Again" and "Go to Home" actions
|
||
- Pre-cached by the service worker on install
|
||
- Shown automatically when a navigation request fails offline
|
||
|
||
### 4. Background Sync (`public/sw.js` + `src/components/PWAInstallPrompt.astro`)
|
||
|
||
When a form submission fails due to no network:
|
||
|
||
1. Form handler calls `window.queueFormSync(url, method, body, formType)`
|
||
2. The payload is stored in **IndexedDB** (`workroot-pwa` DB, `workroot-sync-queue` store)
|
||
3. A background sync tag `form-sync` is registered with the browser
|
||
4. When connectivity is restored, the service worker automatically retries all queued submissions
|
||
5. On success, clients receive a `pwa:sync-success` custom event they can listen to
|
||
|
||
**Usage in form pages:**
|
||
```js
|
||
// In contact or newsletter form submit handler:
|
||
try {
|
||
const res = await fetch('/api/contact', { method: 'POST', body: JSON.stringify(data) });
|
||
if (!res.ok) throw new Error('Server error');
|
||
// handle success
|
||
} catch {
|
||
if (!navigator.onLine) {
|
||
await window.queueFormSync('/api/contact', 'POST', data, 'contact');
|
||
showMessage('You are offline. Your message will be sent when you reconnect.');
|
||
}
|
||
}
|
||
|
||
// Listen for sync success:
|
||
window.addEventListener('pwa:sync-success', (e) => {
|
||
if (e.detail.formType === 'contact') {
|
||
showMessage('Your message was sent successfully!');
|
||
}
|
||
});
|
||
```
|
||
|
||
### 5. Install Prompt (`src/components/PWAInstallPrompt.astro`)
|
||
|
||
- Listens for `beforeinstallprompt` event (Chrome/Edge/Android)
|
||
- Shows a non-intrusive banner after 4 seconds on first visit
|
||
- Banner is suppressed for 7 days after dismissal (stored in `localStorage`)
|
||
- Hides immediately after successful installation (`appinstalled` event)
|
||
- Accessible: uses `role="banner"`, `aria-label`, button labels
|
||
|
||
---
|
||
|
||
## Security Considerations
|
||
|
||
The CSP in `src/middleware.ts` was updated to include:
|
||
|
||
```
|
||
worker-src 'self' → allows service worker from same origin only
|
||
manifest-src 'self' → allows manifest.json from same origin only
|
||
```
|
||
|
||
These are deliberately restrictive — no external workers or manifests are permitted.
|
||
|
||
---
|
||
|
||
## Offline Functionality Testing
|
||
|
||
### Manual Testing in Chrome DevTools
|
||
|
||
1. Open DevTools → **Application** tab
|
||
2. **Service Workers** panel:
|
||
- Confirm `sw.js` is registered and active
|
||
- Use "Offline" checkbox to simulate offline
|
||
3. **Manifest** panel:
|
||
- Verify all manifest fields are correct
|
||
- Check "Add to home screen" works
|
||
4. **Cache Storage** panel:
|
||
- Verify `workroot-static-v1` and `workroot-pages-v1` exist with expected entries
|
||
5. Navigate to `/contact` while offline → should serve cached page
|
||
6. Navigate to a page not in cache while offline → should show `/offline`
|
||
|
||
### Automated Testing
|
||
|
||
The offline behavior can be tested with Playwright:
|
||
|
||
```typescript
|
||
// In a Playwright test:
|
||
await context.setOffline(true);
|
||
await page.goto('/contact');
|
||
// Should either show cached page or /offline fallback
|
||
```
|
||
|
||
---
|
||
|
||
## Updating the Service Worker
|
||
|
||
To force all users to get the new service worker immediately:
|
||
|
||
1. Increment `CACHE_VERSION` in `public/sw.js` (e.g. `'v1'` → `'v2'`)
|
||
2. Old caches will be deleted during the `activate` event
|
||
3. `skipWaiting()` + `clients.claim()` ensure immediate takeover
|
||
|
||
---
|
||
|
||
## Lighthouse PWA Checklist
|
||
|
||
| Criterion | Status |
|
||
|-----------|--------|
|
||
| Registers a service worker | ✅ |
|
||
| Responds with 200 when offline | ✅ (cached pages + `/offline` fallback) |
|
||
| `<meta name="viewport">` set | ✅ (BaseLayout) |
|
||
| `<meta name="theme-color">` set | ✅ `#0891b2` |
|
||
| Web App Manifest with `name`, `short_name`, `icons` | ✅ |
|
||
| Icons at 192×192 and 512×512 | ⚠️ Only `apple-touch-icon.png` (180×180) — add larger icons for perfect score |
|
||
| Manifest `display: standalone` | ✅ |
|
||
| HTTPS in production | ✅ (HSTS enforced) |
|
||
| Install prompt supported | ✅ |
|
||
|
||
### Recommended Improvement
|
||
|
||
Add 192×192 and 512×512 PNG icons to `public/` and reference them in `manifest.json` to achieve a perfect Lighthouse PWA score:
|
||
|
||
```json
|
||
{
|
||
"src": "/icon-192.png",
|
||
"sizes": "192x192",
|
||
"type": "image/png",
|
||
"purpose": "any"
|
||
},
|
||
{
|
||
"src": "/icon-512.png",
|
||
"sizes": "512x512",
|
||
"type": "image/png",
|
||
"purpose": "any maskable"
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## Known Limitations
|
||
|
||
- **iOS Safari:** Background sync is not supported. Queued forms will be retried when the user next opens the app with network access (via the SW `sync` event on supported platforms). On iOS, consider showing a manual retry prompt.
|
||
- **SSR + Service Worker:** Since Astro runs SSR, HTML responses are dynamic. The stale-while-revalidate strategy serves potentially stale HTML while fetching fresh content in the background. This is acceptable for a marketing site.
|
||
- **API routes are always network-only.** This is intentional — form submissions and the health API must never serve stale responses.
|