| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>301 Tariff Analysis Dashboard - US Auto Industry</title> |
| <script src="https://cdn.tailwindcss.com"></script> |
| <script src="https://cdn.jsdelivr.net/npm/chart.js"></script> |
| <script src="https://cdn.jsdelivr.net/npm/chartjs-chart-box-and-violin-plot@3.0.2/build/Chart.BoxPlot.js"></script> |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/PapaParse/5.3.0/papaparse.min.js"></script> |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> |
| <style> |
| .chart-container { |
| transition: all 0.3s ease; |
| cursor: pointer; |
| } |
| .chart-container:hover { |
| transform: translateY(-5px); |
| box-shadow: 0 10px 20px rgba(0,0,0,0.1); |
| } |
| .sidebar { |
| transition: all 0.3s ease; |
| } |
| .data-table { |
| max-height: 400px; |
| overflow-y: auto; |
| } |
| .highlight-row:hover { |
| background-color: #f0f9ff; |
| } |
| .tooltip { |
| position: relative; |
| display: inline-block; |
| } |
| .tooltip .tooltiptext { |
| visibility: hidden; |
| width: 200px; |
| background-color: #333; |
| color: #fff; |
| text-align: center; |
| border-radius: 6px; |
| padding: 5px; |
| position: absolute; |
| z-index: 1; |
| bottom: 125%; |
| left: 50%; |
| margin-left: -100px; |
| opacity: 0; |
| transition: opacity 0.3s; |
| } |
| .tooltip:hover .tooltiptext { |
| visibility: visible; |
| opacity: 1; |
| } |
| .active-filter { |
| background-color: #3B82F6 !important; |
| color: white !important; |
| } |
| </style> |
| </head> |
| <body class="bg-gray-50"> |
| <div class="flex h-screen overflow-hidden"> |
| |
| <div class="sidebar bg-blue-800 text-white w-64 flex-shrink-0 hidden md:block"> |
| <div class="p-4"> |
| <div class="flex items-center space-x-2"> |
| <i class="fas fa-car text-2xl"></i> |
| <h1 class="text-xl font-bold">301 Tariff Analyzer</h1> |
| </div> |
| <p class="text-blue-200 text-sm mt-2">US Auto Industry Focus</p> |
| </div> |
| <nav class="mt-6"> |
| <div class="px-4 py-2 bg-blue-700"> |
| <span class="font-medium">Dashboard</span> |
| </div> |
| <a href="#" class="block px-4 py-2 text-blue-200 hover:bg-blue-700 hover:text-white"> |
| <i class="fas fa-chart-line mr-2"></i>Overview |
| </a> |
| <a href="#" class="block px-4 py-2 text-blue-200 hover:bg-blue-700 hover:text-white"> |
| <i class="fas fa-file-import mr-2"></i>Import Data |
| </a> |
| <a href="#" class="block px-4 py-2 text-blue-200 hover:bg-blue-700 hover:text-white"> |
| <i class="fas fa-search-dollar mr-2"></i>Tariff Analysis |
| </a> |
| <a href="#" class="block px-4 py-2 text-blue-200 hover:bg-blue-700 hover:text-white"> |
| <i class="fas fa-industry mr-2"></i>Company Profiles |
| </a> |
| <a href="#" class="block px-4 py-2 text-blue-200 hover:bg-blue-700 hover:text-white"> |
| <i class="fas fa-cog mr-2"></i>Settings |
| </a> |
| </nav> |
| <div class="absolute bottom-0 w-full p-4"> |
| <div class="bg-blue-700 p-3 rounded-lg"> |
| <p class="text-sm">Need help with tariff analysis?</p> |
| <button class="mt-2 bg-white text-blue-800 px-3 py-1 rounded text-sm font-medium w-full"> |
| Contact Support |
| </button> |
| </div> |
| </div> |
| </div> |
|
|
| |
| <div class="flex-1 overflow-auto"> |
| |
| <header class="bg-white shadow-sm"> |
| <div class="flex justify-between items-center px-6 py-4"> |
| <div class="flex items-center"> |
| <button class="md:hidden mr-4 text-gray-500"> |
| <i class="fas fa-bars text-xl"></i> |
| </button> |
| <h2 class="text-xl font-semibold text-gray-800">301 Tariff Dashboard</h2> |
| </div> |
| <div class="flex items-center space-x-4"> |
| <div class="relative"> |
| <input type="text" placeholder="Search..." class="pl-8 pr-4 py-2 border rounded-lg text-sm"> |
| <i class="fas fa-search absolute left-3 top-3 text-gray-400"></i> |
| </div> |
| <div class="flex items-center space-x-2"> |
| <div class="relative"> |
| <i class="fas fa-bell text-gray-500 text-xl"></i> |
| <span class="absolute top-0 right-0 h-2 w-2 rounded-full bg-red-500"></span> |
| </div> |
| <div class="w-8 h-8 rounded-full bg-blue-600 flex items-center justify-center text-white"> |
| <span>JD</span> |
| </div> |
| </div> |
| </div> |
| </div> |
| </header> |
|
|
| |
| <main class="p-6"> |
| |
| <div class="bg-white rounded-lg shadow p-4 mb-6"> |
| <div class="flex flex-wrap items-center justify-between gap-4"> |
| <div> |
| <label class="block text-sm font-medium text-gray-700 mb-1">Time Period</label> |
| <select id="timePeriod" class="border rounded-md px-3 py-2 text-sm"> |
| <option value="12">Last 12 Months</option> |
| <option value="6">Last 6 Months</option> |
| <option value="3">Last 3 Months</option> |
| <option value="custom">Custom Range</option> |
| </select> |
| </div> |
| <div> |
| <label class="block text-sm font-medium text-gray-700 mb-1">Company</label> |
| <select id="companyFilter" class="border rounded-md px-3 py-2 text-sm"> |
| <option value="all">All Companies</option> |
| <option value="General Motors">General Motors</option> |
| <option value="Ford">Ford</option> |
| <option value="Tesla">Tesla</option> |
| <option value="Stellantis">Stellantis</option> |
| <option value="Toyota USA">Toyota USA</option> |
| </select> |
| </div> |
| <div> |
| <label class="block text-sm font-medium text-gray-700 mb-1">Product Category</label> |
| <select id="productFilter" class="border rounded-md px-3 py-2 text-sm"> |
| <option value="all">All Categories</option> |
| <option value="Vehicle Parts">Vehicle Parts</option> |
| <option value="Engines">Engines</option> |
| <option value="Electronics">Electronics</option> |
| <option value="Steel Components">Steel Components</option> |
| <option value="Aluminum Components">Aluminum Components</option> |
| </select> |
| </div> |
| <div> |
| <label class="block text-sm font-medium text-gray-700 mb-1">Country of Origin</label> |
| <select id="countryFilter" class="border rounded-md px-3 py-2 text-sm"> |
| <option value="all">All Countries</option> |
| <option value="China">China</option> |
| <option value="Mexico">Mexico</option> |
| <option value="Canada">Canada</option> |
| <option value="Germany">Germany</option> |
| <option value="Japan">Japan</option> |
| </select> |
| </div> |
| <button id="applyFilters" class="bg-blue-600 text-white px-4 py-2 rounded-md text-sm font-medium mt-6 hover:bg-blue-700"> |
| Apply Filters |
| </button> |
| </div> |
| </div> |
|
|
| |
| <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-6" id="keyMetrics"> |
| |
| </div> |
|
|
| |
| <div class="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-6"> |
| |
| <div class="bg-white rounded-lg shadow p-4 chart-container" id="monthlyChartContainer"> |
| <div class="flex justify-between items-center mb-4"> |
| <h3 class="font-medium text-gray-800">Monthly Tariff Payments</h3> |
| <div class="flex space-x-2"> |
| <button class="text-xs bg-gray-100 px-2 py-1 rounded chart-toggle active-filter" data-chart="monthlyTariffChart" data-type="USD">USD</button> |
| <button class="text-xs bg-gray-100 px-2 py-1 rounded chart-toggle" data-chart="monthlyTariffChart" data-type="percent">% Change</button> |
| </div> |
| </div> |
| <div class="h-64"> |
| <canvas id="monthlyTariffChart"></canvas> |
| </div> |
| </div> |
|
|
| |
| <div class="bg-white rounded-lg shadow p-4 chart-container" id="companyChartContainer"> |
| <div class="flex justify-between items-center mb-4"> |
| <h3 class="font-medium text-gray-800">Tariff Impact by Company</h3> |
| <div class="flex space-x-2"> |
| <button class="text-xs bg-gray-100 px-2 py-1 rounded chart-toggle active-filter" data-chart="companyImpactChart" data-type="total">Total</button> |
| <button class="text-xs bg-gray-100 px-2 py-1 rounded chart-toggle" data-chart="companyImpactChart" data-type="perShipment">Per Shipment</button> |
| </div> |
| </div> |
| <div class="h-64"> |
| <canvas id="companyImpactChart"></canvas> |
| </div> |
| </div> |
|
|
| |
| <div class="bg-white rounded-lg shadow p-4 chart-container" id="productChartContainer"> |
| <div class="flex justify-between items-center mb-4"> |
| <h3 class="font-medium text-gray-800">Product Category Breakdown</h3> |
| <div class="flex space-x-2"> |
| <button class="text-xs bg-gray-100 px-2 py-1 rounded chart-toggle active-filter" data-chart="productCategoryChart" data-type="value">Value</button> |
| <button class="text-xs bg-gray-100 px-2 py-1 rounded chart-toggle" data-chart="productCategoryChart" data-type="volume">Volume</button> |
| </div> |
| </div> |
| <div class="h-64"> |
| <canvas id="productCategoryChart"></canvas> |
| </div> |
| </div> |
|
|
| |
| <div class="bg-white rounded-lg shadow p-4 chart-container" id="countryChartContainer"> |
| <div class="flex justify-between items-center mb-4"> |
| <h3 class="font-medium text-gray-800">Tariffs by Country of Origin</h3> |
| <div class="flex space-x-2"> |
| <button class="text-xs bg-gray-100 px-2 py-1 rounded chart-toggle active-filter" data-chart="countryOriginChart" data-type="absolute">Absolute</button> |
| <button class="text-xs bg-gray-100 px-2 py-1 rounded chart-toggle" data-chart="countryOriginChart" data-type="relative">Relative</button> |
| </div> |
| </div> |
| <div class="h-64"> |
| <canvas id="countryOriginChart"></canvas> |
| </div> |
| </div> |
| </div> |
|
|
| |
| <div class="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-6"> |
| |
| <div class="bg-white rounded-lg shadow p-4 chart-container" id="distributionChartContainer"> |
| <div class="flex justify-between items-center mb-4"> |
| <h3 class="font-medium text-gray-800">Tariff Rate Distribution</h3> |
| <div class="tooltip"> |
| <i class="fas fa-info-circle text-gray-400"></i> |
| <span class="tooltiptext">Shows the frequency of different tariff rates applied to imports</span> |
| </div> |
| </div> |
| <div class="h-64"> |
| <canvas id="tariffRateDistributionChart"></canvas> |
| </div> |
| </div> |
|
|
| |
| <div class="bg-white rounded-lg shadow p-4 chart-container" id="exemptionChartContainer"> |
| <div class="flex justify-between items-center mb-4"> |
| <h3 class="font-medium text-gray-800">Exemption Status Analysis</h3> |
| <div class="tooltip"> |
| <i class="fas fa-info-circle text-gray-400"></i> |
| <span class="tooltiptext">Breakdown of tariff exemption requests and their status</span> |
| </div> |
| </div> |
| <div class="h-64"> |
| <canvas id="exemptionStatusChart"></canvas> |
| </div> |
| </div> |
| </div> |
|
|
| |
| <div class="bg-white rounded-lg shadow overflow-hidden mb-6"> |
| <div class="p-4 border-b"> |
| <h3 class="font-medium text-gray-800">Detailed Tariff Data</h3> |
| </div> |
| <div class="data-table"> |
| <table class="min-w-full divide-y divide-gray-200"> |
| <thead class="bg-gray-50"> |
| <tr> |
| <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Company</th> |
| <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">HS Code</th> |
| <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Product</th> |
| <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Origin</th> |
| <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Value</th> |
| <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Tariff Rate</th> |
| <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Tariff Amt</th> |
| <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Date</th> |
| </tr> |
| </thead> |
| <tbody class="bg-white divide-y divide-gray-200" id="tariffDataBody"> |
| |
| </tbody> |
| </table> |
| </div> |
| <div class="bg-gray-50 px-4 py-3 flex items-center justify-between border-t border-gray-200"> |
| <div class="flex-1 flex justify-between sm:hidden"> |
| <a href="#" class="relative inline-flex items-center px-4 py-2 border border-gray-300 text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50"> |
| Previous |
| </a> |
| <a href="#" class="ml-3 relative inline-flex items-center px-4 py-2 border border-gray-300 text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50"> |
| Next |
| </a> |
| </div> |
| <div class="hidden sm:flex-1 sm:flex sm:items-center sm:justify-between"> |
| <div> |
| <p class="text-sm text-gray-700"> |
| Showing <span class="font-medium" id="startItem">1</span> to <span class="font-medium" id="endItem">10</span> of <span class="font-medium" id="totalItems">500</span> entries |
| </p> |
| </div> |
| <div> |
| <nav class="relative z-0 inline-flex rounded-md shadow-sm -space-x-px" aria-label="Pagination"> |
| <a href="#" class="relative inline-flex items-center px-2 py-2 rounded-l-md border border-gray-300 bg-white text-sm font-medium text-gray-500 hover:bg-gray-50" id="prevPage"> |
| <span class="sr-only">Previous</span> |
| <i class="fas fa-chevron-left"></i> |
| </a> |
| <a href="#" aria-current="page" class="z-10 bg-blue-50 border-blue-500 text-blue-600 relative inline-flex items-center px-4 py-2 border text-sm font-medium page-link active-page" data-page="1"> |
| 1 |
| </a> |
| <a href="#" class="bg-white border-gray-300 text-gray-500 hover:bg-gray-50 relative inline-flex items-center px-4 py-2 border text-sm font-medium page-link" data-page="2"> |
| 2 |
| </a> |
| <a href="#" class="bg-white border-gray-300 text-gray-500 hover:bg-gray-50 relative inline-flex items-center px-4 py-2 border text-sm font-medium page-link" data-page="3"> |
| 3 |
| </a> |
| <a href="#" class="relative inline-flex items-center px-2 py-2 rounded-r-md border border-gray-300 bg-white text-sm font-medium text-gray-500 hover:bg-gray-50" id="nextPage"> |
| <span class="sr-only">Next</span> |
| <i class="fas fa-chevron-right"></i> |
| </a> |
| </nav> |
| </div> |
| </div> |
| </div> |
| </div> |
|
|
| |
| <div class="bg-white rounded-lg shadow p-6 mb-6"> |
| <h3 class="font-medium text-gray-800 mb-4">Key Insights</h3> |
| <div class="grid grid-cols-1 md:grid-cols-2 gap-6" id="insightsContainer"> |
| |
| </div> |
| </div> |
| </main> |
| </div> |
| </div> |
|
|
| <script> |
| |
| function generateDemoData() { |
| const companies = ['General Motors', 'Ford', 'Tesla', 'Stellantis', 'Toyota USA', 'Honda USA', 'BMW US', 'Mercedes-Benz USA']; |
| const products = ['Vehicle Parts', 'Engines', 'Electronics', 'Steel Components', 'Aluminum Wheels', 'Sensors', 'Battery Components', 'Interior Trim']; |
| const origins = ['China', 'Mexico', 'Canada', 'Germany', 'Japan', 'South Korea', 'Thailand', 'Vietnam']; |
| const hsCodes = ['8708.99', '8708.50', '8483.10', '7326.90', '7616.99', '8536.50', '8507.60', '9401.90']; |
| const exemptionStatuses = ['Approved', 'Pending', 'Denied', 'Not Requested']; |
| |
| const data = []; |
| |
| for (let i = 0; i < 500; i++) { |
| const company = companies[Math.floor(Math.random() * companies.length)]; |
| const product = products[Math.floor(Math.random() * products.length)]; |
| const origin = origins[Math.floor(Math.random() * origins.length)]; |
| const hsCode = hsCodes[Math.floor(Math.random() * hsCodes.length)]; |
| const value = (Math.random() * 500000 + 10000).toFixed(2); |
| const tariffRate = (Math.random() * 25 + 5).toFixed(2); |
| const tariffAmount = (value * tariffRate / 100).toFixed(2); |
| const date = new Date(2022, Math.floor(Math.random() * 12), Math.floor(Math.random() * 28) + 1); |
| const exemptionStatus = exemptionStatuses[Math.floor(Math.random() * exemptionStatuses.length)]; |
| |
| data.push({ |
| id: i + 1, |
| company, |
| hsCode, |
| product, |
| origin, |
| value, |
| tariffRate, |
| tariffAmount, |
| date: date.toLocaleDateString(), |
| exemptionStatus, |
| weight: (Math.random() * 1000 + 100).toFixed(2), |
| quantity: Math.floor(Math.random() * 1000) + 1, |
| portOfEntry: ['Los Angeles', 'New York', 'Houston', 'Savannah', 'Seattle'][Math.floor(Math.random() * 5)], |
| broker: ['FedEx Trade', 'DHL Global', 'UPS Supply', 'Customs Brokers Inc'][Math.floor(Math.random() * 4)], |
| shipmentMethod: ['Air', 'Sea', 'Land'][Math.floor(Math.random() * 3)], |
| processingTime: Math.floor(Math.random() * 10) + 1, |
| inspectionRequired: Math.random() > 0.7 ? 'Yes' : 'No', |
| |
| }); |
| } |
| |
| return data; |
| } |
| |
| let demoData = generateDemoData(); |
| let filteredData = [...demoData]; |
| let currentPage = 1; |
| const itemsPerPage = 10; |
| |
| |
| let monthlyTariffChart, companyImpactChart, productCategoryChart, countryOriginChart, tariffRateDistributionChart, exemptionStatusChart; |
| |
| |
| function filterData() { |
| const company = document.getElementById('companyFilter').value; |
| const product = document.getElementById('productFilter').value; |
| const country = document.getElementById('countryFilter').value; |
| const timePeriod = document.getElementById('timePeriod').value; |
| |
| filteredData = demoData.filter(item => { |
| |
| if (company !== 'all' && item.company !== company) return false; |
| |
| |
| if (product !== 'all' && item.product !== product) return false; |
| |
| |
| if (country !== 'all' && item.origin !== country) return false; |
| |
| |
| if (timePeriod !== 'custom') { |
| const months = parseInt(timePeriod); |
| const itemDate = new Date(item.date); |
| const cutoffDate = new Date(); |
| cutoffDate.setMonth(cutoffDate.getMonth() - months); |
| |
| if (itemDate < cutoffDate) return false; |
| } |
| |
| return true; |
| }); |
| |
| currentPage = 1; |
| updateTable(); |
| updateCharts(); |
| updateMetrics(); |
| updateInsights(); |
| updatePagination(); |
| } |
| |
| |
| function updateTable() { |
| const tableBody = document.getElementById('tariffDataBody'); |
| tableBody.innerHTML = ''; |
| |
| const startIndex = (currentPage - 1) * itemsPerPage; |
| const endIndex = Math.min(startIndex + itemsPerPage, filteredData.length); |
| |
| for (let i = startIndex; i < endIndex; i++) { |
| const item = filteredData[i]; |
| const row = document.createElement('tr'); |
| row.className = 'highlight-row'; |
| row.innerHTML = ` |
| <td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">${item.company}</td> |
| <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">${item.hsCode}</td> |
| <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">${item.product}</td> |
| <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">${item.origin}</td> |
| <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">$${parseFloat(item.value).toLocaleString()}</td> |
| <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">${item.tariffRate}%</td> |
| <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">$${parseFloat(item.tariffAmount).toLocaleString()}</td> |
| <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">${item.date}</td> |
| `; |
| tableBody.appendChild(row); |
| } |
| |
| document.getElementById('startItem').textContent = startIndex + 1; |
| document.getElementById('endItem').textContent = endIndex; |
| document.getElementById('totalItems').textContent = filteredData.length; |
| } |
| |
| |
| function updateMetrics() { |
| const totalTariff = filteredData.reduce((sum, item) => sum + parseFloat(item.tariffAmount), 0); |
| const avgTariffRate = filteredData.reduce((sum, item) => sum + parseFloat(item.tariffRate), 0) / filteredData.length; |
| |
| |
| const companyTotals = {}; |
| filteredData.forEach(item => { |
| if (!companyTotals[item.company]) { |
| companyTotals[item.company] = 0; |
| } |
| companyTotals[item.company] += parseFloat(item.tariffAmount); |
| }); |
| |
| let topCompany = ''; |
| let topAmount = 0; |
| for (const company in companyTotals) { |
| if (companyTotals[company] > topAmount) { |
| topCompany = company; |
| topAmount = companyTotals[company]; |
| } |
| } |
| |
| const metricsContainer = document.getElementById('keyMetrics'); |
| metricsContainer.innerHTML = ` |
| <div class="bg-white rounded-lg shadow p-4"> |
| <div class="flex justify-between items-start"> |
| <div> |
| <p class="text-sm text-gray-500">Total Tariff Payments</p> |
| <p class="text-2xl font-bold mt-1">$${(totalTariff / 1000000).toFixed(1)}M</p> |
| <p class="text-xs text-green-600 mt-1">+${(Math.random() * 10 + 5).toFixed(1)}% from last period</p> |
| </div> |
| <div class="bg-blue-100 p-3 rounded-full"> |
| <i class="fas fa-dollar-sign text-blue-600"></i> |
| </div> |
| </div> |
| </div> |
| <div class="bg-white rounded-lg shadow p-4"> |
| <div class="flex justify-between items-start"> |
| <div> |
| <p class="text-sm text-gray-500">Affected Shipments</p> |
| <p class="text-2xl font-bold mt-1">${filteredData.length}</p> |
| <p class="text-xs text-red-600 mt-1">-${(Math.random() * 5 + 3).toFixed(1)}% from last period</p> |
| </div> |
| <div class="bg-green-100 p-3 rounded-full"> |
| <i class="fas fa-ship text-green-600"></i> |
| </div> |
| </div> |
| </div> |
| <div class="bg-white rounded-lg shadow p-4"> |
| <div class="flex justify-between items-start"> |
| <div> |
| <p class="text-sm text-gray-500">Avg Tariff Rate</p> |
| <p class="text-2xl font-bold mt-1">${avgTariffRate.toFixed(1)}%</p> |
| <p class="text-xs text-green-600 mt-1">+${(Math.random() * 2 + 0.5).toFixed(1)}% from last period</p> |
| </div> |
| <div class="bg-purple-100 p-3 rounded-full"> |
| <i class="fas fa-percent text-purple-600"></i> |
| </div> |
| </div> |
| </div> |
| <div class="bg-white rounded-lg shadow p-4"> |
| <div class="flex justify-between items-start"> |
| <div> |
| <p class="text-sm text-gray-500">Top Impacted Company</p> |
| <p class="text-xl font-bold mt-1">${topCompany || 'N/A'}</p> |
| <p class="text-xs text-gray-600 mt-1">$${(topAmount / 1000000).toFixed(1)}M in tariffs</p> |
| </div> |
| <div class="bg-yellow-100 p-3 rounded-full"> |
| <i class="fas fa-building text-yellow-600"></i> |
| </div> |
| </div> |
| </div> |
| `; |
| } |
| |
| |
| function updateInsights() { |
| |
| const productBreakdown = {}; |
| const exemptionStats = { Approved: 0, Pending: 0, Denied: 0, NotRequested: 0 }; |
| const countryBreakdown = {}; |
| |
| filteredData.forEach(item => { |
| |
| if (!productBreakdown[item.product]) { |
| productBreakdown[item.product] = 0; |
| } |
| productBreakdown[item.product] += parseFloat(item.tariffAmount); |
| |
| |
| if (item.exemptionStatus === 'Not Requested') { |
| exemptionStats.NotRequested++; |
| } else { |
| exemptionStats[item.exemptionStatus]++; |
| } |
| |
| |
| if (!countryBreakdown[item.origin]) { |
| countryBreakdown[item.origin] = 0; |
| } |
| countryBreakdown[item.origin] += parseFloat(item.tariffAmount); |
| }); |
| |
| |
| let topProduct = ''; |
| let topProductAmount = 0; |
| let totalProductAmount = 0; |
| for (const product in productBreakdown) { |
| totalProductAmount += productBreakdown[product]; |
| if (productBreakdown[product] > topProductAmount) { |
| topProduct = product; |
| topProductAmount = productBreakdown[product]; |
| } |
| } |
| |
| const productPercentage = (topProductAmount / totalProductAmount * 100).toFixed(0); |
| |
| |
| const totalExemptionRequests = exemptionStats.Approved + exemptionStats.Pending + exemptionStats.Denied; |
| const approvalRate = totalExemptionRequests > 0 |
| ? (exemptionStats.Approved / totalExemptionRequests * 100).toFixed(0) |
| : 0; |
| |
| |
| let topCountry = ''; |
| let topCountryAmount = 0; |
| for (const country in countryBreakdown) { |
| if (countryBreakdown[country] > topCountryAmount) { |
| topCountry = country; |
| topCountryAmount = countryBreakdown[country]; |
| } |
| } |
| |
| const insightsContainer = document.getElementById('insightsContainer'); |
| insightsContainer.innerHTML = ` |
| <div class="border-l-4 border-blue-500 pl-4"> |
| <h4 class="font-medium text-gray-700 mb-2">Top Impact</h4> |
| <p class="text-sm text-gray-600">${topProduct || 'N/A'} account for ${productPercentage}% of all 301 tariff payments in the filtered data, with ${topCountry || 'N/A'} imports seeing the highest tariff amounts.</p> |
| </div> |
| <div class="border-l-4 border-green-500 pl-4"> |
| <h4 class="font-medium text-gray-700 mb-2">Exemption Trends</h4> |
| <p class="text-sm text-gray-600">Approximately ${totalExemptionRequests > 0 ? Math.round(totalExemptionRequests / filteredData.length * 100) : 0}% of tariff lines have exemption requests, with a ${approvalRate}% approval rate.</p> |
| </div> |
| <div class="border-l-4 border-purple-500 pl-4"> |
| <h4 class="font-medium text-gray-700 mb-2">Company Strategies</h4> |
| <p class="text-sm text-gray-600">Based on the filtered data, companies are facing an average tariff rate of ${(filteredData.reduce((sum, item) => sum + parseFloat(item.tariffRate), 0) / filteredData.length).toFixed(1)}% on affected imports.</p> |
| </div> |
| <div class="border-l-4 border-yellow-500 pl-4"> |
| <h4 class="font-medium text-gray-700 mb-2">Cost Projections</h4> |
| <p class="text-sm text-gray-600">If current rates persist, the filtered imports would result in $${(filteredData.reduce((sum, item) => sum + parseFloat(item.tariffAmount), 0) * 12).toLocaleString()} in additional tariffs over the next 12 months.</p> |
| </div> |
| `; |
| } |
| |
| |
| function updatePagination() { |
| const totalPages = Math.ceil(filteredData.length / itemsPerPage); |
| const paginationContainer = document.querySelector('.relative.z-0.inline-flex'); |
| |
| |
| const existingLinks = document.querySelectorAll('.page-link'); |
| existingLinks.forEach(link => { |
| if (!link.id) { |
| link.remove(); |
| } |
| }); |
| |
| |
| const prevPage = document.getElementById('prevPage'); |
| const nextPage = document.getElementById('nextPage'); |
| |
| |
| let startPage = Math.max(1, currentPage - 1); |
| let endPage = Math.min(totalPages, currentPage + 1); |
| |
| |
| if (currentPage <= 2) { |
| endPage = Math.min(3, totalPages); |
| } |
| if (currentPage >= totalPages - 1) { |
| startPage = Math.max(1, totalPages - 2); |
| } |
| |
| |
| for (let i = startPage; i <= endPage; i++) { |
| const pageLink = document.createElement('a'); |
| pageLink.href = '#'; |
| pageLink.className = `bg-white border-gray-300 text-gray-500 hover:bg-gray-50 relative inline-flex items-center px-4 py-2 border text-sm font-medium page-link ${i === currentPage ? 'active-page bg-blue-50 border-blue-500 text-blue-600' : ''}`; |
| pageLink.setAttribute('data-page', i); |
| pageLink.textContent = i; |
| |
| if (i === 1) { |
| prevPage.insertAdjacentElement('afterend', pageLink); |
| } else { |
| const lastPageLink = document.querySelector('.page-link:not(#prevPage):not(#nextPage):last-of-type'); |
| lastPageLink.insertAdjacentElement('afterend', pageLink); |
| } |
| } |
| |
| |
| prevPage.classList.toggle('opacity-50', currentPage === 1); |
| prevPage.classList.toggle('cursor-not-allowed', currentPage === 1); |
| nextPage.classList.toggle('opacity-50', currentPage === totalPages); |
| nextPage.classList.toggle('cursor-not-allowed', currentPage === totalPages); |
| } |
| |
| |
| function updateCharts() { |
| updateMonthlyTariffChart(); |
| updateCompanyImpactChart(); |
| updateProductCategoryChart(); |
| updateCountryOriginChart(); |
| updateTariffRateDistributionChart(); |
| updateExemptionStatusChart(); |
| } |
| |
| |
| function updateMonthlyTariffChart() { |
| const ctx = document.getElementById('monthlyTariffChart').getContext('2d'); |
| |
| |
| const monthlyData = {}; |
| filteredData.forEach(item => { |
| const date = new Date(item.date); |
| const month = date.getMonth(); |
| |
| if (!monthlyData[month]) { |
| monthlyData[month] = { |
| total: 0, |
| count: 0 |
| }; |
| } |
| |
| monthlyData[month].total += parseFloat(item.tariffAmount); |
| monthlyData[month].count++; |
| }); |
| |
| const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; |
| const tariffData = []; |
| const shipmentData = []; |
| |
| for (let i = 0; i < 12; i++) { |
| tariffData.push(monthlyData[i] ? monthlyData[i].total / 1000000 : 0); |
| shipmentData.push(monthlyData[i] ? monthlyData[i].count : 0); |
| } |
| |
| if (monthlyTariffChart) { |
| monthlyTariffChart.destroy(); |
| } |
| |
| monthlyTariffChart = new Chart(ctx, { |
| type: 'line', |
| data: { |
| labels: months, |
| datasets: [ |
| { |
| label: 'Tariff Payments ($M)', |
| data: tariffData, |
| borderColor: 'rgba(59, 130, 246, 1)', |
| backgroundColor: 'rgba(59, 130, 246, 0.1)', |
| borderWidth: 2, |
| tension: 0.3, |
| fill: true |
| }, |
| { |
| label: 'Shipments', |
| data: shipmentData, |
| borderColor: 'rgba(16, 185, 129, 1)', |
| backgroundColor: 'rgba(16, 185, 129, 0.1)', |
| borderWidth: 2, |
| tension: 0.3, |
| fill: true, |
| yAxisID: 'y1' |
| } |
| ] |
| }, |
| options: { |
| responsive: true, |
| maintainAspectRatio: false, |
| scales: { |
| y: { |
| beginAtZero: true, |
| title: { |
| display: true, |
| text: 'USD (Millions)' |
| } |
| }, |
| y1: { |
| beginAtZero: true, |
| position: 'right', |
| title: { |
| display: true, |
| text: 'Shipments' |
| }, |
| grid: { |
| drawOnChartArea: false |
| } |
| } |
| }, |
| plugins: { |
| tooltip: { |
| mode: 'index', |
| intersect: false |
| }, |
| legend: { |
| position: 'top' |
| } |
| } |
| } |
| }); |
| |
| |
| document.getElementById('monthlyChartContainer').addEventListener('click', function(e) { |
| if (e.target.tagName === 'CANVAS') { |
| const activePoints = monthlyTariffChart.getElementsAtEventForMode(e, 'nearest', { intersect: true }, true); |
| if (activePoints.length > 0) { |
| const monthIndex = activePoints[0].index; |
| const monthName = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'][monthIndex]; |
| |
| |
| const originalFilteredData = [...filteredData]; |
| filteredData = originalFilteredData.filter(item => { |
| const date = new Date(item.date); |
| return date.getMonth() === monthIndex; |
| }); |
| |
| updateTable(); |
| updateCharts(); |
| updateMetrics(); |
| updateInsights(); |
| updatePagination(); |
| |
| |
| alert(`Showing data for ${monthName} only. Click "Apply Filters" to reset.`); |
| } |
| } |
| }); |
| } |
| |
| |
| function updateCompanyImpactChart() { |
| const ctx = document.getElementById('companyImpactChart').getContext('2d'); |
| |
| |
| const companyData = {}; |
| filteredData.forEach(item => { |
| if (!companyData[item.company]) { |
| companyData[item.company] = { |
| total: 0, |
| count: 0, |
| avgRate: 0 |
| }; |
| } |
| |
| companyData[item.company].total += parseFloat(item.tariffAmount); |
| companyData[item.company].count++; |
| companyData[item.company].avgRate += parseFloat(item.tariffRate); |
| }); |
| |
| |
| for (const company in companyData) { |
| companyData[company].avgRate = companyData[company].avgRate / companyData[company].count; |
| } |
| |
| const companies = Object.keys(companyData); |
| const totalData = companies.map(company => companyData[company].total / 1000000); |
| const perShipmentData = companies.map(company => companyData[company].total / companyData[company].count); |
| const avgRateData = companies.map(company => companyData[company].avgRate); |
| |
| if (companyImpactChart) { |
| companyImpactChart.destroy(); |
| } |
| |
| companyImpactChart = new Chart(ctx, { |
| type: 'bar', |
| data: { |
| labels: companies, |
| datasets: [ |
| { |
| label: 'Total Tariff Payments ($M)', |
| data: totalData, |
| backgroundColor: 'rgba(99, 102, 241, 0.7)', |
| borderColor: 'rgba(99, 102, 241, 1)', |
| borderWidth: 1 |
| }, |
| { |
| label: 'Avg Tariff Rate (%)', |
| data: avgRateData, |
| backgroundColor: 'rgba(245, 158, 11, 0.7)', |
| borderColor: 'rgba(245, 158, 11, 1)', |
| borderWidth: 1, |
| type: 'line', |
| yAxisID: 'y1' |
| } |
| ] |
| }, |
| options: { |
| responsive: true, |
| maintainAspectRatio: false, |
| scales: { |
| y: { |
| beginAtZero: true, |
| title: { |
| display: true, |
| text: 'USD (Millions)' |
| } |
| }, |
| y1: { |
| beginAtZero: true, |
| position: 'right', |
| title: { |
| display: true, |
| text: 'Percentage' |
| }, |
| grid: { |
| drawOnChartArea: false |
| } |
| } |
| }, |
| plugins: { |
| tooltip: { |
| mode: 'index', |
| intersect: false |
| }, |
| legend: { |
| position: 'top' |
| } |
| } |
| } |
| }); |
| |
| |
| document.getElementById('companyChartContainer').addEventListener('click', function(e) { |
| if (e.target.tagName === 'CANVAS') { |
| const activePoints = companyImpactChart.getElementsAtEventForMode(e, 'nearest', { intersect: true }, true); |
| if (activePoints.length > 0) { |
| const companyIndex = activePoints[0].index; |
| const companyName = companyImpactChart.data.labels[companyIndex]; |
| |
| |
| document.getElementById('companyFilter').value = companyName; |
| filterData(); |
| } |
| } |
| }); |
| } |
| |
| |
| function updateProductCategoryChart() { |
| const ctx = document.getElementById('productCategoryChart').getContext('2d'); |
| |
| |
| const productData = {}; |
| const productVolume = {}; |
| filteredData.forEach(item => { |
| if (!productData[item.product]) { |
| productData[item.product] = 0; |
| productVolume[item.product] = 0; |
| } |
| |
| productData[item.product] += parseFloat(item.tariffAmount); |
| productVolume[item.product]++; |
| }); |
| |
| const products = Object.keys(productData); |
| const valueData = products.map(product => productData[product] / 1000000); |
| const volumeData = products.map(product => productVolume[product]); |
| |
| if (productCategoryChart) { |
| productCategoryChart.destroy(); |
| } |
| |
| productCategoryChart = new Chart(ctx, { |
| type: 'doughnut', |
| data: { |
| labels: products, |
| datasets: [{ |
| label: 'Tariff Value ($M)', |
| data: valueData, |
| backgroundColor: [ |
| 'rgba(59, 130, 246, 0.7)', |
| 'rgba(16, 185, 129, 0.7)', |
| 'rgba(245, 158, 11, 0.7)', |
| 'rgba(239, 68, 68, 0.7)', |
| 'rgba(139, 92, 246, 0.7)', |
| 'rgba(20, 184, 166, 0.7)' |
| ], |
| borderColor: [ |
| 'rgba(59, 130, 246, 1)', |
| 'rgba(16, 185, 129, 1)', |
| 'rgba(245, 158, 11, 1)', |
| 'rgba(239, 68, 68, 1)', |
| 'rgba(139, 92, 246, 1)', |
| 'rgba(20, 184, 166, 1)' |
| ], |
| borderWidth: 1 |
| }] |
| }, |
| options: { |
| responsive: true, |
| maintainAspectRatio: false, |
| plugins: { |
| legend: { |
| position: 'right' |
| }, |
| tooltip: { |
| callbacks: { |
| label: function(context) { |
| const label = context.label || ''; |
| const value = context.raw || 0; |
| const total = context.dataset.data.reduce((a, b) => a + b, 0); |
| const percentage = Math.round((value / total) * 100); |
| return `${label}: ${percentage}% ($${(value).toFixed(2)}M)`; |
| } |
| } |
| } |
| } |
| } |
| }); |
| |
| |
| document.getElementById('productChartContainer').addEventListener('click', function(e) { |
| if (e.target.tagName === 'CANVAS') { |
| const activePoints = productCategoryChart.getElementsAtEventForMode(e, 'nearest', { intersect: true }, true); |
| if (activePoints.length > 0) { |
| const productIndex = activePoints[0].index; |
| const productName = productCategoryChart.data.labels[productIndex]; |
| |
| |
| document.getElementById('productFilter').value = productName; |
| filterData(); |
| } |
| } |
| }); |
| } |
| |
| |
| function updateCountryOriginChart() { |
| const ctx = document.getElementById('countryOriginChart').getContext('2d'); |
| |
| |
| const countryData = {}; |
| const countryRelative = {}; |
| let totalTariff = 0; |
| |
| filteredData.forEach(item => { |
| if (!countryData[item.origin]) { |
| countryData[item.origin] = 0; |
| countryRelative[item.origin] = 0; |
| } |
| |
| countryData[item.origin] += parseFloat(item.tariffAmount); |
| totalTariff += parseFloat(item.tariffAmount); |
| }); |
| |
| |
| for (const country in countryData) { |
| countryRelative[country] = (countryData[country] / totalTariff * 100).toFixed(1); |
| } |
| |
| const countries = Object.keys(countryData); |
| const absoluteData = countries.map(country => countryData[country] / 1000000); |
| const relativeData = countries.map(country => parseFloat(countryRelative[country])); |
| |
| if (countryOriginChart) { |
| countryOriginChart.destroy(); |
| } |
| |
| countryOriginChart = new Chart(ctx, { |
| type: 'radar', |
| data: { |
| labels: countries, |
| datasets: [ |
| { |
| label: 'Tariff Payments ($M)', |
| data: absoluteData, |
| backgroundColor: 'rgba(59, 130, 246, 0.2)', |
| borderColor: 'rgba(59, 130, 246, 1)', |
| borderWidth: 2, |
| pointBackgroundColor: 'rgba(59, 130, 246, 1)' |
| }, |
| { |
| label: 'Avg Tariff Rate (%)', |
| data: countries.map(country => { |
| const countryItems = filteredData.filter(item => item.origin === country); |
| return countryItems.reduce((sum, item) => sum + parseFloat(item.tariffRate), 0) / countryItems.length; |
| }), |
| backgroundColor: 'rgba(245, 158, 11, 0.2)', |
| borderColor: 'rgba(245, 158, 11, 1)', |
| borderWidth: 2, |
| pointBackgroundColor: 'rgba(245, 158, 11, 1)' |
| } |
| ] |
| }, |
| options: { |
| responsive: true, |
| maintainAspectRatio: false, |
| scales: { |
| r: { |
| angleLines: { |
| display: true |
| }, |
| suggestedMin: 0 |
| } |
| }, |
| plugins: { |
| legend: { |
| position: 'top' |
| } |
| } |
| } |
| }); |
| |
| |
| document.getElementById('countryChartContainer').addEventListener('click', function(e) { |
| if (e.target.tagName === 'CANVAS') { |
| const activePoints = countryOriginChart.getElementsAtEventForMode(e, 'nearest', { intersect: true }, true); |
| if (activePoints.length > 0) { |
| const countryIndex = activePoints[0].index; |
| const countryName = countryOriginChart.data.labels[countryIndex]; |
| |
| |
| document.getElementById('countryFilter').value = countryName; |
| filterData(); |
| } |
| } |
| }); |
| } |
| |
| |
| function updateTariffRateDistributionChart() { |
| const ctx = document.getElementById('tariffRateDistributionChart').getContext('2d'); |
| |
| |
| const tariffRates = filteredData.map(item => parseFloat(item.tariffRate)); |
| |
| if (tariffRateDistributionChart) { |
| tariffRateDistributionChart.destroy(); |
| } |
| |
| |
| const minRate = Math.min(...tariffRates); |
| const maxRate = Math.max(...tariffRates); |
| const binSize = 2; |
| const binCount = Math.ceil((maxRate - minRate) / binSize); |
| |
| const histogramData = []; |
| for (let i = 0; i < binCount; i++) { |
| const binStart = minRate + i * binSize; |
| const binEnd = binStart + binSize; |
| const count = tariffRates.filter(rate => rate >= binStart && rate < binEnd).length; |
| |
| histogramData.push({ |
| x: binStart, |
| y: count |
| }); |
| } |
| |
| tariffRateDistributionChart = new Chart(ctx, { |
| type: 'bar', |
| data: { |
| datasets: [{ |
| label: 'Tariff Rate Distribution', |
| data: histogramData, |
| backgroundColor: 'rgba(99, 102, 241, 0.7)', |
| borderColor: 'rgba(99, 102, 241, 1)', |
| borderWidth: 1 |
| }] |
| }, |
| options: { |
| responsive: true, |
| maintainAspectRatio: false, |
| scales: { |
| x: { |
| type: 'linear', |
| offset: false, |
| grid: { |
| offset: false |
| }, |
| ticks: { |
| stepSize: binSize |
| }, |
| title: { |
| display: true, |
| text: 'Tariff Rate (%)' |
| } |
| }, |
| y: { |
| title: { |
| display: true, |
| text: 'Frequency' |
| } |
| } |
| }, |
| plugins: { |
| legend: { |
| display: false |
| }, |
| tooltip: { |
| callbacks: { |
| title: function(context) { |
| const range = context[0].raw; |
| return `Tariff Rate: ${range.x}% to ${range.x + binSize}%`; |
| }, |
| label: function(context) { |
| return `Frequency: ${context.raw.y}`; |
| } |
| } |
| } |
| } |
| } |
| }); |
| |
| |
| document.getElementById('distributionChartContainer').addEventListener('click', function(e) { |
| if (e.target.tagName === 'CANVAS') { |
| const activePoints = tariffRateDistributionChart.getElementsAtEventForMode(e, 'nearest', { intersect: true }, true); |
| if (activePoints.length > 0) { |
| const binIndex = activePoints[0].index; |
| const binStart = tariffRateDistributionChart.data.datasets[0].data[binIndex].x; |
| const binEnd = binStart + 2; |
| |
| |
| const originalFilteredData = [...filteredData]; |
| filteredData = originalFilteredData.filter(item => { |
| const rate = parseFloat(item.tariffRate); |
| return rate >= binStart && rate < binEnd; |
| }); |
| |
| updateTable(); |
| updateCharts(); |
| updateMetrics(); |
| updateInsights(); |
| updatePagination(); |
| |
| |
| alert(`Showing data for tariff rates between ${binStart}% and ${binEnd}%. Click "Apply Filters" to reset.`); |
| } |
| } |
| }); |
| } |
| |
| |
| function updateExemptionStatusChart() { |
| const ctx = document.getElementById('exemptionStatusChart').getContext('2d'); |
| |
| |
| const exemptionData = {}; |
| const products = [...new Set(filteredData.map(item => item.product))]; |
| |
| products.forEach(product => { |
| exemptionData[product] = { |
| Approved: 0, |
| Pending: 0, |
| Denied: 0, |
| 'Not Requested': 0 |
| }; |
| }); |
| |
| filteredData.forEach(item => { |
| exemptionData[item.product][item.exemptionStatus]++; |
| }); |
| |
| if (exemptionStatusChart) { |
| exemptionStatusChart.destroy(); |
| } |
| |
| exemptionStatusChart = new Chart(ctx, { |
| type: 'bar', |
| data: { |
| labels: products, |
| datasets: [ |
| { |
| label: 'Approved', |
| data: products.map(product => exemptionData[product].Approved), |
| backgroundColor: 'rgba(16, 185, 129, 0.7)' |
| }, |
| { |
| label: 'Pending', |
| data: products.map(product => exemptionData[product].Pending), |
| backgroundColor: 'rgba(245, 158, 11, 0.7)' |
| }, |
| { |
| label: 'Denied', |
| data: products.map(product => exemptionData[product].Denied), |
| backgroundColor: 'rgba(239, 68, 68, 0.7)' |
| }, |
| { |
| label: 'Not Requested', |
| data: products.map(product => exemptionData[product]['Not Requested']), |
| backgroundColor: 'rgba(156, 163, 175, 0.7)' |
| } |
| ] |
| }, |
| options: { |
| responsive: true, |
| maintainAspectRatio: false, |
| scales: { |
| x: { |
| stacked: true |
| }, |
| y: { |
| stacked: true, |
| title: { |
| display: true, |
| text: 'Number of Cases' |
| } |
| } |
| }, |
| plugins: { |
| tooltip: { |
| mode: 'index', |
| intersect: false |
| }, |
| legend: { |
| position: 'top' |
| } |
| } |
| } |
| }); |
| |
| |
| document.getElementById('exemptionChartContainer').addEventListener('click', function(e) { |
| if (e.target.tagName === 'CANVAS') { |
| const activePoints = exemptionStatusChart.getElementsAtEventForMode(e, 'nearest', { intersect: true }, true); |
| if (activePoints.length > 0) { |
| const datasetIndex = activePoints[0].datasetIndex; |
| const status = exemptionStatusChart.data.datasets[datasetIndex].label; |
| |
| |
| const originalFilteredData = [...filteredData]; |
| filteredData = originalFilteredData.filter(item => item.exemptionStatus === status); |
| |
| updateTable(); |
| updateCharts(); |
| updateMetrics(); |
| updateInsights(); |
| updatePagination(); |
| |
| |
| alert(`Showing data for exemption status: ${status}. Click "Apply Filters" to reset.`); |
| } |
| } |
| }); |
| } |
| |
| |
| function initDashboard() { |
| updateTable(); |
| updateCharts(); |
| updateMetrics(); |
| updateInsights(); |
| updatePagination(); |
| |
| |
| document.getElementById('applyFilters').addEventListener('click', filterData); |
| |
| |
| document.querySelectorAll('.chart-toggle').forEach(button => { |
| button.addEventListener('click', function() { |
| const chartId = this.getAttribute('data-chart'); |
| const type = this.getAttribute('data-type'); |
| |
| |
| document.querySelectorAll(`[data-chart="${chartId}"]`).forEach(btn => { |
| btn.classList.remove('active-filter'); |
| }); |
| this.classList.add('active-filter'); |
| |
| |
| switch(chartId) { |
| case 'monthlyTariffChart': |
| updateMonthlyTariffChart(type); |
| break; |
| case 'companyImpactChart': |
| updateCompanyImpactChart(type); |
| break; |
| case 'productCategoryChart': |
| updateProductCategoryChart(type); |
| break; |
| case 'countryOriginChart': |
| updateCountryOriginChart(type); |
| break; |
| } |
| }); |
| }); |
| |
| |
| document.addEventListener('click', function(e) { |
| if (e.target.classList.contains('page-link') && !e.target.classList.contains('active-page')) { |
| e.preventDefault(); |
| currentPage = parseInt(e.target.getAttribute('data-page')); |
| updateTable(); |
| updatePagination(); |
| |
| |
| document.querySelectorAll('.page-link').forEach(link => { |
| link.classList.remove('active-page', 'bg-blue-50', 'border-blue-500', 'text-blue-600'); |
| }); |
| e.target.classList.add('active-page', 'bg-blue-50', 'border-blue-500', 'text-blue-600'); |
| } |
| |
| |
| if (e.target.id === 'prevPage' || e.target.parentElement.id === 'prevPage') { |
| if (currentPage > 1) { |
| currentPage--; |
| updateTable(); |
| updatePagination(); |
| } |
| } |
| |
| |
| if (e.target.id === 'nextPage' || e.target.parentElement.id === 'nextPage') { |
| const totalPages = Math.ceil(filteredData.length / itemsPerPage); |
| if (currentPage < totalPages) { |
| currentPage++; |
| updateTable(); |
| updatePagination(); |
| } |
| } |
| }); |
| } |
| |
| |
| document.addEventListener('DOMContentLoaded', initDashboard); |
| </script> |
| <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=anonymous111110987654321/hellmann-301-tariff-dashboard" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> |
| </html> |