first commit
BIN
public/Kashmina Rice.pdf
Normal file
BIN
public/Padmaja Singh.png
Normal file
|
After Width: | Height: | Size: 320 KiB |
BIN
public/Rajeev Singh.jpg
Normal file
|
After Width: | Height: | Size: 156 KiB |
BIN
public/android-chrome-192x192.png
Normal file
|
After Width: | Height: | Size: 19 KiB |
BIN
public/android-chrome-512x512.png
Normal file
|
After Width: | Height: | Size: 105 KiB |
BIN
public/apple-touch-icon.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
9
public/browserconfig.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<browserconfig>
|
||||
<msapplication>
|
||||
<tile>
|
||||
<square150x150logo src="/icons/mstile-150x150.png"/>
|
||||
<TileColor>#3B82F6</TileColor>
|
||||
</tile>
|
||||
</msapplication>
|
||||
</browserconfig>
|
||||
BIN
public/certifications/Frame-1000003749.png
Normal file
|
After Width: | Height: | Size: 8.1 KiB |
BIN
public/certifications/Frame-1000003750.png
Normal file
|
After Width: | Height: | Size: 3.3 KiB |
BIN
public/certifications/Frame-1000003751.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
public/certifications/image-371-1.png
Normal file
|
After Width: | Height: | Size: 25 KiB |
BIN
public/certifications/image-375-1.png
Normal file
|
After Width: | Height: | Size: 21 KiB |
BIN
public/certifications/image-377-2.png
Normal file
|
After Width: | Height: | Size: 39 KiB |
BIN
public/certifications/image-92.png
Normal file
|
After Width: | Height: | Size: 9.5 KiB |
BIN
public/factory.png
Normal file
|
After Width: | Height: | Size: 285 KiB |
BIN
public/farmer.png
Normal file
|
After Width: | Height: | Size: 3.1 MiB |
BIN
public/favicon-16x16.png
Normal file
|
After Width: | Height: | Size: 711 B |
BIN
public/favicon-32x32.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
public/favicon-96x96.png
Normal file
|
After Width: | Height: | Size: 7.3 KiB |
BIN
public/favicon.ico
Normal file
|
After Width: | Height: | Size: 1.2 MiB |
1
public/hero-bg.jpg
Normal file
@@ -0,0 +1 @@
|
||||
https://images.unsplash.com/photo-1586201375761-83865001e31c?w=1920&h=1080&fit=crop&crop=center
|
||||
BIN
public/images/kashmina_banner.png
Normal file
|
After Width: | Height: | Size: 389 KiB |
BIN
public/images/multigrain-flour.jpg
Normal file
|
After Width: | Height: | Size: 1.8 MiB |
BIN
public/images/rice-hero-slider.jpg
Normal file
|
After Width: | Height: | Size: 1.3 MiB |
BIN
public/kashmina-logo.png
Normal file
|
After Width: | Height: | Size: 210 KiB |
BIN
public/lab.png
Normal file
|
After Width: | Height: | Size: 305 KiB |
BIN
public/logo.png
Normal file
|
After Width: | Height: | Size: 1.4 MiB |
BIN
public/main-loading.jpg
Normal file
|
After Width: | Height: | Size: 2.0 MiB |
99
public/manifest.json
Normal file
@@ -0,0 +1,99 @@
|
||||
{
|
||||
"name": "Padmaaja Rasooi - Premium Rice Products",
|
||||
"short_name": "Padmaaja Rasooi",
|
||||
"description": "Premium quality rice products and grains. Experience the finest rice sourced directly from local farmers.",
|
||||
"start_url": "/",
|
||||
"display": "standalone",
|
||||
"background_color": "#ffffff",
|
||||
"theme_color": "#10B981",
|
||||
"orientation": "portrait-primary",
|
||||
"scope": "/",
|
||||
"lang": "en",
|
||||
"categories": ["business", "food", "shopping", "productivity"],
|
||||
"screenshots": [
|
||||
{
|
||||
"src": "/images/screenshot-wide.png",
|
||||
"sizes": "1280x720",
|
||||
"type": "image/png",
|
||||
"form_factor": "wide"
|
||||
},
|
||||
{
|
||||
"src": "/images/screenshot-narrow.png",
|
||||
"sizes": "375x812",
|
||||
"type": "image/png",
|
||||
"form_factor": "narrow"
|
||||
}
|
||||
],
|
||||
"icons": [
|
||||
{
|
||||
"src": "/android-chrome-192x192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable any"
|
||||
},
|
||||
{
|
||||
"src": "/android-chrome-512x512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable any"
|
||||
},
|
||||
{
|
||||
"src": "/apple-touch-icon.png",
|
||||
"sizes": "180x180",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "/favicon-96x96.png",
|
||||
"sizes": "96x96",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "/favicon-32x32.png",
|
||||
"sizes": "32x32",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "/favicon-16x16.png",
|
||||
"sizes": "16x16",
|
||||
"type": "image/png"
|
||||
}
|
||||
],
|
||||
"shortcuts": [
|
||||
{
|
||||
"name": "Dashboard",
|
||||
"short_name": "Dashboard",
|
||||
"description": "View your customer dashboard",
|
||||
"url": "/dashboard",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/icons/shortcut-dashboard.png",
|
||||
"sizes": "96x96"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Products",
|
||||
"short_name": "Shop",
|
||||
"description": "Browse products",
|
||||
"url": "/products",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/icons/shortcut-products.png",
|
||||
"sizes": "96x96"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Commissions",
|
||||
"short_name": "Earnings",
|
||||
"description": "Check your commissions",
|
||||
"url": "/dashboard/commissions",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/icons/shortcut-commissions.png",
|
||||
"sizes": "96x96"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
BIN
public/rice_bags.png
Normal file
|
After Width: | Height: | Size: 351 KiB |
370
public/sw.js
Normal file
@@ -0,0 +1,370 @@
|
||||
// Cache version - increment this when you want to force cache invalidation
|
||||
const CACHE_VERSION = Date.now()
|
||||
const CACHE_NAME = `padmaaja-rasooi-cache-v${CACHE_VERSION}`
|
||||
const STATIC_CACHE_NAME = `padmaaja-rasooi-static-v${CACHE_VERSION}`
|
||||
const DYNAMIC_CACHE_NAME = `padmaaja-rasooi-dynamic-v${CACHE_VERSION}`
|
||||
|
||||
// Build timestamp for cache busting
|
||||
const BUILD_TIMESTAMP = new Date().toISOString()
|
||||
|
||||
// App version from environment
|
||||
const APP_VERSION = self.APP_VERSION || '1.0.0'
|
||||
|
||||
// Files to cache immediately
|
||||
const STATIC_FILES = [
|
||||
'/',
|
||||
'/offline',
|
||||
'/manifest.json',
|
||||
'/icons/icon-192x192.png',
|
||||
'/icons/icon-512x512.png',
|
||||
'/apple-touch-icon.png',
|
||||
'/favicon-32x32.png',
|
||||
'/favicon-16x16.png',
|
||||
'/logo.png',
|
||||
]
|
||||
|
||||
// API routes that should be cached
|
||||
const API_ROUTES = [
|
||||
'/api/products',
|
||||
'/api/categories',
|
||||
'/api/dashboard',
|
||||
'/api/orders',
|
||||
'/api/cart',
|
||||
'/api/payments'
|
||||
]
|
||||
|
||||
// Install event - cache static files and force update
|
||||
self.addEventListener('install', (event) => {
|
||||
console.log('🚀 Service Worker: Installing... v' + CACHE_VERSION)
|
||||
console.log('📅 Build timestamp:', BUILD_TIMESTAMP)
|
||||
console.log('🏷️ App version:', APP_VERSION)
|
||||
|
||||
event.waitUntil(
|
||||
caches.open(STATIC_CACHE_NAME)
|
||||
.then((cache) => {
|
||||
console.log('📦 Service Worker: Caching static files')
|
||||
return cache.addAll(STATIC_FILES)
|
||||
})
|
||||
.then(() => {
|
||||
console.log('✅ Service Worker: Installation complete')
|
||||
// Force immediate activation of new service worker
|
||||
return self.skipWaiting()
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('❌ Service Worker: Installation failed', error)
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
// Activate event - clean up old caches and notify clients
|
||||
self.addEventListener('activate', (event) => {
|
||||
console.log('Service Worker: Activating... v' + CACHE_VERSION)
|
||||
event.waitUntil(
|
||||
caches.keys()
|
||||
.then((cacheNames) => {
|
||||
// Delete all old caches
|
||||
return Promise.all(
|
||||
cacheNames.map((cacheName) => {
|
||||
if (!cacheName.includes(`v${CACHE_VERSION}`)) {
|
||||
console.log('Service Worker: Deleting old cache', cacheName)
|
||||
return caches.delete(cacheName)
|
||||
}
|
||||
})
|
||||
)
|
||||
})
|
||||
.then(() => {
|
||||
console.log('Service Worker: Activation complete')
|
||||
// Take control of all pages immediately
|
||||
return self.clients.claim()
|
||||
})
|
||||
.then(() => {
|
||||
// Notify all clients about the update
|
||||
return self.clients.matchAll().then(clients => {
|
||||
clients.forEach(client => {
|
||||
client.postMessage({
|
||||
type: 'SW_UPDATED',
|
||||
version: CACHE_VERSION,
|
||||
timestamp: BUILD_TIMESTAMP,
|
||||
appVersion: APP_VERSION
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
// Fetch event - serve from cache or network
|
||||
self.addEventListener('fetch', (event) => {
|
||||
const { request } = event
|
||||
const url = new URL(request.url)
|
||||
|
||||
// Skip non-GET requests
|
||||
if (request.method !== 'GET') return
|
||||
|
||||
// Skip cross-origin requests
|
||||
if (url.origin !== location.origin) return
|
||||
|
||||
// Handle different types of requests
|
||||
if (url.pathname.startsWith('/api/')) {
|
||||
// API requests - network first, then cache
|
||||
event.respondWith(handleApiRequest(request))
|
||||
} else if (url.pathname.startsWith('/_next/static/')) {
|
||||
// Static assets - cache first
|
||||
event.respondWith(handleStaticAssets(request))
|
||||
} else {
|
||||
// Pages - network first, then cache
|
||||
event.respondWith(handlePageRequest(request))
|
||||
}
|
||||
})
|
||||
|
||||
// Handle API requests
|
||||
async function handleApiRequest(request) {
|
||||
const url = new URL(request.url)
|
||||
|
||||
try {
|
||||
// Try network first
|
||||
const networkResponse = await fetch(request)
|
||||
|
||||
// Cache successful responses for GET requests
|
||||
if (networkResponse.ok && request.method === 'GET') {
|
||||
const cache = await caches.open(DYNAMIC_CACHE_NAME)
|
||||
cache.put(request, networkResponse.clone())
|
||||
}
|
||||
|
||||
return networkResponse
|
||||
} catch (error) {
|
||||
// Network failed, try cache
|
||||
const cachedResponse = await caches.match(request)
|
||||
if (cachedResponse) {
|
||||
return cachedResponse
|
||||
}
|
||||
|
||||
// Return offline response for API calls
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
error: 'Network unavailable',
|
||||
offline: true,
|
||||
message: 'You are currently offline. Please check your internet connection to access fresh product information.'
|
||||
}),
|
||||
{
|
||||
status: 503,
|
||||
headers: { 'Content-Type': 'application/json' }
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Handle static assets
|
||||
async function handleStaticAssets(request) {
|
||||
try {
|
||||
// Try cache first
|
||||
const cachedResponse = await caches.match(request)
|
||||
if (cachedResponse) {
|
||||
return cachedResponse
|
||||
}
|
||||
|
||||
// Try network
|
||||
const networkResponse = await fetch(request)
|
||||
|
||||
// Cache the response
|
||||
const cache = await caches.open(STATIC_CACHE_NAME)
|
||||
cache.put(request, networkResponse.clone())
|
||||
|
||||
return networkResponse
|
||||
} catch (error) {
|
||||
// Return cached version or fail silently for assets
|
||||
return caches.match(request)
|
||||
}
|
||||
}
|
||||
|
||||
// Handle page requests
|
||||
async function handlePageRequest(request) {
|
||||
try {
|
||||
// Try network first
|
||||
const networkResponse = await fetch(request)
|
||||
|
||||
// Cache successful responses
|
||||
if (networkResponse.ok) {
|
||||
const cache = await caches.open(DYNAMIC_CACHE_NAME)
|
||||
cache.put(request, networkResponse.clone())
|
||||
}
|
||||
|
||||
return networkResponse
|
||||
} catch (error) {
|
||||
// Network failed, try cache
|
||||
const cachedResponse = await caches.match(request)
|
||||
if (cachedResponse) {
|
||||
return cachedResponse
|
||||
}
|
||||
|
||||
// Return offline page
|
||||
const offlineResponse = await caches.match('/offline')
|
||||
return offlineResponse || new Response('Offline', { status: 503 })
|
||||
}
|
||||
}
|
||||
|
||||
// Background sync for offline actions
|
||||
self.addEventListener('sync', (event) => {
|
||||
console.log('Service Worker: Background sync', event.tag)
|
||||
|
||||
if (event.tag === 'cart-sync') {
|
||||
event.waitUntil(syncCart())
|
||||
} else if (event.tag === 'order-sync') {
|
||||
event.waitUntil(syncOrders())
|
||||
} else if (event.tag === 'contact-sync') {
|
||||
event.waitUntil(syncContactForms())
|
||||
}
|
||||
})
|
||||
|
||||
// Sync cart data when online
|
||||
async function syncCart() {
|
||||
try {
|
||||
// Get stored cart data
|
||||
const cartData = await getStoredData('cart')
|
||||
if (cartData) {
|
||||
// Sync with server
|
||||
await fetch('/api/cart/sync', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(cartData)
|
||||
})
|
||||
|
||||
// Clear stored data
|
||||
await clearStoredData('cart')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Cart sync failed:', error)
|
||||
}
|
||||
}
|
||||
|
||||
// Sync order data when online
|
||||
async function syncOrders() {
|
||||
try {
|
||||
const orderData = await getStoredData('orders')
|
||||
if (orderData) {
|
||||
await fetch('/api/orders/sync', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(orderData)
|
||||
})
|
||||
|
||||
await clearStoredData('orders')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Order sync failed:', error)
|
||||
}
|
||||
}
|
||||
|
||||
// Sync contact form data when online
|
||||
async function syncContactForms() {
|
||||
try {
|
||||
const contactData = await getStoredData('contact')
|
||||
if (contactData) {
|
||||
await fetch('/api/contact/sync', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(contactData)
|
||||
})
|
||||
|
||||
await clearStoredData('contact')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Contact sync failed:', error)
|
||||
}
|
||||
}
|
||||
|
||||
// Push notification handling
|
||||
self.addEventListener('push', (event) => {
|
||||
console.log('Service Worker: Push notification received')
|
||||
|
||||
const options = {
|
||||
body: 'New authentic food products available at Padmaaja Rasooi!',
|
||||
icon: '/icons/icon-192x192.png',
|
||||
badge: '/icons/badge-72x72.png',
|
||||
vibrate: [100, 50, 100],
|
||||
data: {
|
||||
dateOfArrival: Date.now(),
|
||||
primaryKey: 1
|
||||
},
|
||||
actions: [
|
||||
{
|
||||
action: 'explore',
|
||||
title: 'View Products',
|
||||
icon: '/icons/action-products.png'
|
||||
},
|
||||
{
|
||||
action: 'close',
|
||||
title: 'Close',
|
||||
icon: '/icons/action-close.png'
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
event.waitUntil(
|
||||
self.registration.showNotification('Padmaaja Rasooi', options)
|
||||
)
|
||||
})
|
||||
|
||||
// Notification click handling
|
||||
self.addEventListener('notificationclick', (event) => {
|
||||
console.log('Service Worker: Notification clicked')
|
||||
|
||||
event.notification.close()
|
||||
|
||||
if (event.action === 'explore') {
|
||||
event.waitUntil(
|
||||
clients.openWindow('/products')
|
||||
)
|
||||
} else if (event.action === 'close') {
|
||||
// Just close the notification
|
||||
} else {
|
||||
// Default action - open app
|
||||
event.waitUntil(
|
||||
clients.openWindow('/')
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
// Handle messages from the main thread
|
||||
self.addEventListener('message', (event) => {
|
||||
console.log('Service Worker: Message received', event.data)
|
||||
|
||||
if (event.data && event.data.type === 'SKIP_WAITING') {
|
||||
// Force the waiting service worker to become the active service worker
|
||||
self.skipWaiting()
|
||||
}
|
||||
|
||||
if (event.data && event.data.type === 'CACHE_INVALIDATE') {
|
||||
// Clear all caches and force reload
|
||||
event.waitUntil(
|
||||
caches.keys().then(cacheNames => {
|
||||
return Promise.all(
|
||||
cacheNames.map(cacheName => caches.delete(cacheName))
|
||||
)
|
||||
}).then(() => {
|
||||
// Notify client that caches are cleared
|
||||
event.ports[0].postMessage({ success: true })
|
||||
})
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
// Utility functions
|
||||
async function getStoredData(key) {
|
||||
try {
|
||||
const cache = await caches.open(DYNAMIC_CACHE_NAME)
|
||||
const response = await cache.match(`/offline-data/${key}`)
|
||||
return response ? response.json() : null
|
||||
} catch (error) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
async function clearStoredData(key) {
|
||||
try {
|
||||
const cache = await caches.open(DYNAMIC_CACHE_NAME)
|
||||
await cache.delete(`/offline-data/${key}`)
|
||||
} catch (error) {
|
||||
console.error('Failed to clear stored data:', error)
|
||||
}
|
||||
}
|
||||