/** * Data Manager Module * Handles caching and retrieval of trade data to reduce API calls */ const DataManager = (function() { // In-memory cache const memoryCache = {}; // Check if localStorage is available const storageAvailable = function() { try { const storage = window.localStorage; const x = '__storage_test__'; storage.setItem(x, x); storage.removeItem(x); return true; } catch(e) { return false; } }; // Generate a cache key from request parameters const generateCacheKey = function(params) { return `trade_data_${params.reporterCode}_${params.partnerCode}_${params.period}_${params.cmdCode}_${params.flowCode || 'all'}`; }; // Save data to localStorage and memory cache const saveToCache = function(params, data) { const key = generateCacheKey(params); // Save to memory cache memoryCache[key] = { timestamp: Date.now(), data: data }; // Save to localStorage if available if (storageAvailable()) { try { localStorage.setItem(key, JSON.stringify({ timestamp: Date.now(), data: data })); } catch(e) { console.warn('Failed to save to localStorage:', e); } } }; // Get data from cache const getFromCache = function(params) { const key = generateCacheKey(params); // Try memory cache first if (memoryCache[key]) { return memoryCache[key].data; } // Try localStorage if (storageAvailable()) { try { const storedData = localStorage.getItem(key); if (storedData) { const parsed = JSON.parse(storedData); // Refresh memory cache with this data memoryCache[key] = parsed; return parsed.data; } } catch(e) { console.warn('Failed to retrieve from localStorage:', e); } } return null; }; // Check if data is cached const isDataCached = function(params) { return getFromCache(params) !== null; }; // Export CSV from data const exportToCsv = function(data, filename = 'trade_data.csv') { if (!data || !data.columns || !data.rows || data.rows.length === 0) { console.error('Invalid data for CSV export'); return false; } // Create CSV header row let csv = data.columns.join(',') + '\n'; // Add data rows data.rows.forEach(row => { const rowValues = data.columns.map(col => { // Handle value that might contain commas const value = row[col] !== undefined ? row[col] : ''; if (typeof value === 'string' && (value.includes(',') || value.includes('"') || value.includes('\n'))) { return `"${value.replace(/"/g, '""')}"`; } return value; }); csv += rowValues.join(',') + '\n'; }); // Create and trigger download const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' }); const url = URL.createObjectURL(blob); const link = document.createElement('a'); link.setAttribute('href', url); link.setAttribute('download', filename); link.style.visibility = 'hidden'; document.body.appendChild(link); link.click(); document.body.removeChild(link); return true; }; // List all cached data sets const listCachedData = function() { const cachedData = []; // List from memory cache for (const key in memoryCache) { if (key.startsWith('trade_data_')) { const parts = key.split('_'); cachedData.push({ key: key, reporter: parts[2], partner: parts[3], year: parts[4], commodity: parts[5], flow: parts[6], timestamp: new Date(memoryCache[key].timestamp).toLocaleString() }); } } // Combine with any localStorage data not in memory if (storageAvailable()) { for (let i = 0; i < localStorage.length; i++) { const key = localStorage.key(i); if (key.startsWith('trade_data_') && !memoryCache[key]) { try { const data = JSON.parse(localStorage.getItem(key)); const parts = key.split('_'); cachedData.push({ key: key, reporter: parts[2], partner: parts[3], year: parts[4], commodity: parts[5], flow: parts[6], timestamp: new Date(data.timestamp).toLocaleString() }); } catch(e) { console.warn('Failed to parse localStorage item:', key); } } } } return cachedData; }; // Clear specific cache entry const clearCacheEntry = function(key) { // Clear from memory if (memoryCache[key]) { delete memoryCache[key]; } // Clear from localStorage if (storageAvailable()) { localStorage.removeItem(key); } }; // Clear all cache const clearAllCache = function() { // Clear memory cache for (const key in memoryCache) { if (key.startsWith('trade_data_')) { delete memoryCache[key]; } } // Clear localStorage if (storageAvailable()) { const keysToRemove = []; for (let i = 0; i < localStorage.length; i++) { const key = localStorage.key(i); if (key.startsWith('trade_data_')) { keysToRemove.push(key); } } // Remove items separately to avoid index issues keysToRemove.forEach(key => localStorage.removeItem(key)); } }; // Public API return { saveToCache, getFromCache, isDataCached, exportToCsv, listCachedData, clearCacheEntry, clearAllCache }; })();