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'>▿ 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)}>– 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'>+ 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;