myheats

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

commit 85d73ae80442cd29d6a89c4502eb4ea3b37bdcc4
parent 99a58339accd294dd0dfff3b9e87b78534ddfe41
Author: Andreas Gruhler <andreas.gruhler@adfinis.com>
Date:   Fri, 31 Mar 2023 23:11:44 +0200

fix: TypeErrors leaderboard & heats

Diffstat:
Msrc/Leaderboard.js | 140+++++++++++++++++++++++++++++++++++++++++++++----------------------------------
1 file changed, 79 insertions(+), 61 deletions(-)

diff --git a/src/Leaderboard.js b/src/Leaderboard.js @@ -18,32 +18,34 @@ async function getRatingForHeatAndAthlete(heatId, athleteId) { } async function getRatingSummary(heatIds) { - let startlist = {data: []} - startlist = await getStartlistForHeats(heatIds) + const startListWithRatings = [] + + const startlist = await getStartlistForHeats(heatIds) if (startlist.error !== null) { - return {data: []} + // fail silently & return empty startlist in case of errors + return [] } - let startListWithRatings = [] - for (const i of startlist.data) { i.heats = [] for (const h of heatIds) { - let ratings = await getRatingForHeatAndAthlete(h, i.athlete) + const ratings = await getRatingForHeatAndAthlete(h, i.athlete) - let summary = {data: []} - summary = await supabase.from('rating_summary').select('rating_summary') + const summary = await supabase.from('rating_summary').select('rating_summary') .eq('heat_id', h) .eq('athlete_id', i.athlete) - // add heat results of athlete to startlist entry - i.heats.push({ - heatId: h, - ratings: ratings.data, - summary: summary.data.length > 0 ? summary.data[0].rating_summary : 0 - }) + if (summary.error === null) { + // add heat results of athlete to startlist entry + i.heats.push({ + heatId: h, + ratings: ratings.data, + summary: summary.data.length > 0 ? summary.data[0].rating_summary : 0 + }) + } + // else don't push any heats (fail silently) // find best/worst heat i.bestHeat = Math.max(...i.heats.map(h => h.summary)) @@ -90,7 +92,7 @@ function rankByHeat(rankingComp) { } } -async function newHeatFromLeaderboard(e, leaderboard, rankingComp, selectHeatRef, newHeatSize, newHeatName) { +async function newHeatFromLeaderboard(e, leaderboard, rankingComp, selectHeatRef, selectRankRef, newHeatSize, newHeatName) { e.preventDefault() if (leaderboard.length === 0) { @@ -103,8 +105,6 @@ async function newHeatFromLeaderboard(e, leaderboard, rankingComp, selectHeatRef return } - selectHeatRef.current.clearValue() - // create new heat const { data, error } = await supabase .from('heats') @@ -116,7 +116,7 @@ async function newHeatFromLeaderboard(e, leaderboard, rankingComp, selectHeatRef return } - let sortedBoard = leaderboard.sort(rankByHeat(rankingComp)) + const sortedBoard = leaderboard.sort(rankByHeat(rankingComp)) for (let i = 0; i < newHeatSize && i < sortedBoard.length; i++ ) { // add top N athletes from current leaderboard to new heat await supabase @@ -124,54 +124,26 @@ async function newHeatFromLeaderboard(e, leaderboard, rankingComp, selectHeatRef .insert({ heat: data[0].id, athlete: sortedBoard[i].athlete }) } - // clear values in heat Select box to refresh list of heats + // clear values in selects to refresh list of heats selectHeatRef.current.clearValue() + selectRankRef.current.clearValue() alert('Created new heat "' + newHeatName + '" with top ' + newHeatSize + ' athletes') } function Leaderboard() { const [leaderboard, setLeaderboard] = useState([]) - const [heatSelection, setHeatSelection] = useState([{value: 0, label: ''}]) + const [heatSelection, setHeatSelection] = useState([]) const [heats, setHeats] = useState([]) - const [rankingComp, setRankingComp] = useState([{value: 0, label: ''}]) + const [heatOpts, setHeatOpts] = useState([]) + const [rankOpts, setRankOpts] = useState([]) + const [rankingComp, setRankingComp] = useState([]) // state for new heat from top N leaderboard const [newHeatSize, setNewHeatSize] = useState(6) const [newHeatName, setNewHeatName] = useState("Heat name") const selectHeatRef = useRef(); - - // add options to select or rank by heat - const heatOpts = heats.map(h => { - return { - value: h.id, - label: h.name - } - }) - - // add options to rank by best/worst heat - const rankOpts = heatOpts.map(h => { - return { - value: h.value, - label: "Sum " + h.label - } - }) - - rankOpts.push(...[ - { - value: 'start', - label: 'Start Nr.' - }, { - value: 'best', - label: 'Best Heat' - }, { - value: 'worst', - label: 'Worst Heat' - }, { - value: 'total', - label: 'Total Sum (all heats)' - } - ]) + const selectRankRef = useRef(); // subscribe to ratings from judges and // reload all ratings to refresh leaderboard @@ -183,21 +155,66 @@ function Leaderboard() { table: 'ratings', }, async (payload) => { - let ratingSummary = await getRatingSummary(heatSelection.map(h => h.value)) + const ratingSummary = await getRatingSummary(heatSelection.map(h => h.value)) setLeaderboard(ratingSummary) } ).subscribe() useEffect(() => { (async () => { - let heatList = {data: []} - heatList = await supabase.from('heats').select() + const heatList = await supabase.from('heats').select() setHeats(heatList.data) - let ratingSummary = await getRatingSummary(heatSelection.map(h => h.value)) + // add options to select or rank by heat + setHeatOpts(heats.map(h => { + return { + value: h.id, + label: h.name + } + })) + + // add static ranking options + const rankOptions = [ + { + value: 'start', + label: 'Start Nr.' + }, { + value: 'best', + label: 'Best Heat' + }, { + value: 'worst', + label: 'Worst Heat' + }, { + value: 'total', + label: 'Total Sum (all heats)' + } + ] + + // add dynamic options to rank by best/worst heat + const myHeatOpts = heatOpts.map(h => { + return { + value: h.value, + label: "Sum " + h.label + } + }) + + const groupedOptions = [ + { + label: "Overall", + options: rankOptions + }, + { + label: "Heat Sum", + options: myHeatOpts + } + ]; + + setRankOpts(groupedOptions) + + const ratingSummary = await getRatingSummary(heatSelection.map(h => h.value)) setLeaderboard(ratingSummary) })(); - }, [heatSelection, leaderboard]); + }, [heatSelection, heatOpts, heats]); return ( <div className="Leaderboard"> @@ -217,6 +234,7 @@ function Leaderboard() { isMulti options={rankOpts} onChange={h => setRankingComp(h)} + ref={selectRankRef} /> <form> New heat from top <input @@ -226,11 +244,12 @@ function Leaderboard() { onChange={(e) => setNewHeatSize(e.target.value)} />: <input type="text" value={newHeatName} onChange={(e) => setNewHeatName(e.target.value)} /> - <button onClick={(e) => newHeatFromLeaderboard( + <button onClick={e => newHeatFromLeaderboard( e, leaderboard, rankingComp, selectHeatRef, + selectRankRef, newHeatSize, newHeatName )}>Create</button> @@ -291,4 +310,4 @@ function Leaderboard() { ); } -export default Leaderboard; -\ No newline at end of file +export default Leaderboard;