myheats

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

Score.jsx (4491B)


      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       const { data, error } = await res.json()
     71       if (error) {
     72         console.error(error)
     73       }
     74       setHeats(data)
     75 
     76       let startlist = undefined
     77       try {
     78         startlist = await getStartlistForHeats([heatSelection.value])
     79       } catch (error) {
     80         console.error(error)
     81         setLoading(false)
     82         return
     83       }
     84 
     85       setAthleteOpts(startlist.map(s => {
     86         return {
     87           value: s.athlete,
     88           label: s.nr + " " + s.firstname + " " + (s.lastname ? s.lastname : "")
     89         }
     90       }))
     91 
     92       if (heatSelection.value === undefined || athleteSelection.value === undefined) {
     93         setLoading(false)
     94         return
     95       }
     96 
     97       // check if existing score for heat and athlete exists
     98       try {
     99         const currentScore = await getScore(
    100                 heatSelection.value,
    101                 athleteSelection.value,
    102                 session
    103         )
    104         if (score === 0) {
    105           // fallback to current score when no new scoring took place
    106           setScore(currentScore.score)
    107         }
    108       } catch (error) {
    109         console.error(error)
    110       }
    111 
    112       // store new score
    113       try {
    114         await updateScore(score,
    115           heatSelection.value,
    116           athleteSelection.value,
    117           session)
    118       } catch (error) {
    119         console.error(error)
    120       }
    121       setLoading(false)
    122     })();
    123   }, [heatSelection, athleteSelection, session.auth.id, score]);
    124 
    125   return (
    126     <div>
    127       <button disabled={!loading}>{loading ? '↺ loading' : ''}</button>
    128       <table>
    129         <thead>
    130           <tr>
    131             <th>Heat</th>
    132             <th>Athlete</th>
    133             <th>Score</th>
    134           </tr>
    135         </thead>
    136         <tbody>
    137           <tr>
    138             <td data-title='Heat'>
    139               <Select
    140                 options={heatOpts}
    141                 onChange={h => { setHeatSelection(h); setScore(0) }}
    142               />
    143             </td>
    144             <td data-title='Athlete'>
    145               <Select
    146                 options={athleteOpts}
    147                 onChange={a => { setAthleteSelection(a); setScore(0) }}
    148               />
    149             </td>
    150             <td data-title='Score'>
    151               <input
    152                 className='scoreInput'
    153                 type="number"
    154                 value={score}
    155                 onChange={(e) => setScore(e.target.value)}
    156               />
    157             </td>
    158           </tr>
    159         </tbody>
    160       </table>
    161     </div>
    162   )
    163 }
    164 
    165 function Score({session}) {
    166   return (
    167     <div>
    168       {!session.auth ? <Auth /> : <ScoringForm session={session} />}
    169     </div>
    170   )
    171 }
    172 
    173 export default Score