feat: initial commit — local services (backend + manager dashboard + waiter PWA)
Includes all work to date: - local_backend: FastAPI backend with products, orders, tables, shifts, cloud sync - manager_dashboard: React manager UI with product/category management, reports, settings - waiter_pwa: React PWA for waiter devices - Category reparent endpoint and UI - Waiter domain: local_ip sent on heartbeat, waiter_domain persisted from cloud response - QR code modal in AppInfoTab for waiter domain - Product form: number input spinners removed, category pre-selected on new product - Category row: count badge moved to far right Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
90
manager_dashboard/src/App.jsx
Normal file
90
manager_dashboard/src/App.jsx
Normal file
@@ -0,0 +1,90 @@
|
||||
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 OperationsPage from './pages/OperationsPage'
|
||||
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="/operations" replace />} />
|
||||
<Route path="dashboard" element={<Navigate to="/operations" replace />} />
|
||||
<Route path="operations" element={<OperationsPage />} />
|
||||
<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>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user