diff --git a/public/sw.js b/public/sw.js index 1952b88..630b115 100644 --- a/public/sw.js +++ b/public/sw.js @@ -1,182 +1,33 @@ -const CACHE_NAME = 'avante-cache-v2'; -const DATA_CACHE_NAME = 'avante-data-cache-v1'; +// ============================================================================ +// Service Worker de AUTO-LIMPIEZA (kill switch) +// ---------------------------------------------------------------------------- +// El SW anterior (PWA experimental) usaba estrategia cache-first para todo: +// - servía HTML/JS obsoletos tras cada deploy ("los cambios no se ven"), +// - intentaba cache.put() sobre POSTs (TypeError) rompiendo peticiones Livewire, +// - su precache incluía /css/app.css y /js/app.js, que no existen con Vite, +// haciendo fallar la instalación (Failed to execute 'addAll' on 'Cache'). +// +// Este SW sustituye al anterior en cuanto el navegador lo actualiza: +// borra TODOS los caches, se desregistra y recarga las pestañas abiertas. +// Mantener este fichero publicado un tiempo para que todos los clientes +// que tengan la PWA vieja instalada se limpien solos. +// ============================================================================ -// 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.addEventListener('install', () => { 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(); + event.waitUntil((async () => { + // 1) Borrar todos los caches que dejó la versión anterior + const keys = await caches.keys(); + await Promise.all(keys.map((key) => caches.delete(key))); + + // 2) Desregistrarse + await self.registration.unregister(); + + // 3) Recargar las pestañas controladas para que salgan del SW + const clients = await self.clients.matchAll({ type: 'window' }); + clients.forEach((client) => client.navigate(client.url)); + })()); }); - -// 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; - } -} diff --git a/resources/views/layouts/app.blade.php b/resources/views/layouts/app.blade.php index eea89b6..1679f1f 100644 --- a/resources/views/layouts/app.blade.php +++ b/resources/views/layouts/app.blade.php @@ -50,13 +50,12 @@ @stack('scripts') @livewireScripts + {{-- PWA retirada: el sw.js antiguo (cache-first) servía versiones obsoletas + tras cada deploy y rompía POSTs. El sw.js actual es de auto-limpieza: + se registra una última vez para borrar caches y desregistrarse. --}} diff --git a/resources/views/layouts/client.blade.php b/resources/views/layouts/client.blade.php index f1e2dba..2ed90a5 100644 --- a/resources/views/layouts/client.blade.php +++ b/resources/views/layouts/client.blade.php @@ -109,14 +109,11 @@ @stack('scripts') @livewireScripts