Initial scaffold: wages app
Full wage cost reporting app — weekly/monthly views, rolling 12-week/12-month history, budget management, Workforce API sync with SSE backfill, net sales via forecasting public API, department filter, CSV export. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
commit
2e0592eb90
37 changed files with 3078 additions and 0 deletions
77
frontend/src/App.tsx
Normal file
77
frontend/src/App.tsx
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
import { useState } from 'react'
|
||||
import { DollarSign, CalendarDays, TrendingUp, BarChart3, Wallet, Settings } 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 hotelName = import.meta.env.VITE_HOTEL_NAME || 'Hotel'
|
||||
|
||||
return (
|
||||
<div className="app-shell">
|
||||
<nav className="sidebar">
|
||||
<div className="sidebar-logo">
|
||||
<DollarSign size={16} strokeWidth={1.75} />
|
||||
Wage Costs
|
||||
</div>
|
||||
<div className="sidebar-nav">
|
||||
{NAV.filter(n => !n.cap || can(user, n.cap)).map(n => (
|
||||
<div
|
||||
key={n.id}
|
||||
className={`nav-item${page === n.id ? ' active' : ''}`}
|
||||
onClick={() => setPage(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>
|
||||
|
||||
<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} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue