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:
@@ -1,10 +1,12 @@
|
||||
import { createContext, useContext, useCallback, useEffect, useRef } from 'react'
|
||||
import { useQueryClient } from '@tanstack/react-query'
|
||||
import useAuthStore from '../store/authStore'
|
||||
import useConnectionStore from '../store/connectionStore'
|
||||
import { useSSE } from '../hooks/useSSE'
|
||||
import db from '../db/posdb'
|
||||
import client from '../api/client'
|
||||
import { flushOfflinePayments } from '../services/offlinePayments'
|
||||
import { invalidateProductCache } from '../hooks/useProductCache'
|
||||
|
||||
const SSEContext = createContext(null)
|
||||
|
||||
@@ -17,6 +19,7 @@ const HEARTBEAT_INTERVAL = 30_000
|
||||
export function SSEProvider({ children }) {
|
||||
const { token } = useAuthStore()
|
||||
const { setLost, setOnline } = useConnectionStore()
|
||||
const queryClient = useQueryClient()
|
||||
const sseAlive = useRef(false)
|
||||
const heartbeatRef = useRef(null)
|
||||
|
||||
@@ -97,10 +100,14 @@ export function SSEProvider({ children }) {
|
||||
await snapshotTables()
|
||||
break
|
||||
}
|
||||
case 'products_changed': {
|
||||
invalidateProductCache(queryClient)
|
||||
break
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
}, [snapshotTables])
|
||||
}, [snapshotTables, queryClient])
|
||||
|
||||
// ── SSE connection lifecycle ─────────────────────────────────────────────────
|
||||
|
||||
@@ -175,6 +182,32 @@ export function SSEProvider({ children }) {
|
||||
return () => window.removeEventListener('backend-offline', onBackendOffline)
|
||||
}, [])
|
||||
|
||||
// ── Wake-up handshake — fires when tab/app returns from background ────────────
|
||||
|
||||
useEffect(() => {
|
||||
if (!token) return
|
||||
async function onVisible() {
|
||||
if (document.visibilityState !== 'visible') return
|
||||
try {
|
||||
await client.get('/api/system/health')
|
||||
const currentStatus = useConnectionStore.getState().status
|
||||
if (currentStatus === 'lost' || currentStatus === 'emergency' || currentStatus === 'reconnecting') {
|
||||
setOnlineRef.current()
|
||||
reconnect()
|
||||
await fullRefresh()
|
||||
} else if (!sseAlive.current) {
|
||||
// SSE dropped silently while sleeping — re-establish quietly
|
||||
reconnect()
|
||||
await fullRefresh()
|
||||
}
|
||||
} catch {
|
||||
if (!sseAlive.current) setLostRef.current()
|
||||
}
|
||||
}
|
||||
document.addEventListener('visibilitychange', onVisible)
|
||||
return () => document.removeEventListener('visibilitychange', onVisible)
|
||||
}, [token, reconnect, fullRefresh])
|
||||
|
||||
// ── Initial snapshot on login ─────────────────────────────────────────────────
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
Reference in New Issue
Block a user