myheats

Live heats, scoring and leaderboard for sport events
git clone https://git.in0rdr.ch/myheats.git
Log | Files | Refs | Pull requests | README | LICENSE

App.jsx (5312B)


      1 import { Suspense, lazy, useState, useEffect, Fragment } from 'react'
      2 import { BrowserRouter as Router, Routes, Route, Outlet, Link, NavLink } from 'react-router-dom'
      3 import { CookiesProvider, useCookies } from 'react-cookie'
      4 
      5 import './css/App.css'
      6 import './css/blue.css'
      7 import './css/red.css'
      8 import './css/yellow.css'
      9 
     10 const Score = lazy(() => import('./Score'))
     11 const Heats = lazy(() => import('./Heats'))
     12 const Athletes = lazy(() => import('./Athletes'))
     13 const Startlist = lazy(() => import('./Startlist'))
     14 const Auth = lazy(() => import('./Auth'))
     15 const AuthVerify = lazy(() => import('./AuthVerify'))
     16 const Leaderboard = lazy(() => import('./Leaderboard'))
     17 const Settings = lazy(() => import('./Settings'))
     18 
     19 const api_uri = import.meta.env.VITE_API_URI
     20 const api_port = import.meta.env.VITE_API_PORT
     21 
     22 document.title = import.meta.env.VITE_APP_DOC_TITLE ? import.meta.env.VITE_APP_DOC_TITLE : 'My Heats'
     23 
     24 function Layout() {
     25   const [session, setSession, destroySession] = useCookies(['auth'])
     26   const [settings, setSettings] = useState(new Map())
     27 
     28   // load stylesheet based on settings
     29   const colors = ['red', 'blue', 'yellow'];
     30   let theme = ""
     31   let showLogo = "true"
     32   let logoUrl = "/logo192.png"
     33   let showVersion = "true"
     34 
     35   if (settings.style && colors.includes(settings.style)) {
     36     theme = settings.style
     37     //console.info(`I see ${settings.style}. Try one of "style=${colors}" in ${window.location.origin}/settings`)
     38   }
     39   if (settings.showLogo) {
     40     showLogo = settings.showLogo
     41   }
     42   if (settings.logoUrl) {
     43     logoUrl = settings.logoUrl
     44   }
     45   if (settings.showVersion) {
     46     showVersion = settings.showVersion
     47   }
     48 
     49   useEffect(() => {
     50     (async () => {
     51       const res = await fetch(`${api_uri}:${api_port}/v1/leaderboard/allSettings`)
     52       if (res.status !== 204) {
     53         const { data, error } = await res.json()
     54         if (error) {
     55           console.error(error)
     56         } else {
     57           let s = new Map()
     58           data.forEach(setting => {
     59                   s[setting.name] = setting.value
     60           })
     61           setSettings(s)
     62         }
     63       }
     64     })();
     65   }, [])
     66 
     67   return (
     68     <Fragment>
     69       <nav className={theme}>
     70         <ul>
     71           <li>
     72             <NavLink
     73               className={({ isActive, isPending }) => isPending ? "pending" : isActive ? `active ${theme}.active` : ""}
     74               to="/">
     75                 Leaderboard
     76             </NavLink>
     77           </li>
     78           {session.auth ? <li>
     79             <NavLink
     80               className={({ isActive, isPending }) => isPending ? "pending" : isActive ? `active ${theme}.active}` : ""}
     81               to="/score">
     82                 Scoring
     83             </NavLink>
     84           </li> : ''}
     85           {session.auth ? <li>
     86             <NavLink
     87               className={({ isActive, isPending }) => isPending ? "pending" : isActive ? `active ${theme}.active}` : ""}
     88               to="/heats">
     89                 Heats and Startlists
     90             </NavLink>
     91           </li> : ''}
     92           {session.auth ? <li>
     93             <NavLink
     94               className={({ isActive, isPending }) => isPending ? "pending" : isActive ? `active ${theme}.active}` : ""}
     95               to="/athletes">
     96                 Athletes
     97             </NavLink>
     98           </li> : ''}
     99         </ul>
    100       </nav>
    101       <main>
    102         <Outlet />
    103       </main>
    104       <footer>
    105         <br />
    106         {showVersion === 'true' ?
    107           <span>MyHeats <a href="https://code.in0rdr.ch/myheats/refs.html">v0.6-nightly</a></span> : '' }
    108         {showLogo === 'true' ?
    109           <span className="logo"><img src={logoUrl} alt="Logo" /></span> : '' }
    110         <span>
    111           {session.auth ? <button
    112               onClick={() => location.href="/settings"}>
    113                 &#9881;
    114             </button> : ''}
    115         </span>
    116         <span>
    117           {session.auth ? <button onClick={() => destroySession('auth')}>&#10157;</button> :
    118             <NavLink
    119               className={({ isActive, isPending }) => isPending ? "pending" : isActive ? "active" : ""}
    120               to="/auth">
    121                 Login
    122             </NavLink>
    123           }
    124         </span>
    125       </footer>
    126     </Fragment>
    127   )
    128 }
    129 
    130 function NoMatch() {
    131   return (
    132     <div className="NoMatch">
    133       Nothing to see here, <Link to="/">go to leaderboard</Link>
    134     </div>
    135   )
    136 }
    137 
    138 function App() {
    139   const [session] = useCookies(['auth'])
    140 
    141   return (
    142     <Fragment>
    143       <Router>
    144         <Suspense fallback={<div>Loading...</div>}>
    145           <Routes>
    146             <Route path="/" element={<Layout session={session} />}>
    147               <Route path="/" element={<Leaderboard session={session} />} />
    148               <Route path="/score" element={<Score session={session} />} />
    149               <Route path="/heats" element={<Heats session={session} />} />
    150               <Route path="/athletes" element={<Athletes session={session} />} />
    151               <Route path="/startlist/:heatId"  element={<Startlist session={session} />} />
    152               <Route path="/auth" element={<Auth />} />
    153               <Route path="/authverify" element={<AuthVerify />} />
    154               <Route path="/settings" element={<Settings session={session} />} />
    155               <Route path="*" element={<NoMatch />} />
    156             </Route>
    157           </Routes>
    158         </Suspense>
    159       </Router>
    160     </Fragment>
    161   );
    162 }
    163 
    164 export default App;