import { getContext, setContext } from 'svelte' import { api, auth } from './pocketbase.svelte' import { getTeamContext } from './teams.svelte' import type { TrailFlagsResponse } from '$lib/types' const KEY = Symbol('trailFlag') /** * Vorschlag für einen Standardsatz. Bewusst NICHT in der Migration: Seed-Daten * dort kämen bei jedem Containerstart zurück, auch nachdem sie jemand gelöscht * hat. Stattdessen legt sie die Flag-Verwaltung auf Knopfdruck an. * * Der Abgleich läuft über das Label. Wird ein Standard-Flag umbenannt, legt * ein erneuter Aufruf es wieder neu an. Das ist hinnehmbar, weil die * Flag-Verwaltung den Knopf nur zeigt, solange noch gar keine Flags existieren. * * icon: Name eines Lucide-Icons (lucide-svelte) */ export const DEFAULT_FLAGS = [ { label: 'Baum quer', icon: 'TreePine', color: '#ca8a04', severity: 'warnung' }, { label: 'Verblockt', icon: 'Blocks', color: '#ea580c', severity: 'warnung' }, { label: 'Erosion', icon: 'Waves', color: '#a16207', severity: 'warnung' }, { label: 'Sperrung', icon: 'Ban', color: '#dc2626', severity: 'kritisch' }, { label: 'Bauarbeiten', icon: 'Construction', color: '#dc2626', severity: 'kritisch' }, { label: 'Hinweis', icon: 'Info', color: '#2563eb', severity: 'info' }, ] export class TrailFlagStore { records = $state([]) loading = $state(false) error = $state(null) private unsubscribe: (() => void) | null = null private teams = getTeamContext() get scoped(): TrailFlagsResponse[] { const teamId = this.teams.activeId if (!teamId) return [] return this.records.filter((r) => r.team === teamId) } getById(id: string): TrailFlagsResponse | undefined { return this.records.find((r) => r.id === id) } /** * Flag-Typen anlegen, ändern und löschen darf nur, wer das Team führt — * dieselbe Regel wie in createRule/updateRule/deleteRule der Collection. * Die Prüfung hier blendet nur UI aus; durchgesetzt wird sie serverseitig. */ get canManage(): boolean { const uid = auth.user?.id if (!uid) return false const team = this.teams.records.find((t) => t.id === this.teams.activeId) if (!team) return false return team.owner === uid || ((team.admins ?? []) as string[]).includes(uid) } async load() { this.loading = true this.error = null try { this.records = await api.collection('trail_flags').getFullList({ sort: 'label', requestKey: null, }) } catch (e: any) { this.error = e.message ?? 'Fehler beim Laden der Flags' console.error(e) } finally { this.loading = false } } subscribe() { if (this.unsubscribe) return api.collection('trail_flags').subscribe('*', (e) => { const idx = this.records.findIndex((r) => r.id === e.record.id) if (e.action === 'create' && idx === -1) this.records = [...this.records, e.record] 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('trail_flags subscribe failed:', e)) } async create(data: { label: string; icon: string; color: string; severity: string }) { const teamId = this.teams.activeId if (!teamId) throw new Error('Kein aktives Team') return await api.collection('trail_flags').create({ ...data, team: teamId }) } async edit(id: string, data: Partial) { return await api.collection('trail_flags').update(id, data) } async remove(id: string) { await api.collection('trail_flags').delete(id) } /** * Legt die Standard-Flags an, die noch fehlen. Vorhandene bleiben * unangetastet — der Knopf lässt sich also gefahrlos mehrfach drücken. * Gibt die Anzahl neu angelegter Flags zurück. */ async seedDefaults(teamId = this.teams.activeId): Promise { if (!teamId) throw new Error('Kein aktives Team') // Beim Anlegen eines Teams ist man dessen Owner, aber der Datensatz // steckt womöglich noch nicht im Store — dann greift die Prüfung ins // Leere. Für das eigene, gerade erzeugte Team wird sie übersprungen; // die createRule der Collection setzt sie ohnehin serverseitig durch. if (teamId === this.teams.activeId && !this.canManage) { throw new Error('Dafür brauchst du Administratorrechte im Team.') } const existing = new Set( this.records.filter((f) => f.team === teamId).map((f) => f.label), ) let created = 0 for (const flag of DEFAULT_FLAGS) { if (existing.has(flag.label)) continue try { await api.collection('trail_flags').create({ ...flag, team: teamId }) created++ } catch (e: any) { // Bricht mitten in der Schleife etwas ab, sollen die bereits // angelegten Flags nicht verschwiegen werden. throw new Error( `${created} von ${DEFAULT_FLAGS.length} Typen angelegt, dann: ` + (e.message ?? 'unbekannter Fehler'), ) } } return created } destroy() { if (this.unsubscribe) { this.unsubscribe() this.unsubscribe = null } } } export function setTrailFlagContext() { const store = new TrailFlagStore() setContext(KEY, store) return store } export function getTrailFlagContext(): TrailFlagStore { return getContext(KEY) }