Snapshot of in-progress work across local_backend, manager_dashboard, and waiter_pwa (pricing, chat, fiscal, prep zones, recovery codes, CRM, inventory, permissions), plus the nginx/docker-compose deploy fixes for the Unraid + NPM reverse-proxy setup. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
78 lines
2.7 KiB
JavaScript
78 lines
2.7 KiB
JavaScript
import { create } from 'zustand'
|
|
|
|
/**
|
|
* Tracks live connection state.
|
|
*
|
|
* status:
|
|
* 'online' — server reachable, SSE connected, normal operation
|
|
* 'reconnecting' — blip detected, grace period (amber bar)
|
|
* 'offline' — confirmed unreachable, working from local DB (red bar)
|
|
*
|
|
* The old 'emergency' and 'lost' states are gone. The app now enters offline
|
|
* mode automatically — no user prompt needed.
|
|
*
|
|
* After 5 minutes offline the bar flashes to signal a serious problem.
|
|
*/
|
|
|
|
const GRACE_MS = 4_000 // how long to wait before declaring offline
|
|
const FLASH_MS = 5 * 60 * 1000 // 5 minutes before bar starts flashing
|
|
|
|
const useConnectionStore = create((set, get) => ({
|
|
status: 'online', // 'online' | 'reconnecting' | 'offline'
|
|
sseAlive: false, // true when SSE stream is actively connected
|
|
lostAt: null, // Date when connection was first lost
|
|
flashing: false, // true when offline > 5 min
|
|
_graceTimer: null,
|
|
_flashTimer: null,
|
|
|
|
setSseAlive: (alive) => set({ sseAlive: alive }),
|
|
|
|
setLost: () => {
|
|
const { status, _graceTimer } = get()
|
|
if (status === 'offline') return
|
|
if (status === 'reconnecting') return
|
|
|
|
const graceTimer = setTimeout(() => {
|
|
if (get().status !== 'reconnecting') return
|
|
const lostAt = get().lostAt
|
|
|
|
// Start the 5-min flash timer
|
|
const flashTimer = setTimeout(() => {
|
|
if (get().status === 'offline') set({ flashing: true })
|
|
}, FLASH_MS)
|
|
|
|
set({ status: 'offline', _graceTimer: null, _flashTimer: flashTimer })
|
|
}, GRACE_MS)
|
|
|
|
set({ status: 'reconnecting', lostAt: new Date(), _graceTimer: graceTimer })
|
|
},
|
|
|
|
setOnline: () => {
|
|
const { _graceTimer, _flashTimer } = get()
|
|
if (_graceTimer) clearTimeout(_graceTimer)
|
|
if (_flashTimer) clearTimeout(_flashTimer)
|
|
set({ status: 'online', lostAt: null, flashing: false, _graceTimer: null, _flashTimer: null })
|
|
},
|
|
|
|
// Convenience accessors
|
|
isOnline: () => get().status === 'online',
|
|
isOffline: () => get().status === 'offline' || get().status === 'reconnecting',
|
|
|
|
// Legacy compat — SSEContext still calls these
|
|
enterEmergency: () => {
|
|
// treated as manual offline entry (immediate, no grace period)
|
|
const { _graceTimer, _flashTimer } = get()
|
|
if (_graceTimer) clearTimeout(_graceTimer)
|
|
if (_flashTimer) clearTimeout(_flashTimer)
|
|
const flashTimer = setTimeout(() => {
|
|
if (get().status === 'offline') set({ flashing: true })
|
|
}, FLASH_MS)
|
|
set({ status: 'offline', lostAt: new Date(), flashing: false, _graceTimer: null, _flashTimer: flashTimer })
|
|
},
|
|
exitEmergency: () => get().setOnline(),
|
|
isEmergency: () => get().status === 'offline',
|
|
isLost: () => get().status === 'offline',
|
|
}))
|
|
|
|
export default useConnectionStore
|