import { getContext, setContext } from 'svelte' import { api } from './pocketbase.svelte' import { app } from './app.svelte' import { getTeamContext } from './teams.svelte' import { dayOf, todayISO } from '$lib/time' import type { EventsResponse } from '$lib/types' const KEY = Symbol('event') /** Wie man zu einem Event dazukommt. */ export const PARTICIPATION_LABEL: Record = { offen: 'Offen', bestaetigung: 'Mit Bestätigung', geschlossen: 'Geschlossen', } export class EventStore { records = $state([]) loading = $state(false) error = $state(null) private unsubscribe: (() => void) | null = null private teams = getTeamContext() /** Records gefiltert nach aktivem Team */ get scoped(): EventsResponse[] { const teamId = this.teams.activeId if (!teamId) return [] return this.records.filter((r) => r.team === teamId) } async load() { this.loading = true this.error = null try { this.records = await api.collection('events').getFullList({ sort: '-created', requestKey: null, }) } catch (e: any) { this.error = e.message ?? 'Fehler beim Laden der Events' console.error(e) } finally { this.loading = false } } subscribe() { if (this.unsubscribe) return api.collection('events').subscribe('*', (e) => { const idx = this.records.findIndex((r) => r.id === e.record.id) if (e.action === 'create' && idx === -1) this.records = [e.record, ...this.records] else if (e.action === 'update' && idx !== -1) this.records[idx] = e.record else if (e.action === 'delete' && idx !== -1) this.records = this.records.filter((r) => r.id !== e.record.id) }).then((unsub) => { this.unsubscribe = unsub }).catch((e) => console.warn('events subscribe failed:', e)) } async create(data: Partial) { const teamId = this.teams.activeId if (!teamId) throw new Error('Kein aktives Team') const rec = await api.collection('events').create({ ...data, team: teamId }) return rec } async edit(id: string, data: Partial) { return await api.collection('events').update(id, data) } remove(record: EventsResponse) { return new Promise((resolve, reject) => { app.confirm.request({ title: 'Event löschen?', text: `Möchten Sie das Event „${record.name || record.description}" wirklich löschen?`, yes: async () => { try { await api.collection('events').delete(record.id) app.confirm.close() resolve() } catch (e) { app.confirm.close() reject(e) } }, no: () => { app.confirm.close() resolve() }, }) }) } getById(id: string) { return this.records.find((r) => r.id === id) } /** * Nach Datum sortiert, das nächste zuerst. Events ohne Datum hängen * hinten dran — sie sind noch nicht terminiert. */ get byDate(): EventsResponse[] { return [...this.scoped].sort((a, b) => { const da = dayOf(a.starts) const db = dayOf(b.starts) if (!da && !db) return 0 if (!da) return 1 if (!db) return -1 return da < db ? -1 : da > db ? 1 : 0 }) } /** * Läuft heute. Ohne Enddatum zählt der Starttag allein — genau dann ist * ein eintägiges Event „heute". */ get active(): EventsResponse[] { const today = todayISO() return this.byDate.filter((r) => { const from = dayOf(r.starts) if (!from) return false const to = dayOf(r.ends) || from return from <= today && today <= to }) } /** Steht noch an. */ get upcoming(): EventsResponse[] { const today = todayISO() return this.byDate.filter((r) => dayOf(r.starts) > today) } /** Vorbei — der letzte Tag liegt hinter uns. */ get past(): EventsResponse[] { const today = todayISO() return this.byDate .filter((r) => { const from = dayOf(r.starts) if (!from) return false return (dayOf(r.ends) || from) < today }) .reverse() } /** Noch ohne Termin. */ get undated(): EventsResponse[] { return this.byDate.filter((r) => !dayOf(r.starts)) } destroy() { if (this.unsubscribe) { this.unsubscribe() this.unsubscribe = null } } } export function setEventContext() { const store = new EventStore() setContext(KEY, store) return store } export function getEventContext(): EventStore { return getContext(KEY) }