From db8393c3a9072ac6427439bec76423222b2d8e22 Mon Sep 17 00:00:00 2001 From: Hornwitser Date: Mon, 10 Mar 2025 14:40:02 +0100 Subject: [PATCH] Add crew designator to events Distinguish between events for attendees to see and events that are meant only for the crew. --- components/Timetable.vue | 38 +++++++++++----- server/generate-demo-schedule.ts | 77 ++++++++++++++++++++++++++++---- shared/types/schedule.d.ts | 1 + 3 files changed, 96 insertions(+), 20 deletions(-) diff --git a/components/Timetable.vue b/components/Timetable.vue index 746de66..a4ebdb8 100644 --- a/components/Timetable.vue +++ b/components/Timetable.vue @@ -62,9 +62,10 @@ v-for="row, index in locationRows.get(location.id)" :key="index" :colSpan="row.span" - :class='{"event": row.slots.size }' + :class='{"event": row.slots.size, "crew": row.crew }' + :title="row.title" > - {{ [...row.slots].map(slot => eventBySlotId.get(slot.id)!.name).join(", ") }} + {{ row.title }} @@ -310,16 +311,20 @@ function padStretch(stretch: Stretch, timezone: string): Stretch { } function tableElementsFromStretches( - stretches: Iterable, locations: ScheduleLocation[], timezone: string, + stretches: Iterable, + events: ScheduleEvent[], + locations: ScheduleLocation[], + timezone: string, ) { type Col = { minutes?: number }; type DayHead = { span: number, content?: string } type HourHead = { span: number, content?: string } - type LocationCell = { span: number, slots: Set } + type LocationCell = { span: number, slots: Set, title: string, crew?: boolean } const columnGroups: { className?: string, cols: Col[] }[] = []; const dayHeaders: DayHead[] = []; const hourHeaders: HourHead[]= []; const locationRows = new Map(locations.map(location => [location.id, []])); + const eventBySlotId = new Map(events.flatMap(event => event.slots.map(slot => [slot.id, event]))); function startColumnGroup(className?: string) { columnGroups.push({ className, cols: []}) @@ -331,7 +336,13 @@ function tableElementsFromStretches( hourHeaders.push({ span: 0, content }) } function startLocation(id: string, slots = new Set()) { - locationRows.get(id)!.push({ span: 0, slots }); + const rows = locationRows.get(id)!; + if (rows.length) { + const row = rows[rows.length - 1]; + row.title = [...row.slots].map(slot => eventBySlotId.get(slot.id)!.name).join(", "); + row.crew = [...row.slots].every(slot => eventBySlotId.get(slot.id)!.crew); + } + rows.push({ span: 0, slots, title: "" }); } function pushColumn(minutes?: number) { columnGroups[columnGroups.length - 1].cols.push({ minutes }) @@ -414,6 +425,7 @@ function tableElementsFromStretches( dayHeaders: dayHeaders.filter(day => day.span), hourHeaders: hourHeaders.filter(hour => hour.span), locationRows: new Map([...locationRows].map(([id, cells]) => [id, cells.filter(cell => cell.span)])), + eventBySlotId, }; } @@ -431,16 +443,14 @@ const timezone = computed({ set: (value: string) => { debugTimezone.value = value }, }); -const elements = computed(() => tableElementsFromStretches(stretches.value, schedule.value.locations, timezone.value)); +const elements = computed(() => tableElementsFromStretches( + stretches.value, schedule.value.events, schedule.value.locations, timezone.value +)); const columnGroups = computed(() => elements.value.columnGroups); const dayHeaders = computed(() => elements.value.dayHeaders); const hourHeaders = computed(() => elements.value.hourHeaders); const locationRows = computed(() => elements.value.locationRows); -const eventBySlotId = computed(() => new Map( - schedule.value.events.flatMap( - event => event.slots.map(slot => [slot.id, event]) - ) -)); +const eventBySlotId = computed(() => elements.value.eventBySlotId); diff --git a/server/generate-demo-schedule.ts b/server/generate-demo-schedule.ts index bf7aacd..dc199fc 100644 --- a/server/generate-demo-schedule.ts +++ b/server/generate-demo-schedule.ts @@ -32,16 +32,27 @@ const events = [ "d1 12:00 4h clubhouse", "d2 12:00 4h clubhouse", "d3 12:00 4h clubhouse", - ] + ], }, + { name: "Arcade Setup", crew: true, slots: ["d1 11:00 1h clubhouse"], }, { name: "Bonfire Stories", description: "Share your stories as we sit cosily around the campfire.", - slots: ["d2 18:00 2h campfire"] + slots: ["d2 18:00 2h campfire"], + }, + { name: "Stage Rigging", crew: true, slots: ["d1 11:00 7h30m stage"]}, + { + name: "Reconfigure for DJ", + crew: true, + slots: [ + "d2 19:00 1h stage", + "d3 18:45 1h15m stage", + ], }, { name: "DJ Alpha", slots: ["d2 20:00 2h stage"] }, { name: "DJ Bravo", slots: ["d3 20:00 2h stage"] }, { name: "DJ Charlie", slots: ["d3 22:00 2h stage"] }, + { name: "Prepare Fursuit Games", crew: true, slots: ["d4 17:00 1h clubhouse"] }, { name: "Fursuit Games", description: "Playful time for the suiters.", @@ -50,6 +61,8 @@ const events = [ { name: "Fishing Trip", slots: ["d3 12:00 3h30m outside"]}, { name: "Opening", slots: ["d1 18:30 1h30m stage"]}, { name: "Closing", slots: ["d5 16:00 1h stage"]}, + { name: "Stage Teardown", crew: true, slots: ["d5 17:00 4h stage"]}, + { name: "Setup Board Games", crew: true, slots: ["d1 11:30 30m summerhouse"]}, { name: "Board Games", slots: [ @@ -57,9 +70,18 @@ const events = [ "d2 12:00 4h summerhouse", "d3 12:00 4h summerhouse", "d4 12:00 4h summerhouse", - ] + ], }, + { name: "Teardown Board Games", crew: true, slots: ["d4 16:00 30m summerhouse"]}, { name: "📷meet", slots: ["d3 19:00 1h10m summerhouse"]}, + { + name: "Prepare Karaoke", + crew: true, + slots: [ + "d3 20:00 1h clubhouse", + "d4 20:00 1h clubhouse", + ], + }, { name: "Karaoke", slots: [ @@ -68,9 +90,21 @@ const events = [ ], }, { name: "Dance", slots: ["d1 20:00 3h stage"]}, + { name: "Prepare Charity Auction", crew: true, slots: ["d4 10:00 3h stage"]}, { name: "Charity Auction", slots: ["d4 13:00 2h stage"]}, + { name: "Tournament Preparation", crew: true, slots: ["d2 15:00 2h stage"]}, { name: "Tournament", slots: ["d2 17:00 2h stage"]}, { name: "Canoe Trip", slots: ["d4 11:00 4h30m outside"]}, + { + name: "Dinner Preparations", + crew: true, + slots: [ + "d1 14:00 2h campfire", + "d2 14:00 2h campfire", + "d3 14:00 2h campfire", + "d4 14:00 2h campfire", + ], + }, { name: "Dinner", slots: [ @@ -80,10 +114,24 @@ const events = [ "d4 16:00 1h campfire", ], }, + { + name: "Dinner Cleanup", + crew: true, + slots: [ + "d1 17:00 1h campfire", + "d2 17:00 1h campfire", + "d3 17:00 1h campfire", + "d4 17:00 1h campfire", + ], + }, + { name: "Prepare Film Night", crew: true, slots: ["d4 19:00 2h stage"]}, { name: "Film Night", slots: ["d4 21:00 2h stage"]}, - { name: "Photo", slots: ["d3 18:00 45m stage"]}, + { name: "Prepare Group Photo", crew: true, slots: ["d3 17:00 1h stage"]}, + { name: "Group Photo", slots: ["d3 18:00 45m stage"]}, + { name: "Setup Artist Alley", crew: true, slots: ["d4 10:00 2h clubhouse"]}, { name: "Artist Alley", slots: ["d4 12:00 4h clubhouse"]}, - { name: "Feedback Panel", slots: ["d5 18:00 4h clubhouse"]}, + { name: "Teardown Artist Alley", crew: true, slots: ["d4 16:00 1h clubhouse"]}, + { name: "Feedback Panel", slots: ["d5 18:00 1h clubhouse"]}, ]; function toId(name: string) { @@ -94,7 +142,7 @@ function toIso(date: Date) { return date.toISOString().replace(":00.000Z", "Z"); } -function toSlot(origin: Date, id: string, shorthand: string, index: number): TimeSlot { +function toSlot(origin: Date, id: string, shorthand: string, index: number, counts: Map): TimeSlot { const [day, start, duration, location] = shorthand.split(" "); const [startHours, startMinutes] = start.split(":").map(time => parseInt(time, 10)); const dayNumber = parseInt(day.slice(1)); @@ -113,6 +161,7 @@ function toSlot(origin: Date, id: string, shorthand: string, index: number): Tim start: toIso(startDate), end: toIso(endDate), locations: [location], + interested: counts.get(`${id}-${index}`), }; } @@ -125,13 +174,22 @@ export function generateDemoSchedule(): Schedule { origin.setUTCSeconds(0); origin.setUTCMilliseconds(0); + const counts = new Map() + for (const account of generateDemoAccounts()) { + for (const id of account.interestedIds ?? []) { + counts.set(id, (counts.get(id) ?? 0) + 1); + } + } + return { events: events.map( - ({ name, description, slots }) => ({ + ({ name, crew, description, slots }) => ({ id: toId(name), name, + crew, description, - slots: slots.map((shorthand, index) => toSlot(origin, toId(name), shorthand, index)) + interested: counts.get(toId(name)), + slots: slots.map((shorthand, index) => toSlot(origin, toId(name), shorthand, index, counts)) }) ), locations: locations.map( @@ -178,6 +236,7 @@ export function generateDemoAccounts(): Account[] { // These have a much higher probability of being in someone's interested list. const desiredEvent = ["opening", "closing", "fursuit-games"]; + const nonCrewEvents = events.filter(event => !event.crew); for (const account of accounts) { const interestedIds: string[] = []; @@ -189,7 +248,7 @@ export function generateDemoAccounts(): Account[] { const eventsToAdd = Math.floor(random() * 10); while (interestedIds.length < eventsToAdd) { - const event = events[Math.floor(random() * events.length)]; + const event = nonCrewEvents[Math.floor(random() * nonCrewEvents.length)]; const eventId = toId(event.name); if (interestedIds.some(id => id.replace(/-\d+$/, "") === eventId)) { continue; diff --git a/shared/types/schedule.d.ts b/shared/types/schedule.d.ts index c09fdb0..698ac22 100644 --- a/shared/types/schedule.d.ts +++ b/shared/types/schedule.d.ts @@ -1,6 +1,7 @@ export interface ScheduleEvent { name: string, id: string, + crew?: boolean, host?: string, cancelled?: boolean, description?: string,