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 c87d07ab995bf37c9d9ae7da37fe9477a965d491
parent b0853f4821f4a8f8dce63f78c40f20d5c284cc87
Author: Andreas Gruhler <andreas.gruhler@adfinis.com>
Date:   Thu,  9 Mar 2023 00:16:07 +0100

feat: add sorting for best/worst/sum

Diffstat:
Msrc/App.css | 21+++++++++++++++++++++
Msrc/App.js | 71++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------
2 files changed, 77 insertions(+), 15 deletions(-)

diff --git a/src/App.css b/src/App.css @@ -9,4 +9,25 @@ tr, th,td { .right { text-align: right; +} + +/* set medal icon for first three rows */ +table tr:first-child td:first-child::after { + content: "🥇"; +} +table tr:nth-child(2) td:first-child::after { + content: "🥈"; +} +table tr:nth-child(3) td:first-child::after { + content: "🥉"; +} + +/* increment rank row number */ +table tr { + counter-increment: rowNumber; +} +table tr td:first-child::before { + content: counter(rowNumber); + min-width: 1em; + margin-right: 0.5em; } \ No newline at end of file diff --git a/src/App.js b/src/App.js @@ -49,6 +49,13 @@ async function getRatingSummary(heatIds) { ratings: ratings.data, summary: summary.data.length > 0 ? summary.data[0].rating_summary : 0 }) + + // find best/worst heat + i.bestHeat = Math.max(...i.heats.map(h => h.summary)) + i.worstHeat = Math.min(...i.heats.map(h => h.summary)) + + // sum up all totals across heats + i.sum = i.heats.map(h => h.summary).reduce((a, b) => a + b, 0) } startListWithRatings.push(i) @@ -61,8 +68,22 @@ function rankByHeat(rankingComp) { return function(a, b) { // rank by chosen heat or ranking comparator for (const r of rankingComp) { - if (b.heats.find(h => h.heatId === r.value)?.summary - a.heats.find(h => h.heatId === r.value)?.summary !== 0) { - return b.heats.find(h => h.heatId === r.value)?.summary - a.heats.find(h => h.heatId === r.value)?.summary + switch(r.value) { + case 'best': + if (b.bestHeat - a.bestHeat !== 0) { + // rank by best heat first + return b.bestHeat - a.bestHeat + } + // rank by least worst heat for identical best heats + return b.worstHeat - a.worstHeat + case 'worst': + // rank by worst heat + return b.worstHeat - a.worstHeat + default: + // rank by heat totals + if (b.heats.find(h => h.heatId === r.value)?.summary - a.heats.find(h => h.heatId === r.value)?.summary !== 0) { + return b.heats.find(h => h.heatId === r.value)?.summary - a.heats.find(h => h.heatId === r.value)?.summary + } } } } @@ -74,11 +95,31 @@ function App() { const [heats, setHeats] = useState([]); const [rankingComp, setRankingComp] = useState([{value: 0, label: ''}]); + // add options to select or rank by heat + let heatOpts = heats.map(h => { + return { + value: h.id, + label: h.name + } + }) + + // add options to rank by best/worst heat + let rankOpts = heatOpts.concat([ + { + value: 'best', + label: 'Best Heat' + }, { + value: 'worst', + label: 'Worst Heat' + } + ]) + useEffect(() => { (async () => { let heatList = {data: []} heatList = await supabase.from('heats').select() setHeats(heatList.data) + let ratingSummary = await getRatingSummary(heatSelection.map(h => h.value)) setLeaderboard(ratingSummary) })(); @@ -107,34 +148,30 @@ function App() { <Select closeMenuOnSelect={false} isMulti - options={heats.map(h => {return { - value: h.id, - label: h.name - }})} + options={heatOpts} onChange={h => setHeatSelection(h)} /> Rank by (in this order): <Select closeMenuOnSelect={false} isMulti - options={heats.map(h => {return { - value: h.id, - label: h.name - }})} + options={rankOpts} onChange={h => setRankingComp(h)} /> </div> - <h1>🏅 Leaderboard</h1> + <h1>Leaderboard</h1> </header> <table> <thead> <tr> - <th>#</th> + <th>Rank</th> + <th>Start Nr.</th> <th>Firstname</th> <th>Lastname</th> <th>Birthday</th> <th>School</th> - <th colSpan={heatSelection.length * 2 + 2}>Ratings & Total</th> + <th colSpan={heatSelection.length * 2}>Heat Ratings & Sum</th> + <th colSpan={3}>Summary (all heats)</th> </tr> <tr> <th></th> @@ -142,16 +179,19 @@ function App() { <th></th> <th></th> <th></th> + <th></th> {heatSelection.map(h => ( <th key={h.value} colSpan={2}>{h.label}</th> ))} <th>👍 Best</th> <th>👎 Worst</th> + <th>Total</th> </tr> </thead> <tbody> {leaderboard.sort(rankByHeat(rankingComp)).map(i => ( <tr key={i.id}> + <td></td> <td>{i.nr}</td> <td>{i.firstname}</td> <td>{i.lastname}</td> @@ -163,8 +203,9 @@ function App() { <td className='right'>{i.heats.find(heat => heat.heatId === h.value)?.summary}</td> </Fragment> ))} - <th>{Math.max(...i.heats.map(h => h.summary))}</th> - <th>{Math.min(...i.heats.map(h => h.summary))}</th> + <td>{i.bestHeat}</td> + <td>{i.worstHeat}</td> + <td>{i.sum}</td> </tr> ))} </tbody>