Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Sales Dashboard | NASKSOFT</title> | |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> | |
| <link rel="stylesheet" href="style.css"> | |
| </head> | |
| <body> | |
| <custom-navbar></custom-navbar> | |
| <main class="dashboard"> | |
| <div class="container"> | |
| <div class="dashboard-header"> | |
| <h1>Sales Data Analysis</h1> | |
| <div class="dashboard-actions"> | |
| <button class="btn btn-outline" id="exportBtn"><i class="fas fa-download"></i> Export</button> | |
| <button class="btn btn-primary" id="refreshBtn"><i class="fas fa-sync-alt"></i> Refresh</button> | |
| </div> | |
| </div> | |
| <div class="stats-grid"> | |
| <div class="stat-card"> | |
| <h3>Total Sales</h3> | |
| <div class="stat-value" id="totalSales">$1,291,833</div> | |
| </div> | |
| <div class="stat-card"> | |
| <h3>Total Units</h3> | |
| <div class="stat-value" id="totalUnits">1,838</div> | |
| </div> | |
| <div class="stat-card"> | |
| <h3>Avg. Sale</h3> | |
| <div class="stat-value" id="avgSale">$702.85</div> | |
| </div> | |
| <div class="stat-card"> | |
| <h3>Top Product</h3> | |
| <div class="stat-value" id="topProduct">Television</div> | |
| </div> | |
| </div> | |
| <div class="dashboard-grid"> | |
| <div class="dashboard-card wide"> | |
| <h3>Sales Trend by Month</h3> | |
| <div class="chart-container"> | |
| <canvas id="salesTrendChart"></canvas> | |
| </div> | |
| </div> | |
| <div class="dashboard-card"> | |
| <h3>Sales by Region</h3> | |
| <div class="chart-container"> | |
| <canvas id="regionChart"></canvas> | |
| </div> | |
| </div> | |
| <div class="dashboard-card"> | |
| <h3>Sales by Product</h3> | |
| <div class="chart-container"> | |
| <canvas id="productChart"></canvas> | |
| </div> | |
| </div> | |
| <div class="dashboard-card wide"> | |
| <h3>Top Performers</h3> | |
| <div class="chart-container"> | |
| <canvas id="salespersonChart"></canvas> | |
| </div> | |
| </div> | |
| <div class="dashboard-card wide"> | |
| <h3>Sales Data</h3> | |
| <div class="data-table"> | |
| <table id="salesTable"> | |
| <thead> | |
| <tr> | |
| <th>Date</th> | |
| <th>Region</th> | |
| <th>Manager</th> | |
| <th>Salesperson</th> | |
| <th>Item</th> | |
| <th>Units</th> | |
| <th>Unit Price</th> | |
| <th>Sale Amount</th> | |
| </tr> | |
| </thead> | |
| <tbody> | |
| <!-- Data will be populated by JavaScript --> | |
| </tbody> | |
| </table> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </main> | |
| <custom-footer></custom-footer> | |
| <script src="https://cdn.jsdelivr.net/npm/chart.js"></script> | |
| <script src="https://cdn.jsdelivr.net/npm/luxon@3.0.1"></script> | |
| <script src="https://cdn.jsdelivr.net/npm/chartjs-adapter-luxon@1.2.0"></script> | |
| <script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-datalabels@2.0.0"></script> | |
| <script src="components/navbar.js"></script> | |
| <script src="components/footer.js"></script> | |
| <script> | |
| document.addEventListener('DOMContentLoaded', function() { | |
| // Sample data - in a real app this would come from an API | |
| const salesData = [ | |
| { date: '1/6/18', region: 'East', manager: 'Martha', salesperson: 'Alexander', item: 'Television', units: 95, unitPrice: 1198, saleAmount: 113810 }, | |
| { date: '1/23/18', region: 'Central', manager: 'Hermann', salesperson: 'Shelli', item: 'Home Theater', units: 50, unitPrice: 500, saleAmount: 25000 }, | |
| // ... all other data rows ... | |
| { date: '12/21/19', region: 'Central', manager: 'Martha', salesperson: 'Steven', item: 'Home Theater', units: 28, unitPrice: 500, saleAmount: 14000 } | |
| ]; | |
| // Calculate stats | |
| const totalSales = salesData.reduce((sum, sale) => sum + sale.saleAmount, 0); | |
| const totalUnits = salesData.reduce((sum, sale) => sum + sale.units, 0); | |
| const avgSale = totalSales / salesData.length; | |
| // Find top product | |
| const productSales = {}; | |
| salesData.forEach(sale => { | |
| productSales[sale.item] = (productSales[sale.item] || 0) + sale.saleAmount; | |
| }); | |
| const topProduct = Object.entries(productSales).sort((a, b) => b[1] - a[1])[0][0]; | |
| // Update stats | |
| document.getElementById('totalSales').textContent = `$${totalSales.toLocaleString()}`; | |
| document.getElementById('totalUnits').textContent = totalUnits.toLocaleString(); | |
| document.getElementById('avgSale').textContent = `$${avgSale.toFixed(2)}`; | |
| document.getElementById('topProduct').textContent = topProduct; | |
| // Populate table | |
| const tableBody = document.querySelector('#salesTable tbody'); | |
| salesData.forEach(sale => { | |
| const row = document.createElement('tr'); | |
| row.innerHTML = ` | |
| <td>${sale.date}</td> | |
| <td>${sale.region}</td> | |
| <td>${sale.manager}</td> | |
| <td>${sale.salesperson}</td> | |
| <td>${sale.item}</td> | |
| <td>${sale.units}</td> | |
| <td>$${sale.unitPrice.toLocaleString()}</td> | |
| <td>$${sale.saleAmount.toLocaleString()}</td> | |
| `; | |
| tableBody.appendChild(row); | |
| }); | |
| // Initialize charts | |
| initCharts(salesData); | |
| }); | |
| function initCharts(data) { | |
| // Group data by month | |
| const monthlySales = {}; | |
| data.forEach(sale => { | |
| const date = new Date(sale.date); | |
| const monthYear = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}`; | |
| monthlySales[monthYear] = (monthlySales[monthYear] || 0) + sale.saleAmount; | |
| }); | |
| // Sales Trend Chart | |
| const trendCtx = document.getElementById('salesTrendChart').getContext('2d'); | |
| new Chart(trendCtx, { | |
| type: 'line', | |
| data: { | |
| labels: Object.keys(monthlySales), | |
| datasets: [{ | |
| label: 'Sales Amount', | |
| data: Object.values(monthlySales), | |
| backgroundColor: 'rgba(75, 192, 192, 0.2)', | |
| borderColor: 'rgba(75, 192, 192, 1)', | |
| borderWidth: 2, | |
| tension: 0.1 | |
| }] | |
| }, | |
| options: { | |
| responsive: true, | |
| plugins: { | |
| legend: { display: false }, | |
| tooltip: { | |
| callbacks: { | |
| label: ctx => `$${ctx.raw.toLocaleString()}` | |
| } | |
| } | |
| }, | |
| scales: { | |
| y: { | |
| beginAtZero: true, | |
| ticks: { | |
| callback: value => `$${value.toLocaleString()}` | |
| } | |
| } | |
| } | |
| } | |
| }); | |
| // Region Chart | |
| const regionData = {}; | |
| data.forEach(sale => { | |
| regionData[sale.region] = (regionData[sale.region] || 0) + sale.saleAmount; | |
| }); | |
| const regionCtx = document.getElementById('regionChart').getContext('2d'); | |
| new Chart(regionCtx, { | |
| type: 'doughnut', | |
| data: { | |
| labels: Object.keys(regionData), | |
| datasets: [{ | |
| data: Object.values(regionData), | |
| backgroundColor: [ | |
| 'rgba(255, 99, 132, 0.7)', | |
| 'rgba(54, 162, 235, 0.7)', | |
| 'rgba(255, 206, 86, 0.7)' | |
| ], | |
| borderColor: [ | |
| 'rgba(255, 99, 132, 1)', | |
| 'rgba(54, 162, 235, 1)', | |
| 'rgba(255, 206, 86, 1)' | |
| ], | |
| borderWidth: 1 | |
| }] | |
| }, | |
| options: { | |
| responsive: true, | |
| plugins: { | |
| legend: { position: 'bottom' }, | |
| tooltip: { | |
| callbacks: { | |
| label: ctx => `$${ctx.raw.toLocaleString()} (${((ctx.raw / ctx.dataset.data.reduce((a, b) => a + b, 0)) * 100).toFixed(1)}%)` | |
| } | |
| } | |
| } | |
| } | |
| }); | |
| // Product Chart | |
| const productCtx = document.getElementById('productChart').getContext('2d'); | |
| new Chart(productCtx, { | |
| type: 'bar', | |
| data: { | |
| labels: Object.keys(productData), | |
| datasets: [{ | |
| label: 'Sales Amount', | |
| data: Object.values(productData), | |
| backgroundColor: 'rgba(153, 102, 255, 0.7)', | |
| borderColor: 'rgba(153, 102, 255, 1)', | |
| borderWidth: 1 | |
| }] | |
| }, | |
| options: { | |
| responsive: true, | |
| plugins: { | |
| legend: { display: false }, | |
| tooltip: { | |
| callbacks: { | |
| label: ctx => `$${ctx.raw.toLocaleString()}` | |
| } | |
| } | |
| }, | |
| scales: { | |
| y: { | |
| beginAtZero: true, | |
| ticks: { | |
| callback: value => `$${value.toLocaleString()}` | |
| } | |
| } | |
| } | |
| } | |
| }); | |
| // Salesperson Chart | |
| const salespersonData = {}; | |
| data.forEach(sale => { | |
| salespersonData[sale.salesperson] = (salespersonData[sale.salesperson] || 0) + sale.saleAmount; | |
| }); | |
| const salespersonCtx = document.getElementById('salespersonChart').getContext('2d'); | |
| new Chart(salespersonCtx, { | |
| type: 'bar', | |
| data: { | |
| labels: Object.keys(salespersonData), | |
| datasets: [{ | |
| label: 'Sales Amount', | |
| data: Object.values(salespersonData), | |
| backgroundColor: 'rgba(54, 162, 235, 0.7)', | |
| borderColor: 'rgba(54, 162, 235, 1)', | |
| borderWidth: 1 | |
| }] | |
| }, | |
| options: { | |
| responsive: true, | |
| plugins: { | |
| legend: { display: false }, | |
| tooltip: { | |
| callbacks: { | |
| label: ctx => `$${ctx.raw.toLocaleString()}` | |
| } | |
| } | |
| }, | |
| scales: { | |
| y: { | |
| beginAtZero: true, | |
| ticks: { | |
| callback: value => `$${value.toLocaleString()}` | |
| } | |
| } | |
| } | |
| } | |
| }); | |
| } | |
| </script> | |
| </body> | |
| </html> |