feat: major dashboard & waiter PWA overhaul
- Manager dashboard: replaced monolithic DashboardTab/OperationsPage with new DashboardPage; added OrderDetailModal, ShiftDetailModal, DeleteConfirmModal, PaymentMethodModal; updated Sidebar routing and App navigation - Reports: reworked WorkDaySummary, OrderHistory, ShiftsOverview with detail modals - Backend routers: extended orders, reports, shifts, products, business_day endpoints; updated cloud_sync service - Waiter PWA: refreshed app icons, improved ConnectionLostModal UX, updated TableCard, SSEContext, connectionStore; added useProductCache hook; vite config tweaks Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
69
waiter_pwa/src/hooks/useProductCache.js
Normal file
69
waiter_pwa/src/hooks/useProductCache.js
Normal file
@@ -0,0 +1,69 @@
|
||||
import { useQuery, useQueryClient } from '@tanstack/react-query'
|
||||
import { useEffect } from 'react'
|
||||
import client from '../api/client'
|
||||
import db from '../db/posdb'
|
||||
|
||||
export const PRODUCTS_KEY = ['products']
|
||||
export const CATEGORIES_KEY = ['categories']
|
||||
|
||||
async function fetchAndCacheProducts() {
|
||||
const [prodRes, catRes] = await Promise.all([
|
||||
client.get('/api/products/'),
|
||||
client.get('/api/products/categories'),
|
||||
])
|
||||
const products = prodRes.data
|
||||
const categories = catRes.data
|
||||
|
||||
// Write to IndexedDB in the background — don't await so UI isn't blocked
|
||||
db.products.bulkPut(products).catch(() => {})
|
||||
db.categories.bulkPut(categories).catch(() => {})
|
||||
|
||||
return { products, categories }
|
||||
}
|
||||
|
||||
async function loadFromCache() {
|
||||
const [products, categories] = await Promise.all([
|
||||
db.products.toArray(),
|
||||
db.categories.toArray(),
|
||||
])
|
||||
if (products.length === 0 && categories.length === 0) return null
|
||||
return { products, categories }
|
||||
}
|
||||
|
||||
export function useProductCache() {
|
||||
const queryClient = useQueryClient()
|
||||
|
||||
const query = useQuery({
|
||||
queryKey: PRODUCTS_KEY,
|
||||
queryFn: fetchAndCacheProducts,
|
||||
// Serve stale data instantly — products don't change every second
|
||||
staleTime: 5 * 60 * 1000, // 5 min before background re-fetch
|
||||
gcTime: 60 * 60 * 1000, // keep in memory for 1 hour
|
||||
placeholderData: undefined,
|
||||
})
|
||||
|
||||
// On mount, if the query has no data yet, seed it from IndexedDB immediately
|
||||
// so the UI renders without waiting for the network round-trip
|
||||
useEffect(() => {
|
||||
const cached = queryClient.getQueryData(PRODUCTS_KEY)
|
||||
if (cached) return
|
||||
|
||||
loadFromCache().then(idbData => {
|
||||
if (idbData) {
|
||||
// Set as placeholder — React Query will still fetch in background
|
||||
queryClient.setQueryData(PRODUCTS_KEY, idbData)
|
||||
}
|
||||
})
|
||||
}, [queryClient])
|
||||
|
||||
return {
|
||||
products: query.data?.products ?? [],
|
||||
categories: query.data?.categories ?? [],
|
||||
isLoading: query.isLoading && !query.data,
|
||||
}
|
||||
}
|
||||
|
||||
// Call this from SSEContext when products_changed arrives
|
||||
export function invalidateProductCache(queryClient) {
|
||||
queryClient.invalidateQueries({ queryKey: PRODUCTS_KEY })
|
||||
}
|
||||
Reference in New Issue
Block a user