myheats
created pr with
2.1
cmds
checkout latest patchset:
ssh pr.in0rdr.ch print 2 | git am -3checkout any patchset in a patch request:
ssh pr.in0rdr.ch print 2.[rev] | git am -3add changes to patch request:
git format-patch main --stdout | ssh pr.in0rdr.ch pr add 2
Patchset
2.1
change low width table design to description tables
Berchtold Samuel
2024-10-08Semantic diff summary
0 added,
3 modified,
0 signature changed,
0 removed
across 2 analyzed files
(3 files skipped: unsupported file type)
+2
-2
index.html
#
| ... | ... | @@ -3,7 +3,7 @@ | |
| 3 | 3 | <head> | |
| 4 | 4 | <meta charset="UTF-8" /> | |
| 5 | 5 | <link rel="icon" type="image/x-icon" href="/favicon.ico" /> | |
| 6 | - | <meta name="viewport" content="width=device-width, initial-scale=1.0" /> | |
| 6 | + | <meta name="viewport" content="width=device-width" /> | |
| 7 | 7 | <link rel="apple-touch-icon" href="/logo192.png" /> | |
| 8 | 8 | <!-- | |
| 9 | 9 | manifest.json provides metadata used when your web app is installed on a |
| ... | ... | @@ -13,7 +13,7 @@ | |
| 13 | 13 | <title>My Heats</title> | |
| 14 | 14 | </head> | |
| 15 | 15 | <body> | |
| 16 | - | <noscript>You need to enable JavaScript to run this app.</noscript> | |
| 16 | + | <noscript>Why You scared of JavaScript so much, failure.</noscript> | |
| 17 | 17 | <div id="root"></div> | |
| 18 | 18 | <script type="module" src="/src/frontend/main.jsx"></script> | |
| 19 | 19 | </body> |
+2
-2
src/frontend/App.jsx
#
+150
-100
src/frontend/Leaderboard.jsx
#
| ... | ... | @@ -204,46 +204,51 @@ function NewHeatForm(leaderboard, rankingComp, selectHeatRef, selectRankRef, ses | |
| 204 | 204 | <p> | |
| 205 | 205 | Create new heat with top N athletes from the sorted leaderboard (<i>* required</i>). | |
| 206 | 206 | </p> | |
| 207 | - | <form method='post' onSubmit={e => newHeatFromLeaderboard( | |
| 208 | - | e, | |
| 209 | - | leaderboard, | |
| 210 | - | rankingComp, | |
| 211 | - | selectHeatRef, | |
| 212 | - | selectRankRef, | |
| 213 | - | session, | |
| 214 | - | )}> | |
| 215 | - | <table> | |
| 216 | - | <thead> | |
| 217 | - | <tr> | |
| 218 | - | <th>New heat name *</th> | |
| 219 | - | <th>Location</th> | |
| 220 | - | <th>Planned start</th> | |
| 221 | - | <th>Include top N</th> | |
| 222 | - | <td></td> | |
| 223 | - | </tr> | |
| 224 | - | </thead> | |
| 225 | - | <tbody> | |
| 226 | - | <tr> | |
| 227 | - | <td data-title='New heat name *'> | |
| 228 | - | <input type='text' name='name' /> | |
| 229 | - | </td> | |
| 230 | - | <td data-title='Location'> | |
| 231 | - | <input type='text' name='location' /> | |
| 232 | - | </td> | |
| 233 | - | <td data-title='Planned start'> | |
| 234 | - | <input | |
| 235 | - | type='time' | |
| 236 | - | name='planned_start' /> | |
| 237 | - | </td> | |
| 238 | - | <td data-title='Include top N'> | |
| 239 | - | <input type='number' name='size' /> | |
| 240 | - | </td> | |
| 241 | - | <td> | |
| 242 | - | <button type='submit'>+ new</button> | |
| 243 | - | </td> | |
| 244 | - | </tr> | |
| 245 | - | </tbody> | |
| 246 | - | </table> | |
| 207 | + | <form | |
| 208 | + | method="post" | |
| 209 | + | onSubmit={(e) => | |
| 210 | + | newHeatFromLeaderboard( | |
| 211 | + | e, | |
| 212 | + | leaderboard, | |
| 213 | + | rankingComp, | |
| 214 | + | selectHeatRef, | |
| 215 | + | selectRankRef, | |
| 216 | + | session | |
| 217 | + | ) | |
| 218 | + | } | |
| 219 | + | > | |
| 220 | + | <div className="table-container"> | |
| 221 | + | <table> | |
| 222 | + | <thead> | |
| 223 | + | <tr> | |
| 224 | + | <th>New heat name *</th> | |
| 225 | + | <th>Location</th> | |
| 226 | + | <th>Planned start</th> | |
| 227 | + | <th>Include top N</th> | |
| 228 | + | <td></td> | |
| 229 | + | </tr> | |
| 230 | + | </thead> | |
| 231 | + | <tbody> | |
| 232 | + | <tr> | |
| 233 | + | <td data-title="New heat name *"> | |
| 234 | + | <input type="text" name="name" /> | |
| 235 | + | </td> | |
| 236 | + | <td data-title="Location"> | |
| 237 | + | <input type="text" name="location" /> | |
| 238 | + | </td> | |
| 239 | + | <td data-title="Planned start"> | |
| 240 | + | <input type="time" name="planned_start" /> | |
| 241 | + | </td> | |
| 242 | + | <td data-title="Include top N"> | |
| 243 | + | <input type="number" name="size" /> | |
| 244 | + | </td> | |
| 245 | + | <td> | |
| 246 | + | <button type="submit">+ new</button> | |
| 247 | + | </td> | |
| 248 | + | </tr> | |
| 249 | + | </tbody> | |
| 250 | + | </table> | |
| 251 | + | </div> | |
| 247 | 252 | </form> | |
| 248 | 253 | </div> | |
| 249 | 254 | ) |
| ... | ... | @@ -351,85 +356,130 @@ function Leaderboard({session}) { | |
| 351 | 356 | }, [heatSelection]); | |
| 352 | 357 | ||
| 353 | 358 | return ( | |
| 354 | - | <div> | |
| 355 | - | <div className='Leaderboard'> | |
| 359 | + | <> | |
| 360 | + | <div className="Leaderboard"> | |
| 356 | 361 | <header> | |
| 357 | - | <button disabled={!loading} className='loading' >↺ loading</button> | |
| 358 | - | <button className={`show-details ${details ? 'toggled' : ''}`} onClick={() => showDetails(!details)}> | |
| 359 | - | <div className='thumb'></div> | |
| 360 | - | <span>{details ? 'less' : 'more'}</span> | |
| 362 | + | <button disabled={!loading} className="loading"> | |
| 363 | + | ↺ loading | |
| 361 | 364 | </button> | |
| 362 | - | <label htmlFor='heat'>Heats to display</label> | |
| 365 | + | <button | |
| 366 | + | className={`show-details ${details ? "toggled" : ""}`} | |
| 367 | + | onClick={() => showDetails(!details)} | |
| 368 | + | > | |
| 369 | + | <div className="thumb"></div> | |
| 370 | + | <span>{details ? "less" : "more"}</span> | |
| 371 | + | </button> | |
| 372 | + | <label htmlFor="heat">Heats to display</label> | |
| 363 | 373 | <Select | |
| 364 | 374 | closeMenuOnSelect={false} | |
| 365 | 375 | isMulti | |
| 366 | 376 | options={heatOpts} | |
| 367 | - | onChange={h => setHeatSelection(h)} | |
| 377 | + | onChange={(h) => setHeatSelection(h)} | |
| 368 | 378 | ref={selectHeatRef} | |
| 369 | - | id='heat' /> | |
| 370 | - | <label htmlFor='rank' className={details ? '' : 'hidden'}>Rank by</label> | |
| 371 | - | <Select | |
| 379 | + | id="heat" | |
| 380 | + | /> | |
| 381 | + | <label htmlFor="rank" className={details ? "" : "hidden"}> | |
| 382 | + | Rank by | |
| 383 | + | </label> | |
| 384 | + | <Select | |
| 372 | 385 | closeMenuOnSelect={false} | |
| 373 | 386 | isMulti | |
| 374 | 387 | options={rankOpts} | |
| 375 | 388 | defaultValue={rankOpts[0].options[3]} | |
| 376 | - | onChange={h => setRankingComp(h)} | |
| 389 | + | onChange={(h) => setRankingComp(h)} | |
| 377 | 390 | ref={selectRankRef} | |
| 378 | - | className={details ? '' : 'hidden'} | |
| 379 | - | id='rank' /> | |
| 391 | + | className={details ? "" : "hidden"} | |
| 392 | + | id="rank" | |
| 393 | + | /> | |
| 380 | 394 | </header> | |
| 381 | - | <table className={details ? 'leaderboard' : 'hide-rank'}> | |
| 382 | - | <thead> | |
| 383 | - | <tr> | |
| 384 | - | <th className={details ? 'right' : 'hidden'}>Rank</th> | |
| 385 | - | <th className='right'>Start Nr.</th> | |
| 386 | - | <th>Name</th> | |
| 387 | - | <th className={details ? '' : 'hidden'}>Birthday</th> | |
| 388 | - | <th className={details ? '' : 'hidden'}>School</th> | |
| 389 | - | {heatSelection.map(h => ( | |
| 390 | - | <th className={details ? 'right' : 'hidden'} key={h.value}>{h.label}</th> | |
| 395 | + | <div className="table-container"> | |
| 396 | + | <table className={details ? "leaderboard" : "hide-rank"}> | |
| 397 | + | <thead> | |
| 398 | + | <tr> | |
| 399 | + | <th className={details ? "right" : "hidden"}>Rank</th> | |
| 400 | + | <th className="right">Start Nr.</th> | |
| 401 | + | <th>Name</th> | |
| 402 | + | <th className={details ? "" : "hidden"}>Birthday</th> | |
| 403 | + | <th className={details ? "" : "hidden"}>School</th> | |
| 404 | + | {heatSelection.map((h) => ( | |
| 405 | + | <th className={details ? "right" : "hidden"} key={h.value}> | |
| 406 | + | {h.label} | |
| 407 | + | </th> | |
| 408 | + | ))} | |
| 409 | + | <th className={details ? "right" : "hidden"}>Best</th> | |
| 410 | + | <th className={details ? "right" : "hidden"}>Worst</th> | |
| 411 | + | <th className="right">Total</th> | |
| 412 | + | </tr> | |
| 413 | + | </thead> | |
| 414 | + | <tbody> | |
| 415 | + | {leaderboard.sort(rankByHeat(rankingComp)).map((i) => ( | |
| 416 | + | <tr key={i.id}> | |
| 417 | + | <td className={details ? "right" : "hidden"}></td> | |
| 418 | + | <td data-title="Start Nr." className="right"> | |
| 419 | + | {i.nr} | |
| 420 | + | </td> | |
| 421 | + | <td data-title="Name"> | |
| 422 | + | {i.firstname} {i.lastname} | |
| 423 | + | </td> | |
| 424 | + | <td data-title="Birthday" className={details ? "" : "hidden"}> | |
| 425 | + | {i.birthday | |
| 426 | + | ? new Date(i.birthday).toLocaleDateString( | |
| 427 | + | locale, | |
| 428 | + | dateOptions | |
| 429 | + | ) | |
| 430 | + | : ""} | |
| 431 | + | </td> | |
| 432 | + | <td data-title="School" className={details ? "" : "hidden"}> | |
| 433 | + | {i.school} | |
| 434 | + | </td> | |
| 435 | + | {heatSelection.map((h) => ( | |
| 436 | + | // list all scores from the judges seperated with '+' signs, show sum on right side | |
| 437 | + | <td | |
| 438 | + | key={h.value} | |
| 439 | + | className={details ? "right" : "hidden"} | |
| 440 | + | data-title={h.label} | |
| 441 | + | > | |
| 442 | + | {formatScores(i, h)} | |
| 443 | + | </td> | |
| 444 | + | ))} | |
| 445 | + | <td | |
| 446 | + | className={details ? "right" : "hidden"} | |
| 447 | + | data-title="Best" | |
| 448 | + | > | |
| 449 | + | {i.bestHeat} | |
| 450 | + | </td> | |
| 451 | + | <td | |
| 452 | + | className={details ? "right" : "hidden"} | |
| 453 | + | data-title="Worst" | |
| 454 | + | > | |
| 455 | + | {i.worstHeat} | |
| 456 | + | </td> | |
| 457 | + | <td className="right total" data-title="Total"> | |
| 458 | + | {i.sum} | |
| 459 | + | </td> | |
| 460 | + | </tr> | |
| 391 | 461 | ))} | |
| 392 | - | <th className={details ? 'right' : 'hidden'}>Best</th> | |
| 393 | - | <th className={details ? 'right' : 'hidden'}>Worst</th> | |
| 394 | - | <th className='right'>Total</th> | |
| 395 | - | </tr> | |
| 396 | - | </thead> | |
| 397 | - | <tbody> | |
| 398 | - | {leaderboard.sort(rankByHeat(rankingComp)).map(i => ( | |
| 399 | - | <tr key={i.id}> | |
| 400 | - | <td className={details ? 'right' : 'hidden'}></td> | |
| 401 | - | <td data-title='Start Nr.' className='right'>{i.nr}</td> | |
| 402 | - | <td data-title='Name'>{i.firstname} {i.lastname}</td> | |
| 403 | - | <td data-title='Birthday' className={details ? '' : 'hidden'}> | |
| 404 | - | {i.birthday ? new Date(i.birthday).toLocaleDateString(locale, dateOptions) : ''} | |
| 405 | - | </td> | |
| 406 | - | <td data-title='School' className={details ? '' : 'hidden'}>{i.school}</td> | |
| 407 | - | {heatSelection.map(h => ( | |
| 408 | - | <Fragment key={h.value}> | |
| 409 | - | {/* list all scores from the judges seperated with '+' signs, show sum on right side */} | |
| 410 | - | <td className={details ? 'right' : 'hidden'} data-title={h.label}>{formatScores(i, h)}</td> | |
| 411 | - | </Fragment> | |
| 412 | - | ))} | |
| 413 | - | <td className={details ? 'right' : 'hidden'} data-title='Best'>{i.bestHeat}</td> | |
| 414 | - | <td className={details ? 'right' : 'hidden'} data-title='Worst'>{i.worstHeat}</td> | |
| 415 | - | <td className='right total' data-title='Total'>{i.sum}</td> | |
| 416 | - | </tr> | |
| 417 | - | ))} | |
| 418 | - | </tbody> | |
| 419 | - | </table> | |
| 462 | + | </tbody> | |
| 463 | + | </table> | |
| 464 | + | </div> | |
| 420 | 465 | </div> | |
| 421 | 466 | <ExportForm | |
| 422 | 467 | leaderboard={leaderboard} | |
| 423 | 468 | heatSelection={heatSelection} | |
| 424 | - | rankingComp={rankingComp} /> | |
| 425 | - | {session.auth ? <NewHeatForm | |
| 426 | - | leaderboard={leaderboard} | |
| 427 | 469 | rankingComp={rankingComp} | |
| 428 | - | selectHeatRef={selectHeatRef} | |
| 429 | - | selectRankRef={selectRankRef} | |
| 430 | - | session={session} | |
| 431 | - | /> : ''} | |
| 432 | - | </div> | |
| 470 | + | /> | |
| 471 | + | {session.auth ? ( | |
| 472 | + | <NewHeatForm | |
| 473 | + | leaderboard={leaderboard} | |
| 474 | + | rankingComp={rankingComp} | |
| 475 | + | selectHeatRef={selectHeatRef} | |
| 476 | + | selectRankRef={selectRankRef} | |
| 477 | + | session={session} | |
| 478 | + | /> | |
| 479 | + | ) : ( | |
| 480 | + | "" | |
| 481 | + | )} | |
| 482 | + | </> | |
| 433 | 483 | ) | |
| 434 | 484 | } | |
| 435 | 485 |
+37
-36
src/frontend/css/App.css
#
| ... | ... | @@ -99,10 +99,11 @@ footer span button { | |
| 99 | 99 | } | |
| 100 | 100 | ||
| 101 | 101 | .loginForm, .exportForm { | |
| 102 | - | margin: 30px; | |
| 102 | + | margin: auto; | |
| 103 | + | padding: 3rem; | |
| 103 | 104 | } | |
| 104 | 105 | .loginForm button, .loginForm input, .exportForm button { | |
| 105 | - | width: 250px; | |
| 106 | + | width: 100%; | |
| 106 | 107 | display: block; | |
| 107 | 108 | } | |
| 108 | 109 |
| ... | ... | @@ -214,55 +220,50 @@ td.total { | |
| 214 | 220 | } | |
| 215 | 221 | ||
| 216 | 222 | /* https://css-tricks.com/making-tables-responsive-with-minimal-css */ | |
| 217 | - | @media(max-width: 1100px) { | |
| 223 | + | /* Mobile / Small screen styles */ | |
| 224 | + | @media (max-width: 768px) { | |
| 225 | + | table.leaderboard { | |
| 226 | + | display: block; | |
| 227 | + | } | |
| 228 | + | ||
| 218 | 229 | table.leaderboard thead { | |
| 219 | - | left: -9999px; | |
| 220 | - | position: absolute; | |
| 221 | - | visibility: hidden; | |
| 230 | + | display: none; | |
| 222 | 231 | } | |
| 223 | 232 | ||
| 224 | - | table.leaderboard tr { | |
| 225 | - | display: flex; | |
| 226 | - | flex-direction: row; | |
| 227 | - | flex-wrap: wrap; | |
| 228 | - | padding: 20px 0; | |
| 233 | + | table.leaderboard tbody, | |
| 234 | + | table.leaderboard tr, | |
| 235 | + | table.leaderboard td { | |
| 236 | + | display: block; | |
| 229 | 237 | } | |
| 230 | 238 | ||
| 231 | - | table.leaderboard tr:not(:last-child) { | |
| 232 | - | border-bottom: 1px solid #e1e1e7; | |
| 239 | + | table.leaderboard tr { | |
| 240 | + | padding: 2rem; | |
| 233 | 241 | } | |
| 234 | 242 | ||
| 235 | 243 | table.leaderboard td { | |
| 236 | - | margin: 0 -1px -1px 0; | |
| 237 | - | padding-top: 35px; | |
| 238 | - | margin-bottom: 25px; | |
| 244 | + | display: flex; | |
| 245 | + | padding: 10px; | |
| 246 | + | border-bottom: 1px solid #ddd; | |
| 239 | 247 | position: relative; | |
| 240 | - | width: 35%; | |
| 241 | - | text-align: left !important; | |
| 242 | 248 | } | |
| 243 | 249 | ||
| 244 | - | table.leaderboard td:before { | |
| 245 | - | content: attr(data-title); | |
| 246 | - | position: absolute; | |
| 247 | - | top: 3px; | |
| 248 | - | left: 20px; | |
| 250 | + | table.leaderboard:not(.hide-rank) tr td:first-child::before { | |
| 251 | + | content: counter(rowNumber) ". Rank"; | |
| 252 | + | } | |
| 253 | + | ||
| 254 | + | table.leaderboard td[data-title]:before { | |
| 255 | + | content: attr(data-title) ": "; | |
| 256 | + | display: inline-block; | |
| 257 | + | width: 50%; | |
| 258 | + | padding-right: 1rem; | |
| 259 | + | text-align: right; | |
| 260 | + | align-content: center; | |
| 249 | 261 | font-size: 0.8em; | |
| 250 | 262 | text-transform: uppercase; | |
| 251 | 263 | color: #b0b0b6; | |
| 252 | 264 | } | |
| 253 | 265 | ||
| 254 | - | table.leaderboard td:nth-child(-n+6) { | |
| 255 | - | /* background: rgb(236, 236, 236); */ | |
| 256 | - | } | |
| 257 | - | ||
| 258 | - | table td button { | |
| 259 | - | position: absolute; | |
| 260 | - | bottom: 0; | |
| 261 | - | height: 50px; | |
| 262 | - | width: 100%; | |
| 263 | - | } | |
| 264 | - | ||
| 265 | - | table td:empty { | |
| 266 | - | display: none; | |
| 266 | + | table.leaderboard tr { | |
| 267 | + | border-bottom: 1px solid #ddd; | |
| 267 | 268 | } | |
| 268 | 269 | } |