// LittleShop Admin - Service Worker // Provides offline functionality and app-like experience const CACHE_NAME = 'littleshop-admin-v2'; const urlsToCache = [ // Only cache static assets, not dynamic admin pages '/lib/bootstrap/css/bootstrap.min.css', '/lib/fontawesome/css/all.min.css', '/lib/bootstrap-icons/bootstrap-icons.css', '/css/modern-admin.css', '/lib/jquery/jquery.min.js', '/lib/bootstrap/js/bootstrap.bundle.min.js', '/js/modern-mobile.js', '/js/pwa.js', '/lib/fontawesome/webfonts/fa-solid-900.woff2', '/lib/fontawesome/webfonts/fa-brands-400.woff2' ]; // Install event - cache resources self.addEventListener('install', (event) => { console.log('SW: Installing service worker'); event.waitUntil( caches.open(CACHE_NAME) .then((cache) => { console.log('SW: Caching app shell'); return cache.addAll(urlsToCache); }) ); }); // Activate event - clean up old caches self.addEventListener('activate', (event) => { console.log('SW: Activating service worker'); event.waitUntil( caches.keys().then((cacheNames) => { return Promise.all( cacheNames.map((cacheName) => { if (cacheName !== CACHE_NAME) { console.log('SW: Deleting old cache:', cacheName); return caches.delete(cacheName); } }) ); }) ); }); // Push event - handle incoming push notifications self.addEventListener('push', (event) => { console.log('SW: Push notification received'); if (!event.data) { console.log('SW: Push notification received without data'); return; } try { const data = event.data.json(); console.log('SW: Push notification data:', data); const notificationOptions = { body: data.body || 'New notification', icon: data.icon || '/icons/icon-192x192.png', badge: data.badge || '/icons/icon-72x72.png', tag: 'littleshop-notification', requireInteraction: false, silent: false, data: data.data || {} }; event.waitUntil( self.registration.showNotification(data.title || 'LittleShop Admin', notificationOptions) ); } catch (error) { console.error('SW: Error parsing push notification data:', error); // Show generic notification event.waitUntil( self.registration.showNotification('LittleShop Admin', { body: 'New notification received', icon: '/icons/icon-192x192.png', badge: '/icons/icon-72x72.png', tag: 'littleshop-notification' }) ); } }); // Notification click event - handle user interaction with notifications self.addEventListener('notificationclick', (event) => { console.log('SW: Notification clicked'); event.notification.close(); const notificationData = event.notification.data || {}; // Determine URL to open let urlToOpen = '/Admin/Dashboard'; if (notificationData.url) { urlToOpen = notificationData.url; } else if (notificationData.orderId) { urlToOpen = `/Admin/Orders/Details/${notificationData.orderId}`; } event.waitUntil( clients.matchAll({ type: 'window', includeUncontrolled: true }) .then((clientList) => { // Check if there's already a window/tab open with the target URL for (const client of clientList) { if (client.url.includes(urlToOpen) && 'focus' in client) { return client.focus(); } } // If no window/tab is already open, open a new one if (clients.openWindow) { return clients.openWindow(urlToOpen); } }) ); }); // Fetch event - network-first for admin pages, cache-first for assets self.addEventListener('fetch', (event) => { // Only handle GET requests if (event.request.method !== 'GET') { return; } const url = new URL(event.request.url); // Network-first strategy for admin pages (always fresh data) if (url.pathname.startsWith('/Admin/')) { event.respondWith( fetch(event.request).catch(() => { // If network fails, try cache as fallback return caches.match(event.request); }) ); return; } // Cache-first strategy for static assets event.respondWith( caches.match(event.request) .then((response) => { // Return cached version or fetch from network return response || fetch(event.request).catch(() => { // If both cache and network fail, show offline page for HTML requests if (event.request.headers.get('accept').includes('text/html')) { return new Response(` LittleShop Admin - Offline
📱

You're offline

Please check your internet connection and try again.

Try Again `, { headers: { 'Content-Type': 'text/html' } }); } }); }) ); });