Initial kitchen scaffold — Phase 1 kitchen port (build-verified 2026-07-11)
FastAPI backend (Python 3.11, MSSQL ODBC for SambaPOS, Azure DI OCR),
kitchen_db on central PG. React/TS/Vite frontend with navy sidebar layout.
Backend: auth.py (APP_SLUG=kitchen, SimpleNamespace — archive routes use
.kitchen_id/.is_admin without modification), main.py (51 migrations, scheduler,
internal router for KDS bookings feed), api/internal.py, full archive API
(31 routers: invoices, recipes, menus, sambapos, resos, newbook, disputes,
purchase_orders, etc.), models, migrations, OCR pipeline.
kitchen_id pinned to 1 (B1 — single hotel).
Frontend: AuthGate (app=kitchen, token shim for archive compat — B5b pending),
Layout (navy sidebar, 6 sections, Lucide icons, teal --app-primary),
App.tsx (Outlet pattern, UploadApp outside Layout), index.css (full :root block).
strict: false — archive components have type issues; build clean.
Note: 45 archive components call fetch('/api/...') without /kitchen/ prefix
(B5b). Runtime 404s; deferred until after initial testing.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
commit
8d688b459d
10003 changed files with 1928395 additions and 0 deletions
123
frontend/src/components/Layout.tsx
Normal file
123
frontend/src/components/Layout.tsx
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
import { NavLink, Outlet } from 'react-router-dom'
|
||||
import {
|
||||
LayoutDashboard, Upload, FileText, Search, List, AlertCircle,
|
||||
ShoppingCart, TrendingUp, BarChart2, PieChart, Activity, RefreshCw,
|
||||
Calendar, Users, Hotel, BookOpen, Wheat, ChefHat, UtensilsCrossed,
|
||||
Menu, ShieldCheck, Package, Settings, ChevronRight,
|
||||
} from 'lucide-react'
|
||||
import { useAuth } from './AuthGate'
|
||||
import { can } from '../types'
|
||||
|
||||
const ICON = { size: 15, strokeWidth: 1.75 }
|
||||
|
||||
function SidebarSection({ label, children }: { label: string; children: React.ReactNode }) {
|
||||
return (
|
||||
<div className="sidebar-section">
|
||||
<div className="sidebar-section-label">{label}</div>
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function NavItem({ to, label, icon: Icon }: { to: string; label: string; icon: React.ComponentType<any> }) {
|
||||
return (
|
||||
<NavLink to={to} className={({ isActive }) => isActive ? 'active' : ''}>
|
||||
<Icon {...ICON} />
|
||||
{label}
|
||||
</NavLink>
|
||||
)
|
||||
}
|
||||
|
||||
export default function Layout() {
|
||||
const { user } = useAuth()
|
||||
|
||||
return (
|
||||
<div className="app-shell">
|
||||
<aside className="sidebar">
|
||||
<div className="sidebar-logo">
|
||||
<ChefHat size={18} strokeWidth={1.75} color="var(--app-primary)" />
|
||||
Kitchen
|
||||
</div>
|
||||
|
||||
<nav className="sidebar-nav">
|
||||
<NavItem to="/dashboard" label="Dashboard" icon={LayoutDashboard} />
|
||||
|
||||
{can(user, 'invoices') && (
|
||||
<SidebarSection label="Invoices">
|
||||
<NavItem to="/upload" label="Upload" icon={Upload} />
|
||||
<NavItem to="/invoices" label="Invoices" icon={FileText} />
|
||||
<NavItem to="/search/invoices" label="Search" icon={Search} />
|
||||
<NavItem to="/search/line-items" label="Line Items" icon={List} />
|
||||
{can(user, 'disputes') && <NavItem to="/disputes" label="Disputes" icon={AlertCircle} />}
|
||||
{can(user, 'orders') && <NavItem to="/purchase-orders" label="Purchase Orders" icon={ShoppingCart} />}
|
||||
</SidebarSection>
|
||||
)}
|
||||
|
||||
{can(user, 'view') && (
|
||||
<SidebarSection label="Reports">
|
||||
<NavItem to="/gp" label="Kitchen Flash" icon={TrendingUp} />
|
||||
<NavItem to="/purchases-report" label="Purchases" icon={BarChart2} />
|
||||
<NavItem to="/allowances-report" label="Allowances" icon={PieChart} />
|
||||
<NavItem to="/sales-gp" label="Sales GP%" icon={Activity} />
|
||||
<NavItem to="/usage-variance" label="Usage Variance" icon={ChevronRight} />
|
||||
<NavItem to="/purchases/reconcile" label="Reconcile" icon={RefreshCw} />
|
||||
</SidebarSection>
|
||||
)}
|
||||
|
||||
{can(user, 'logbook') && (
|
||||
<SidebarSection label="Kitchen">
|
||||
<NavItem to="/logbook" label="Allowance Logbook" icon={BookOpen} />
|
||||
<NavItem to="/budget" label="Budget" icon={PieChart} />
|
||||
{can(user, 'orders') && <NavItem to="/event-orders" label="Event Orders" icon={Package} />}
|
||||
</SidebarSection>
|
||||
)}
|
||||
|
||||
{can(user, 'view') && (
|
||||
<SidebarSection label="Bookings">
|
||||
<NavItem to="/resos" label="ResOS Calendar" icon={Calendar} />
|
||||
<NavItem to="/resos-stats" label="Booking Stats" icon={BarChart2} />
|
||||
<NavItem to="/residents-table-chart" label="Residents Chart" icon={Users} />
|
||||
<NavItem to="/newbook" label="NewBook Data" icon={Hotel} />
|
||||
</SidebarSection>
|
||||
)}
|
||||
|
||||
{can(user, 'recipes') && (
|
||||
<SidebarSection label="Recipes">
|
||||
<NavItem to="/ingredients" label="Ingredients" icon={Wheat} />
|
||||
<NavItem to="/recipes" label="Recipes" icon={ChefHat} />
|
||||
<NavItem to="/dishes" label="Dishes" icon={UtensilsCrossed} />
|
||||
{can(user, 'menus') && <NavItem to="/menus" label="Menus" icon={Menu} />}
|
||||
<NavItem to="/allergens" label="Allergens" icon={ShieldCheck} />
|
||||
<NavItem to="/price-impact" label="Price Impact" icon={TrendingUp} />
|
||||
</SidebarSection>
|
||||
)}
|
||||
|
||||
{can(user, 'settings') && (
|
||||
<SidebarSection label="">
|
||||
<NavItem to="/settings" label="Settings" icon={Settings} />
|
||||
</SidebarSection>
|
||||
)}
|
||||
</nav>
|
||||
|
||||
<div className="sidebar-user">{user.name || user.email}</div>
|
||||
</aside>
|
||||
|
||||
{/* Mobile top bar */}
|
||||
<header className="top-bar">
|
||||
<ChefHat size={16} strokeWidth={1.75} color="var(--app-primary)" />
|
||||
<span className="top-bar-title">Kitchen</span>
|
||||
<nav className="top-bar-nav">
|
||||
<NavLink to="/dashboard" className={({ isActive }) => isActive ? 'active' : ''}>Dashboard</NavLink>
|
||||
{can(user, 'invoices') && <NavLink to="/invoices" className={({ isActive }) => isActive ? 'active' : ''}>Invoices</NavLink>}
|
||||
{can(user, 'view') && <NavLink to="/gp" className={({ isActive }) => isActive ? 'active' : ''}>Reports</NavLink>}
|
||||
{can(user, 'recipes') && <NavLink to="/recipes" className={({ isActive }) => isActive ? 'active' : ''}>Recipes</NavLink>}
|
||||
{can(user, 'settings') && <NavLink to="/settings" className={({ isActive }) => isActive ? 'active' : ''}>Settings</NavLink>}
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<main className="page-content">
|
||||
<Outlet />
|
||||
</main>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue