myheats

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

Score.jsx (4618B)


      1 import { lazy, useEffect, useState } from 'react'
      2 import { getStartlistForHeats } from './Leaderboard'
      3 const Auth = lazy(() => import('./Auth'))
      4 import Select from 'react-select'
      5 
      6 const api_uri = import.meta.env.VITE_API_URI
      7 const api_port = import.meta.env.VITE_API_PORT
      8 
      9 async function getScore(heatId, athleteId, session) {
     10   const res = await fetch(`${api_uri}:${api_port}/v1/leaderboard/getScore`, {
     11     method: 'POST',
     12     headers: {
     13             'Content-Type': 'application/json',
     14             'Authorization': `Bearer ${session.auth.token}`,
     15     },
     16     body: JSON.stringify({
     17       "heat": heatId,
     18       "athlete": athleteId,
     19       "judge": session.auth.id,
     20     }),
     21   })
     22   const { data, error } = await res.json()
     23   if (error) {
     24     throw error
     25   }
     26   return data
     27 }
     28 
     29 async function updateScore(score, heatId, athleteId, session) {
     30   const res = await fetch(`${api_uri}:${api_port}/v1/leaderboard/setScore`, {
     31     method: 'POST',
     32     headers: {
     33             'Content-Type': 'application/json',
     34             'Authorization': `Bearer ${session.auth.token}`,
     35     },
     36     body: JSON.stringify({
     37       "score": score,
     38       "heat": heatId,
     39       "athlete": athleteId,
     40       "judge": session.auth.id,
     41     }),
     42   })
     43   const { data, error } = await res.json()
     44   if (error) {
     45     throw error
     46   }
     47   return data
     48 }
     49 
     50 function ScoringForm({session}) {
     51   const [heats, setHeats] = useState([])
     52   const [heatSelection, setHeatSelection] = useState(0)
     53   const [athleteOpts, setAthleteOpts] = useState([])
     54   const [athleteSelection, setAthleteSelection] = useState(0)
     55   const [score, setScore] = useState(0)
     56   const [loading, setLoading] = useState(false)
     57 
     58   // add options to select or rank by heat
     59   const heatOpts = heats.map(h => {
     60     return {
     61       value: h.id,
     62       label: h.name
     63     }
     64   })
     65 
     66   useEffect(() => {
     67     (async () => {
     68       setLoading(true)
     69       const res = await fetch(`${api_uri}:${api_port}/v1/leaderboard/allHeats`, {
     70         headers: {
     71                 'Content-Type': 'application/json',
     72                 'Authorization': `Bearer ${session.auth.token}`,
     73         }
     74       })
     75       const { data, error } = await res.json()
     76       if (error) {
     77         console.error(error)
     78       }
     79       setHeats(data)
     80 
     81       let startlist = undefined
     82       try {
     83         startlist = await getStartlistForHeats([heatSelection.value], session)
     84       } catch (error) {
     85         console.error(error)
     86         setLoading(false)
     87         return
     88       }
     89 
     90       setAthleteOpts(startlist.map(s => {
     91         return {
     92           value: s.athlete,
     93           label: s.nr + " " + s.firstname + " " + (s.lastname ? s.lastname : "")
     94         }
     95       }))
     96 
     97       if (heatSelection.value === undefined || athleteSelection.value === undefined) {
     98         setLoading(false)
     99         return
    100       }
    101 
    102       // check if existing score for heat and athlete exists
    103       try {
    104         const currentScore = await getScore(
    105                 heatSelection.value,
    106                 athleteSelection.value,
    107                 session
    108         )
    109         if (score === 0) {
    110           // fallback to current score when no new scoring took place
    111           setScore(currentScore.score)
    112         }
    113       } catch (error) {
    114         console.info("No initial score for this heat and athelete, creating initial record")
    115       }
    116 
    117       // store new score
    118       try {
    119         await updateScore(score,
    120           heatSelection.value,
    121           athleteSelection.value,
    122           session)
    123       } catch (error) {
    124         console.error(error)
    125       }
    126       setLoading(false)
    127     })();
    128   }, [heatSelection, athleteSelection, session.auth.id, score]);
    129 
    130   return (
    131     <div className='Scoring'>
    132       <header>
    133         <button disabled={!loading} className='loading'>↺ loading</button>
    134       </header>
    135       <ul>
    136         <li>
    137           <label htmlFor='heat'>Heat</label>
    138           <Select
    139             options={heatOpts}
    140             onChange={h => { setHeatSelection(h); setScore(0) }}
    141             id='heat' />
    142         </li>
    143         <li>
    144           <label htmlFor='athlete'>Athlete</label>
    145           <Select
    146             options={athleteOpts}
    147             onChange={a => { setAthleteSelection(a); setScore(0) }}
    148             id='athlete' />
    149         </li>
    150         <li>
    151           <label htmlFor='score'>Score</label>
    152           <input
    153             className='scoreInput'
    154             type="number"
    155             value={score}
    156             onChange={(e) => setScore(e.target.value)}
    157             id='score' />
    158         </li>
    159       </ul>
    160     </div>
    161   )
    162 }
    163 
    164 function Score({session}) {
    165   return (
    166     <div>
    167       {!session.auth ? <Auth /> : <ScoringForm session={session} />}
    168     </div>
    169   )
    170 }
    171 
    172 export default Score