import { getContext, setContext } from 'svelte' import { api } from './pocketbase.svelte' import { app } from './app.svelte' import { getTeamContext } from './teams.svelte' import type { RidersResponse, UsersResponse } from '$lib/types' const KEY = Symbol('rider') function sortByNumber(records: RidersResponse[]) { return [...records].sort((a, b) => { const na = parseInt(a.number ?? '0', 10) const nb = parseInt(b.number ?? '0', 10) return na - nb }) } export function fullName(r: RidersResponse) { const parts = [r.firstname, r.lastname].filter(Boolean) return parts.join(' ').trim() || r.name || '' } export class RiderStore { records = $state([]) loading = $state(false) error = $state(null) searchQuery = $state('') private unsubscribe: (() => void) | null = null private teams = getTeamContext() get scoped(): RidersResponse[] { const teamId = this.teams.activeId if (!teamId) return [] return this.records.filter((r) => r.team === teamId) } /** * Anlegen, ändern und löschen darf nur die Teamleitung — dieselbe Regel * wie in createRule/updateRule/deleteRule der Collection. Die Prüfung * hier blendet nur UI aus; durchgesetzt wird sie serverseitig. */ get canManage(): boolean { return this.teams.isAdmin() } async load() { this.loading = true this.error = null try { const raw = await api.collection('riders').getFullList({ requestKey: null }) this.records = sortByNumber(raw) } catch (e: any) { this.error = e.message ?? 'Fehler beim Laden der Fahrer' console.error(e) } finally { this.loading = false } } subscribe() { if (this.unsubscribe) return api.collection('riders').subscribe('*', (e) => { const idx = this.records.findIndex((r) => r.id === e.record.id) if (e.action === 'create' && idx === -1) { this.records = sortByNumber([...this.records, e.record]) } else if (e.action === 'update' && idx !== -1) { this.records[idx] = e.record this.records = sortByNumber(this.records) } 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('riders subscribe failed:', e)) } async create(data: Partial) { const teamId = this.teams.activeId if (!teamId) throw new Error('Kein aktives Team') return await api.collection('riders').create({ ...data, team: teamId }) } async edit(id: string, data: Partial) { return await api.collection('riders').update(id, data) } remove(record: RidersResponse) { return new Promise((resolve, reject) => { app.confirm.request({ title: 'Fahrer löschen?', text: `Möchten Sie „#${record.number} ${fullName(record)}" wirklich löschen? Alle zugehörigen Zeiten werden ebenfalls gelöscht.`, yes: async () => { try { await api.collection('riders').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) } /** Fahrer, der zu einem Login gehört — für „das bin ich"-Ansichten. */ getByUser(userId: string) { return this.scoped.filter((r) => r.user === userId) } /** Bestehendes Konto zuordnen oder die Zuordnung lösen. */ async linkUser(riderId: string, userId: string | null) { return await this.edit(riderId, { user: userId ?? '' } as Partial) } /** * Legt dem Fahrer ein Login an und verknüpft es. Das Konto entsteht im * Team-Store — es ist zuerst ein Teammitglied und erst dann dieser Fahrer. */ async createLogin( rider: RidersResponse, data: { email: string; password: string; name?: string }, ): Promise { const user = await this.teams.createMember({ ...data, name: data.name?.trim() || fullName(rider), }) await this.linkUser(rider.id, user.id) return user } getByEvent(eventId: string) { return this.scoped.filter((r) => r.event === eventId) } isNumberTaken(number: string, eventId: string, excludeId?: string) { return this.scoped.some( (r) => r.number === number && r.event === eventId && r.id !== excludeId, ) } get filtered() { const q = this.searchQuery.toLowerCase().trim() if (!q) return this.scoped return this.scoped.filter((r) => { return ( (r.number ?? '').toLowerCase().includes(q) || (r.firstname ?? '').toLowerCase().includes(q) || (r.lastname ?? '').toLowerCase().includes(q) || (r.name ?? '').toLowerCase().includes(q) ) }) } destroy() { if (this.unsubscribe) { this.unsubscribe() this.unsubscribe = null } } } export function setRiderContext() { const store = new RiderStore() setContext(KEY, store) return store } export function getRiderContext(): RiderStore { return getContext(KEY) }