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


      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 className='Scoring'>
    127       <header>
    128         <button disabled={!loading} className='loading'>↺ loading</button>
    129       </header>
    130       <ul>
    131         <li>
    132           <label htmlFor='heat'>Heat</label>
    133           <Select
    134             options={heatOpts}
    135             onChange={h => { setHeatSelection(h); setScore(0) }}
    136             id='heat' />
    137         </li>
    138         <li>
    139           <label htmlFor='athlete'>Athlete</label>
    140           <Select
    141             options={athleteOpts}
    142             onChange={a => { setAthleteSelection(a); setScore(0) }}
    143             id='athlete' />
    144         </li>
    145         <li>
    146           <label htmlFor='score'>Score</label>
    147           <input
    148             className='scoreInput'
    149             type="number"
    150             value={score}
    151             onChange={(e) => setScore(e.target.value)}
    152             id='score' />
    153         </li>
    154       </ul>
    155     </div>
    156   )
    157 }
    158 
    159 function Score({session}) {
    160   return (
    161     <div>
    162       {!session.auth ? <Auth /> : <ScoringForm session={session} />}
    163     </div>
    164   )
    165 }
    166 
    167 export default Score