2026-07-06 14:55:32 -05:00
|
|
|
import { useEffect } from 'react'
|
2026-03-09 22:14:51 -05:00
|
|
|
import { BrowserRouter as Router, Routes, Route, Link, useLocation } from 'react-router-dom'
|
2026-07-06 14:55:32 -05:00
|
|
|
import { Home, PawPrint, Activity, Heart, FlaskConical, Settings, ExternalLink, SearchX } from 'lucide-react'
|
2026-03-08 22:52:40 -05:00
|
|
|
import Dashboard from './pages/Dashboard'
|
|
|
|
|
import DogList from './pages/DogList'
|
|
|
|
|
import DogDetail from './pages/DogDetail'
|
|
|
|
|
import PedigreeView from './pages/PedigreeView'
|
|
|
|
|
import LitterList from './pages/LitterList'
|
2026-03-09 20:50:42 -05:00
|
|
|
import LitterDetail from './pages/LitterDetail'
|
2026-03-08 22:52:40 -05:00
|
|
|
import BreedingCalendar from './pages/BreedingCalendar'
|
2026-03-09 20:23:17 -05:00
|
|
|
import PairingSimulator from './pages/PairingSimulator'
|
2026-03-09 22:14:51 -05:00
|
|
|
import SettingsPage from './pages/SettingsPage'
|
2026-03-10 15:27:06 -05:00
|
|
|
import ExternalDogs from './pages/ExternalDogs'
|
2026-03-09 22:14:51 -05:00
|
|
|
import { useSettings } from './hooks/useSettings'
|
2026-07-06 14:55:32 -05:00
|
|
|
import { ToastProvider, useToast } from './components/Toast'
|
|
|
|
|
import { ConfirmProvider } from './components/ConfirmDialog'
|
2026-03-08 22:52:40 -05:00
|
|
|
import './App.css'
|
|
|
|
|
|
2026-07-06 14:55:32 -05:00
|
|
|
// `match` lets a nav item claim extra path prefixes (e.g. Dogs owns /pedigree/:id)
|
|
|
|
|
function NavLink({ to, icon: Icon, label, match = [] }) {
|
2026-03-09 22:14:51 -05:00
|
|
|
const location = useLocation()
|
2026-07-06 14:55:32 -05:00
|
|
|
const prefixes = [to, ...match]
|
|
|
|
|
const isActive = to === '/'
|
|
|
|
|
? location.pathname === '/'
|
|
|
|
|
: prefixes.some(p => location.pathname === p || location.pathname.startsWith(p + '/'))
|
2026-03-08 22:52:40 -05:00
|
|
|
return (
|
2026-03-09 22:14:51 -05:00
|
|
|
<Link to={to} className={`nav-link${isActive ? ' active' : ''}`}>
|
|
|
|
|
<Icon size={20} />
|
|
|
|
|
<span>{label}</span>
|
|
|
|
|
</Link>
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-06 14:55:32 -05:00
|
|
|
function NotFound() {
|
|
|
|
|
return (
|
|
|
|
|
<div className="container" style={{ textAlign: 'center', padding: '4rem 1rem' }}>
|
|
|
|
|
<SearchX size={48} color="var(--text-muted)" style={{ marginBottom: '1rem' }} />
|
|
|
|
|
<h2 style={{ marginBottom: '0.5rem' }}>Page not found</h2>
|
|
|
|
|
<p style={{ color: 'var(--text-muted)', marginBottom: '1.5rem' }}>
|
|
|
|
|
That page doesn't exist or may have been moved.
|
|
|
|
|
</p>
|
|
|
|
|
<Link to="/" className="btn btn-primary">Back to Dashboard</Link>
|
|
|
|
|
</div>
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-09 22:14:51 -05:00
|
|
|
function AppInner() {
|
2026-07-06 14:55:32 -05:00
|
|
|
const { settings, loadError } = useSettings()
|
|
|
|
|
const toast = useToast()
|
2026-03-09 22:14:51 -05:00
|
|
|
const kennelName = settings?.kennel_name || 'BREEDR'
|
|
|
|
|
|
2026-07-06 14:55:32 -05:00
|
|
|
useEffect(() => {
|
|
|
|
|
if (loadError) toast.error('Failed to load kennel settings — using defaults')
|
|
|
|
|
}, [loadError, toast])
|
|
|
|
|
|
2026-03-09 22:14:51 -05:00
|
|
|
return (
|
|
|
|
|
<div className="app">
|
|
|
|
|
<nav className="navbar">
|
|
|
|
|
<div className="container">
|
|
|
|
|
<div className="nav-brand">
|
|
|
|
|
<img
|
|
|
|
|
src="/static/br-logo.png"
|
|
|
|
|
alt="BREEDR Logo"
|
|
|
|
|
className="brand-logo"
|
|
|
|
|
/>
|
|
|
|
|
<span className="brand-text">{kennelName}</span>
|
2026-03-08 22:52:40 -05:00
|
|
|
</div>
|
2026-03-09 22:14:51 -05:00
|
|
|
<div className="nav-links">
|
|
|
|
|
<NavLink to="/" icon={Home} label="Dashboard" />
|
2026-07-06 14:55:32 -05:00
|
|
|
<NavLink to="/dogs" icon={PawPrint} label="Dogs" match={['/pedigree']} />
|
2026-03-10 15:27:06 -05:00
|
|
|
<NavLink to="/external" icon={ExternalLink} label="External" />
|
2026-03-09 22:14:51 -05:00
|
|
|
<NavLink to="/litters" icon={Activity} label="Litters" />
|
|
|
|
|
<NavLink to="/breeding" icon={Heart} label="Breeding" />
|
|
|
|
|
<NavLink to="/pairing" icon={FlaskConical} label="Pairing" />
|
|
|
|
|
<NavLink to="/settings" icon={Settings} label="Settings" />
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</nav>
|
|
|
|
|
|
|
|
|
|
<main className="main-content">
|
|
|
|
|
<Routes>
|
|
|
|
|
<Route path="/" element={<Dashboard />} />
|
|
|
|
|
<Route path="/dogs" element={<DogList />} />
|
|
|
|
|
<Route path="/dogs/:id" element={<DogDetail />} />
|
2026-03-10 15:27:06 -05:00
|
|
|
<Route path="/external" element={<ExternalDogs />} />
|
2026-03-09 22:14:51 -05:00
|
|
|
<Route path="/pedigree/:id" element={<PedigreeView />} />
|
|
|
|
|
<Route path="/litters" element={<LitterList />} />
|
|
|
|
|
<Route path="/litters/:id" element={<LitterDetail />} />
|
|
|
|
|
<Route path="/breeding" element={<BreedingCalendar />} />
|
|
|
|
|
<Route path="/pairing" element={<PairingSimulator />} />
|
|
|
|
|
<Route path="/settings" element={<SettingsPage />} />
|
2026-07-06 14:55:32 -05:00
|
|
|
<Route path="*" element={<NotFound />} />
|
2026-03-09 22:14:51 -05:00
|
|
|
</Routes>
|
|
|
|
|
</main>
|
|
|
|
|
</div>
|
|
|
|
|
)
|
|
|
|
|
}
|
2026-03-08 22:52:40 -05:00
|
|
|
|
2026-03-09 22:14:51 -05:00
|
|
|
function App() {
|
|
|
|
|
return (
|
|
|
|
|
<Router>
|
2026-07-06 14:55:32 -05:00
|
|
|
<ToastProvider>
|
|
|
|
|
<ConfirmProvider>
|
|
|
|
|
<AppInner />
|
|
|
|
|
</ConfirmProvider>
|
|
|
|
|
</ToastProvider>
|
2026-03-08 22:52:40 -05:00
|
|
|
</Router>
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-09 18:47:27 -05:00
|
|
|
export default App
|