myheats

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

Heats.jsx (4999B)


      1 import { lazy, useEffect, useState } from 'react'
      2 import { generatePath, Link } from 'react-router-dom'
      3 import { exportHeatsToCSV } from './utils'
      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 const Auth = lazy(() => import('./Auth'))
     10 
     11 export async function addNewHeat(name, heatLocation, plannedStart, session) {
     12   try {
     13     const res = await fetch(`${api_uri}:${api_port}/v1/leaderboard/newHeat`, {
     14       method: 'POST',
     15       headers: {
     16               'Content-Type': 'application/json',
     17               'Authorization': `Bearer ${session.auth.token}`,
     18       },
     19       body: JSON.stringify({
     20         "name": name,
     21         "location": heatLocation,
     22         "planned_start": plannedStart
     23       }),
     24     })
     25     const { data, error } = await res.json()
     26     return data
     27   } catch (error) {
     28     throw(error)
     29   }
     30 }
     31 
     32 async function addHeat(e, session) {
     33   e.preventDefault()
     34 
     35   // Read the form data
     36   const formData = new FormData(e.target);
     37   const formJson = Object.fromEntries(formData.entries());
     38 
     39   // create new heat
     40   try {
     41     const heat = await addNewHeat(
     42             formJson.name,
     43             formJson.location,
     44             // planned_start is an empty string if unset
     45             // https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/time
     46             formJson.planned_start === '' ? null : formJson.planned_start,
     47 	    session
     48     )
     49     window.location.reload()
     50   } catch (error) {
     51     console.error('Failed to create new heat: ' + error.message)
     52   }
     53 }
     54 
     55 export async function removeHeat(heatId, session) {
     56   const res = await fetch(`${api_uri}:${api_port}/v1/leaderboard/removeHeat`, {
     57     method: 'POST',
     58     headers: {
     59             'Content-Type': 'application/json',
     60             'Authorization': `Bearer ${session.auth.token}`,
     61     },
     62     body: JSON.stringify({
     63       "heat_id": heatId
     64     }),
     65   })
     66   return await res.json()
     67 }
     68 
     69 async function deleteHeat(e, heatId, heatName, session) {
     70   e.preventDefault()
     71 
     72   if (window.confirm('Do you really want to delete heat "' + heatName + '"?')) {
     73     const { data, error } = await removeHeat(heatId, session)
     74     if (error === undefined) {
     75       window.location.reload()
     76     } else {
     77       console.error(error)
     78     }
     79   }
     80 }
     81 
     82 // export heats
     83 function ExportForm(heats) {
     84   return (
     85     <div className='exportForm'>
     86       <form method='post' onSubmit={e => exportHeatsToCSV(e, heats)}>
     87         <button type='submit'>&#9663; export</button>
     88       </form>
     89     </div>
     90   )
     91 }
     92 
     93 function HeatForm({session}) {
     94   const [loading, setLoading] = useState(false)
     95   const [heats, setHeats] = useState([])
     96   const dateOptions = {
     97             year: "numeric",
     98             month: "2-digit",
     99             day: "2-digit",
    100         }
    101 
    102   useEffect(() => {
    103     (async () => {
    104       setLoading(true)
    105       const res = await fetch(`${api_uri}:${api_port}/v1/leaderboard/allHeats`)
    106       const { data, error } = await res.json()
    107       if (error) {
    108         console.error(error)
    109       }
    110       setHeats(data)
    111       setLoading(false)
    112     })();
    113   }, [])
    114 
    115   return (
    116     <div className='HeatForm'>
    117       <button disabled={!loading} className='loading'>↺ loading</button>
    118       <form method='post' onSubmit={e => addHeat(e, session)}>
    119         <table>
    120           <thead>
    121             <tr>
    122               <th>Created at</th>
    123               <th>Name *</th>
    124               <th>Location</th>
    125               <th>Planned start</th>
    126               <th>New/delete</th>
    127             </tr>
    128           </thead>
    129           <tbody>
    130             {heats.map(h => (
    131               <tr key={h.id}>
    132                 <td data-title='Created at' className='right'>{new Date(h.created_at).toLocaleDateString(locale, dateOptions)}</td>
    133                 <td data-title='Name'><Link to={generatePath('/heats/startlist/:heatId', {heatId:h.id})}>{h.name}</Link></td>
    134                 <td data-title='Location'>{h.location}</td>
    135                 <td data-title='Planned start'>{h.planned_start}</td>
    136                 <td><button onClick={e => deleteHeat(e, h.id, h.name, session)}>&ndash; del</button></td>
    137               </tr>
    138             ))}
    139             <tr className='input'>
    140               <td className='right'><i>* required</i></td>
    141               <td data-title='Name *'>
    142                 <input type='text' name='name' />
    143               </td>
    144               <td data-title='Location'>
    145                 <input type='text' name='location' />
    146               </td>
    147               <td data-title='Planned start' className='right'>
    148                 <input
    149                   type='time'
    150                   name='planned_start' />
    151               </td>
    152               <td>
    153                 <button type='submit'>&#43; new</button>
    154               </td>
    155             </tr>
    156           </tbody>
    157         </table>
    158       </form>
    159       <ExportForm heats={heats} />
    160     </div>
    161   )
    162 }
    163 
    164 function Heats({session}) {
    165     return (
    166       <div>
    167         {!session.auth ? <Auth /> : <HeatForm session={session} />}
    168       </div>
    169     )
    170   }
    171   
    172 export default Heats;