5393e85a74
Build & Deploy / build-and-deploy (push) Successful in 2m18s
- races table: name, date, description, is_active
- stations table: ordered checkpoints with GPS per race
- New /api/races and /api/races/{id}/stations endpoints
- Upload now requires race + station selection; uses station GPS
so images without GPS EXIF are accepted
- passages filtered by active race throughout
- RacePage: create races, manage stations (add/edit/delete checkpoints)
- Navbar shows active race name
- Start and finish stations created automatically per race
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
55 lines
2.1 KiB
React
55 lines
2.1 KiB
React
import { useEffect, useState } from 'react'
|
|
import { NavLink, Route, Routes } from 'react-router-dom'
|
|
import { api } from './api.js'
|
|
import StartlistPage from './pages/StartlistPage.jsx'
|
|
import ReviewPage from './pages/ReviewPage.jsx'
|
|
import ResultsPage from './pages/ResultsPage.jsx'
|
|
import PassagesPage from './pages/PassagesPage.jsx'
|
|
import UploadPage from './pages/UploadPage.jsx'
|
|
import RacePage from './pages/RacePage.jsx'
|
|
import './App.css'
|
|
|
|
function Nav({ activeRace }) {
|
|
const linkClass = ({ isActive }) => (isActive ? 'nav-link active' : 'nav-link')
|
|
return (
|
|
<nav className="navbar">
|
|
<span className="navbar-brand">Timing</span>
|
|
<NavLink to="/" className={linkClass} end>Løp</NavLink>
|
|
<NavLink to="/startlist" className={linkClass}>Startliste</NavLink>
|
|
<NavLink to="/upload" className={linkClass}>Last opp</NavLink>
|
|
<NavLink to="/passages" className={linkClass}>Passeringer</NavLink>
|
|
<NavLink to="/review" className={linkClass}>Gjennomgang</NavLink>
|
|
<NavLink to="/results" className={linkClass}>Resultater</NavLink>
|
|
{activeRace && (
|
|
<span style={{ marginLeft: 'auto', fontSize: '0.8rem', color: '#2ecc71', fontWeight: 600 }}>
|
|
★ {activeRace.name}
|
|
</span>
|
|
)}
|
|
</nav>
|
|
)
|
|
}
|
|
|
|
export default function App() {
|
|
const [activeRace, setActiveRace] = useState(null)
|
|
|
|
useEffect(() => {
|
|
api.getActiveRace().then(r => setActiveRace(r?.race_id ? r : null))
|
|
}, [])
|
|
|
|
return (
|
|
<>
|
|
<Nav activeRace={activeRace} />
|
|
<main className="container">
|
|
<Routes>
|
|
<Route path="/" element={<RacePage onRaceChange={() => api.getActiveRace().then(r => setActiveRace(r?.race_id ? r : null))} />} />
|
|
<Route path="/startlist" element={<StartlistPage />} />
|
|
<Route path="/upload" element={<UploadPage />} />
|
|
<Route path="/passages" element={<PassagesPage activeRace={activeRace} />} />
|
|
<Route path="/review" element={<ReviewPage activeRace={activeRace} />} />
|
|
<Route path="/results" element={<ResultsPage activeRace={activeRace} />} />
|
|
</Routes>
|
|
</main>
|
|
</>
|
|
)
|
|
}
|