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 3810931c36b65fdf390e374950523441060beeff
parent a5c4c577c6e59aab4e9812964a27b9fc67c28c1e
Author: Andreas Gruhler <andreas.gruhler@adfinis.com>
Date:   Thu,  6 Apr 2023 01:06:45 +0200

feat: add athletes

Diffstat:
Mschema/athletes.sql | 11+++++++++--
Msrc/Athletes.js | 75+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/Leaderboard.js | 6+++---
3 files changed, 87 insertions(+), 5 deletions(-)

diff --git a/schema/athletes.sql b/schema/athletes.sql @@ -59,11 +59,11 @@ ALTER TABLE public.athletes ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY -- --- Name: athletes slopestyle_snowboard_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- Name: athletes athletes_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres -- ALTER TABLE ONLY public.athletes - ADD CONSTRAINT slopestyle_snowboard_pkey PRIMARY KEY (id); + ADD CONSTRAINT athletes_pkey PRIMARY KEY (id); -- @@ -74,6 +74,13 @@ CREATE POLICY "Enable delete for authenticated users only" ON public.athletes FO -- +-- Name: athletes Enable insert for authenticated users only; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Enable insert for authenticated users only" ON public.athletes FOR INSERT TO authenticated WITH CHECK (true); + + +-- -- Name: athletes Enable read access for all users; Type: POLICY; Schema: public; Owner: postgres -- diff --git a/src/Athletes.js b/src/Athletes.js @@ -3,6 +3,53 @@ import { supabase } from './supabaseClient'; const Auth = lazy(() => import('./Auth')); +async function addAthlete(e) { + e.preventDefault() + + // Read the form data + const formData = new FormData(e.target); + const formJson = Object.fromEntries(formData.entries()); + + if (defaultsSet(formJson)) { + alert('Check data of the new athlete, seems like the defaults') + return + } + + // create new athlete + const { data, error } = await supabase + .from('athletes') + .insert({ + nr: formJson.nr, + firstname: formJson.firstname, + lastname: formJson.lastname, + birthday: formJson.birthday ? formJson.birthday : null, + school: formJson.school + }) + .select() + + if (error === null) { + window.location.reload() + } else { + alert('Failed to create new athlete: ' + error.message) + } +} + +function defaultsSet({nr, firstname, lastname, birthday, school}) { + return ( + nr === '00' + || firstname === 'Firstname' + || lastname === 'Lastname' + || birthday === '0000-00-00' + || school === 'School/team' + ) +} + +function validateBirthday(e) { + if (e.target.validity.patternMismatch) { + e.target.value = e.target.defaultValue + } +} + async function deleteAthlete(e, athleteId, athleteFirstName, athleteLastName) { e.preventDefault() @@ -33,6 +80,7 @@ function AthleteForm({session}) { return ( <div> <h1>Manage Athletes <button disabled={!loading}>{loading ? '🔄 loading' : ''}</button></h1> + <form method="post" onSubmit={addAthlete}> <table> <thead> <tr> @@ -57,8 +105,35 @@ function AthleteForm({session}) { <td className='right'><button onClick={e => deleteAthlete(e, a.id, a.firstname, a.lastname)}>🗑️</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> + <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> + </form> </div> ) } diff --git a/src/Leaderboard.js b/src/Leaderboard.js @@ -260,12 +260,12 @@ function Leaderboard() { /> <form> New heat from top <input - type="number" - size="5" + type='number' + size='5' value={newHeatSize} onChange={(e) => setNewHeatSize(e.target.value)} />: - <input type="text" value={newHeatName} onChange={(e) => setNewHeatName(e.target.value)} /> + <input type='text' value={newHeatName} onChange={(e) => setNewHeatName(e.target.value)} /> <button onClick={e => newHeatFromLeaderboard( e, leaderboard,