import { defineStore } from 'pinia' import { useRequestFetch } from '#imports' export interface TableMetadata { table: string rowCount: number primaryKey: string approxSizeBytes: number columns: string[] createdAtRange?: { from: string to: string } lastRefreshed?: string sampleRow?: Record } export interface MetadataState { metadata: TableMetadata[] loading: boolean error: string | null lastUpdated: string | null initialized: boolean } export const useMetadataStore = defineStore('metadata', { state: (): MetadataState => ({ metadata: [], loading: false, error: null, lastUpdated: null, initialized: false }), getters: { /** * Get metadata for all tables */ allTables: (state): TableMetadata[] => state.metadata, /** * Get metadata for a specific table */ getTableMetadata: (state) => (tableName: string): TableMetadata | undefined => { return state.metadata.find(meta => meta.table === tableName) }, /** * Get total number of tables */ totalTables: (state): number => state.metadata.length, /** * Get total number of records across all tables */ totalRecords: (state): number => { return state.metadata.reduce((sum, meta) => sum + (meta.rowCount || 0), 0) }, /** * Get list of all table names */ tableNames: (state): string[] => { return state.metadata.map(meta => meta.table) }, /** * Check if metadata is available */ hasMetadata: (state): boolean => state.metadata.length > 0, /** * Check if metadata is currently loading */ isLoading: (state): boolean => state.loading, /** * Check if there's an error */ hasError: (state): boolean => !!state.error, /** * Get formatted last updated time */ formattedLastUpdated: (state): string => { if (!state.lastUpdated) return 'Nunca' try { return new Date(state.lastUpdated).toLocaleString('es-ES', { year: 'numeric', month: 'long', day: 'numeric', hour: '2-digit', minute: '2-digit' }) } catch { return 'Fecha inválida' } }, /** * Check if metadata is stale (older than 5 minutes) */ isStale: (state): boolean => { if (!state.lastUpdated) return true const lastUpdate = new Date(state.lastUpdated) const now = new Date() const fiveMinutes = 5 * 60 * 1000 return (now.getTime() - lastUpdate.getTime()) > fiveMinutes } }, actions: { /** * Load metadata lazily (only if not already loaded) */ async loadMetadata(force = false): Promise { // Don't load if already loading if (this.loading) return // Don't load if already initialized and not forced if (this.initialized && !force && !this.isStale) return await this.fetchMetadata() }, /** * Force refresh metadata */ async refreshMetadata(): Promise { await this.fetchMetadata() }, /** * Internal method to fetch metadata from API */ async fetchMetadata(): Promise { this.loading = true this.error = null try { const requestFetch = useRequestFetch() const response = await requestFetch('/api/metadata') if (Array.isArray(response)) { this.metadata = response.map((meta: any) => ({ ...meta, lastRefreshed: meta.lastRefreshed || new Date().toISOString() })) this.lastUpdated = new Date().toISOString() this.initialized = true // Persist to localStorage for offline access if (process.client) { try { localStorage.setItem('metadata-cache', JSON.stringify({ metadata: this.metadata, lastUpdated: this.lastUpdated })) } catch (error) { console.warn('Failed to persist metadata to localStorage:', error) } } } else { throw new Error('Invalid metadata response format') } } catch (error: any) { this.error = this.extractErrorMessage(error) console.error('Error fetching metadata:', error) // Try to load from cache if available if (process.client && !this.hasMetadata) { this.loadFromCache() } } finally { this.loading = false } }, /** * Load metadata from localStorage cache */ loadFromCache(): void { if (!process.client) return try { const cached = localStorage.getItem('metadata-cache') if (cached) { const parsedCache = JSON.parse(cached) this.metadata = parsedCache.metadata || [] this.lastUpdated = parsedCache.lastUpdated || null this.initialized = true } } catch (error) { console.warn('Failed to load metadata from cache:', error) } }, /** * Clear all metadata */ clearMetadata(): void { this.metadata = [] this.error = null this.lastUpdated = null this.initialized = false if (process.client) { try { localStorage.removeItem('metadata-cache') } catch (error) { console.warn('Failed to clear metadata cache:', error) } } }, /** * Extract error message from various error types */ extractErrorMessage(error: unknown): string { if (error && typeof error === 'object' && 'statusMessage' in error) { return String((error as { statusMessage: string }).statusMessage) } if (error instanceof Error) { return error.message } return 'Error inesperado al cargar metadatos' }, /** * Initialize store (called on app startup) */ async initialize(): Promise { // Load from cache first for immediate availability this.loadFromCache() // Then try to fetch fresh data await this.loadMetadata() } }, // Enable persistence for better UX persist: process.client ? { key: 'metadata-store', storage: localStorage, pick: ['metadata', 'lastUpdated', 'initialized'] } : false })