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 (5695B)


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