const CACHE_NAME = 'bakery-cache-v1'; const STATIC_CACHE_NAME = 'bakery-static-v1'; const DYNAMIC_CACHE_NAME = 'bakery-dynamic-v1'; const FONT_CACHE_NAME = 'bakery-fonts-v1'; const IMAGE_CACHE_NAME = 'bakery-images-v1'; // Assets to cache on install const STATIC_ASSETS = [ '/', 'index.html', 'manifest.json', 'data/products.json', 'assets/css/main.css', 'assets/css/animations.css', 'assets/css/cart.css', 'assets/css/products.css', 'assets/css/modal.css', 'assets/css/additional.css', 'assets/js/config.js', 'assets/js/utils.js', 'assets/js/customer-manager.js', 'assets/js/cart.js', 'assets/js/auth.js', 'assets/js/products.js', 'assets/js/modal-manager.js', 'assets/js/payment-manager.js', 'assets/js/notification-service.js', 'assets/js/order-manager.js', 'assets/images/logo.svg', 'assets/images/hero-banner.svg', ]; // Font files to cache const FONT_FILES = [ 'https://fonts.googleapis.com/css2?family=Prompt:wght@300;400;500;600;700&display=swap', 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css', 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/webfonts/fa-solid-900.woff2', 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/webfonts/fa-regular-400.woff2', 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/webfonts/fa-brands-400.woff2' ]; // API routes that should be cached const API_ROUTES = [ '/api/products', '/api/categories' ]; // Install event - cache static assets self.addEventListener('install', event => { event.waitUntil( Promise.all([ // Cache static assets caches.open(STATIC_CACHE_NAME).then(cache => { console.log('Caching static assets'); return cache.addAll(STATIC_ASSETS); }), // Cache fonts caches.open(FONT_CACHE_NAME).then(cache => { console.log('Caching fonts'); return cache.addAll(FONT_FILES); }) ]) .then(() => self.skipWaiting()) ); }); // Activate event - clean up old caches self.addEventListener('activate', event => { event.waitUntil( Promise.all([ // Remove old caches caches.keys().then(cacheNames => { return Promise.all( cacheNames .filter(cacheName => { return cacheName.startsWith('bakery-') && ![STATIC_CACHE_NAME, DYNAMIC_CACHE_NAME, FONT_CACHE_NAME, IMAGE_CACHE_NAME].includes(cacheName); }) .map(cacheName => caches.delete(cacheName)) ); }), // Claim clients self.clients.claim() ]) ); }); // Fetch event - handle requests self.addEventListener('fetch', event => { const request = event.request; const url = new URL(request.url); // Handle different types of requests if (request.method !== 'GET') { // Don't cache non-GET requests return event.respondWith(fetch(request)); } // Font files if (FONT_FILES.some(font => request.url.includes(font))) { return event.respondWith(handleFontRequest(request)); } // API requests if (API_ROUTES.some(route => url.pathname.includes(route))) { return event.respondWith(handleApiRequest(request)); } // Image requests if (request.destination === 'image') { return event.respondWith(handleImageRequest(request)); } // Static assets if (STATIC_ASSETS.includes(url.pathname)) { return event.respondWith(handleStaticRequest(request)); } // Dynamic content return event.respondWith(handleDynamicRequest(request)); }); // Handle font requests async function handleFontRequest(request) { try { // Try network first const response = await fetch(request); const cache = await caches.open(FONT_CACHE_NAME); cache.put(request, response.clone()); return response; } catch (error) { // Fallback to cache const cachedResponse = await caches.match(request); return cachedResponse || Promise.reject('Font not found in cache'); } } // Handle API requests async function handleApiRequest(request) { try { // Network first for API requests const response = await fetch(request); const cache = await caches.open(DYNAMIC_CACHE_NAME); cache.put(request, response.clone()); return response; } catch (error) { const cachedResponse = await caches.match(request); if (cachedResponse) { return cachedResponse; } // If no cached response, return offline fallback return new Response( JSON.stringify({ error: 'ไม่สามารถเชื่อมต่อกับเซิร์ฟเวอร์ได้', offline: true }), { headers: {'Content-Type': 'application/json'} } ); } } // Handle image requests async function handleImageRequest(request) { try { // Try cache first for images const cachedResponse = await caches.match(request); if (cachedResponse) { return cachedResponse; } // If not in cache, fetch from network const response = await fetch(request); const cache = await caches.open(IMAGE_CACHE_NAME); cache.put(request, response.clone()); return response; } catch (error) { // Return placeholder image if fetch fails return new Response( '' + '' + '' + 'Image', { headers: {'Content-Type': 'image/svg+xml'} } ); } } // Handle static requests async function handleStaticRequest(request) { // Cache first strategy for static assets const cachedResponse = await caches.match(request); if (cachedResponse) { return cachedResponse; } // If not in cache, fetch from network try { const response = await fetch(request); const cache = await caches.open(STATIC_CACHE_NAME); cache.put(request, response.clone()); return response; } catch (error) { // Return offline page for navigation requests if (request.mode === 'navigate') { return caches.match('offline.html'); } return Promise.reject('Failed to fetch'); } } // Handle dynamic requests async function handleDynamicRequest(request) { // Network first strategy for dynamic content try { const response = await fetch(request); const cache = await caches.open(DYNAMIC_CACHE_NAME); cache.put(request, response.clone()); return response; } catch (error) { const cachedResponse = await caches.match(request); if (cachedResponse) { return cachedResponse; } // Return offline page for navigation requests if (request.mode === 'navigate') { return caches.match('offline.html'); } return Promise.reject('Failed to fetch'); } } // Handle sync events for offline actions self.addEventListener('sync', event => { if (event.tag === 'order-sync') { event.waitUntil(syncOrders()); } }); // Sync pending orders when online async function syncOrders() { try { const pendingOrders = await getPendingOrders(); for (const order of pendingOrders) { await sendOrder(order); } } catch (error) { console.error('Order sync failed:', error); } } // Push notification event self.addEventListener('push', event => { if (event.data) { const data = event.data.json(); const options = { body: data.message, icon: 'assets/images/icons/icon-192x192.png', badge: 'assets/images/icons/badge-72x72.png', vibrate: [100, 50, 100], data: { url: data.url } }; event.waitUntil( self.registration.showNotification(data.title, options) ); } }); // Notification click event self.addEventListener('notificationclick', event => { event.notification.close(); if (event.notification.data.url) { event.waitUntil( clients.openWindow(event.notification.data.url) ); } });