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:
2026-05-21 15:24:54 +03:00
parent aa92623802
commit 5de89a722c
40 changed files with 1906 additions and 1171 deletions

View File

@@ -4,26 +4,50 @@ import { create } from 'zustand'
* Tracks the live connection state and emergency mode flag.
*
* States:
* 'online' — server reachable, SSE connected, normal operation
* 'lost' — server unreachable, modal shown (Wait / Emergency)
* 'emergency' — user chose emergency mode, working from IndexedDB snapshot
* 'online' — server reachable, SSE connected, normal operation
* 'reconnecting' — connection blip detected; 5-second grace before showing full modal
* 'lost' — grace period expired, modal shown (Wait / Emergency)
* 'emergency' — user chose emergency mode, working from IndexedDB snapshot
*/
const GRACE_MS = 5_000
const useConnectionStore = create((set, get) => ({
status: 'online', // 'online' | 'lost' | 'emergency'
lostAt: null, // Date when connection was lost
status: 'online', // 'online' | 'reconnecting' | 'lost' | 'emergency'
lostAt: null,
_graceTimer: null,
setLost: () => {
if (get().status === 'online') {
set({ status: 'lost', lostAt: new Date() })
}
const { status, _graceTimer } = get()
// Already lost or in emergency — no-op
if (status === 'lost' || status === 'emergency') return
// Already in grace period — don't restart the timer
if (status === 'reconnecting') return
// Start grace period
const timer = setTimeout(() => {
// Only escalate if we're still in reconnecting (not recovered in the meantime)
if (get().status === 'reconnecting') {
set({ status: 'lost', _graceTimer: null })
}
}, GRACE_MS)
set({ status: 'reconnecting', lostAt: new Date(), _graceTimer: timer })
},
setOnline: () => set({ status: 'online', lostAt: null }),
setOnline: () => {
const { _graceTimer } = get()
if (_graceTimer) clearTimeout(_graceTimer)
set({ status: 'online', lostAt: null, _graceTimer: null })
},
enterEmergency: () => set({ status: 'emergency' }),
enterEmergency: () => {
const { _graceTimer } = get()
if (_graceTimer) clearTimeout(_graceTimer)
set({ status: 'emergency', _graceTimer: null })
},
// Called when server comes back while in emergency mode — triggers sync then go online
exitEmergency: () => set({ status: 'online', lostAt: null }),
exitEmergency: () => set({ status: 'online', lostAt: null, _graceTimer: null }),
isOnline: () => get().status === 'online',
isLost: () => get().status === 'lost',