fix
This commit is contained in:
@@ -124,6 +124,7 @@ const isLoadingAll = ref(false)
|
|||||||
const loadingProgress = ref(0)
|
const loadingProgress = ref(0)
|
||||||
|
|
||||||
// Get the table store for this specific datasource (using name, not table)
|
// Get the table store for this specific datasource (using name, not table)
|
||||||
|
// The plugin has already loaded all caches, so this just retrieves the existing instance
|
||||||
const tableStore = computed(() => {
|
const tableStore = computed(() => {
|
||||||
if (typeof $getTableStore === 'function') {
|
if (typeof $getTableStore === 'function') {
|
||||||
return $getTableStore(props.metadata.name)
|
return $getTableStore(props.metadata.name)
|
||||||
|
|||||||
@@ -277,8 +277,12 @@ const totalRowCount = computed(() => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// Methods
|
// Methods
|
||||||
function selectTable(tableName: string) {
|
async function selectTable(tableName: string) {
|
||||||
if (selectedTableName.value === tableName) return
|
console.log(`[Explorer] selectTable called with: ${tableName}`)
|
||||||
|
if (selectedTableName.value === tableName) {
|
||||||
|
console.log(`[Explorer] Table ${tableName} already selected, skipping`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
selectedTableName.value = tableName
|
selectedTableName.value = tableName
|
||||||
|
|
||||||
@@ -286,20 +290,31 @@ function selectTable(tableName: string) {
|
|||||||
if (typeof $getTableStore === 'function') {
|
if (typeof $getTableStore === 'function') {
|
||||||
const store = $getTableStore(tableName)
|
const store = $getTableStore(tableName)
|
||||||
if (store) {
|
if (store) {
|
||||||
|
console.log(`[Explorer] Got store for ${tableName} via plugin`)
|
||||||
currentTableStore.value = store
|
currentTableStore.value = store
|
||||||
// Initialize the store (loads from cache or fetches)
|
|
||||||
store.initialize()
|
// Load from cache first (async from IndexedDB)
|
||||||
|
await store.loadFromCache()
|
||||||
|
console.log(`[Explorer] After loadFromCache, store has ${store.recordCount} records`)
|
||||||
|
|
||||||
|
// Note: We don't call initialize() here because MetadatosCard will handle loading
|
||||||
|
// initialize() would trigger a fetch, but we want manual control via the buttons
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Fallback: create store directly
|
// Fallback: create store directly
|
||||||
|
console.log(`[Explorer] Creating store for ${tableName} directly (fallback)`)
|
||||||
currentTableStore.value = useTableDataStore(tableName)
|
currentTableStore.value = useTableDataStore(tableName)
|
||||||
currentTableStore.value.initialize()
|
await currentTableStore.value.loadFromCache()
|
||||||
|
console.log(`[Explorer] After loadFromCache, store has ${currentTableStore.value.recordCount} records`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function refreshTableData() {
|
async function refreshTableData() {
|
||||||
|
console.log('[Explorer] refreshTableData called')
|
||||||
if (currentTableStore.value) {
|
if (currentTableStore.value) {
|
||||||
|
console.log(`[Explorer] Refreshing data for current table (${selectedTableName.value})`)
|
||||||
await currentTableStore.value.refreshData()
|
await currentTableStore.value.refreshData()
|
||||||
|
console.log(`[Explorer] After refresh, store has ${currentTableStore.value.recordCount} records`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -340,13 +355,15 @@ function formatCellValue(value: unknown): string {
|
|||||||
|
|
||||||
// Lifecycle
|
// Lifecycle
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
|
console.log('[Explorer] onMounted: Initializing metadata store')
|
||||||
await metadataStore.initialize()
|
await metadataStore.initialize()
|
||||||
|
|
||||||
// Auto-select first table if available
|
// Auto-select first table if available
|
||||||
if (metadataStore.hasMetadata && !selectedTableName.value) {
|
if (metadataStore.hasMetadata && !selectedTableName.value) {
|
||||||
const firstTable = metadataStore.allTables[0]
|
const firstTable = metadataStore.allTables[0]
|
||||||
if (firstTable) {
|
if (firstTable) {
|
||||||
selectTable(firstTable.table)
|
console.log(`[Explorer] Auto-selecting first table: ${firstTable.name}`)
|
||||||
|
selectTable(firstTable.name) // Use name instead of table
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -354,7 +371,8 @@ onMounted(async () => {
|
|||||||
// Auto-select first table when metadata becomes available
|
// Auto-select first table when metadata becomes available
|
||||||
watch(() => metadataStore.hasMetadata, (hasMetadata) => {
|
watch(() => metadataStore.hasMetadata, (hasMetadata) => {
|
||||||
if (hasMetadata && !selectedTableName.value && metadataStore.allTables.length > 0) {
|
if (hasMetadata && !selectedTableName.value && metadataStore.allTables.length > 0) {
|
||||||
selectTable(metadataStore.allTables[0].table)
|
console.log('[Explorer] Metadata became available, auto-selecting first table')
|
||||||
|
selectTable(metadataStore.allTables[0].name) // Use name instead of table
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
64
nuxt4-app/app/plugins/tableStores.client.ts
Normal file
64
nuxt4-app/app/plugins/tableStores.client.ts
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
import { useMetadataStore } from '~/stores/metadata'
|
||||||
|
import { createTableDataStore } from '~/stores/tableDataFactory'
|
||||||
|
|
||||||
|
export default defineNuxtPlugin(async (nuxtApp) => {
|
||||||
|
console.log('[TableStoresPlugin] Initializing...')
|
||||||
|
|
||||||
|
// Wait for metadata to be available
|
||||||
|
const metadataStore = useMetadataStore()
|
||||||
|
|
||||||
|
// Initialize metadata first
|
||||||
|
await metadataStore.initialize()
|
||||||
|
console.log(`[TableStoresPlugin] Metadata initialized, found ${metadataStore.allTables.length} tables`)
|
||||||
|
|
||||||
|
// Create stores for all available tables and preload from cache
|
||||||
|
const tableStores = new Map<string, ReturnType<typeof createTableDataStore>>()
|
||||||
|
const storeInstances = new Map<string, ReturnType<ReturnType<typeof createTableDataStore>>>()
|
||||||
|
|
||||||
|
// Load all caches in parallel for better performance
|
||||||
|
const cacheLoadPromises = metadataStore.allTables.map(async (metadata) => {
|
||||||
|
const datasourceName = metadata.name // Use datasource name, not table name
|
||||||
|
console.log(`[TableStoresPlugin] Creating store for datasource: ${datasourceName}`)
|
||||||
|
|
||||||
|
const storeFactory = createTableDataStore(datasourceName, 100)
|
||||||
|
const storeInstance = storeFactory()
|
||||||
|
|
||||||
|
// Register both factory and instance
|
||||||
|
tableStores.set(datasourceName, storeFactory)
|
||||||
|
storeInstances.set(datasourceName, storeInstance)
|
||||||
|
|
||||||
|
// Load from cache immediately
|
||||||
|
await storeInstance.loadFromCache()
|
||||||
|
console.log(`[TableStoresPlugin] Loaded ${storeInstance.recordCount} records for ${datasourceName}`)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Wait for all caches to load
|
||||||
|
await Promise.all(cacheLoadPromises)
|
||||||
|
console.log('[TableStoresPlugin] All caches loaded successfully')
|
||||||
|
|
||||||
|
// Provide access to table stores
|
||||||
|
return {
|
||||||
|
provide: {
|
||||||
|
tableStores,
|
||||||
|
// Helper function to get a table store (returns existing instance)
|
||||||
|
getTableStore: (tableName: string) => {
|
||||||
|
// First try to get existing instance
|
||||||
|
const existingInstance = storeInstances.get(tableName)
|
||||||
|
if (existingInstance) {
|
||||||
|
return existingInstance
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fall back to creating new instance from factory
|
||||||
|
const storeFactory = tableStores.get(tableName)
|
||||||
|
if (!storeFactory) {
|
||||||
|
console.warn(`[TableStoresPlugin] Table store for "${tableName}" not found`)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
const newInstance = storeFactory()
|
||||||
|
storeInstances.set(tableName, newInstance)
|
||||||
|
return newInstance
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
|
import { tableDataStorage } from '~/utils/storage'
|
||||||
|
|
||||||
export interface TableDataState<T = Record<string, unknown>> {
|
export interface TableDataState<T = Record<string, unknown>> {
|
||||||
data: T[]
|
data: T[]
|
||||||
@@ -23,8 +24,8 @@ export interface TableDataActions<T = Record<string, unknown>> {
|
|||||||
loadData(force?: boolean): Promise<void>
|
loadData(force?: boolean): Promise<void>
|
||||||
refreshData(): Promise<void>
|
refreshData(): Promise<void>
|
||||||
fetchData(): Promise<void>
|
fetchData(): Promise<void>
|
||||||
clearData(): void
|
clearData(): Promise<void>
|
||||||
loadFromCache(): void
|
loadFromCache(): Promise<void>
|
||||||
extractErrorMessage(error: unknown): string
|
extractErrorMessage(error: unknown): string
|
||||||
initialize(): Promise<void>
|
initialize(): Promise<void>
|
||||||
getRecord(id: string | number): T | undefined
|
getRecord(id: string | number): T | undefined
|
||||||
@@ -169,16 +170,16 @@ export function createTableDataStore<T = Record<string, unknown>>(
|
|||||||
this.lastUpdated = new Date().toISOString()
|
this.lastUpdated = new Date().toISOString()
|
||||||
this.initialized = true
|
this.initialized = true
|
||||||
|
|
||||||
// Persist to localStorage for offline access
|
// Persist to IndexedDB for offline access
|
||||||
if (process.client) {
|
if (process.client) {
|
||||||
try {
|
try {
|
||||||
localStorage.setItem(cacheKey, JSON.stringify({
|
await tableDataStorage.setItem(cacheKey, {
|
||||||
data: this.data,
|
data: this.data,
|
||||||
lastUpdated: this.lastUpdated,
|
lastUpdated: this.lastUpdated,
|
||||||
limit: this.limit
|
limit: this.limit
|
||||||
}))
|
})
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn(`Failed to persist ${tableName} data to localStorage:`, error)
|
console.warn(`Failed to persist ${tableName} data to IndexedDB:`, error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
@@ -195,9 +196,9 @@ export function createTableDataStore<T = Record<string, unknown>>(
|
|||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load data from localStorage cache
|
* Load data from IndexedDB cache
|
||||||
*/
|
*/
|
||||||
loadFromCache(): void {
|
async loadFromCache(): Promise<void> {
|
||||||
if (!process.client) {
|
if (!process.client) {
|
||||||
console.log(`[${tableName}] loadFromCache: Not on client, skipping`)
|
console.log(`[${tableName}] loadFromCache: Not on client, skipping`)
|
||||||
return
|
return
|
||||||
@@ -205,9 +206,8 @@ export function createTableDataStore<T = Record<string, unknown>>(
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
console.log(`[${tableName}] loadFromCache: Attempting to load from key: ${cacheKey}`)
|
console.log(`[${tableName}] loadFromCache: Attempting to load from key: ${cacheKey}`)
|
||||||
const cached = localStorage.getItem(cacheKey)
|
const parsedCache = await tableDataStorage.getItem(cacheKey)
|
||||||
if (cached) {
|
if (parsedCache) {
|
||||||
const parsedCache = JSON.parse(cached)
|
|
||||||
console.log(`[${tableName}] loadFromCache: Found ${parsedCache.data?.length || 0} records in cache`)
|
console.log(`[${tableName}] loadFromCache: Found ${parsedCache.data?.length || 0} records in cache`)
|
||||||
this.data = parsedCache.data || []
|
this.data = parsedCache.data || []
|
||||||
this.lastUpdated = parsedCache.lastUpdated || null
|
this.lastUpdated = parsedCache.lastUpdated || null
|
||||||
@@ -225,7 +225,7 @@ export function createTableDataStore<T = Record<string, unknown>>(
|
|||||||
/**
|
/**
|
||||||
* Clear all data
|
* Clear all data
|
||||||
*/
|
*/
|
||||||
clearData(): void {
|
async clearData(): Promise<void> {
|
||||||
this.data = []
|
this.data = []
|
||||||
this.error = null
|
this.error = null
|
||||||
this.lastUpdated = null
|
this.lastUpdated = null
|
||||||
@@ -233,7 +233,7 @@ export function createTableDataStore<T = Record<string, unknown>>(
|
|||||||
|
|
||||||
if (process.client) {
|
if (process.client) {
|
||||||
try {
|
try {
|
||||||
localStorage.removeItem(cacheKey)
|
await tableDataStorage.removeItem(cacheKey)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn(`Failed to clear ${tableName} data cache:`, error)
|
console.warn(`Failed to clear ${tableName} data cache:`, error)
|
||||||
}
|
}
|
||||||
@@ -260,7 +260,7 @@ export function createTableDataStore<T = Record<string, unknown>>(
|
|||||||
*/
|
*/
|
||||||
async initialize(): Promise<void> {
|
async initialize(): Promise<void> {
|
||||||
// Load from cache first for immediate availability
|
// Load from cache first for immediate availability
|
||||||
this.loadFromCache()
|
await this.loadFromCache()
|
||||||
|
|
||||||
// Then try to fetch fresh data
|
// Then try to fetch fresh data
|
||||||
await this.loadData()
|
await this.loadData()
|
||||||
@@ -349,30 +349,29 @@ export function createTableDataStore<T = Record<string, unknown>>(
|
|||||||
|
|
||||||
console.log(`[${tableName}] loadAllDataInBatches: Finished loading ${this.data.length} records`)
|
console.log(`[${tableName}] loadAllDataInBatches: Finished loading ${this.data.length} records`)
|
||||||
|
|
||||||
// Persist to localStorage
|
// Persist to IndexedDB
|
||||||
if (process.client) {
|
if (process.client) {
|
||||||
try {
|
try {
|
||||||
console.log(`[${tableName}] loadAllDataInBatches: Persisting to localStorage with key: ${cacheKey}`)
|
console.log(`[${tableName}] loadAllDataInBatches: Persisting to IndexedDB with key: ${cacheKey}`)
|
||||||
const dataToSave = {
|
const dataToSave = {
|
||||||
data: this.data,
|
data: this.data,
|
||||||
lastUpdated: this.lastUpdated,
|
lastUpdated: this.lastUpdated,
|
||||||
limit: this.limit
|
limit: this.limit
|
||||||
}
|
}
|
||||||
console.log(`[${tableName}] loadAllDataInBatches: Saving ${dataToSave.data.length} records`)
|
console.log(`[${tableName}] loadAllDataInBatches: Saving ${dataToSave.data.length} records`)
|
||||||
localStorage.setItem(cacheKey, JSON.stringify(dataToSave))
|
await tableDataStorage.setItem(cacheKey, dataToSave)
|
||||||
console.log(`[${tableName}] loadAllDataInBatches: Successfully saved to localStorage`)
|
console.log(`[${tableName}] loadAllDataInBatches: Successfully saved to IndexedDB`)
|
||||||
|
|
||||||
// Verify it was saved
|
// Verify it was saved
|
||||||
const saved = localStorage.getItem(cacheKey)
|
const saved = await tableDataStorage.getItem(cacheKey)
|
||||||
if (saved) {
|
if (saved) {
|
||||||
const parsed = JSON.parse(saved)
|
console.log(`[${tableName}] loadAllDataInBatches: Verification - IndexedDB contains ${saved.data?.length || 0} records`)
|
||||||
console.log(`[${tableName}] loadAllDataInBatches: Verification - localStorage contains ${parsed.data?.length || 0} records`)
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`[${tableName}] loadAllDataInBatches: Failed to persist data to localStorage:`, error)
|
console.error(`[${tableName}] loadAllDataInBatches: Failed to persist data to IndexedDB:`, error)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
console.log(`[${tableName}] loadAllDataInBatches: Not on client, skipping localStorage`)
|
console.log(`[${tableName}] loadAllDataInBatches: Not on client, skipping IndexedDB`)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (onProgress) {
|
if (onProgress) {
|
||||||
@@ -466,30 +465,29 @@ export function createTableDataStore<T = Record<string, unknown>>(
|
|||||||
this.lastUpdated = new Date().toISOString()
|
this.lastUpdated = new Date().toISOString()
|
||||||
console.log(`[${tableName}] loadLatestDataInBatches: Added ${newRecordsCount} new records, total: ${this.data.length}`)
|
console.log(`[${tableName}] loadLatestDataInBatches: Added ${newRecordsCount} new records, total: ${this.data.length}`)
|
||||||
|
|
||||||
// Persist to localStorage
|
// Persist to IndexedDB
|
||||||
if (process.client) {
|
if (process.client) {
|
||||||
try {
|
try {
|
||||||
console.log(`[${tableName}] loadLatestDataInBatches: Persisting to localStorage with key: ${cacheKey}`)
|
console.log(`[${tableName}] loadLatestDataInBatches: Persisting to IndexedDB with key: ${cacheKey}`)
|
||||||
const dataToSave = {
|
const dataToSave = {
|
||||||
data: this.data,
|
data: this.data,
|
||||||
lastUpdated: this.lastUpdated,
|
lastUpdated: this.lastUpdated,
|
||||||
limit: this.limit
|
limit: this.limit
|
||||||
}
|
}
|
||||||
console.log(`[${tableName}] loadLatestDataInBatches: Saving ${dataToSave.data.length} records`)
|
console.log(`[${tableName}] loadLatestDataInBatches: Saving ${dataToSave.data.length} records`)
|
||||||
localStorage.setItem(cacheKey, JSON.stringify(dataToSave))
|
await tableDataStorage.setItem(cacheKey, dataToSave)
|
||||||
console.log(`[${tableName}] loadLatestDataInBatches: Successfully saved to localStorage`)
|
console.log(`[${tableName}] loadLatestDataInBatches: Successfully saved to IndexedDB`)
|
||||||
|
|
||||||
// Verify it was saved
|
// Verify it was saved
|
||||||
const saved = localStorage.getItem(cacheKey)
|
const saved = await tableDataStorage.getItem(cacheKey)
|
||||||
if (saved) {
|
if (saved) {
|
||||||
const parsed = JSON.parse(saved)
|
console.log(`[${tableName}] loadLatestDataInBatches: Verification - IndexedDB contains ${saved.data?.length || 0} records`)
|
||||||
console.log(`[${tableName}] loadLatestDataInBatches: Verification - localStorage contains ${parsed.data?.length || 0} records`)
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`[${tableName}] loadLatestDataInBatches: Failed to persist data to localStorage:`, error)
|
console.error(`[${tableName}] loadLatestDataInBatches: Failed to persist data to IndexedDB:`, error)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
console.log(`[${tableName}] loadLatestDataInBatches: Not on client, skipping localStorage`)
|
console.log(`[${tableName}] loadLatestDataInBatches: Not on client, skipping IndexedDB`)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (onProgress) {
|
if (onProgress) {
|
||||||
|
|||||||
305
nuxt4-app/app/utils/storage.ts
Normal file
305
nuxt4-app/app/utils/storage.ts
Normal file
@@ -0,0 +1,305 @@
|
|||||||
|
/**
|
||||||
|
* Storage utility that uses IndexedDB for table data and localStorage for config/secrets
|
||||||
|
*
|
||||||
|
* Strategy:
|
||||||
|
* - IndexedDB: ALL table/datasource data (can store hundreds of MB)
|
||||||
|
* - localStorage: Only for secrets, variables, configurations (small data)
|
||||||
|
*/
|
||||||
|
|
||||||
|
const DB_NAME = 'analitica-nucleo-db'
|
||||||
|
const DB_VERSION = 1
|
||||||
|
const STORE_NAME = 'table-data'
|
||||||
|
|
||||||
|
let dbInstance: IDBDatabase | null = null
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize IndexedDB
|
||||||
|
*/
|
||||||
|
async function initDB(): Promise<IDBDatabase> {
|
||||||
|
if (dbInstance) return dbInstance
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const request = indexedDB.open(DB_NAME, DB_VERSION)
|
||||||
|
|
||||||
|
request.onerror = () => {
|
||||||
|
console.error('[Storage] IndexedDB error:', request.error)
|
||||||
|
reject(request.error)
|
||||||
|
}
|
||||||
|
|
||||||
|
request.onsuccess = () => {
|
||||||
|
dbInstance = request.result
|
||||||
|
console.log('[Storage] IndexedDB initialized successfully')
|
||||||
|
resolve(dbInstance)
|
||||||
|
}
|
||||||
|
|
||||||
|
request.onupgradeneeded = (event) => {
|
||||||
|
const db = (event.target as IDBOpenDBRequest).result
|
||||||
|
|
||||||
|
// Create object store if it doesn't exist
|
||||||
|
if (!db.objectStoreNames.contains(STORE_NAME)) {
|
||||||
|
db.createObjectStore(STORE_NAME, { keyPath: 'key' })
|
||||||
|
console.log('[Storage] Created object store:', STORE_NAME)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deep clone to remove reactivity and make data serializable for IndexedDB
|
||||||
|
*/
|
||||||
|
function toPlainObject(obj: any): any {
|
||||||
|
// Use JSON parse/stringify to deep clone and remove all reactivity/proxies
|
||||||
|
try {
|
||||||
|
return JSON.parse(JSON.stringify(obj))
|
||||||
|
} catch (error) {
|
||||||
|
console.error('[Storage] Failed to convert to plain object:', error)
|
||||||
|
return obj
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save data to IndexedDB
|
||||||
|
*/
|
||||||
|
async function saveToIndexedDB(key: string, data: any): Promise<boolean> {
|
||||||
|
try {
|
||||||
|
const db = await initDB()
|
||||||
|
|
||||||
|
// Convert reactive objects to plain objects
|
||||||
|
const plainData = toPlainObject(data)
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const transaction = db.transaction([STORE_NAME], 'readwrite')
|
||||||
|
const store = transaction.objectStore(STORE_NAME)
|
||||||
|
|
||||||
|
const request = store.put({
|
||||||
|
key,
|
||||||
|
value: plainData,
|
||||||
|
timestamp: Date.now()
|
||||||
|
})
|
||||||
|
|
||||||
|
request.onsuccess = () => {
|
||||||
|
console.log(`[Storage] Saved to IndexedDB: ${key}`)
|
||||||
|
resolve(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
request.onerror = () => {
|
||||||
|
console.error(`[Storage] Failed to save to IndexedDB: ${key}`, request.error)
|
||||||
|
reject(request.error)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
console.error('[Storage] IndexedDB save error:', error)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load data from IndexedDB
|
||||||
|
*/
|
||||||
|
async function loadFromIndexedDB(key: string): Promise<any | null> {
|
||||||
|
try {
|
||||||
|
const db = await initDB()
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const transaction = db.transaction([STORE_NAME], 'readonly')
|
||||||
|
const store = transaction.objectStore(STORE_NAME)
|
||||||
|
const request = store.get(key)
|
||||||
|
|
||||||
|
request.onsuccess = () => {
|
||||||
|
if (request.result) {
|
||||||
|
console.log(`[Storage] Loaded from IndexedDB: ${key}`)
|
||||||
|
resolve(request.result.value)
|
||||||
|
} else {
|
||||||
|
console.log(`[Storage] No data found in IndexedDB for: ${key}`)
|
||||||
|
resolve(null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
request.onerror = () => {
|
||||||
|
console.error(`[Storage] Failed to load from IndexedDB: ${key}`, request.error)
|
||||||
|
reject(request.error)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
console.error('[Storage] IndexedDB load error:', error)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove data from IndexedDB
|
||||||
|
*/
|
||||||
|
async function removeFromIndexedDB(key: string): Promise<boolean> {
|
||||||
|
try {
|
||||||
|
const db = await initDB()
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const transaction = db.transaction([STORE_NAME], 'readwrite')
|
||||||
|
const store = transaction.objectStore(STORE_NAME)
|
||||||
|
const request = store.delete(key)
|
||||||
|
|
||||||
|
request.onsuccess = () => {
|
||||||
|
console.log(`[Storage] Removed from IndexedDB: ${key}`)
|
||||||
|
resolve(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
request.onerror = () => {
|
||||||
|
console.error(`[Storage] Failed to remove from IndexedDB: ${key}`, request.error)
|
||||||
|
reject(request.error)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
console.error('[Storage] IndexedDB remove error:', error)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Storage interface for table/datasource data (uses IndexedDB exclusively)
|
||||||
|
*/
|
||||||
|
export const tableDataStorage = {
|
||||||
|
/**
|
||||||
|
* Save table data to IndexedDB (for large datasets)
|
||||||
|
*/
|
||||||
|
async setItem(key: string, data: any): Promise<void> {
|
||||||
|
if (!process.client) {
|
||||||
|
console.log('[TableDataStorage] Not on client, skipping storage')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const dataString = JSON.stringify(data)
|
||||||
|
const dataSizeKB = new Blob([dataString]).size / 1024
|
||||||
|
console.log(`[TableDataStorage] Saving ${dataSizeKB.toFixed(2)} KB to IndexedDB for key: ${key}`)
|
||||||
|
|
||||||
|
try {
|
||||||
|
const success = await saveToIndexedDB(key, data)
|
||||||
|
if (success) {
|
||||||
|
console.log(`[TableDataStorage] ✓ Successfully saved ${dataSizeKB.toFixed(2)} KB to IndexedDB`)
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
throw new Error('Failed to save to IndexedDB')
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('[TableDataStorage] ✗ Failed to save to IndexedDB:', error)
|
||||||
|
throw new Error(`No se pudo guardar la tabla: ${error}`)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load table data from IndexedDB
|
||||||
|
*/
|
||||||
|
async getItem(key: string): Promise<any | null> {
|
||||||
|
if (!process.client) {
|
||||||
|
console.log('[TableDataStorage] Not on client, skipping storage')
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`[TableDataStorage] Loading from IndexedDB: ${key}`)
|
||||||
|
|
||||||
|
try {
|
||||||
|
const data = await loadFromIndexedDB(key)
|
||||||
|
if (data !== null) {
|
||||||
|
const dataString = JSON.stringify(data)
|
||||||
|
const dataSizeKB = new Blob([dataString]).size / 1024
|
||||||
|
console.log(`[TableDataStorage] ✓ Loaded ${dataSizeKB.toFixed(2)} KB from IndexedDB`)
|
||||||
|
return data
|
||||||
|
} else {
|
||||||
|
console.log(`[TableDataStorage] No data found in IndexedDB for: ${key}`)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('[TableDataStorage] ✗ Failed to load from IndexedDB:', error)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove table data from IndexedDB
|
||||||
|
*/
|
||||||
|
async removeItem(key: string): Promise<void> {
|
||||||
|
if (!process.client) return
|
||||||
|
|
||||||
|
console.log(`[TableDataStorage] Removing from IndexedDB: ${key}`)
|
||||||
|
|
||||||
|
try {
|
||||||
|
await removeFromIndexedDB(key)
|
||||||
|
console.log(`[TableDataStorage] ✓ Removed from IndexedDB: ${key}`)
|
||||||
|
} catch (error) {
|
||||||
|
console.error('[TableDataStorage] ✗ Failed to remove from IndexedDB:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Storage interface for config/secrets (uses localStorage exclusively)
|
||||||
|
*/
|
||||||
|
export const configStorage = {
|
||||||
|
/**
|
||||||
|
* Save config/secrets to localStorage (for small data only)
|
||||||
|
*/
|
||||||
|
setItem(key: string, data: any): void {
|
||||||
|
if (!process.client) {
|
||||||
|
console.log('[ConfigStorage] Not on client, skipping storage')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const dataString = JSON.stringify(data)
|
||||||
|
const dataSizeKB = new Blob([dataString]).size / 1024
|
||||||
|
console.log(`[ConfigStorage] Saving ${dataSizeKB.toFixed(2)} KB to localStorage for key: ${key}`)
|
||||||
|
|
||||||
|
try {
|
||||||
|
localStorage.setItem(key, dataString)
|
||||||
|
console.log(`[ConfigStorage] ✓ Saved to localStorage`)
|
||||||
|
} catch (error: any) {
|
||||||
|
if (error.name === 'QuotaExceededError') {
|
||||||
|
console.error('[ConfigStorage] ✗ localStorage quota exceeded')
|
||||||
|
throw new Error('No hay espacio en localStorage para guardar la configuración')
|
||||||
|
}
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load config/secrets from localStorage
|
||||||
|
*/
|
||||||
|
getItem(key: string): any | null {
|
||||||
|
if (!process.client) {
|
||||||
|
console.log('[ConfigStorage] Not on client, skipping storage')
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`[ConfigStorage] Loading from localStorage: ${key}`)
|
||||||
|
|
||||||
|
try {
|
||||||
|
const item = localStorage.getItem(key)
|
||||||
|
if (item) {
|
||||||
|
const data = JSON.parse(item)
|
||||||
|
const dataSizeKB = new Blob([item]).size / 1024
|
||||||
|
console.log(`[ConfigStorage] ✓ Loaded ${dataSizeKB.toFixed(2)} KB from localStorage`)
|
||||||
|
return data
|
||||||
|
} else {
|
||||||
|
console.log(`[ConfigStorage] No data found in localStorage for: ${key}`)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('[ConfigStorage] ✗ Failed to load from localStorage:', error)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove config/secrets from localStorage
|
||||||
|
*/
|
||||||
|
removeItem(key: string): void {
|
||||||
|
if (!process.client) return
|
||||||
|
|
||||||
|
console.log(`[ConfigStorage] Removing from localStorage: ${key}`)
|
||||||
|
|
||||||
|
try {
|
||||||
|
localStorage.removeItem(key)
|
||||||
|
console.log(`[ConfigStorage] ✓ Removed from localStorage: ${key}`)
|
||||||
|
} catch (error) {
|
||||||
|
console.error('[ConfigStorage] ✗ Failed to remove from localStorage:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
import { useMetadataStore } from '~/stores/metadata'
|
|
||||||
import { createTableDataStore } from '~/stores/tableDataFactory'
|
|
||||||
|
|
||||||
export default defineNuxtPlugin(async (nuxtApp) => {
|
|
||||||
// Wait for metadata to be available
|
|
||||||
const metadataStore = useMetadataStore()
|
|
||||||
|
|
||||||
// Initialize metadata first
|
|
||||||
await metadataStore.initialize()
|
|
||||||
|
|
||||||
// Create stores for all available tables
|
|
||||||
const tableStores = new Map<string, ReturnType<typeof createTableDataStore>>()
|
|
||||||
|
|
||||||
metadataStore.allTables.forEach((table) => {
|
|
||||||
const storeName = table.table
|
|
||||||
const storeFactory = createTableDataStore(storeName, 100)
|
|
||||||
|
|
||||||
// Register the store
|
|
||||||
tableStores.set(storeName, storeFactory)
|
|
||||||
|
|
||||||
// Optionally initialize stores in the background (lazy loading)
|
|
||||||
// You can uncomment this to preload all data
|
|
||||||
// const storeInstance = storeFactory()
|
|
||||||
// storeInstance.loadFromCache()
|
|
||||||
})
|
|
||||||
|
|
||||||
// Provide access to table stores
|
|
||||||
return {
|
|
||||||
provide: {
|
|
||||||
tableStores,
|
|
||||||
// Helper function to get a table store
|
|
||||||
getTableStore: (tableName: string) => {
|
|
||||||
const storeFactory = tableStores.get(tableName)
|
|
||||||
if (!storeFactory) {
|
|
||||||
console.warn(`Table store for "${tableName}" not found`)
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
return storeFactory()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
Reference in New Issue
Block a user