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(
'<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100">' +
'<rect width="100" height="100" fill="#eee"/>' +
'<text x="50" y="50" text-anchor="middle" dy=".3em" fill="#999">' +
'Image</text></svg>',
{
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)
);
}
});