Avg. Order Value
${formatCurrency(avgOrderValue)}
Conversion Rate
${conversionRate.toFixed(2)}%
`;
};
const renderDataTable = () => {
dataTableBody.innerHTML = '';
const sortedOrders = [...state.orders].sort((a, b) => new Date(b.date) - new Date(a.date));
sortedOrders.forEach(order => {
const row = `
| ${order.date} |
#${1000 + order.id} |
${order.productName} |
${order.category} |
${formatCurrency(order.price * order.quantity)} |
`;
dataTableBody.innerHTML += row;
});
};
const renderCharts = () => {
// Revenue Trend Chart
const revenueByDate = state.orders.reduce((acc, order) => {
const revenue = order.price * order.quantity;
acc[order.date] = (acc[order.date] || 0) + revenue;
return acc;
}, {});
const sortedDates = Object.keys(revenueByDate).sort((a, b) => new Date(a) - new Date(b));
if (revenueChart) revenueChart.destroy();
revenueChart = new Chart(revenueCanvas.getContext('2d'), {
type: 'line',
data: {
labels: sortedDates,
datasets: [{
label: 'Revenue',
data: sortedDates.map(date => revenueByDate[date]),
borderColor: '#8b5cf6',
backgroundColor: 'rgba(139, 92, 246, 0.1)',
fill: true,
tension: 0.1
}]
},
options: {
responsive: true, maintainAspectRatio: false, plugins: { legend: { display: false } },
scales: { x: { type: 'time', time: { unit: 'day' } }, y: { beginAtZero: true, ticks: { callback: (value) => formatCurrency(value) } } }
}
});
// Top Selling Products Chart
const salesByProduct = state.orders.reduce((acc, order) => {
acc[order.productName] = (acc[order.productName] || 0) + order.quantity;
return acc;
}, {});
const sortedProducts = Object.entries(salesByProduct).sort((a, b) => b[1] - a[1]).slice(0, 5);
const productLabels = sortedProducts.map(p => p[0]);
const productData = sortedProducts.map(p => p[1]);
if (productsChart) productsChart.destroy();
productsChart = new Chart(productsCanvas.getContext('2d'), {
type: 'bar',
data: {
labels: productLabels,
datasets: [{
label: 'Units Sold',
data: productData,
backgroundColor: ['#a78bfa', '#c4b5fd', '#ddd6fe', '#ede9fe', '#f5f3ff']
}]
},
options: {
indexAxis: 'y', responsive: true, maintainAspectRatio: false, plugins: { legend: { display: false } }
}
});
};
const renderConfigRows = () => {
configRowsContainer.innerHTML = '';
const sortedOrders = [...state.orders].sort((a, b) => new Date(b.date) - new Date(a.date));
sortedOrders.forEach(order => {
const row = `
`;
configRowsContainer.innerHTML += row;
});
addConfigEventListeners();
};
// --- EVENT HANDLERS ---
const handleConfigChange = (e) => {
const id = parseInt(e.target.dataset.id);
const field = e.target.dataset.field;
const value = (e.target.type === 'number') ? parseFloat(e.target.value) || 0 : e.target.value;
const order = state.orders.find(o => o.id === id);
if (order) order[field] = value;
renderAll();
};
const handleAddOrder = () => {
const newId = state.orders.length > 0 ? Math.max(...state.orders.map(o => o.id)) + 1 : 1;
const today = new Date().toISOString().split('T')[0];
state.orders.push({ id: newId, date: today, productName: "New Product", category: "General", price: 0, quantity: 1 });
renderAll();
};
const handleRemoveOrder = (e) => {
const id = parseInt(e.target.dataset.id);
state.orders = state.orders.filter(o => o.id !== id);
renderAll();
};
const addConfigEventListeners = () => {
document.querySelectorAll('.config-input').forEach(input => input.addEventListener('change', handleConfigChange));
document.querySelectorAll('.remove-order-btn').forEach(button => button.addEventListener('click', handleRemoveOrder));
};
const handleDownloadPdf = () => {
const { jsPDF } = window.jspdf;
const pdfContent = document.getElementById('pdf-content');
document.querySelectorAll('.no-print').forEach(el => el.style.visibility = 'hidden');
Chart.defaults.animation = false;
html2canvas(pdfContent, { scale: 2, useCORS: true, backgroundColor: '#ffffff' }).then(canvas => {
document.querySelectorAll('.no-print').forEach(el => el.style.visibility = 'visible');
Chart.defaults.animation = true;
const imgData = canvas.toDataURL('image/png');
const pdf = new jsPDF({ orientation: 'p', unit: 'mm', format: 'a4' });
const pdfWidth = pdf.internal.pageSize.getWidth();
const imgWidth = pdfWidth - 20;
const imgHeight = canvas.height * imgWidth / canvas.width;
pdf.addImage(imgData, 'PNG', 10, 10, imgWidth, imgHeight);
pdf.save('Online-Store-Performance.pdf');
});
};
// --- TABBING LOGIC ---
let currentTabIndex = 0;
const updateTabButtons = () => {
prevTabBtn.disabled = currentTabIndex === 0;
nextTabBtn.disabled = currentTabIndex === tabContents.length - 1;
};
const switchTab = (index) => {
tabButtons.forEach(btn => btn.classList.remove('active'));
tabContents.forEach(content => content.classList.remove('active'));
tabButtons[index].classList.add('active');
tabContents[index].classList.add('active');
currentTabIndex = index;
updateTabButtons();
};
tabButtons.forEach((button, index) => button.addEventListener('click', () => switchTab(index)));
prevTabBtn.addEventListener('click', () => { if (currentTabIndex > 0) switchTab(currentTabIndex - 1); });
nextTabBtn.addEventListener('click', () => { if (currentTabIndex < tabContents.length - 1) switchTab(currentTabIndex + 1); });
// --- INITIALIZATION ---
if (kpiCardsContainer && addOrderBtn && downloadPdfBtn) {
addOrderBtn.addEventListener('click', handleAddOrder);
downloadPdfBtn.addEventListener('click', handleDownloadPdf);
renderAll();
updateTabButtons();
} else {
console.error("Essential dashboard elements could not be found.");
}
});