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