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