commit 077905c2a3350fbbd8b78670f35ec56fcac201ee
parent cd3c28307d376295499eb4e30a2d1a348a553ca9
Author: Andreas Gruhler <andreas.gruhler@adfinis.com>
Date: Fri, 7 Apr 2023 19:16:58 +0200
feat: add athletes to startlist
Diffstat:
4 files changed, 105 insertions(+), 65 deletions(-)
diff --git a/schema/startlist.sql b/schema/startlist.sql
@@ -27,8 +27,8 @@ SET default_table_access_method = heap;
CREATE TABLE public.startlist (
id bigint NOT NULL,
created_at timestamp with time zone DEFAULT now(),
- heat bigint,
- athlete bigint
+ heat bigint NOT NULL,
+ athlete bigint NOT NULL
);
@@ -60,7 +60,7 @@ ALTER TABLE public.startlist ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTIT
--
ALTER TABLE ONLY public.startlist
- ADD CONSTRAINT startlist_pkey PRIMARY KEY (id);
+ ADD CONSTRAINT startlist_pkey PRIMARY KEY (heat, athlete);
--
diff --git a/src/Athletes.js b/src/Athletes.js
@@ -80,59 +80,59 @@ function AthleteForm({session}) {
return (
<div>
<h1>Athletes <button disabled={!loading}>{loading ? '🔄 loading' : ''}</button></h1>
- <form method="post" onSubmit={addAthlete}>
- <table>
- <thead>
- <tr>
- <th>Created at</th>
- <th>Nr</th>
- <th>Firstname</th>
- <th>Lastname</th>
- <th>Birthday</th>
- <th>School</th>
- <th>Delete</th>
- </tr>
- </thead>
- <tbody>
- {athletes.map(a => (
- <tr key={a.id}>
- <td className='right'>{new Date(a.created_at).toLocaleString()}</td>
- <td className='right'>{a.nr}</td>
- <td>{a.firstname}</td>
- <td>{a.lastname}</td>
- <td className='right'>{a.birthday}</td>
- <td>{a.school}</td>
- <td className='right'><button onClick={e => deleteAthlete(e, a.id, a.firstname, a.lastname)}>🗑️</button></td>
+ <form method='post' onSubmit={addAthlete}>
+ <table>
+ <thead>
+ <tr>
+ <th>Created at</th>
+ <th>Nr</th>
+ <th>Firstname</th>
+ <th>Lastname</th>
+ <th>Birthday</th>
+ <th>School</th>
+ <th>New/delete</th>
</tr>
- ))}
- <tr>
- <td className='right'><i>* required</i></td>
- <td className='right'>
- <input type='number' name='nr' defaultValue='00' /> *
+ </thead>
+ <tbody>
+ {athletes.map(a => (
+ <tr key={a.id}>
+ <td className='right'>{new Date(a.created_at).toLocaleString()}</td>
+ <td className='right'>{a.nr}</td>
+ <td>{a.firstname}</td>
+ <td>{a.lastname}</td>
+ <td className='right'>{a.birthday}</td>
+ <td>{a.school}</td>
+ <td className='right'><button onClick={e => deleteAthlete(e, a.id, a.firstname, a.lastname)}>🗑️ delete</button></td>
+ </tr>
+ ))}
+ <tr>
+ <td className='right'><i>* required</i></td>
+ <td className='right'>
+ <input type='number' name='nr' defaultValue='00' /> *
</td>
- <td>
- <input type='text' name='firstname' defaultValue='Firstname' /> *
- </td>
- <td>
- <input type='text' name='lastname' defaultValue='Lastname' />
+ <td>
+ <input type='text' name='firstname' defaultValue='Firstname' /> *
</td>
- <td className='right'>
- <input
- type='text'
- name='birthday'
- defaultValue='0000-00-00'
- pattern='(\d{4}-\d{2}-\d{2})*'
- onBlur={(e) => validateBirthday(e)} />
- </td>
- <td>
- <input type='text' name='school' defaultValue='School/team' />
- </td>
- <td className='right'>
- <button type='submit'>➕ add</button>
- </td>
- </tr>
- </tbody>
- </table>
+ <td>
+ <input type='text' name='lastname' defaultValue='Lastname' />
+ </td>
+ <td className='right'>
+ <input
+ type='text'
+ name='birthday'
+ defaultValue='0000-00-00'
+ pattern='(\d{4}-\d{2}-\d{2})*'
+ onBlur={(e) => validateBirthday(e)} />
+ </td>
+ <td>
+ <input type='text' name='school' defaultValue='School/team' />
+ </td>
+ <td className='right'>
+ <button type='submit'>➕ new</button>
+ </td>
+ </tr>
+ </tbody>
+ </table>
</form>
</div>
)
diff --git a/src/Heats.js b/src/Heats.js
@@ -54,9 +54,9 @@ function HeatForm({session}) {
<td>{h.location}</td>
<td className='right'>{h.planned_start}</td>
<td className='right'>
- <button onClick={e => navigate(generatePath('/startlist/:heatId', {heatId:h.id}))}>🏁</button>
+ <button onClick={e => navigate(generatePath('/startlist/:heatId', {heatId:h.id}))}>🏁 startlist</button>
</td>
- <td className='right'><button onClick={e => deleteHeat(e, h.id, h.name)}>🗑️</button></td>
+ <td className='right'><button onClick={e => deleteHeat(e, h.id, h.name)}>🗑️ delete</button></td>
</tr>
))}
</tbody>
diff --git a/src/Startlist.js b/src/Startlist.js
@@ -1,17 +1,27 @@
import { lazy, useEffect, useState } from 'react'
import { useParams } from 'react-router-dom'
+import Select from 'react-select'
import { supabase } from './supabaseClient'
-import { hasSelectionSupport } from '@testing-library/user-event/dist/utils'
const Auth = lazy(() => import('./Auth'))
+async function addAthleteToHeat(e, selectedAthlete, heatId) {
+ e.preventDefault()
+
+ const { error } = await supabase.from('startlist').upsert({
+ athlete: selectedAthlete.value,
+ heat: heatId
+ })
+
+ if (error === null)
+ window.location.reload()
+}
+
async function removeAthleteFromHeat(e, startlistId, athleteFirstName, athleteLastName, heatName) {
e.preventDefault()
const athleteName = athleteFirstName + (athleteLastName ? ' ' + athleteLastName : '')
- if (window.confirm(
- 'Do you really want to remove athlete "' + athleteName + '" from heat "' + heatName + '"?'
- )) {
+ if (window.confirm('Do you really want to remove athlete "' + athleteName + '" from heat "' + heatName + '"?')) {
await supabase
.from('startlist')
.delete()
@@ -24,19 +34,23 @@ function StartlistForm({heatId}) {
const [heatName, setheatName] = useState("")
const [heatLocation, setheatLocation] = useState("")
const [heatStart, setheatStart] = useState("")
+ const [startlist, setStartlist] = useState([])
+ const [athleteOpts, setAthleteOpts] = useState([])
+ const [selectedAthlete, setselectedAthlete] = useState({})
const [loading, setLoading] = useState(false)
- const [startlist, setStartlist] = useState([])
useEffect(() => {
(async () => {
setLoading(true)
+
const startlistData = await supabase
.from('startlist')
.select(`
id,
heat,
athlete (
+ id,
nr,
firstname,
lastname,
@@ -44,19 +58,34 @@ function StartlistForm({heatId}) {
school
)
`)
- .eq('heat', heatId)
+ .eq('heat', heatId)
+
if (startlistData.error === null)
setStartlist(startlistData.data)
+
const heatData = await supabase
.from('heats')
.select('name, location, planned_start')
.eq('id', heatId)
.single()
+
if (heatData.error === null) {
setheatName(heatData.data.name)
setheatLocation(heatData.data.location)
setheatStart(heatData.data.planned_start)
}
+
+ const athleteList = await supabase.from('athletes').select()
+ if (athleteList.error === null) {
+ let options = athleteList.data.map(a => {
+ return {
+ value: a.id,
+ label: a.nr + " " + a.firstname + " " + (a.lastname ? a.lastname : "")
+ }
+ })
+ setAthleteOpts(options)
+ }
+
setLoading(false)
})();
}, [heatId])
@@ -70,7 +99,6 @@ function StartlistForm({heatId}) {
<li><b>Planned start:</b> {heatStart ? heatStart : 'n/a'}</li>
</ul>
</div>
- {/* <form method="post"> */}
<table>
<thead>
<tr>
@@ -79,7 +107,7 @@ function StartlistForm({heatId}) {
<th>Lastname</th>
<th>Birthday</th>
<th>School</th>
- <th>Remove from heat</th>
+ <th>Add/remove</th>
</tr>
</thead>
<tbody>
@@ -96,12 +124,24 @@ function StartlistForm({heatId}) {
i.athlete.firstname,
i.athlete.lastname,
heatName
- )}>🗑️</button></td>
+ )}>🗑️ remove</button></td>
</tr>
))}
+ <tr>
+ <td></td>
+ <td colSpan={2}>
+ <Select
+ options={athleteOpts}
+ onChange={(e) => setselectedAthlete(e)}
+ />
+ </td>
+ <td colSpan={2}></td>
+ <td className='right'>
+ <button onClick={(e) => addAthleteToHeat(e, selectedAthlete, heatId)}>➕ add</button>
+ </td>
+ </tr>
</tbody>
</table>
- {/* </form> */}
</div>
)
}