- Add manifest.json, sw.js with cache-first/network-first strategies - Add sync-engine.js with IndexedDB queue for offline operations - Add /pos/api/sync/inventory endpoint for offline inventory cache - Add /pos/sw.js route for proper SW scope registration - Generate PWA icons (192x192, 512x512) - Update all 10 HTML templates with manifest link, theme-color meta, SW registration, and sync-engine script Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
135 lines
4.1 KiB
JavaScript
135 lines
4.1 KiB
JavaScript
// /home/Autopartes/pos/static/pwa/sw.js
|
|
// Nexus POS — Service Worker v1
|
|
|
|
const CACHE_NAME = 'nexus-pos-v1';
|
|
|
|
const APP_SHELL = [
|
|
'/pos/login',
|
|
'/pos/sale',
|
|
'/pos/catalog',
|
|
'/pos/inventory',
|
|
'/pos/customers',
|
|
'/pos/invoicing',
|
|
'/pos/accounting',
|
|
'/pos/dashboard',
|
|
'/pos/config',
|
|
'/pos/reports',
|
|
'/pos/static/css/tokens.css',
|
|
'/pos/static/css/common.css',
|
|
'/pos/static/js/app-init.js',
|
|
'/pos/static/js/sidebar.js',
|
|
'/pos/static/js/login.js',
|
|
'/pos/static/js/pos.js',
|
|
'/pos/static/js/catalog.js',
|
|
'/pos/static/js/inventory.js',
|
|
'/pos/static/js/customers.js',
|
|
'/pos/static/js/invoicing.js',
|
|
'/pos/static/js/accounting.js',
|
|
'/pos/static/js/dashboard.js',
|
|
'/pos/static/js/config.js',
|
|
'/pos/static/js/reports.js',
|
|
'/pos/static/js/offline-banner.js',
|
|
'/pos/static/js/sync-engine.js',
|
|
'/pos/static/pwa/manifest.json',
|
|
'/pos/static/pwa/icon-192.png',
|
|
'/pos/static/pwa/icon-512.png'
|
|
];
|
|
|
|
// ─── Install: pre-cache app shell ────────────────────────────────
|
|
self.addEventListener('install', function (event) {
|
|
event.waitUntil(
|
|
caches.open(CACHE_NAME).then(function (cache) {
|
|
return cache.addAll(APP_SHELL);
|
|
}).then(function () {
|
|
return self.skipWaiting();
|
|
})
|
|
);
|
|
});
|
|
|
|
// ─── Activate: purge old caches ──────────────────────────────────
|
|
self.addEventListener('activate', function (event) {
|
|
event.waitUntil(
|
|
caches.keys().then(function (names) {
|
|
return Promise.all(
|
|
names.filter(function (n) { return n !== CACHE_NAME; })
|
|
.map(function (n) { return caches.delete(n); })
|
|
);
|
|
}).then(function () {
|
|
return self.clients.claim();
|
|
})
|
|
);
|
|
});
|
|
|
|
// ─── Fetch strategy ──────────────────────────────────────────────
|
|
self.addEventListener('fetch', function (event) {
|
|
var url = new URL(event.request.url);
|
|
|
|
// API calls → network-first
|
|
if (url.pathname.indexOf('/pos/api/') !== -1) {
|
|
event.respondWith(networkFirst(event.request));
|
|
return;
|
|
}
|
|
|
|
// Everything else → cache-first
|
|
event.respondWith(cacheFirst(event.request));
|
|
});
|
|
|
|
function cacheFirst(request) {
|
|
return caches.match(request).then(function (cached) {
|
|
if (cached) {
|
|
// Update cache in background
|
|
fetch(request).then(function (response) {
|
|
if (response && response.status === 200) {
|
|
caches.open(CACHE_NAME).then(function (cache) {
|
|
cache.put(request, response);
|
|
});
|
|
}
|
|
}).catch(function () { /* offline, ignore */ });
|
|
return cached;
|
|
}
|
|
return fetch(request).then(function (response) {
|
|
if (response && response.status === 200) {
|
|
var clone = response.clone();
|
|
caches.open(CACHE_NAME).then(function (cache) {
|
|
cache.put(request, clone);
|
|
});
|
|
}
|
|
return response;
|
|
});
|
|
});
|
|
}
|
|
|
|
function networkFirst(request) {
|
|
return fetch(request).then(function (response) {
|
|
if (response && response.status === 200) {
|
|
var clone = response.clone();
|
|
caches.open(CACHE_NAME).then(function (cache) {
|
|
cache.put(request, clone);
|
|
});
|
|
}
|
|
return response;
|
|
}).catch(function () {
|
|
return caches.match(request);
|
|
});
|
|
}
|
|
|
|
// ─── Background Sync ─────────────────────────────────────────────
|
|
self.addEventListener('sync', function (event) {
|
|
if (event.tag === 'nexus-pos-sync') {
|
|
event.respondWith && event.waitUntil(
|
|
self.clients.matchAll().then(function (clients) {
|
|
clients.forEach(function (client) {
|
|
client.postMessage({ type: 'SYNC_REQUESTED' });
|
|
});
|
|
})
|
|
);
|
|
}
|
|
});
|
|
|
|
// ─── Message handler ─────────────────────────────────────────────
|
|
self.addEventListener('message', function (event) {
|
|
if (event.data && event.data.type === 'SKIP_WAITING') {
|
|
self.skipWaiting();
|
|
}
|
|
});
|