myheats

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

Athletes.jsx (5295B)


      1 import { lazy, useEffect, useState } from 'react'
      2 import { exportAthletesToCSV } from './utils'
      3 const Auth = lazy(() => import('./Auth'))
      4 
      5 const api_uri = import.meta.env.VITE_API_URI
      6 const api_port = import.meta.env.VITE_API_PORT
      7 const locale = import.meta.env.VITE_LOCALE
      8 
      9 async function addAthlete(e, session) {
     10   e.preventDefault()
     11 
     12   // Read the form data
     13   const formData = new FormData(e.target);
     14   const formJson = Object.fromEntries(formData.entries());
     15 
     16   // create new athlete
     17   const res = await fetch(`${api_uri}:${api_port}/v1/leaderboard/addAthlete`, {
     18     method: 'POST',
     19     headers: {
     20             'Content-Type': 'application/json',
     21             'Authorization': `Bearer ${session.auth.token}`,
     22     },
     23     body: JSON.stringify({
     24       "nr": formJson.nr,
     25       "firstname": formJson.firstname,
     26       "lastname": formJson.lastname,
     27       "birthday": formJson.birthday ? formJson.birthday : null,
     28       "school": formJson.school
     29     }),
     30   })
     31   const { data, error } = await res.json()
     32   if (error) {
     33     alert('Failed to create new athlete: ' + error.message)
     34   }
     35   window.location.reload()
     36 }
     37 
     38 async function deleteAthlete(e, athleteId, athleteFirstName, athleteLastName, session) {
     39   e.preventDefault()
     40 
     41   const athleteName = athleteFirstName + (athleteLastName ? ' ' + athleteLastName : '')
     42   if (window.confirm('Do you really want to delete athlete "' + athleteName + '"?')) {
     43     const res = await fetch(`${api_uri}:${api_port}/v1/leaderboard/removeAthlete`, {
     44       method: 'POST',
     45       headers: {
     46               'Content-Type': 'application/json',
     47               'Authorization': `Bearer ${session.auth.token}`,
     48       },
     49       body: JSON.stringify({
     50         "athlete_id": athleteId,
     51       }),
     52     })
     53     const { data, error } = await res.json()
     54     if (error) {
     55       alert('Failed to delete athlete: ' + error.message)
     56     }
     57     window.location.reload()
     58   }
     59 }
     60 
     61 // export athletes
     62 function ExportForm(athletes) {
     63   return (
     64     <div className='exportForm'>
     65       <form method='post' onSubmit={e => exportAthletesToCSV(e, athletes)}>
     66         <button type='submit'>&#9663; export</button>
     67       </form>
     68     </div>
     69   )
     70 }
     71 
     72 function AthleteForm({session}) {
     73   const [loading, setLoading] = useState(false)
     74   const [athletes, setAthletes] = useState([])
     75   const dateOptions = {
     76             year: "numeric",
     77             month: "2-digit",
     78             day: "2-digit",
     79         }
     80 
     81   useEffect(() => {
     82     (async () => {
     83       setLoading(true)
     84       const res = await fetch(`${api_uri}:${api_port}/v1/leaderboard/allAthletes`, {
     85         headers: {
     86                 'Content-Type': 'application/json',
     87                 'Authorization': `Bearer ${session.auth.token}`,
     88         }
     89       })
     90       const { data, error } = await res.json()
     91       if (error) {
     92         console.log(error)
     93       } else {
     94         setAthletes(data)
     95       }
     96       setLoading(false)
     97     })();
     98   }, [])
     99 
    100   return (
    101     <div className='AthleteForm'>
    102       <button disabled={!loading} className='loading'>↺ loading</button>
    103       <form method='post' onSubmit={e => addAthlete(e, session)}>
    104         <table>
    105           <thead>
    106             <tr>
    107               <th>Created at</th>
    108               <th className="right">Start Nr. *</th>
    109               <th>Firstname *</th>
    110               <th>Lastname</th>
    111               <th>Birthday</th>
    112               <th>School</th>
    113               <th>New/delete</th>
    114             </tr>
    115           </thead>
    116           <tbody>
    117             {athletes.map(a => (
    118               <tr key={a.id}>
    119                 <td data-title='Created at' className='right'>{new Date(a.created_at).toLocaleDateString(locale, dateOptions)}</td>
    120                 <td data-title='Start Nr.' className='right'>{a.nr}</td>
    121                 <td data-title='Firstname'>{a.firstname}</td>
    122                 <td data-title='Lastname'>{a.lastname}</td>
    123                 <td data-title='Birthday'>{a.birthday ? new Date(a.birthday).toLocaleDateString(locale, dateOptions) : ''}</td>
    124                 <td data-title='School'>{a.school}</td>
    125                 <td><button onClick={e => deleteAthlete(e, a.id, a.firstname, a.lastname, session)}>&ndash; del</button></td>
    126               </tr>
    127             ))}
    128             <tr className='input'>
    129               <td className='right'><i>* required</i></td>
    130               <td data-title='Start Nr. *' className='right'>
    131                 <input type='number' name='nr' />
    132               </td>
    133               <td data-title='Firstname *'>
    134                 <input type='text' name='firstname' />
    135               </td>
    136               <td data-title='Lastname'>
    137                 <input type='text' name='lastname' />
    138               </td>
    139               <td data-title='Birthday' className='right'>
    140                 <input
    141                   type='date'
    142                   name='birthday' />
    143               </td>
    144               <td data-title='School'>
    145                 <input type='text' name='school' />
    146               </td>
    147               <td>
    148                 <button type='submit'>&#43; new</button>
    149               </td>
    150             </tr>
    151           </tbody>
    152         </table>
    153       </form>
    154       <ExportForm athletes={athletes} />
    155     </div>
    156   )
    157 }
    158 
    159 function Athletes({session}) {
    160     return (
    161       <div>
    162         {!session.auth ? <Auth /> : <AthleteForm session={session} />}
    163       </div>
    164     )
    165   }
    166   
    167 export default Athletes