const CACHE_NAME = 'avante-cache-v2'; const DATA_CACHE_NAME = 'avante-data-cache-v1'; // Files to cache for offline functionality const FILES_TO_CACHE = [ '/', '/dashboard', '/reports/dashboard', '/client', '/client/projects', '/manifest.json', '/css/app.css', '/js/app.js', 'https://unpkg.com/leaflet@1.9.4/dist/leaflet.css', 'https://unpkg.com/leaflet@1.9.4/dist/leaflet.js', 'https://cdnjs.cloudflare.com/ajax/libs/leaflet.draw/1.0.4/leaflet.draw.css', 'https://cdnjs.cloudflare.com/ajax/libs/leaflet.draw/1.0.4/leaflet.draw.js', 'https://fonts.bunny.net/css?family=figtree:400,500,600&display=swap', '/icons/icon-72x72.png', '/icons/icon-96x96.png', '/icons/icon-128x128.png', '/icons/icon-144x144.png', '/icons/icon-152x152.png', '/icons/icon-192x192.png', '/icons/icon-384x384.png', '/icons/icon-512x512.png' ]; // Install the service worker and cache the app shell self.addEventListener('install', (event) => { console.log('[ServiceWorker] Install'); event.waitUntil( caches.open(CACHE_NAME) .then((cache) => { console.log('[ServiceWorker] Caching app shell'); return cache.addAll(FILES_TO_CACHE); }) ); self.skipWaiting(); }); // Activate the service worker and clean up old caches self.addEventListener('activate', (event) => { console.log('[ServiceWorker] Activate'); event.waitUntil( caches.keys().then((keyList) => { return Promise.all( keyList.map((key) => { if (key !== CACHE_NAME && key !== DATA_CACHE_NAME) { console.log('[ServiceWorker] Removing old cache', key); return caches.delete(key); } }) ); }) ); self.clients.claim(); }); // Fetch strategy: Network first, falling back to cache self.addEventListener('fetch', (event) => { // Handle API requests differently - cache first, then network if (event.request.url.includes('/api/') || event.request.url.includes('/offline/')) { event.respondWith( caches.open(DATA_CACHE_NAME) .then((cache) => { return fetch(event.request) .then((response) => { // Clone the response to put in cache and return original if (response.status === 200) { cache.put(event.request.url, response.clone()); } return response; }) .catch(() => { // If network fails, try to get from cache return caches.match(event.request); }) }) ); return; } // For everything else, use cache first with network fallback event.respondWith( caches.match(event.request) .then((cachedResponse) => { if (cachedResponse) { return cachedResponse; } // If not in cache, fetch from network and cache it return caches.open(CACHE_NAME) .then((cache) => { return fetch(event.request) .then((networkResponse) => { // Don't cache non-successful responses if (networkResponse && networkResponse.status === 200 && networkResponse.type === 'basic') { cache.put(event.request, networkResponse.clone()); } return networkResponse; }); }); }) .catch(() => { // If both cache and network fail, show offline fallback if (event.request.mode === 'navigate') { return caches.match('/'); } }) ); }); // Handle background sync for offline actions self.addEventListener('sync', (event) => { console.log('[ServiceWorker] Background syncing', event.tag); if (event.tag === 'offline-sync') { event.waitUntil(syncOfflineActions()); } }); // Handle push notifications self.addEventListener('push', (event) => { console.log('[ServiceWorker] Push received'); const options = { body: event.data ? event.data.text() : 'Tienes una nueva notificación', icon: '/icons/icon-192x192.png', badge: '/icons/icon-72x72.png', vibrate: [100, 50, 100], data: { dateOfArrival: Date.now(), primaryKey: 1 } }; event.waitUntil( self.registration.showNotification('Avante', options) ); }); self.addEventListener('notificationclick', (event) => { console.log('[ServiceWorker] Notification click: ', event.notification); event.notification.close(); // Determine what to open based on notification data const urlToOpen = '/client'; // Default to client portal event.waitUntil( clients.matchAll({ type: 'window', includeUncontrolled: true }).then((clientList) => { for (const client of clientList) { if (client.url === urlToOpen && 'focus' in client) { return client.focus(); } } if (clients.openWindow) { return clients.openWindow(urlToOpen); } }) ); }); // Helper function to sync offline actions async function syncOfflineActions() { console.log('[ServiceWorker] Syncing offline actions'); // This would typically make a request to your backend to sync pending actions // For now, we'll just log it try { const response = await fetch('/offline/sync', { method: 'POST', headers: { 'Content-Type': 'application/json', } }); return response.json(); } catch (error) { console.error('[ServiceWorker] Sync failed:', error); throw error; } }