/** * CacheManager - Gerenciador central do sistema de cache * Coordena IndexedDB, sincronização e acesso aos dados */ class CacheManager { constructor(config = {}) { this.dbName = config.dbName || 'AcoCalcProDB'; this.version = config.version || 1; this.db = null; this.config = { debug: config.debug || false, autoSync: config.autoSync || false, cacheExpiry: config.cacheExpiry || (7 * 24 * 60 * 60 * 1000), // 7 dias ...config }; this.stores = [ 'cantoneiras', 'barras', 'barras_chatas', 'barras_roscadas', 'tubos_circulares', 'tubos_rhs', 'chapas', 'perfis_i', 'perfis_w', 'perfis_hp', '_metadata', '_config' ]; } /** * Inicializa o IndexedDB */ async init() { return new Promise((resolve, reject) => { if (!window.indexedDB) { console.warn('⚠️ IndexedDB não disponível - usando fallback para CSV'); resolve(false); return; } const request = indexedDB.open(this.dbName, this.version); request.onerror = () => { console.error('❌ Erro ao abrir IndexedDB:', request.error); reject(request.error); }; request.onsuccess = () => { this.db = request.result; if (this.config.debug) { console.log('✅ IndexedDB inicializado:', this.dbName); } resolve(true); }; request.onupgradeneeded = (event) => { const db = event.target.result; // Criar stores para cada tipo de perfil this.stores.forEach(storeName => { if (!db.objectStoreNames.contains(storeName)) { const store = db.createObjectStore(storeName, { keyPath: 'id' }); // Criar índices para busca rápida if (storeName !== '_metadata' && storeName !== '_config') { store.createIndex('nome', 'nome', { unique: false }); store.createIndex('tipo', 'tipo', { unique: false }); } if (this.config.debug) { console.log(`✅ Store criada: ${storeName}`); } } }); }; }); } /** * Verifica saúde do cache */ async checkHealth() { if (!this.db) { return { healthy: false, error: 'Database not initialized' }; } try { const stats = {}; for (const storeName of this.stores) { if (storeName.startsWith('_')) continue; const count = await this.count(storeName); const metadata = await this.getMetadata(storeName); stats[storeName] = { count, lastSync: metadata?.lastSync || null, size: metadata?.size || 0 }; } return { healthy: true, stats, totalSize: Object.values(stats).reduce((sum, s) => sum + s.size, 0) }; } catch (error) { return { healthy: false, error: error.message }; } } /** * Limpa todo o cache */ async clearAll() { if (!this.db) { throw new Error('Database not initialized'); } const promises = this.stores .filter(s => !s.startsWith('_')) .map(storeName => this.clear(storeName)); await Promise.all(promises); if (this.config.debug) { console.log('✅ Todo o cache foi limpo'); } } /** * Conta registros em uma store */ async count(storeName) { return new Promise((resolve, reject) => { const transaction = this.db.transaction([storeName], 'readonly'); const store = transaction.objectStore(storeName); const request = store.count(); request.onsuccess = () => resolve(request.result); request.onerror = () => reject(request.error); }); } /** * Limpa uma store específica */ async clear(storeName) { return new Promise((resolve, reject) => { const transaction = this.db.transaction([storeName], 'readwrite'); const store = transaction.objectStore(storeName); const request = store.clear(); request.onsuccess = () => resolve(); request.onerror = () => reject(request.error); }); } /** * Busca metadados de um tipo */ async getMetadata(tipo) { return new Promise((resolve, reject) => { const transaction = this.db.transaction(['_metadata'], 'readonly'); const store = transaction.objectStore('_metadata'); const request = store.get(tipo); request.onsuccess = () => resolve(request.result || null); request.onerror = () => reject(request.error); }); } /** * Salva metadados de um tipo */ async setMetadata(tipo, metadata) { return new Promise((resolve, reject) => { const transaction = this.db.transaction(['_metadata'], 'readwrite'); const store = transaction.objectStore('_metadata'); const request = store.put({ tipo, ...metadata }); request.onsuccess = () => resolve(); request.onerror = () => reject(request.error); }); } /** * Retorna estatísticas de uso */ getStats() { return this.checkHealth(); } } // Exportar para uso global window.CacheManager = CacheManager; console.log('✅ CacheManager carregado');