diff --git a/frontend/index.html b/frontend/index.html
index f683e74..bdd7565 100644
--- a/frontend/index.html
+++ b/frontend/index.html
@@ -2,12 +2,43 @@
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
PriceGhost - Track Product Prices
+
+
+
diff --git a/frontend/public/icon.svg b/frontend/public/icon.svg
new file mode 100644
index 0000000..947eda9
--- /dev/null
+++ b/frontend/public/icon.svg
@@ -0,0 +1,21 @@
+
diff --git a/frontend/public/manifest.json b/frontend/public/manifest.json
new file mode 100644
index 0000000..5015675
--- /dev/null
+++ b/frontend/public/manifest.json
@@ -0,0 +1,25 @@
+{
+ "name": "PriceGhost",
+ "short_name": "PriceGhost",
+ "description": "Track product prices and get notified when they drop",
+ "start_url": "/",
+ "display": "standalone",
+ "background_color": "#0f172a",
+ "theme_color": "#6366f1",
+ "orientation": "portrait-primary",
+ "icons": [
+ {
+ "src": "/icon.svg",
+ "sizes": "any",
+ "type": "image/svg+xml",
+ "purpose": "any"
+ },
+ {
+ "src": "/icon.svg",
+ "sizes": "512x512",
+ "type": "image/svg+xml",
+ "purpose": "maskable"
+ }
+ ],
+ "categories": ["shopping", "finance", "utilities"]
+}
diff --git a/frontend/public/sw.js b/frontend/public/sw.js
new file mode 100644
index 0000000..1c26075
--- /dev/null
+++ b/frontend/public/sw.js
@@ -0,0 +1,64 @@
+const CACHE_NAME = 'priceghost-v1';
+const STATIC_ASSETS = [
+ '/',
+ '/icon.svg',
+ '/manifest.json'
+];
+
+// Install - cache static assets
+self.addEventListener('install', (event) => {
+ event.waitUntil(
+ caches.open(CACHE_NAME).then((cache) => {
+ return cache.addAll(STATIC_ASSETS);
+ })
+ );
+ self.skipWaiting();
+});
+
+// Activate - clean up old caches
+self.addEventListener('activate', (event) => {
+ event.waitUntil(
+ caches.keys().then((cacheNames) => {
+ return Promise.all(
+ cacheNames
+ .filter((name) => name !== CACHE_NAME)
+ .map((name) => caches.delete(name))
+ );
+ })
+ );
+ self.clients.claim();
+});
+
+// Fetch - network first, fallback to cache
+self.addEventListener('fetch', (event) => {
+ // Skip non-GET requests
+ if (event.request.method !== 'GET') return;
+
+ // Skip API requests - always go to network
+ if (event.request.url.includes('/api/')) return;
+
+ event.respondWith(
+ fetch(event.request)
+ .then((response) => {
+ // Clone the response before caching
+ const responseClone = response.clone();
+ caches.open(CACHE_NAME).then((cache) => {
+ cache.put(event.request, responseClone);
+ });
+ return response;
+ })
+ .catch(() => {
+ // Network failed, try cache
+ return caches.match(event.request).then((cachedResponse) => {
+ if (cachedResponse) {
+ return cachedResponse;
+ }
+ // If it's a navigation request, return the cached index
+ if (event.request.mode === 'navigate') {
+ return caches.match('/');
+ }
+ return new Response('Offline', { status: 503 });
+ });
+ })
+ );
+});