Product Performance Dashboard
Performance Overview
Total Revenue
$0
Total Units Sold
0
Average Rating
0/5
Return Rate
0%
Monthly Revenue
Top 5 Products by Units Sold
Detailed Product Performance
| Product | Units Sold | Revenue | Avg. Rating | Return Rate |
|---|
Customer Feedback Analysis
Overall Rating Distribution
Recent Reviews
Add New Product Data
Add Sales Record
${'★'.repeat(r.rating)}${'☆'.repeat(5-r.rating)}
"${r.comment}"
`; reviewList.appendChild(el); }); } function renderConfigForms() { const productSelect = document.getElementById('sales-product-select'); productSelect.innerHTML = ''; state.products.forEach(p => { const option = document.createElement('option'); option.value = p.id; option.textContent = p.name; productSelect.appendChild(option); }); } // --- EVENT HANDLERS & LOGIC --- // function attachEventListeners() { prevBtn.addEventListener('click', () => changeTab(state.activeTab - 1)); nextBtn.addEventListener('click', () => changeTab(state.activeTab + 1)); downloadPdfBtn.addEventListener('click', handleDownloadPdf); document.getElementById('add-product-form').addEventListener('submit', handleAddProduct); document.getElementById('add-sales-form').addEventListener('submit', handleAddSales); } window.changeTab = (tabIndex) => { if (tabIndex < 0 || tabIndex >= tabButtons.length) return; state.activeTab = tabIndex; updateTabUI(); }; function updateTabUI() { tabButtons.forEach((b, i) => b.classList.toggle('active', i === state.activeTab)); tabButtons.forEach((b, i) => b.classList.toggle('inactive', i !== state.activeTab)); tabPanes.forEach((p, i) => p.classList.toggle('hidden', i !== state.activeTab)); prevBtn.disabled = state.activeTab === 0; nextBtn.disabled = state.activeTab === tabButtons.length - 1; prevBtn.classList.toggle('opacity-50', prevBtn.disabled); nextBtn.classList.toggle('opacity-50', nextBtn.disabled); } function handleAddProduct(e) { e.preventDefault(); const form = e.target; state.products.push({ id: Date.now(), name: form.elements['product-name'].value, price: parseFloat(form.elements['product-price'].value), }); form.reset(); render(); changeTab(1); } function handleAddSales(e) { e.preventDefault(); const form = e.target; state.sales.push({ productId: parseInt(form.elements['sales-product-select'].value), unitsSold: parseInt(form.elements['sales-units'].value), date: form.elements['sales-date'].value, returns: 0 // Assume 0 returns for manually added data }); form.reset(); render(); changeTab(1); } async function handleDownloadPdf() { const downloadBtn = document.getElementById('download-pdf-btn'); if (!downloadBtn || downloadBtn.disabled) return; const originalBtnContent = downloadBtn.innerHTML; downloadBtn.disabled = true; downloadBtn.innerHTML = `Generating PDF...`; try { const { jsPDF } = window.jspdf; const contentToClone = document.getElementById('product-performance-container'); const contentToExport = contentToClone.cloneNode(true); contentToExport.querySelectorAll('.pdf-hide').forEach(el => el.remove()); contentToExport.querySelectorAll('.tab-pane:not(#tab-0-content)').forEach(el => el.remove()); contentToExport.querySelector('#tab-0-content').classList.remove('hidden'); document.body.appendChild(contentToExport); contentToExport.style.cssText = 'position:absolute; left:0; top:0; width:1024px; z-index:-1; background-color:white; padding:2rem;'; const canvas = await html2canvas(contentToExport, { scale: 2, useCORS: true, logging: false }); document.body.removeChild(contentToExport); const imgData = canvas.toDataURL('image/png'); const pdf = new jsPDF({ orientation: 'p', unit: 'pt', format: 'a4' }); const pdfWidth = pdf.internal.pageSize.getWidth(); const scaledHeight = (canvas.height * pdfWidth) / canvas.width; pdf.addImage(imgData, 'PNG', 0, 0, pdfWidth, scaledHeight); pdf.save('Product_Performance_Summary.pdf'); } catch (error) { console.error("Failed to generate PDF:", error); } finally { downloadBtn.disabled = false; downloadBtn.innerHTML = originalBtnContent; } } // --- UTILITY FUNCTIONS --- // function calculateMetrics() { let totalRevenue = 0; let totalUnitsSold = 0; let totalReturns = 0; state.products.forEach(p => { const productSales = state.sales.filter(s => s.productId === p.id); p.totalUnitsSold = productSales.reduce((sum, s) => sum + s.unitsSold, 0); p.totalRevenue = p.totalUnitsSold * p.price; p.totalReturns = productSales.reduce((sum, s) => sum + s.returns, 0); p.returnRate = p.totalUnitsSold > 0 ? (p.totalReturns / p.totalUnitsSold) * 100 : 0; const productReviews = state.reviews.filter(r => r.productId === p.id); p.averageRating = productReviews.length > 0 ? productReviews.reduce((sum, r) => sum + r.rating, 0) / productReviews.length : 0; totalRevenue += p.totalRevenue; totalUnitsSold += p.totalUnitsSold; totalReturns += p.totalReturns; }); state.totalRevenue = totalRevenue; state.totalUnitsSold = totalUnitsSold; state.averageRating = state.reviews.length > 0 ? state.reviews.reduce((sum, r) => sum + r.rating, 0) / state.reviews.length : 0; state.overallReturnRate = totalUnitsSold > 0 ? (totalReturns / totalUnitsSold) * 100 : 0; } // --- START THE APP --- // initialize(); });