jomasego commited on
Commit
44cb0eb
·
verified ·
1 Parent(s): c21a1ba

Upload static/js/enhanced_visualizations.js with huggingface_hub

Browse files
Files changed (1) hide show
  1. static/js/enhanced_visualizations.js +835 -0
static/js/enhanced_visualizations.js ADDED
@@ -0,0 +1,835 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Enhanced Trade Visualizations
3
+ * Adds advanced chart capabilities and data caching to the Trade Flow Predictor
4
+ */
5
+
6
+ document.addEventListener('DOMContentLoaded', function() {
7
+ try {
8
+ // Cache management functionality
9
+ initCacheManagement();
10
+
11
+ // Enhanced chart controls for all tabs
12
+ setupChartControls();
13
+
14
+ // Enhance existing trade data views with additional visualizations
15
+ enhanceExistingTabs();
16
+
17
+ console.log('Enhanced visualizations initialized successfully');
18
+ } catch (err) {
19
+ console.error('Error initializing enhanced visualizations:', err);
20
+ }
21
+ });
22
+
23
+ /**
24
+ * Initialize the cache management tab functionality
25
+ */
26
+ function initCacheManagement() {
27
+ const refreshCacheBtn = document.getElementById('refreshCacheBtn');
28
+ const clearAllCacheBtn = document.getElementById('clearAllCacheBtn');
29
+ const cachedDataList = document.getElementById('cachedDataList');
30
+ const cacheVisualization = document.getElementById('cacheVisualization');
31
+ const chartTypeButtons = document.querySelectorAll('.chart-type-btn');
32
+
33
+ let activeChartType = 'bar';
34
+ let selectedCacheKey = null;
35
+
36
+ // Initial cache listing
37
+ refreshCacheList();
38
+
39
+ // Set up event listeners
40
+ if (refreshCacheBtn) {
41
+ refreshCacheBtn.addEventListener('click', refreshCacheList);
42
+ }
43
+
44
+ if (clearAllCacheBtn) {
45
+ clearAllCacheBtn.addEventListener('click', function() {
46
+ if (confirm('Are you sure you want to clear all cached trade data?')) {
47
+ DataManager.clearAllCache();
48
+ refreshCacheList();
49
+ cacheVisualization.style.display = 'none';
50
+ }
51
+ });
52
+ }
53
+
54
+ // Chart type selection
55
+ chartTypeButtons.forEach(button => {
56
+ button.addEventListener('click', function() {
57
+ chartTypeButtons.forEach(btn => btn.classList.remove('active'));
58
+ this.classList.add('active');
59
+ activeChartType = this.getAttribute('data-type');
60
+
61
+ if (selectedCacheKey) {
62
+ visualizeCachedData(selectedCacheKey, activeChartType);
63
+ }
64
+ });
65
+ });
66
+
67
+ /**
68
+ * Refresh the cache listing UI
69
+ */
70
+ function refreshCacheList() {
71
+ const cachedItems = DataManager.listCachedData();
72
+
73
+ if (!cachedDataList) return;
74
+
75
+ if (cachedItems.length === 0) {
76
+ cachedDataList.innerHTML = `
77
+ <div class="cached-data-item" style="text-align:center;padding:20px;color:#666;">
78
+ No cached data available. Use the other tabs to fetch and visualize trade data.
79
+ </div>
80
+ `;
81
+ return;
82
+ }
83
+
84
+ // Sort items by timestamp (newest first)
85
+ cachedItems.sort((a, b) => {
86
+ const timestampA = new Date(a.timestamp).getTime();
87
+ const timestampB = new Date(b.timestamp).getTime();
88
+ return timestampB - timestampA;
89
+ });
90
+
91
+ // Build the list
92
+ let html = '';
93
+ cachedItems.forEach(item => {
94
+ const timestamp = new Date(item.timestamp).toLocaleString();
95
+ html += `
96
+ <div class="cached-data-item">
97
+ <div>
98
+ <div><strong>${getCountryName(item.reporter)} → ${getCountryName(item.partner)}</strong></div>
99
+ <div class="cached-data-info">Year: ${item.year} | Commodity: ${item.commodity} | Flow: ${item.flow === 'all' ? 'All' : item.flow}</div>
100
+ <div class="cached-data-info">Cached: ${timestamp}</div>
101
+ </div>
102
+ <div class="cached-data-actions">
103
+ <button class="action-btn view-cache" data-key="${item.key}"><i class="fas fa-chart-bar"></i> Visualize</button>
104
+ <button class="action-btn export-cache" data-key="${item.key}"><i class="fas fa-file-export"></i> Export</button>
105
+ <button class="action-btn delete delete-cache" data-key="${item.key}"><i class="fas fa-trash"></i></button>
106
+ </div>
107
+ </div>
108
+ `;
109
+ });
110
+
111
+ cachedDataList.innerHTML = html;
112
+
113
+ // Add event listeners for actions
114
+ document.querySelectorAll('.view-cache').forEach(button => {
115
+ button.addEventListener('click', function() {
116
+ const key = this.getAttribute('data-key');
117
+ selectedCacheKey = key;
118
+
119
+ // Find and activate a chart type button if none is active
120
+ if (!document.querySelector('.chart-type-btn.active')) {
121
+ const barChartBtn = document.querySelector('.chart-type-btn[data-type="bar"]');
122
+ if (barChartBtn) {
123
+ barChartBtn.classList.add('active');
124
+ activeChartType = 'bar';
125
+ }
126
+ }
127
+
128
+ visualizeCachedData(key, activeChartType);
129
+ });
130
+ });
131
+
132
+ document.querySelectorAll('.export-cache').forEach(button => {
133
+ button.addEventListener('click', function() {
134
+ const key = this.getAttribute('data-key');
135
+ const cachedData = DataManager.getFromCache({key: key});
136
+
137
+ if (cachedData) {
138
+ // Extract metadata from the key
139
+ const parts = key.split('_');
140
+ const filename = `trade_${parts[2]}_${parts[3]}_${parts[4]}_${parts[5]}.csv`;
141
+
142
+ DataManager.exportToCsv(cachedData, filename);
143
+ }
144
+ });
145
+ });
146
+
147
+ document.querySelectorAll('.delete-cache').forEach(button => {
148
+ button.addEventListener('click', function() {
149
+ const key = this.getAttribute('data-key');
150
+ if (confirm('Delete this cached data item?')) {
151
+ DataManager.clearCacheEntry(key);
152
+ refreshCacheList();
153
+
154
+ if (selectedCacheKey === key) {
155
+ selectedCacheKey = null;
156
+ cacheVisualization.style.display = 'none';
157
+ }
158
+ }
159
+ });
160
+ });
161
+ }
162
+
163
+ /**
164
+ * Visualize cached data with the specified chart type
165
+ */
166
+ function visualizeCachedData(cacheKey, chartType) {
167
+ if (!cacheVisualization) return;
168
+
169
+ const cachedData = DataManager.getFromCache({key: cacheKey});
170
+ if (!cachedData) {
171
+ cacheVisualization.innerHTML = '<p>Error: Could not retrieve cached data</p>';
172
+ cacheVisualization.style.display = 'block';
173
+ return;
174
+ }
175
+
176
+ cacheVisualization.innerHTML = ''; // Clear previous chart
177
+ cacheVisualization.style.display = 'block';
178
+
179
+ // Extract metadata from the key
180
+ const parts = cacheKey.split('_');
181
+ const reporter = getCountryName(parts[2]);
182
+ const partner = getCountryName(parts[3]);
183
+ const year = parts[4];
184
+ const commodity = parts[5];
185
+
186
+ // Determine title based on data
187
+ let title = `${reporter} - ${partner} Trade (${year})`;
188
+ if (commodity !== 'TOTAL') {
189
+ title += ` | Commodity: ${commodity}`;
190
+ }
191
+
192
+ // Create appropriate chart based on type
193
+ switch (chartType) {
194
+ case 'bar':
195
+ TradeCharts.createBarChart('cacheVisualization', cachedData, {
196
+ valueField: 'TradeValue',
197
+ labelField: 'cmdDescE',
198
+ title: title
199
+ });
200
+ break;
201
+
202
+ case 'pie':
203
+ TradeCharts.createPieChart('cacheVisualization', cachedData, {
204
+ valueField: 'TradeValue',
205
+ labelField: 'cmdDescE',
206
+ title: title
207
+ });
208
+ break;
209
+
210
+ case 'line':
211
+ // For line charts, we need time series data which might not be available in this cache
212
+ // Using a single cache entry, we'll simulate time data for demonstration
213
+ const timeData = simulateTimeSeriesData(cachedData, year);
214
+ TradeCharts.createLineChart('cacheVisualization', timeData, {
215
+ valueField: 'value',
216
+ labelField: 'year',
217
+ title: `${reporter} - ${partner} Trade Trend`
218
+ });
219
+ break;
220
+
221
+ case 'treemap':
222
+ TradeCharts.createTreemap('cacheVisualization', cachedData, {
223
+ valueField: 'TradeValue',
224
+ labelField: 'cmdDescE',
225
+ title: title
226
+ });
227
+ break;
228
+
229
+ case 'map':
230
+ // Create a simplified map for demonstration
231
+ // In a real app, you would use real geographical data
232
+ const mapData = prepareMapData(cachedData, parts[2], parts[3]);
233
+ TradeCharts.createWorldMapChart('cacheVisualization', mapData, {
234
+ valueField: 'value',
235
+ labelField: 'country',
236
+ countryCodeField: 'code',
237
+ title: `${reporter} - ${partner} Trade Flow`
238
+ });
239
+ break;
240
+ }
241
+ }
242
+
243
+ /**
244
+ * Helper to get country name from code
245
+ */
246
+ function getCountryName(code) {
247
+ if (window.COUNTRY_CODES) {
248
+ const country = COUNTRY_CODES.find(c => c.code === code);
249
+ return country ? country.name : code;
250
+ }
251
+ return code;
252
+ }
253
+
254
+ /**
255
+ * Generate simulated time series data for demonstration
256
+ */
257
+ function simulateTimeSeriesData(data, currentYear) {
258
+ // Extract total trade value from the data
259
+ let totalValue = 0;
260
+ if (data.rows && data.rows.length > 0) {
261
+ totalValue = data.rows.reduce((sum, row) => {
262
+ return sum + (parseFloat(row.TradeValue) || 0);
263
+ }, 0);
264
+ } else {
265
+ return [];
266
+ }
267
+
268
+ // Generate data for 5 years (current year and 4 years prior)
269
+ const currentYearNum = parseInt(currentYear);
270
+ const timeData = [];
271
+
272
+ // Create a random but somewhat realistic trend
273
+ for (let i = 0; i < 5; i++) {
274
+ const year = currentYearNum - 4 + i;
275
+ // Random variation between 70% and 130% of the average, with an upward trend
276
+ const factor = 0.7 + (i * 0.075) + (Math.random() * 0.3);
277
+ const value = totalValue * factor;
278
+
279
+ timeData.push({
280
+ year: year.toString(),
281
+ value: value
282
+ });
283
+ }
284
+
285
+ return timeData;
286
+ }
287
+
288
+ /**
289
+ * Prepare data for world map visualization
290
+ */
291
+ function prepareMapData(data, reporterCode, partnerCode) {
292
+ // Create a simple dataset for the map with just the two countries
293
+ const mapData = [];
294
+
295
+ // Add reporter country
296
+ if (window.COUNTRY_CODES) {
297
+ const reporter = COUNTRY_CODES.find(c => c.code === reporterCode);
298
+ if (reporter) {
299
+ mapData.push({
300
+ country: reporter.name,
301
+ code: reporter.code,
302
+ value: 100 // Placeholder value
303
+ });
304
+ }
305
+
306
+ // Add partner country
307
+ const partner = COUNTRY_CODES.find(c => c.code === partnerCode);
308
+ if (partner) {
309
+ mapData.push({
310
+ country: partner.name,
311
+ code: partner.code,
312
+ value: 75 // Placeholder value
313
+ });
314
+ }
315
+ }
316
+
317
+ return mapData;
318
+ }
319
+ }
320
+
321
+ /**
322
+ * Set up advanced chart controls for each visualization tab
323
+ */
324
+ function setupChartControls() {
325
+ // Chart control panels for each visualization tab
326
+ const tabsWithCharts = [
327
+ { id: 'exports-country', chartDiv: 'exportsCountryChart' },
328
+ { id: 'imports-country', chartDiv: 'importsCountryChart' },
329
+ { id: 'exports-product', chartDiv: 'exportsProductChart' },
330
+ { id: 'imports-product', chartDiv: 'importsProductChart' },
331
+ { id: 'rankings', chartDiv: 'rankingsChart' },
332
+ { id: 'bilateral', chartDiv: 'bilateralChart' }
333
+ ];
334
+
335
+ tabsWithCharts.forEach(tab => {
336
+ const tabContent = document.getElementById(`tab-content-${tab.id}`);
337
+ const chartDiv = document.getElementById(tab.chartDiv);
338
+
339
+ if (tabContent && chartDiv) {
340
+ // Add chart control panel
341
+ const controlsDiv = document.createElement('div');
342
+ controlsDiv.className = 'chart-controls';
343
+ controlsDiv.innerHTML = `
344
+ <h4>Visualization Options:</h4>
345
+ <button class="chart-type-btn" data-type="bar" data-target="${tab.chartDiv}"><i class="fas fa-chart-bar"></i> Bar Chart</button>
346
+ <button class="chart-type-btn" data-type="pie" data-target="${tab.chartDiv}"><i class="fas fa-chart-pie"></i> Pie Chart</button>
347
+ <button class="chart-type-btn" data-type="line" data-target="${tab.chartDiv}"><i class="fas fa-chart-line"></i> Line Chart</button>
348
+ <button class="chart-type-btn" data-type="treemap" data-target="${tab.chartDiv}"><i class="fas fa-th-large"></i> Treemap</button>
349
+ `;
350
+
351
+ // Insert controls before the chart
352
+ chartDiv.parentNode.insertBefore(controlsDiv, chartDiv);
353
+
354
+ // Convert chart div to advanced chart container
355
+ chartDiv.className = 'advanced-chart-container';
356
+ }
357
+ });
358
+
359
+ // Event delegation for chart type buttons
360
+ document.addEventListener('click', function(event) {
361
+ if (event.target.classList.contains('chart-type-btn') ||
362
+ event.target.parentElement.classList.contains('chart-type-btn')) {
363
+
364
+ const button = event.target.classList.contains('chart-type-btn') ?
365
+ event.target :
366
+ event.target.parentElement;
367
+
368
+ const chartType = button.getAttribute('data-type');
369
+ const targetChart = button.getAttribute('data-target');
370
+
371
+ if (chartType && targetChart) {
372
+ // Remove active class from siblings
373
+ const siblings = button.parentElement.querySelectorAll('.chart-type-btn');
374
+ siblings.forEach(sib => sib.classList.remove('active'));
375
+
376
+ // Add active class to this button
377
+ button.classList.add('active');
378
+
379
+ // Update chart visualization
380
+ updateChartType(targetChart, chartType);
381
+ }
382
+ }
383
+ });
384
+ }
385
+
386
+ /**
387
+ * Update chart visualization based on selected type
388
+ */
389
+ function updateChartType(chartDivId, chartType) {
390
+ const chartDiv = document.getElementById(chartDivId);
391
+ if (!chartDiv) return;
392
+
393
+ // Get tab-specific data
394
+ let data = null;
395
+ let options = {};
396
+
397
+ // Determine which data to use based on chart div id
398
+ switch (chartDivId) {
399
+ case 'exportsCountryChart':
400
+ if (window.exportsCountryTableData) {
401
+ data = window.exportsCountryTableData;
402
+ options = {
403
+ valueField: 'value',
404
+ labelField: 'country',
405
+ title: 'Exports by Country'
406
+ };
407
+ }
408
+ break;
409
+
410
+ case 'importsCountryChart':
411
+ if (window.importsCountryTableData) {
412
+ data = window.importsCountryTableData;
413
+ options = {
414
+ valueField: 'value',
415
+ labelField: 'country',
416
+ title: 'Imports by Country'
417
+ };
418
+ }
419
+ break;
420
+
421
+ case 'exportsProductChart':
422
+ if (window.exportsProductTableData) {
423
+ data = window.exportsProductTableData;
424
+ options = {
425
+ valueField: 'primaryValue',
426
+ labelField: 'cmdDescE',
427
+ title: 'Exports by Product'
428
+ };
429
+ }
430
+ break;
431
+
432
+ case 'importsProductChart':
433
+ if (window.importsProductTableData) {
434
+ data = window.importsProductTableData;
435
+ options = {
436
+ valueField: 'primaryValue',
437
+ labelField: 'cmdDescE',
438
+ title: 'Imports by Product'
439
+ };
440
+ }
441
+ break;
442
+
443
+ case 'rankingsChart':
444
+ if (window.rankingsTableData) {
445
+ data = window.rankingsTableData;
446
+ options = {
447
+ valueField: 'value',
448
+ labelField: 'country',
449
+ title: 'Country Rankings'
450
+ };
451
+ }
452
+ break;
453
+
454
+ case 'bilateralChart':
455
+ if (window.bilateralTableData) {
456
+ data = window.bilateralTableData;
457
+ options = {
458
+ valueField: 'primaryValue',
459
+ labelField: 'cmdDescE',
460
+ title: 'Bilateral Trade'
461
+ };
462
+ }
463
+ break;
464
+ }
465
+
466
+ if (!data) {
467
+ chartDiv.innerHTML = '<div style="padding:20px;text-align:center;color:#666;">No data available for visualization</div>';
468
+ return;
469
+ }
470
+
471
+ // Create the appropriate chart
472
+ switch (chartType) {
473
+ case 'bar':
474
+ TradeCharts.createBarChart(chartDivId, data, options);
475
+ break;
476
+
477
+ case 'pie':
478
+ TradeCharts.createPieChart(chartDivId, data, options);
479
+ break;
480
+
481
+ case 'line':
482
+ // For proper line charts, we would need time series data
483
+ // Here we're just demonstrating with the available data
484
+ TradeCharts.createLineChart(chartDivId, data, options);
485
+ break;
486
+
487
+ case 'treemap':
488
+ TradeCharts.createTreemap(chartDivId, data, options);
489
+ break;
490
+ }
491
+ }
492
+
493
+ /**
494
+ * Enhance existing tab functionality with advanced visualizations and data caching
495
+ */
496
+ function enhanceExistingTabs() {
497
+ // Hook into each form submission to cache results
498
+ const formIds = [
499
+ 'tradeForm',
500
+ 'exportsCountryForm',
501
+ 'importsCountryForm',
502
+ 'exportsProductForm',
503
+ 'importsProductForm',
504
+ 'rankingsForm',
505
+ 'bilateralForm',
506
+ 'dataDownloadForm'
507
+ ];
508
+
509
+ formIds.forEach(formId => {
510
+ const form = document.getElementById(formId);
511
+ if (form) {
512
+ // Store original submit handler
513
+ const originalSubmit = form.onsubmit;
514
+
515
+ // Replace with enhanced handler that caches data
516
+ form.addEventListener('submit', function(e) {
517
+ // Still let the original handler run
518
+ if (originalSubmit) {
519
+ if (originalSubmit(e) === false) {
520
+ return false;
521
+ }
522
+ }
523
+
524
+ // Add data caching after fetch
525
+ enhanceFetchWithCaching(formId);
526
+ });
527
+ }
528
+ });
529
+
530
+ // Override the fetch and chart rendering functions to use our advanced charts
531
+ overrideChartFunctions();
532
+ }
533
+
534
+ /**
535
+ * Enhance the fetch process with data caching
536
+ */
537
+ function enhanceFetchWithCaching(formId) {
538
+ // This function hooks into the response handling of each tab's fetch
539
+ // We'll use a MutationObserver to detect when data is loaded
540
+
541
+ // Determine which result div to watch based on form id
542
+ let resultSelector = '';
543
+ let params = {};
544
+
545
+ switch (formId) {
546
+ case 'tradeForm':
547
+ resultSelector = '#results';
548
+ params = getFormParams('tradeForm', ['reporterCode', 'partnerCode', 'period', 'cmdCode', 'flowCode']);
549
+ break;
550
+
551
+ case 'exportsCountryForm':
552
+ resultSelector = '#exportsCountryResults';
553
+ params = getFormParams('exportsCountryForm', ['exportsCountryYear', 'exportsCountryCommodity', 'exportsCountryFlow']);
554
+ // Set fixed params for this form
555
+ params.reporterCode = 'ALL';
556
+ params.partnerCode = '0';
557
+ params.period = params.exportsCountryYear;
558
+ params.cmdCode = params.exportsCountryCommodity;
559
+ params.flowCode = params.exportsCountryFlow;
560
+ break;
561
+
562
+ // Similar patterns for other forms...
563
+ case 'importsCountryForm':
564
+ resultSelector = '#importsCountryResults';
565
+ params = getFormParams('importsCountryForm', ['importsCountryYear', 'importsCountryCommodity', 'importsCountryFlow']);
566
+ params.reporterCode = 'ALL';
567
+ params.partnerCode = '0';
568
+ params.period = params.importsCountryYear;
569
+ params.cmdCode = params.importsCountryCommodity;
570
+ params.flowCode = params.importsCountryFlow;
571
+ break;
572
+
573
+ case 'exportsProductForm':
574
+ resultSelector = '#exportsProductResults';
575
+ params = {
576
+ reporterCode: document.getElementById('exportsProductCountry')?.value || 'ALL',
577
+ partnerCode: '0',
578
+ period: document.getElementById('exportsProductYear')?.value || '2022',
579
+ cmdCode: 'ALL',
580
+ flowCode: 'X'
581
+ };
582
+ break;
583
+
584
+ case 'importsProductForm':
585
+ resultSelector = '#importsProductResults';
586
+ params = {
587
+ reporterCode: document.getElementById('importsProductCountry')?.value || 'ALL',
588
+ partnerCode: '0',
589
+ period: document.getElementById('importsProductYear')?.value || '2022',
590
+ cmdCode: 'ALL',
591
+ flowCode: 'M'
592
+ };
593
+ break;
594
+
595
+ case 'rankingsForm':
596
+ resultSelector = '#rankingsResults';
597
+ params = getFormParams('rankingsForm', ['rankingsYear', 'rankingsCommodity', 'rankingsFlow']);
598
+ params.reporterCode = 'ALL';
599
+ params.partnerCode = 'ALL';
600
+ params.period = params.rankingsYear;
601
+ params.cmdCode = params.rankingsCommodity;
602
+ params.flowCode = params.rankingsFlow;
603
+ break;
604
+
605
+ case 'bilateralForm':
606
+ resultSelector = '#bilateralResults';
607
+ params = {
608
+ reporterCode: document.getElementById('bilateralReporter')?.value || 'ALL',
609
+ partnerCode: document.getElementById('bilateralPartner')?.value || 'ALL',
610
+ period: document.getElementById('bilateralYear')?.value || '2022',
611
+ cmdCode: document.getElementById('bilateralCommodity')?.value || 'TOTAL',
612
+ flowCode: ''
613
+ };
614
+ break;
615
+
616
+ case 'dataDownloadForm':
617
+ resultSelector = '#dataDownloadStatus';
618
+ params = {
619
+ reporterCode: document.getElementById('dataDownloadReporter')?.value || 'ALL',
620
+ partnerCode: document.getElementById('dataDownloadPartner')?.value || 'ALL',
621
+ period: document.getElementById('dataDownloadYear')?.value || '2022',
622
+ cmdCode: document.getElementById('dataDownloadCommodity')?.value || 'TOTAL',
623
+ flowCode: document.getElementById('dataDownloadFlow')?.value || ''
624
+ };
625
+ break;
626
+ }
627
+
628
+ if (!resultSelector) return;
629
+
630
+ // Set up observer to watch for data being loaded
631
+ const resultsDiv = document.querySelector(resultSelector);
632
+ if (!resultsDiv) return;
633
+
634
+ // Observer watches for changes to the results div
635
+ const observer = new MutationObserver((mutations) => {
636
+ for (const mutation of mutations) {
637
+ if (mutation.type === 'childList' || mutation.type === 'attributes') {
638
+ // Check if results were loaded
639
+ if (resultsDiv.innerHTML && !resultsDiv.innerHTML.includes('Loading')) {
640
+ // Results loaded, cache the data if applicable
641
+ switch (formId) {
642
+ case 'tradeForm':
643
+ if (window.lastFetchedData) {
644
+ DataManager.saveToCache(params, window.lastFetchedData);
645
+ }
646
+ break;
647
+
648
+ case 'exportsCountryForm':
649
+ if (window.exportsCountryTableData) {
650
+ const formattedData = {
651
+ columns: ['country', 'code', 'value'],
652
+ rows: window.exportsCountryTableData
653
+ };
654
+ DataManager.saveToCache(params, formattedData);
655
+ }
656
+ break;
657
+
658
+ case 'importsCountryForm':
659
+ if (window.importsCountryTableData) {
660
+ const formattedData = {
661
+ columns: ['country', 'code', 'value'],
662
+ rows: window.importsCountryTableData
663
+ };
664
+ DataManager.saveToCache(params, formattedData);
665
+ }
666
+ break;
667
+
668
+ // Similar patterns for other forms...
669
+ case 'exportsProductForm':
670
+ case 'importsProductForm':
671
+ case 'rankingsForm':
672
+ case 'bilateralForm':
673
+ case 'dataDownloadForm':
674
+ // Similar caching logic
675
+ break;
676
+ }
677
+
678
+ // Disconnect observer after caching
679
+ observer.disconnect();
680
+ }
681
+ }
682
+ }
683
+ });
684
+
685
+ // Start observing
686
+ observer.observe(resultsDiv, { childList: true, subtree: true, attributes: true });
687
+ }
688
+
689
+ /**
690
+ * Override default chart rendering functions to use our enhanced charts
691
+ */
692
+ function overrideChartFunctions() {
693
+ // Save original functions
694
+ if (window.renderModernChart && !window._originalRenderModernChart) {
695
+ window._originalRenderModernChart = window.renderModernChart;
696
+
697
+ // Override with enhanced version
698
+ window.renderModernChart = function(rows, chartDivId) {
699
+ const chartDiv = document.getElementById(chartDivId);
700
+ if (!chartDiv) return;
701
+
702
+ console.log(`Rendering chart for ${chartDivId} with ${rows.length} rows`, rows[0]);
703
+
704
+ // Convert to proper advanced chart container
705
+ chartDiv.className = 'advanced-chart-container';
706
+
707
+ // Add chart controls if not present
708
+ if (!chartDiv.previousElementSibling || !chartDiv.previousElementSibling.classList.contains('chart-controls')) {
709
+ const controlsDiv = document.createElement('div');
710
+ controlsDiv.className = 'chart-controls';
711
+ controlsDiv.innerHTML = `
712
+ <button class="chart-type-btn active" data-type="bar" data-target="${chartDivId}"><i class="fas fa-chart-bar"></i> Bar</button>
713
+ <button class="chart-type-btn" data-type="pie" data-target="${chartDivId}"><i class="fas fa-chart-pie"></i> Pie</button>
714
+ <button class="chart-type-btn" data-type="treemap" data-target="${chartDivId}"><i class="fas fa-th-large"></i> Treemap</button>
715
+ `;
716
+ chartDiv.parentNode.insertBefore(controlsDiv, chartDiv);
717
+ }
718
+
719
+ // Make sure the chart div is visible
720
+ chartDiv.style.display = 'block';
721
+
722
+ // Determine chart options based on the data
723
+ let options = {
724
+ title: 'Trade Data'
725
+ };
726
+
727
+ if (rows && rows.length > 0) {
728
+ // For exports/imports by product data
729
+ if (chartDivId === 'exportsProductChart' || chartDivId === 'importsProductChart') {
730
+ // Find appropriate fields for product charts
731
+ const valueField = findValueField(rows[0]);
732
+ const labelField = findLabelField(rows[0]);
733
+
734
+ options = {
735
+ valueField: valueField,
736
+ labelField: labelField,
737
+ title: chartDivId === 'exportsProductChart' ? 'Exports by Product' : 'Imports by Product',
738
+ limit: 15 // Limit to top 15 products for readability
739
+ };
740
+ }
741
+ // For country-based data
742
+ else if (rows[0].hasOwnProperty('country')) {
743
+ options.valueField = 'value';
744
+ options.labelField = 'country';
745
+ options.title = 'Trade by Country';
746
+ }
747
+ // General fallback for product data
748
+ else if (rows[0].hasOwnProperty('cmdCode') || rows[0].hasOwnProperty('productCode')) {
749
+ options.valueField = findValueField(rows[0]);
750
+ options.labelField = findLabelField(rows[0]);
751
+ options.title = 'Trade by Product';
752
+ }
753
+ }
754
+
755
+ // Create bar chart by default
756
+ TradeCharts.createBarChart(chartDivId, rows, options);
757
+ };
758
+
759
+ // Helper function to find appropriate value field in the data
760
+ function findValueField(row) {
761
+ if (row.hasOwnProperty('primaryValue')) return 'primaryValue';
762
+ if (row.hasOwnProperty('TradeValue')) return 'TradeValue';
763
+ if (row.hasOwnProperty('Value')) return 'Value';
764
+ if (row.hasOwnProperty('value')) return 'value';
765
+
766
+ // If no known value field, find any numeric property
767
+ for (const key in row) {
768
+ if (typeof row[key] === 'number' || !isNaN(parseFloat(row[key]))) {
769
+ return key;
770
+ }
771
+ }
772
+
773
+ return 'value'; // Default fallback
774
+ }
775
+
776
+ // Helper function to find appropriate label field in the data
777
+ function findLabelField(row) {
778
+ if (row.hasOwnProperty('cmdDescE')) return 'cmdDescE';
779
+ if (row.hasOwnProperty('productDesc')) return 'productDesc';
780
+ if (row.hasOwnProperty('cmdCode')) return 'cmdCode';
781
+ if (row.hasOwnProperty('productCode')) return 'productCode';
782
+ if (row.hasOwnProperty('country')) return 'country';
783
+
784
+ // Default to first string property
785
+ for (const key in row) {
786
+ if (typeof row[key] === 'string') {
787
+ return key;
788
+ }
789
+ }
790
+
791
+ return 'label'; // Default fallback
792
+ }
793
+ }
794
+
795
+ // Override plotting for prediction chart
796
+ if (window.plotPredictionChart && !window._originalPlotPredictionChart) {
797
+ window._originalPlotPredictionChart = window.plotPredictionChart;
798
+
799
+ window.plotPredictionChart = function(rows) {
800
+ const chartDiv = document.getElementById('predictionChart');
801
+ if (!chartDiv) return;
802
+
803
+ // Convert to proper advanced chart container
804
+ chartDiv.className = 'advanced-chart-container';
805
+
806
+ // Create line chart for prediction
807
+ TradeCharts.createLineChart('predictionChart', rows, {
808
+ valueField: 'value',
809
+ labelField: 'year',
810
+ title: 'Trade Prediction',
811
+ fill: true,
812
+ seriesField: 'type' // To distinguish historical vs predicted
813
+ });
814
+ };
815
+ }
816
+ }
817
+
818
+ /**
819
+ * Helper function to get form parameters
820
+ */
821
+ function getFormParams(formId, fieldIds) {
822
+ const params = {};
823
+ const form = document.getElementById(formId);
824
+
825
+ if (form) {
826
+ fieldIds.forEach(fieldId => {
827
+ const field = document.getElementById(fieldId);
828
+ if (field) {
829
+ params[fieldId] = field.value;
830
+ }
831
+ });
832
+ }
833
+
834
+ return params;
835
+ }