|
255 | 255 |
|
256 | 256 | // Inline event creation state |
257 | 257 | let isAddingEvent = $state(false); |
258 | | - let tableContainer: HTMLDivElement; |
| 258 | + let tableContainer = $state<HTMLDivElement | null>(null); |
259 | 259 | // svelte-ignore state_referenced_locally |
260 | 260 | let newEvent = $state({ |
261 | 261 | name: '', |
|
283 | 283 | } |
284 | 284 | }); |
285 | 285 |
|
286 | | - // Auto-generate check-in code when isPublic is checked |
| 286 | + // Auto-generate check-in code when isPublic is checked, clear when unchecked |
287 | 287 | let prevNewEventIsPublic = $state(false); |
288 | 288 | $effect(() => { |
289 | 289 | if (newEvent.isPublic && !prevNewEventIsPublic) { |
290 | 290 | newEvent.checkInCode = generateCheckInCode(); |
291 | 291 | } |
| 292 | + else if (!newEvent.isPublic && prevNewEventIsPublic) { |
| 293 | + newEvent.checkInCode = ''; |
| 294 | + } |
292 | 295 | prevNewEventIsPublic = newEvent.isPublic; |
293 | 296 | }); |
294 | 297 |
|
|
329 | 332 | } |
330 | 333 | }); |
331 | 334 |
|
332 | | - // Auto-generate check-in code when isPublic is checked (only if no existing code) |
| 335 | + // Auto-generate check-in code when isPublic is checked (only if no existing code), clear when unchecked |
333 | 336 | let prevEditEventIsPublic = $state(false); |
334 | 337 | $effect(() => { |
335 | 338 | if (editEvent.isPublic && !prevEditEventIsPublic && !editEvent.checkInCode) { |
336 | 339 | editEvent.checkInCode = generateCheckInCode(); |
337 | 340 | } |
| 341 | + else if (!editEvent.isPublic && prevEditEventIsPublic) { |
| 342 | + editEvent.checkInCode = ''; |
| 343 | + } |
338 | 344 | prevEditEventIsPublic = editEvent.isPublic; |
339 | 345 | }); |
340 | 346 |
|
|
436 | 442 | } |
437 | 443 |
|
438 | 444 | async function toggleEventRow(eventId: string, eventDateTime: string) { |
439 | | - // If same row clicked, do nothing (don't collapse) |
| 445 | + // If same row clicked, collapse it |
440 | 446 | if (expandedEventId === eventId) { |
| 447 | + expandedEventId = null; |
| 448 | + rosterData = []; |
441 | 449 | return; |
442 | 450 | } |
443 | 451 |
|
|
943 | 951 | Hide past events |
944 | 952 | </label> |
945 | 953 | </div> |
946 | | - <button |
947 | | - onclick={async () => { |
948 | | - newEvent.date = getTodayDate(); |
949 | | - isAddingEvent = true; |
950 | | - await tick(); |
951 | | - tableContainer?.scrollTo({ top: 0, behavior: 'smooth' }); |
952 | | - }} |
953 | | - class="bg-blue-600 text-white px-4 py-2 rounded hover:bg-blue-700" |
954 | | - disabled={isAddingEvent} |
955 | | - > |
956 | | - + Add Event |
957 | | - </button> |
| 954 | + {#if isAddingEvent} |
| 955 | + <button |
| 956 | + onclick={cancelAddEvent} |
| 957 | + class="bg-red-600 text-white px-4 py-2 rounded hover:bg-red-700" |
| 958 | + > |
| 959 | + Cancel |
| 960 | + </button> |
| 961 | + {:else} |
| 962 | + <button |
| 963 | + onclick={async () => { |
| 964 | + newEvent.date = getTodayDate(); |
| 965 | + isAddingEvent = true; |
| 966 | + await tick(); |
| 967 | + tableContainer?.scrollTo({ top: 0, behavior: 'smooth' }); |
| 968 | + }} |
| 969 | + class="bg-blue-600 text-white px-4 py-2 rounded hover:bg-blue-700" |
| 970 | + > |
| 971 | + + Add Event |
| 972 | + </button> |
| 973 | + {/if} |
958 | 974 | </div> |
959 | 975 |
|
960 | 976 | {#if markedForDelete.size > 0} |
|
989 | 1005 | <table class="w-full border-collapse"> |
990 | 1006 | <thead class="sticky top-0 z-10"> |
991 | 1007 | <tr class="bg-gray-100 text-left"> |
992 | | - <th class="p-3 border-b font-semibold"> |
| 1008 | + <th class="p-2 border-b font-semibold"> |
993 | 1009 | <button |
994 | 1010 | onclick={() => toggleSort('name')} |
995 | 1011 | class="inline-flex items-center gap-1 hover:text-blue-600" |
|
1010 | 1026 | {/if} |
1011 | 1027 | </button> |
1012 | 1028 | </th> |
1013 | | - <th class="p-3 border-b font-semibold"> |
| 1029 | + <th class="p-2 border-b font-semibold"> |
1014 | 1030 | <button |
1015 | 1031 | onclick={() => toggleSort('dateTime')} |
1016 | 1032 | class="inline-flex items-center gap-1 hover:text-blue-600" |
|
1031 | 1047 | {/if} |
1032 | 1048 | </button> |
1033 | 1049 | </th> |
1034 | | - <th class="p-3 border-b font-semibold"> |
| 1050 | + <th class="p-2 border-b font-semibold"> |
1035 | 1051 | <button |
1036 | 1052 | onclick={() => toggleSort('eventType')} |
1037 | 1053 | class="inline-flex items-center gap-1 hover:text-blue-600" |
|
1052 | 1068 | {/if} |
1053 | 1069 | </button> |
1054 | 1070 | </th> |
1055 | | - <th class="p-3 border-b font-semibold w-32"> |
| 1071 | + <th class="p-2 border-b font-semibold w-32"> |
1056 | 1072 | <button |
1057 | 1073 | onclick={() => toggleSort('cohort')} |
1058 | 1074 | class="inline-flex items-center gap-1 hover:text-blue-600" |
|
1073 | 1089 | {/if} |
1074 | 1090 | </button> |
1075 | 1091 | </th> |
1076 | | - <th class="p-3 border-b font-semibold"> |
| 1092 | + <th class="p-2 border-b font-semibold"> |
1077 | 1093 | <button |
1078 | 1094 | onclick={() => toggleSort('attendance')} |
1079 | 1095 | class="inline-flex items-center gap-1 hover:text-blue-600" |
|
1094 | 1110 | {/if} |
1095 | 1111 | </button> |
1096 | 1112 | </th> |
1097 | | - <th class="p-3 border-b font-semibold">Code</th> |
1098 | | - <th class="p-3 border-b font-semibold">Survey</th> |
1099 | | - <th class="p-3 border-b font-semibold w-16"></th> |
| 1113 | + <th class="p-2 border-b font-semibold">Code</th> |
| 1114 | + <th class="p-2 border-b font-semibold">Survey</th> |
| 1115 | + <th class="p-2 border-b font-semibold w-16"></th> |
1100 | 1116 | </tr> |
1101 | 1117 | </thead> |
1102 | 1118 | <tbody> |
|
1254 | 1270 | <path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd" /> |
1255 | 1271 | </svg> |
1256 | 1272 | </button> |
1257 | | - <button |
1258 | | - onclick={cancelAddEvent} |
1259 | | - class="text-red-600 hover:text-red-800" |
1260 | | - title="Cancel" |
1261 | | - > |
1262 | | - <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor"> |
1263 | | - <path fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd" /> |
1264 | | - </svg> |
1265 | | - </button> |
1266 | 1273 | </div> |
1267 | 1274 | </td> |
1268 | 1275 | </tr> |
|
1491 | 1498 | if (hasRoster) toggleEventRow(event.id, event.dateTime); |
1492 | 1499 | }} |
1493 | 1500 | > |
1494 | | - <td class="p-3"> |
| 1501 | + <td class="p-2"> |
1495 | 1502 | <span class="inline-flex items-center gap-2"> |
1496 | 1503 | {#if hasRoster} |
1497 | 1504 | <svg |
|
1509 | 1516 | {event.name || '(Untitled)'} |
1510 | 1517 | </span> |
1511 | 1518 | </td> |
1512 | | - <td class="p-3"> |
| 1519 | + <td class="p-2"> |
1513 | 1520 | <div>{formatDateOnly(event.dateTime)}</div> |
1514 | 1521 | <div class="text-gray-500 text-sm"> |
1515 | 1522 | {formatTimeOnly(event.dateTime)}{#if event.endDateTime} – {formatTimeOnly(event.endDateTime)}{/if} |
1516 | 1523 | </div> |
1517 | 1524 | </td> |
1518 | | - <td class="p-3"> |
| 1525 | + <td class="p-2"> |
1519 | 1526 | {#if event.eventType} |
1520 | 1527 | <span class="{EVENT_TYPE_COLORS[event.eventType].tailwind} font-medium"> |
1521 | 1528 | {event.eventType} |
|
1524 | 1531 | <span class="text-gray-400">—</span> |
1525 | 1532 | {/if} |
1526 | 1533 | </td> |
1527 | | - <td class="p-3 w-32"> |
| 1534 | + <td class="p-2 w-32"> |
1528 | 1535 | {#if event.isPublic || event.cohortIds.length > 0} |
1529 | 1536 | {@const cohortNames = getCohortNames(event.cohortIds).join(', ')} |
1530 | 1537 | {@const displayText = event.isPublic ? (cohortNames ? `Open, ${cohortNames}` : 'Open') : cohortNames} |
|
1535 | 1542 | <span class="text-gray-400">—</span> |
1536 | 1543 | {/if} |
1537 | 1544 | </td> |
1538 | | - <td class="p-3"> |
| 1545 | + <td class="p-2"> |
1539 | 1546 | {#if expectedAttendance !== null} |
1540 | 1547 | <span class="font-mono text-sm">{event.attendanceCount ?? 0}/{expectedAttendance}</span> |
1541 | 1548 | {:else} |
1542 | 1549 | <span class="text-gray-400">{event.attendanceCount ?? 0}</span> |
1543 | 1550 | {/if} |
1544 | 1551 | </td> |
1545 | | - <td class="p-3"> |
| 1552 | + <td class="p-2"> |
1546 | 1553 | {#if event.checkInCode} |
1547 | 1554 | <span class="font-mono text-sm">{event.checkInCode}</span> |
1548 | 1555 | {:else} |
1549 | 1556 | <span class="text-gray-400">—</span> |
1550 | 1557 | {/if} |
1551 | 1558 | </td> |
1552 | | - <td class="p-3"> |
| 1559 | + <td class="p-2"> |
1553 | 1560 | {#if event.surveyUrl} |
1554 | 1561 | <a |
1555 | 1562 | href={event.surveyUrl /* eslint-disable-line svelte/no-navigation-without-resolve -- external URL */} |
|
1573 | 1580 | </span> |
1574 | 1581 | {/if} |
1575 | 1582 | </td> |
1576 | | - <td class="p-3"> |
| 1583 | + <td class="p-2"> |
1577 | 1584 | <div class="flex gap-1"> |
1578 | 1585 | <button |
1579 | 1586 | onclick={(e) => { |
|
0 commit comments