Trending Product Analyzer

Trending Product Analyzer

Analyze sales data to identify and visualize product trends.

Trend Analysis Summary

Configure your data and press "Analyze Trends" on the configuration tab to see the report.

Top Trending Product

${results.topTrending.name}

Highest Growth

+${results.topTrending.growth.toFixed(2)}%

Most Stable Product

${results.mostStable.name}

Total Sales Volume

${results.totalVolume.toLocaleString()}

`; let tableHtml = ``; results.analyzedProducts.forEach(p => { const trendColor = p.trendScore > 1 ? 'text-green-600' : p.trendScore < -1 ? 'text-red-600' : 'text-gray-600'; tableHtml += ``; }); tableHtml += `
Product Trend Score % Growth Total Sales
${p.name} ${p.trendScore.toFixed(2)} ${p.growth.toFixed(2)}% ${p.totalSales.toLocaleString()}
`; resultsContainer.innerHTML = tableHtml; renderChart(results.analyzedProducts); }; const renderChart = (data) => { const ctx = document.getElementById('trends-chart').getContext('2d'); if(trendsChart) trendsChart.destroy(); const datasets = data.map((p, index) => { const colors = ['#3B82F6', '#10B981', '#F59E0B', '#EF4444', '#8B5CF6']; return { label: p.name, data: p.numericSales, borderColor: colors[index % colors.length], tension: 0.1 } }); const maxDataPoints = Math.max(...data.map(p => p.numericSales.length)); const labels = Array.from({length: maxDataPoints}, (_, i) => `Month ${i+1}`); trendsChart = new Chart(ctx, { type: 'line', data: { labels, datasets }, options: { responsive: true, plugins: { title: { display: true, text: 'Monthly Product Sales' }}} }); }; // --- CORE LOGIC --- const handleAnalysis = () => { analyzeBtnSpinner.classList.remove('hidden'); analyzeBtnText.textContent = 'Analyzing...'; analyzeBtn.disabled = true; setTimeout(() => { let totalVolume = 0; const analyzedProducts = products.map(p => { const numericSales = p.salesData.split(',').map(s => parseFloat(s.trim())).filter(n => !isNaN(n)); if (numericSales.length < 2) { return { ...p, numericSales, trendScore: 0, growth: 0, totalSales: 0 }; } // Simple linear regression for trend score (slope) const n = numericSales.length; let sum_x = 0, sum_y = 0, sum_xy = 0, sum_xx = 0; for (let i = 0; i < n; i++) { sum_x += i; sum_y += numericSales[i]; sum_xy += i * numericSales[i]; sum_xx += i * i; } const slope = (n * sum_xy - sum_x * sum_y) / (n * sum_xx - sum_x * sum_x); const totalSales = numericSales.reduce((a, b) => a + b, 0); totalVolume += totalSales; const growth = ((numericSales[n - 1] - numericSales[0]) / numericSales[0]) * 100; return { ...p, numericSales, trendScore: slope, growth, totalSales }; }).sort((a,b) => b.trendScore - a.trendScore); const results = { analyzedProducts, totalVolume, topTrending: analyzedProducts[0], mostStable: [...analyzedProducts].sort((a,b) => Math.abs(a.trendScore) - Math.abs(b.trendScore))[0] }; renderDashboard(results); switchTab('dashboard'); downloadPdfBtn.disabled = false; analyzeBtnSpinner.classList.add('hidden'); analyzeBtnText.textContent = 'Analyze Trends'; analyzeBtn.disabled = false; }, 500); }; // --- UI & EVENT HANDLERS --- const switchTab = (tabId) => { currentTab = tabId; Object.values(tabPanes).forEach(pane => pane.classList.add('hidden')); tabPanes[tabId].classList.remove('hidden'); Object.values(tabButtons).forEach(btn => btn.classList.replace('tab-active', 'tab-inactive')); tabButtons[tabId].classList.replace('tab-inactive', 'tab-active'); updateNavButtons(); }; const navigateTabs = (direction) => { const currentIndex = tabs.indexOf(currentTab); const newIndex = direction === 'next' ? currentIndex + 1 : currentIndex - 1; if (newIndex >= 0 && newIndex < tabs.length) switchTab(tabs[newIndex]); }; const updateNavButtons = () => { const currentIndex = tabs.indexOf(currentTab); prevBtn.disabled = currentIndex === 0; nextBtn.disabled = currentIndex === tabs.length - 1; prevBtn.classList.toggle('opacity-50', prevBtn.disabled); nextBtn.classList.toggle('opacity-50', nextBtn.disabled); }; const handlePdfDownload = () => { const pdfRenderContainer = document.getElementById('pdf-render-content'); const pdfContent = document.getElementById('pdf-content').innerHTML; const header = `

Product Trend Analysis Report

`; pdfRenderContainer.innerHTML = header + pdfContent + '
'; html2canvas(pdfRenderContainer, { scale: 2 }).then(canvas => { const imgData = canvas.toDataURL('image/png'); const { jsPDF } = window.jspdf; const pdf = new jsPDF({ orientation: 'landscape', unit: 'pt', format: 'a4' }); const pdfWidth = pdf.internal.pageSize.getWidth(), margin = 40; const contentWidth = pdfWidth - margin * 2; const pdfHeight = (canvas.height * contentWidth) / canvas.width; pdf.addImage(imgData, 'PNG', margin, margin, contentWidth, pdfHeight); pdf.save('Product-Trend-Report.pdf'); }); }; // --- EVENT LISTENERS --- window.switchTab = switchTab; window.navigateTabs = navigateTabs; analyzeBtn.addEventListener('click', handleAnalysis); downloadPdfBtn.addEventListener('click', handlePdfDownload); addProductBtn.addEventListener('click', () => { products.push({ id: nextId++, name: 'New Product', salesData: '10,20,30' }); renderConfigTable(); }); configTableBody.addEventListener('input', e => { if (!e.target.classList.contains('cfg-input')) return; const id = parseInt(e.target.closest('tr').dataset.id); const prop = e.target.dataset.prop; const item = products.find(p => p.id === id); if (item) item[prop] = e.target.value; }); configTableBody.addEventListener('click', e => { if (!e.target.classList.contains('rm-btn')) return; const id = parseInt(e.target.closest('tr').dataset.id); products = products.filter(p => p.id !== id); renderConfigTable(); }); // --- INITIALIZATION --- renderConfigTable(); updateNavButtons(); switchTab('dashboard'); });
Scroll to Top