Matches the slide-in sidebar + top-bar burger pattern used by other apps (maintenance, cashup) instead of the bottom tab bar.
95 lines
3.3 KiB
TypeScript
95 lines
3.3 KiB
TypeScript
import { useState } from 'react'
|
|
import { DollarSign, CalendarDays, TrendingUp, BarChart3, Wallet, Settings, Menu } from 'lucide-react'
|
|
import AuthGate, { useAuth } from './components/AuthGate'
|
|
import { UpdateBanner } from './components/UpdateBanner'
|
|
import { useVersionCheck } from './hooks/useVersionCheck'
|
|
import { can } from './types'
|
|
import Weekly from './pages/Weekly'
|
|
import Monthly from './pages/Monthly'
|
|
import Rolling12Weeks from './pages/Rolling12Weeks'
|
|
import Rolling12Months from './pages/Rolling12Months'
|
|
import Budgets from './pages/Budgets'
|
|
import SettingsPage from './pages/Settings'
|
|
|
|
type Page = 'weekly' | 'monthly' | 'rolling-weeks' | 'rolling-months' | 'budgets' | 'settings'
|
|
|
|
const NAV: { id: Page; label: string; icon: React.ElementType; cap?: string }[] = [
|
|
{ id: 'weekly', label: 'Weekly', icon: CalendarDays },
|
|
{ id: 'monthly', label: 'Monthly', icon: TrendingUp },
|
|
{ id: 'rolling-weeks', label: '12 Weeks', icon: BarChart3 },
|
|
{ id: 'rolling-months', label: '12 Months', icon: BarChart3 },
|
|
{ id: 'budgets', label: 'Budgets', icon: Wallet, cap: 'budget' },
|
|
{ id: 'settings', label: 'Settings', icon: Settings, cap: 'settings' },
|
|
]
|
|
|
|
function Shell() {
|
|
const { user } = useAuth()
|
|
const [page, setPage] = useState<Page>('weekly')
|
|
const [menuOpen, setMenuOpen] = useState(false)
|
|
|
|
const hotelName = import.meta.env.VITE_HOTEL_NAME || 'Hotel'
|
|
|
|
const visibleNav = NAV.filter(n => !n.cap || can(user, n.cap))
|
|
|
|
function selectPage(id: Page) {
|
|
setPage(id)
|
|
setMenuOpen(false)
|
|
}
|
|
|
|
return (
|
|
<div className={`app-shell${menuOpen ? ' menu-open' : ''}`}>
|
|
<nav className="sidebar">
|
|
<div className="sidebar-logo">
|
|
<DollarSign size={16} strokeWidth={1.75} />
|
|
Wage Costs
|
|
</div>
|
|
<div className="sidebar-nav">
|
|
{visibleNav.map(n => (
|
|
<div
|
|
key={n.id}
|
|
className={`nav-item${page === n.id ? ' active' : ''}`}
|
|
onClick={() => selectPage(n.id)}
|
|
>
|
|
<n.icon size={16} strokeWidth={1.75} />
|
|
{n.label}
|
|
</div>
|
|
))}
|
|
</div>
|
|
<div style={{ padding: '12px 16px', fontSize: '11px', color: 'rgba(255,255,255,0.3)' }}>
|
|
{hotelName}
|
|
</div>
|
|
</nav>
|
|
|
|
{menuOpen && <div className="menu-backdrop" onClick={() => setMenuOpen(false)} />}
|
|
|
|
<header className="top-bar">
|
|
<button className="top-bar-burger" onClick={() => setMenuOpen(o => !o)}>
|
|
<Menu size={20} strokeWidth={1.75} />
|
|
</button>
|
|
<DollarSign size={18} strokeWidth={1.75} color="var(--gold)" />
|
|
<span className="top-bar-title">Wage Costs</span>
|
|
</header>
|
|
|
|
<main className="content">
|
|
{page === 'weekly' && <Weekly />}
|
|
{page === 'monthly' && <Monthly />}
|
|
{page === 'rolling-weeks' && <Rolling12Weeks />}
|
|
{page === 'rolling-months' && <Rolling12Months />}
|
|
{page === 'budgets' && <Budgets />}
|
|
{page === 'settings' && <SettingsPage />}
|
|
</main>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export default function App() {
|
|
const updateAvailable = useVersionCheck('/wages/health')
|
|
return (
|
|
<>
|
|
<AuthGate>
|
|
<Shell />
|
|
</AuthGate>
|
|
<UpdateBanner visible={updateAvailable} />
|
|
</>
|
|
)
|
|
}
|