Files
xenia-pos-local/manager_dashboard/src/App.jsx
bonamin 5de89a722c 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>
2026-05-21 15:24:54 +03:00

91 lines
3.1 KiB
JavaScript

import { useEffect, useState } from 'react'
import { BrowserRouter, Routes, Route, Navigate, useNavigate } from 'react-router-dom'
import useAuthStore from './store/authStore'
import AppLayout from './layouts/AppLayout'
import LoginPage from './pages/LoginPage'
import SetupWizard from './pages/SetupWizard'
import DashboardPage from './pages/DashboardPage'
import TablesPage from './pages/TablesPage'
import OrderDetailPage from './pages/OrderDetailPage'
import ManagementPage from './pages/ManagementPage'
import ReportsPage from './pages/reports/ReportsPage'
import SettingsPage from './pages/Settings/SettingsPage'
import client from './api/client'
function Spinner() {
return (
<div className="min-h-screen bg-slate-50 flex items-center justify-center">
<div className="w-6 h-6 rounded-full border-2 border-sky-500 border-t-transparent animate-spin" />
</div>
)
}
// Rehydrates user from stored token before rendering any routes.
// Prevents the flicker where a valid token causes a redirect to /login on refresh.
function AuthRehydrator({ children }) {
const { token, user, rehydrate, logout } = useAuthStore()
const [ready, setReady] = useState(false)
useEffect(() => {
if (token && !user) {
client.get('/api/auth/me')
.then(r => { rehydrate(r.data, token) })
.catch(() => { logout() })
.finally(() => setReady(true))
} else {
setReady(true)
}
}, []) // intentionally runs once on mount
if (!ready) return <Spinner />
return children
}
function RequireAuth({ children }) {
const token = useAuthStore(s => s.token)
return token ? children : <Navigate to="/login" replace />
}
// Checks /api/setup/status on mount and redirects to /setup if no managers exist.
function SetupGuard({ children }) {
const [checked, setChecked] = useState(false)
const navigate = useNavigate()
useEffect(() => {
client.get('/api/setup/status')
.then(({ data }) => {
if (data.needs_setup) navigate('/setup', { replace: true })
})
.catch(() => {
// Backend unreachable — proceed, login will surface the error.
})
.finally(() => setChecked(true))
}, [navigate])
if (!checked) return <Spinner />
return children
}
export default function App() {
return (
<BrowserRouter>
<AuthRehydrator>
<Routes>
<Route path="/setup" element={<SetupWizard />} />
<Route path="/login" element={<SetupGuard><LoginPage /></SetupGuard>} />
<Route path="/" element={<RequireAuth><AppLayout /></RequireAuth>}>
<Route index element={<Navigate to="/dashboard" replace />} />
<Route path="operations" element={<Navigate to="/dashboard" replace />} />
<Route path="dashboard" element={<DashboardPage />} />
<Route path="tables" element={<TablesPage />} />
<Route path="orders/:orderId" element={<OrderDetailPage />} />
<Route path="management" element={<ManagementPage />} />
<Route path="reports" element={<ReportsPage />} />
<Route path="settings" element={<SettingsPage />} />
</Route>
</Routes>
</AuthRehydrator>
</BrowserRouter>
)
}