import { defineStore } from 'pinia' import { ref, computed } from 'vue' export interface LogEntry { entry_id: string source_id: string sequence: number timestamp_iso: string | null severity: string | null repeat_count: number out_of_order: boolean matched_patterns: string[] text: string rank: number } export interface LogSource { source_id: string entry_count: number earliest: string | null latest: string | null error_count: number } const BASE = import.meta.env.BASE_URL.replace(/\/$/, '') async function apiFetch(path: string, params?: Record): Promise { const url = new URL(`${BASE}${path}`, window.location.origin) if (params) { for (const [k, v] of Object.entries(params)) { if (v !== undefined && v !== '') url.searchParams.set(k, v) } } const res = await fetch(url.toString()) if (!res.ok) throw new Error(`API ${path} returned ${res.status}`) return res.json() } export const useSearchStore = defineStore('search', () => { const query = ref('') const sourceFilter = ref() const severityFilter = ref() const sinceFilter = ref() const limit = ref(50) const results = ref([]) const loading = ref(false) const error = ref(null) const total = ref(0) const sources = ref([]) const sourcesLoaded = ref(false) const severityOptions = ['DEBUG', 'INFO', 'WARN', 'ERROR', 'CRITICAL'] const hasResults = computed(() => results.value.length > 0) async function runSearch() { if (!query.value.trim()) return loading.value = true error.value = null try { const data = await apiFetch<{ count: number; results: LogEntry[] }>('/api/search', { q: query.value, source: sourceFilter.value, severity: severityFilter.value, since: sinceFilter.value, limit: String(limit.value), }) results.value = data.results total.value = data.count } catch (e) { error.value = e instanceof Error ? e.message : String(e) } finally { loading.value = false } } async function loadSources() { if (sourcesLoaded.value) return try { const data = await apiFetch<{ sources: LogSource[] }>('/api/sources') sources.value = data.sources sourcesLoaded.value = true } catch { // non-fatal } } function clearFilters() { sourceFilter.value = undefined severityFilter.value = undefined sinceFilter.value = undefined limit.value = 50 } return { query, sourceFilter, severityFilter, sinceFilter, limit, results, loading, error, total, sources, sourcesLoaded, severityOptions, hasResults, runSearch, loadSources, clearFilters, } })