Compare commits

..

3 commits

Author SHA1 Message Date
a32a49b281 Add UI for setting cancelled status
All checks were successful
/ build (push) Successful in 1m35s
/ deploy (push) Successful in 16s
Add indications in event cards, event slot cards and the timetable for
an event or event slot being cancelled by striking it through and
dimming the text colour.  And a checkbox in the event and event slot
list to edit the cancelled status.  And a diff entry for the cancelled
status on events and event slots.
2025-09-21 23:15:10 +02:00
7314b26c77 Fix tab visuals in Firefox
The rendering of the tabs would not include the spacer in Firefox for
if the width was not set for some reason.
2025-09-21 23:13:26 +02:00
5d4cdb7b83 Fix session-expired event causing events to stop
If a session-expired event is hit that does not match the current
session then it should be ignored and event processing continue without
it.  This was incorrectly implemented and instead event processing
would stop when any session-expired event was hit, not just the ones
matching the current session.

Fix logic to properly ignore session-expired events when it doesn't
match the current session.
2025-09-21 22:15:11 +02:00
9 changed files with 71 additions and 8 deletions

View file

@ -3,7 +3,10 @@
SPDX-License-Identifier: AGPL-3.0-or-later SPDX-License-Identifier: AGPL-3.0-or-later
--> -->
<template> <template>
<section class="event"> <section
class="event"
:class="{ cancelled: event.cancelled }"
>
<h3>{{ event.name }}</h3> <h3>{{ event.name }}</h3>
<p v-if=event.host> <p v-if=event.host>
Host: {{ event.host }} Host: {{ event.host }}
@ -36,7 +39,12 @@
<h4>Timeslots</h4> <h4>Timeslots</h4>
<ul> <ul>
<li v-for="slot in event.slots.values()" :key="slot.id"> <li
v-for="slot in event.slots.values()"
:key="slot.id"
class="slot"
:class="{ cancelled: slot.cancelled }"
>
{{ formatTime(slot.start) }} - {{ formatTime(slot.end) }} {{ formatTime(slot.start) }} - {{ formatTime(slot.end) }}
<button <button
v-if="accountStore.valid && event.slots.size > 1" v-if="accountStore.valid && event.slots.size > 1"
@ -91,6 +99,10 @@ async function toggle(type: "event" | "slot", id: number, slotIds?: number[]) {
padding: 0.5rem; padding: 0.5rem;
border-radius: 0.5rem; border-radius: 0.5rem;
} }
.event.cancelled>*, .slot.cancelled {
text-decoration: line-through;
color: grey;
}
.event h3 { .event h3 {
margin: 0; margin: 0;
} }
@ -98,7 +110,9 @@ async function toggle(type: "event" | "slot", id: number, slotIds?: number[]) {
margin-block-start: 0.5rem; margin-block-start: 0.5rem;
} }
.notice { .event .notice {
text-decoration: none;
color: CanvasText;
display: flex; display: flex;
width: fit-content; width: fit-content;
gap: 0.5rem; gap: 0.5rem;

View file

@ -3,7 +3,10 @@
SPDX-License-Identifier: AGPL-3.0-or-later SPDX-License-Identifier: AGPL-3.0-or-later
--> -->
<template> <template>
<section class="eventSlot"> <section
class="eventSlot"
:class="{ cancelled: slot.cancelled || event?.cancelled }"
>
<hgroup> <hgroup>
<h3>{{ event?.name }}</h3> <h3>{{ event?.name }}</h3>
<p> <p>
@ -75,6 +78,10 @@ function formatTime(time: DateTime) {
padding: 0.5rem; padding: 0.5rem;
border-radius: 0.5rem; border-radius: 0.5rem;
} }
.eventSlot.cancelled>* {
text-decoration: line-through;
color: grey;
}
.eventSlot h3 { .eventSlot h3 {
margin: 0; margin: 0;
} }
@ -82,7 +89,9 @@ function formatTime(time: DateTime) {
margin-block-start: 0.5rem; margin-block-start: 0.5rem;
} }
.notice { .eventSlot .notice {
text-decoration: none;
color: CanvasText;
display: flex; display: flex;
width: fit-content; width: fit-content;
gap: 0.5rem; gap: 0.5rem;

View file

@ -29,6 +29,12 @@
:after='event.crew ? "No" : "Yes"' :after='event.crew ? "No" : "Yes"'
:state :state
/> />
<DiffFieldString
title="Cancelled"
:before='event.serverCancelled ? "Yes" : "No"'
:after='event.cancelled ? "Yes" : "No"'
:state
/>
<DiffFieldString <DiffFieldString
title="Notice" title="Notice"
:before="event.serverNotice" :before="event.serverNotice"

View file

@ -24,6 +24,12 @@
:entities="schedule.locations" :entities="schedule.locations"
:state :state
/> />
<DiffFieldString
title="Cancelled"
:before='slot.serverCancelled ? "Yes" : "No"'
:after='slot.cancelled ? "Yes" : "No"'
:state
/>
<DiffFieldSetEntityId <DiffFieldSetEntityId
title="Assigned" title="Assigned"
:before="slot.serverAssigned" :before="slot.serverAssigned"

View file

@ -12,6 +12,7 @@
<th>duration</th> <th>duration</th>
<th>event</th> <th>event</th>
<th>location</th> <th>location</th>
<th title="cancelled">c</th>
<th>assigned</th> <th>assigned</th>
<th v-if="edit"></th> <th v-if="edit"></th>
</tr> </tr>
@ -50,6 +51,7 @@
/> />
</td> </td>
<td></td> <td></td>
<td></td>
<td> <td>
Add at Add at
<button <button
@ -97,6 +99,11 @@
v-model="es.slot.locationIds" v-model="es.slot.locationIds"
/> />
</td> </td>
<td>
<input
type="checkbox"
v-model="es.slot.cancelled">
</td>
<td> <td>
<SelectMultiEntity <SelectMultiEntity
:entities="usersStore.users" :entities="usersStore.users"
@ -149,6 +156,7 @@
/> />
</td> </td>
<td></td> <td></td>
<td></td>
<td colspan="2"> <td colspan="2">
<button <button
type="button" type="button"
@ -177,6 +185,7 @@
<td>{{ es.end.diff(es.start).toFormat('hh:mm') }}</td> <td>{{ es.end.diff(es.start).toFormat('hh:mm') }}</td>
<td>{{ es.event?.name }}</td> <td>{{ es.event?.name }}</td>
<td></td> <td></td>
<td>{{ es.slot.cancelled ? "x" : undefined }}</td>
<td><AssignedCrew :modelValue="es.slot.assigned" :edit="false" /></td> <td><AssignedCrew :modelValue="es.slot.assigned" :edit="false" /></td>
</template> </template>
</tr> </tr>

View file

@ -14,6 +14,7 @@
<th>notice</th> <th>notice</th>
<th>description</th> <th>description</th>
<th>p</th> <th>p</th>
<th title="cancelled">c</th>
<th>s</th> <th>s</th>
<th v-if="edit"></th> <th v-if="edit"></th>
</tr> </tr>
@ -69,6 +70,13 @@
@change="event.crew = !event.crew" @change="event.crew = !event.crew"
> >
</td> </td>
<td>
<input
type="checkbox"
:disabled="!canEdit(event)"
v-model="event.cancelled"
>
</td>
<td>{{ event.slots.size ? event.slots.size : "" }}</td> <td>{{ event.slots.size ? event.slots.size : "" }}</td>
<td> <td>
<button <button
@ -123,6 +131,7 @@
> >
</td> </td>
<td></td> <td></td>
<td></td>
<td> <td>
<button <button
v-if="eventExists(newEventName)" v-if="eventExists(newEventName)"

View file

@ -55,7 +55,6 @@ nav {
} }
.tab { .tab {
display: flex; display: flex;
flex-wrap: wrap;
} }
.tab.active { .tab.active {
padding-block-start: 1px; padding-block-start: 1px;
@ -65,6 +64,7 @@ nav {
} }
.tab .spacer { .tab .spacer {
flex: 1 0 0.75rem; flex: 1 0 0.75rem;
width: 0.75rem;
} }
.tab .flap, .tab .flap,
.tab .spacer { .tab .spacer {

View file

@ -102,7 +102,11 @@
v-for="cell, index in row" v-for="cell, index in row"
:key="index" :key="index"
:colSpan="cell.span" :colSpan="cell.span"
:class='{"event": cell.slot, "crew": cell.event?.crew }' :class="{
event: cell.slot,
crew: cell.event?.crew,
cancelled: cell.event?.cancelled || cell.slot?.cancelled,
}"
:title="cell.event?.name" :title="cell.event?.name"
> >
{{ cell.event?.notice ? "⚠️" : undefined }} {{ cell.event?.notice ? "⚠️" : undefined }}
@ -791,4 +795,9 @@ tr.hours>th + th.dayShift div {
.event.crew { .event.crew {
background-color: color-mix(in oklab, var(--background), rgb(127, 127, 127) 60%); background-color: color-mix(in oklab, var(--background), rgb(127, 127, 127) 60%);
} }
.event.cancelled {
color: color-mix(in oklab, CanvasText, rgb(127, 127, 127) 80%);
text-decoration: line-through;
}
</style> </style>

View file

@ -192,8 +192,9 @@ function sendEventToStream(stream: EventStream, event: ApiEvent) {
if (event.type === "session-expired") { if (event.type === "session-expired") {
if (stream.sessionId === event.sessionId) { if (stream.sessionId === event.sessionId) {
stream.close("session expired"); stream.close("session expired");
return false;
} }
return false; return true;
} }
// Account events are specially handled and only sent to the user they belong to. // Account events are specially handled and only sent to the user they belong to.