- 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>
This commit is contained in:
+25
-9
@@ -1,36 +1,52 @@
|
||||
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() {
|
||||
function Nav({ activeRace }) {
|
||||
const linkClass = ({ isActive }) => (isActive ? 'nav-link active' : 'nav-link')
|
||||
return (
|
||||
<nav className="navbar">
|
||||
<span className="navbar-brand">Timing Admin</span>
|
||||
<NavLink to="/" className={linkClass} end>Startliste</NavLink>
|
||||
<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>
|
||||
<NavLink to="/upload" className={linkClass}>Last opp</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 />
|
||||
<Nav activeRace={activeRace} />
|
||||
<main className="container">
|
||||
<Routes>
|
||||
<Route path="/" element={<StartlistPage />} />
|
||||
<Route path="/passages" element={<PassagesPage />} />
|
||||
<Route path="/review" element={<ReviewPage />} />
|
||||
<Route path="/results" element={<ResultsPage />} />
|
||||
<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>
|
||||
</>
|
||||
|
||||
Reference in New Issue
Block a user