Files
Class_0908/sales_dashboard.html
2025-09-08 16:23:54 +08:00

317 lines
12 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Sales Dashboard</title>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<style>
body { font-family: Arial, sans-serif; margin: 20px; background-color: #f4f4f4; color: #333; }
.container { max-width: 900px; margin: auto; background: #fff; padding: 20px; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
h1, h2 { color: #0056b3; border-bottom: 2px solid #eee; padding-bottom: 10px; margin-bottom: 20px; }
.stats-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 20px; margin-bottom: 30px; }
.stat-box { background: #e9f7ff; padding: 15px; border-radius: 5px; text-align: center; }
.stat-box h3 { margin: 0 0 10px 0; color: #007bff; }
.stat-box p { font-size: 1.8em; font-weight: bold; color: #333; }
.chart-container { margin-bottom: 30px; background: #fff; padding: 20px; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
.chart-container canvas { max-width: 100%; height: auto; }
.navbar {
background-color: #333;
padding: 10px 0;
text-align: center;
margin-bottom: 20px;
border-radius: 8px;
}
.nav-list {
list-style: none;
padding: 0;
margin: 0;
display: flex;
justify-content: center;
}
.nav-item {
margin: 0 15px;
}
.nav-link {
color: white;
text-decoration: none;
font-weight: bold;
padding: 8px 15px;
border-radius: 5px;
transition: background-color 0.3s ease;
}
.nav-link:hover {
background-color: #575757;
}
/* New styles for summary boxes */
.summary-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 20px;
margin-bottom: 30px;
}
.summary-box {
background: #e0f7fa; /* Light blue */
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
text-align: center;
}
.summary-box h3 {
margin-top: 0;
color: #00796b; /* Darker blue-green */
}
.summary-box p {
font-size: 2em;
font-weight: bold;
color: #004d40; /* Even darker blue-green */
}
/* Styles for the data table */
.data-table-container {
margin-top: 30px;
background: #fff;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.data-table {
width: 100%;
border-collapse: collapse;
margin-top: 15px;
}
.data-table th, .data-table td {
border: 1px solid #ddd;
padding: 8px;
text-align: left;
}
.data-table th {
background-color: #f2f2f2;
font-weight: bold;
}
.data-table tbody tr:nth-child(even) {
background-color: #f9f9f9;
}
.data-table tbody tr:hover {
background-color: #f1f1f1;
}
</style>
</head>
<body>
<nav class="navbar">
<ul class="nav-list">
<li class="nav-item"><a href="#" class="nav-link">Overview</a></li>
<li class="nav-item"><a href="#" class="nav-link">Product Sales</a></li>
<li class="nav-item"><a href="#" class="nav-link">Regional Sales</a></li>
<li class="nav-item"><a href="sales_records.html" class="nav-link">Sales Records</a></li>
</ul>
</nav>
<div class="dashboard-container">
<h1>Sales Dashboard</h1>
<div class="summary-grid">
<div class="summary-box">
<h3>Total Sales</h3>
<p id="totalSales">Loading...</p>
</div>
<div class="summary-box">
<h3>Total Orders</h3>
<p id="totalOrders">Loading...</p>
</div>
<div class="summary-box">
<h3>Product Categories</h3>
<p id="productCategories">Loading...</p>
</div>
<div class="summary-box">
<h3>Sales Regions</h3>
<p id="salesRegions">Loading...</p>
</div>
</div>
<h2>Sales by Product Category</h2>
<div class="chart-container">
<canvas id="categoryBarChart"></canvas>
</div>
<h2>Sales Over Time</h2>
<div class="chart-container">
<canvas id="timeLineChart"></canvas>
</div>
<h2>All Sales Data</h2>
<div class="data-table-container">
<table class="data-table" id="allSalesDataTable">
<thead>
<tr>
<!-- Headers will be dynamically generated -->
</tr>
</thead>
<tbody>
<!-- Data will be dynamically loaded -->
</tbody>
</table>
</div>
</div>
<script>
// Function to fetch and display summary data
async function fetchSummaryData() {
try {
const response = await fetch('http://127.0.0.1:5000/dashboard_summary');
const data = await response.json();
document.getElementById('totalSales').innerText = `${data.total_sales.toFixed(2)}`;
document.getElementById('totalOrders').innerText = data.total_orders;
document.getElementById('productCategories').innerText = data.product_categories;
document.getElementById('salesRegions').innerText = data.sales_regions;
} catch (error) {
console.error('Error fetching summary data:', error);
document.getElementById('totalSales').innerText = 'Error';
document.getElementById('totalOrders').innerText = 'Error';
document.getElementById('productCategories').innerText = 'Error';
document.getElementById('salesRegions').innerText = 'Error';
}
}
// Function to fetch and render charts
async function fetchChartData() {
try {
const response = await fetch('http://127.0.0.1:5000/dashboard_charts');
const data = await response.json();
// Bar Chart: Sales by Product Category
const categoryBarCtx = document.getElementById('categoryBarChart').getContext('2d');
new Chart(categoryBarCtx, {
type: 'bar',
data: {
labels: data.category_sales.labels,
datasets: [{
label: 'Sales Amount',
data: data.category_sales.data,
backgroundColor: 'rgba(75, 192, 192, 0.6)',
borderColor: 'rgba(75, 192, 192, 1)',
borderWidth: 1
}]
},
options: {
responsive: true,
scales: {
y: {
beginAtZero: true,
title: {
display: true,
text: 'Sales Amount'
}
}
},
plugins: {
title: {
display: true,
text: 'Sales by Product Category'
}
}
}
});
// Line Chart: Sales Over Time
const timeLineCtx = document.getElementById('timeLineChart').getContext('2d');
new Chart(timeLineCtx, {
type: 'line',
data: {
labels: data.time_sales.labels,
datasets: [{
label: 'Sales Amount',
data: data.time_sales.data,
backgroundColor: 'rgba(153, 102, 255, 0.6)',
borderColor: 'rgba(153, 102, 255, 1)',
borderWidth: 1,
fill: false
}]
},
options: {
responsive: true,
scales: {
y: {
beginAtZero: true,
title: {
display: true,
text: 'Sales Amount'
}
}
},
plugins: {
title: {
display: true,
text: 'Sales Over Time'
}
}
}
});
} catch (error) {
console.error('Error fetching chart data:', error);
}
}
// Function to fetch and display all sales data in a table
async function fetchAllSalesData() {
try {
const response = await fetch('http://127.0.0.1:5000/sales_data');
const data = await response.json();
const table = document.getElementById('allSalesDataTable');
const thead = table.querySelector('thead tr');
const tbody = table.querySelector('tbody');
// Clear existing content
thead.innerHTML = '';
tbody.innerHTML = '';
if (data.length > 0) {
// Create headers
const headers = Object.keys(data[0]);
headers.forEach(header => {
const th = document.createElement('th');
th.innerText = header;
thead.appendChild(th);
});
// Populate table rows
data.forEach(record => {
const tr = document.createElement('tr');
headers.forEach(header => {
const td = document.createElement('td');
td.innerText = record[header];
tr.appendChild(td);
});
tbody.appendChild(tr);
});
} else {
const tr = document.createElement('tr');
const td = document.createElement('td');
td.colSpan = 10; // Arbitrary large number to span all columns
td.innerText = 'No sales data available.';
tr.appendChild(td);
tbody.appendChild(tr);
}
} catch (error) {
console.error('Error fetching all sales data:', error);
const table = document.getElementById('allSalesDataTable');
const tbody = table.querySelector('tbody');
tbody.innerHTML = '<tr><td colspan="10">Error loading data.</td></tr>';
}
}
// Run all fetch functions on page load
document.addEventListener('DOMContentLoaded', () => {
fetchSummaryData();
fetchChartData();
fetchAllSalesData();
});
</script>
</body>
</html>