// Service Worker for PWA - Caching and offline support const CACHE_NAME = "route-commerce-v1"; const OFFLINE_URL = "/offline"; const STATIC_ASSETS = [ "/", "/manifest.json", "/favicon.svg", "/og-default.jpg", ]; // Install event - cache static assets self.addEventListener("install", (event) => { event.waitUntil( caches.open(CACHE_NAME).then((cache) => { return cache.addAll(STATIC_ASSETS); }) ); self.skipWaiting(); }); // Activate event - clean old caches self.addEventListener("activate", (event) => { event.waitUntil( caches.keys().then((cacheNames) => { return Promise.all( cacheNames .filter((name) => name !== CACHE_NAME) .map((name) => caches.delete(name)) ); }) ); self.clients.claim(); }); // Fetch event - network first, fallback to cache self.addEventListener("fetch", (event) => { // Skip non-GET requests if (event.request.method !== "GET") return; // Skip API requests if (event.request.url.includes("/api/")) return; // Skip cross-origin requests if (!event.request.url.startsWith(self.location.origin)) return; event.respondWith( fetch(event.request) .then((response) => { // Clone response for caching const responseClone = response.clone(); // Cache successful responses if (response.status === 200) { caches.open(CACHE_NAME).then((cache) => { cache.put(event.request, responseClone); }); } return response; }) .catch(() => { // Return cached response or offline page return caches.match(event.request).then((cachedResponse) => { if (cachedResponse) { return cachedResponse; } // Return offline page for navigation requests if (event.request.mode === "navigate") { return caches.match(OFFLINE_URL); } return new Response("Network error", { status: 408 }); }); }) ); }); // Push notification handling self.addEventListener("push", (event) => { if (!event.data) return; const data = event.data.json(); const options = { body: data.body, icon: "/icons/icon-192x192.png", badge: "/icons/badge-72x72.png", vibrate: [100, 50, 100], data: { url: data.url || "/", }, actions: data.actions || [], }; event.waitUntil(self.registration.showNotification(data.title, options)); }); // Notification click handling self.addEventListener("notificationclick", (event) => { event.notification.close(); const url = event.notification.data?.url || "/"; event.waitUntil( clients.matchAll({ type: "window", includeUncontrolled: true }).then((clientList) => { // Focus existing window or open new one for (const client of clientList) { if (client.url === url && "focus" in client) { return client.focus(); } } if (clients.openWindow) { return clients.openWindow(url); } }) ); }); // Background sync for offline actions self.addEventListener("sync", (event) => { if (event.tag === "sync-orders") { event.waitUntil(syncOrders()); } }); async function syncOrders() { // Implement order sync logic console.log("Syncing orders in background..."); }