aboutsummaryrefslogtreecommitdiffstats
path: root/web/stores
diff options
context:
space:
mode:
authorLeonardo Bishop <me@leonardobishop.com>2025-08-14 18:07:12 +0100
committerLeonardo Bishop <me@leonardobishop.com>2025-08-14 18:07:12 +0100
commit4697556cac819c47d068819b9fc9c3b4ea84e279 (patch)
treeb832d8fc6b643a8b9d0eeca35c1268e1649da731 /web/stores
parentdd49c9205bb04844b686b9c3396c40eb49d25826 (diff)
Merge confplanner-web and replace fiber with native net/http
Diffstat (limited to 'web/stores')
-rw-r--r--web/stores/error.ts13
-rw-r--r--web/stores/favourites.ts43
-rw-r--r--web/stores/schedule.ts168
-rw-r--r--web/stores/selected-event.ts14
4 files changed, 238 insertions, 0 deletions
diff --git a/web/stores/error.ts b/web/stores/error.ts
new file mode 100644
index 0000000..2c234f4
--- /dev/null
+++ b/web/stores/error.ts
@@ -0,0 +1,13 @@
+import { defineStore } from "pinia";
+
+export const useErrorStore = defineStore('error', () => {
+ const error = ref(null as string | null)
+ const setError = (newError: string) => {
+ error.value = newError
+ }
+ const clearError = () => {
+ error.value = null
+ }
+
+ return {error, setError, clearError}
+})
diff --git a/web/stores/favourites.ts b/web/stores/favourites.ts
new file mode 100644
index 0000000..2bf7257
--- /dev/null
+++ b/web/stores/favourites.ts
@@ -0,0 +1,43 @@
+import { defineStore } from "pinia";
+import { type Event } from "./schedule";
+
+interface Favourite {
+ id: number;
+ eventGuid?: string;
+ eventId?: number;
+}
+
+export const useFavouritesStore = defineStore('favourites', () => {
+ const favourites = ref([] as Favourite[])
+ const status = ref('idle' as 'idle' | 'pending')
+
+ const setFavourites = (newFavourites: Favourite[]) => {
+ favourites.value = newFavourites
+ }
+ const addFavourite = (favourite: Favourite) => {
+ favourites.value.push(favourite)
+ }
+ const removeFavourite = (favourite: { eventGuid?: string, eventId?: number }) => {
+ if (favourite.eventGuid) {
+ favourites.value = favourites.value.filter(f => f.eventGuid !== favourite.eventGuid)
+ }
+ if (favourite.eventId) {
+ favourites.value = favourites.value.filter(f => f.eventId !== favourite.eventId)
+ }
+ }
+ const isFavourite = (event: Event) => {
+ return favourites.value.some(f => {
+ if (f.eventGuid) {
+ return f.eventGuid === event.guid
+ } else if (f.eventId) {
+ return f.eventId === event.id
+ }
+ })
+ }
+
+ const setStatus = (newStatus: 'idle' | 'pending') => {
+ status.value = newStatus
+ }
+
+ return {favourites, status, setFavourites, addFavourite, removeFavourite, isFavourite, setStatus}
+})
diff --git a/web/stores/schedule.ts b/web/stores/schedule.ts
new file mode 100644
index 0000000..d5f4b4c
--- /dev/null
+++ b/web/stores/schedule.ts
@@ -0,0 +1,168 @@
+import { TZDate } from "@date-fns/tz";
+import { addDays } from "date-fns";
+import { defineStore } from "pinia";
+
+interface Schedule {
+ conference: Conference;
+ tracks: Track[];
+ days: Day[];
+}
+
+interface Conference {
+ title: string;
+ venue: string;
+ city: string;
+ start: Date;
+ end: Date;
+ days: number;
+ dayChange: string;
+ timeslotDuration: string;
+ baseUrl: string;
+ timeZoneName: string;
+}
+
+interface Track {
+ name: string;
+ slug: string;
+}
+
+interface Day {
+ date: string;
+ start: Date;
+ end: Date;
+ rooms: Room[];
+}
+
+interface Room {
+ name: string;
+ events: Event[];
+}
+
+export interface Event {
+ id: number;
+ guid: string;
+ date: string;
+ start: Date;
+ end: Date;
+ duration: string;
+ room: string;
+ url: string;
+ track: Track;
+ type: string;
+ title: string;
+ abstract: string;
+ persons: Person[];
+ attachments: Attachment[];
+ links: Link[];
+}
+
+interface Person {
+ id: number;
+ name: string;
+}
+
+interface Attachment {
+ type: string;
+ href: string;
+ name: string;
+}
+
+interface Link {
+ href: string;
+ name: string;
+}
+
+export const useScheduleStore = defineStore('schedule', () => {
+ const schedule = ref(null as Schedule | null)
+
+ const events = ref([] as Event[])
+ const eventsPerDay = ref({} as { [key: string]: Event[] })
+ const eventsPerTrack = ref({} as { [key: string]: Event[] })
+
+ const tracks = ref({} as { [key: string]: Track })
+
+ const setSchedule = (newSchedule: Schedule) => {
+ tracks.value = {}
+ newSchedule.tracks.forEach(track => {
+ track.slug = convertToSlug(track.name)
+ tracks.value[track.name] = track
+ });
+
+ newSchedule.conference.start = new TZDate(newSchedule.conference.start, newSchedule.conference.timeZoneName)
+ newSchedule.conference.end = addDays(new TZDate(newSchedule.conference.end, newSchedule.conference.timeZoneName), 1)
+
+ events.value = []
+ newSchedule.days.forEach(day => {
+ day.start = new TZDate(day.start, newSchedule.conference.timeZoneName)
+ day.end = new TZDate(day.end, newSchedule.conference.timeZoneName)
+ day.rooms.forEach(room => {
+ room.events.forEach(event => {
+ normalizeDates(event, newSchedule.conference.timeZoneName)
+
+ events.value.push(event)
+
+ event.track = tracks.value[event.track as unknown as string]
+ })
+ })
+ })
+ events.value.sort((a, b) => {
+ return a.start.getTime() - b.start.getTime()
+ })
+
+ schedule.value = newSchedule
+
+ eventsPerDay.value = {}
+ events.value.forEach(event => {
+ const date = event.date.split('T')[0]
+ if (!eventsPerDay.value[date]) {
+ eventsPerDay.value[date] = []
+ }
+ eventsPerDay.value[date].push(event)
+ })
+
+ eventsPerTrack.value = {}
+ events.value.forEach(event => {
+ if (!eventsPerTrack.value[event.track.name]) {
+ eventsPerTrack.value[event.track.name] = []
+ }
+ eventsPerTrack.value[event.track.name].push(event)
+ });
+ }
+
+ const isConferenceOngoing = () => {
+ if (!schedule.value) {
+ return false
+ }
+ return new Date() >= schedule.value.conference.start && new Date() < schedule.value.conference.end
+ }
+
+ const isConferenceFinished = () => {
+ if (!schedule.value) {
+ return false
+ }
+ return new Date() > schedule.value.conference.end
+ }
+
+ const getStartDate = () => {
+ return schedule.value?.conference.start || 0
+ }
+
+ return {schedule, events, eventsPerDay, eventsPerTrack, setSchedule, isConferenceOngoing, isConferenceFinished, getStartDate}
+})
+
+function normalizeDates(event: Event, timeZone: string) {
+ event.start = new TZDate(event.start, timeZone)
+ event.end = new TZDate(event.end, timeZone)
+}
+
+function parseDuration(duration: string) {
+ const [hours, minutes] = duration.split(':').map(Number)
+ return hours * 60 * 60 * 1000 + minutes * 60 * 1000
+}
+
+function convertToSlug(text: string) {
+ return text
+ .toLowerCase()
+ .replace(/ /g, '-')
+ .replace(/[^\w-]+/g, '')
+} \ No newline at end of file
diff --git a/web/stores/selected-event.ts b/web/stores/selected-event.ts
new file mode 100644
index 0000000..88e3a00
--- /dev/null
+++ b/web/stores/selected-event.ts
@@ -0,0 +1,14 @@
+import { type Event } from "./schedule";
+import { defineStore } from "pinia";
+
+export const useSelectedEventStore = defineStore('selected-event', () => {
+ const selectedEvent = ref(null as Event | null)
+ const setSelectedEvent = (event: Event) => {
+ selectedEvent.value = event
+ }
+ const clearSelectedEvent = () => {
+ selectedEvent.value = null
+ }
+
+ return {selectedEvent, setSelectedEvent, clearSelectedEvent}
+})