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


      1 import { lazy, useEffect, useState } from 'react'
      2 import { getStartlistForHeats } from './Leaderboard'
      3 import Select from 'react-select'
      4 import { supabase } from './supabaseClient'
      5 
      6 const Auth = lazy(() => import('./Auth'))
      7 
      8 async function updateScore(score, heatId, athleteId, userId) {
      9   await supabase.from('scores').upsert({
     10     score: score,
     11     heat: heatId,
     12     athlete: athleteId,
     13     judge: userId
     14   })
     15 }
     16 
     17 function ScoringForm({session}) {
     18   const [heats, setHeats] = useState([])
     19   const [heatSelection, setHeatSelection] = useState(0)
     20   const [athleteOpts, setAthleteOpts] = useState([])
     21   const [athleteSelection, setAthleteSelection] = useState(0)
     22   const [score, setScore] = useState(0)
     23   const [loading, setLoading] = useState(false)
     24 
     25   // add options to select or rank by heat
     26   const heatOpts = heats.map(h => {
     27     return {
     28       value: h.id,
     29       label: h.name
     30     }
     31   })
     32 
     33   useEffect(() => {
     34     (async () => {
     35       setLoading(true)
     36       const heatList = await supabase.from('heats').select()
     37       setHeats(heatList.data)
     38 
     39       const startlist = await getStartlistForHeats([heatSelection.value])
     40 
     41       if (startlist.error) {
     42         setLoading(false)
     43         return
     44       }
     45 
     46       setAthleteOpts(startlist.data.map(s => {
     47         return {
     48           value: s.athlete,
     49           label: s.nr + " " + s.firstname + " " + (s.lastname ? s.lastname : "")
     50         }
     51       }))
     52 
     53       if (heatSelection.value === undefined || athleteSelection.value === undefined) {
     54         setLoading(false)
     55         return
     56       }
     57 
     58       // check if existing score for heat and athlete exists
     59       const currentScore = await supabase.from('scores').select()
     60         .eq('heat', heatSelection.value)
     61         .eq('athlete', athleteSelection.value)
     62         .eq('judge', session.user.id)
     63 
     64       if (score === 0 && currentScore.data?.length > 0) {
     65         // fallback to current score when no new scoring took place
     66         setScore(currentScore.data[0].score)
     67       } else {
     68         // store new score
     69         updateScore(score,
     70           heatSelection.value,
     71           athleteSelection.value,
     72           session.user.id)
     73       }
     74       setLoading(false)
     75     })();
     76   }, [heatSelection, athleteSelection, session.user.id, score]);
     77 
     78   return (
     79     <div>
     80       <button disabled={!loading}>{loading ? '↺ loading' : ''}</button>
     81       <table>
     82         <thead>
     83           <tr>
     84             <th>Heat</th>
     85             <th>Athlete</th>
     86             <th>Score</th>
     87           </tr>
     88         </thead>
     89         <tbody>
     90           <tr>
     91             <td data-title='Heat'>
     92               <Select
     93                 options={heatOpts}
     94                 onChange={h => { setHeatSelection(h); setScore(0) }}
     95               />
     96             </td>
     97             <td data-title='Athlete'>
     98               <Select
     99                 options={athleteOpts}
    100                 onChange={a => { setAthleteSelection(a); setScore(0) }}
    101               />
    102             </td>
    103             <td data-title='Score'>
    104               <input
    105                 className='scoreInput'
    106                 type="number"
    107                 value={score}
    108                 onChange={(e) => setScore(e.target.value)}
    109               />
    110             </td>
    111           </tr>
    112         </tbody>
    113       </table>
    114     </div>
    115   )
    116 }
    117 
    118 function Score({session}) {
    119   return (
    120     <div>
    121       {!session ? <Auth /> : <ScoringForm session={session} />}
    122     </div>
    123   )
    124 }
    125 
    126 export default Score