Sentiment Analysis Dashboard
Net Sentiment Score
Total Feedback
0
Positive
0
Negative
0
Sentiment Trend (Last 7 Days)
Analysis
Sentiment by Topic
Feedback by Source
Recent Feedback Log
| Comment | Source | Topic | Sentiment |
|---|
Log New Feedback
All Feedback Records
| Date | Comment | Sentiment | Action |
|---|
Error: A required library is missing.
'; return; } // --- DATA MANAGEMENT --- let feedbackData = JSON.parse(localStorage.getItem('sa_feedback')) || []; const getSampleData = () => { const today = new Date('2025-07-09'); const d = (daysAgo) => new Date(today.getTime() - daysAgo * 24 * 60 * 60 * 1000).toISOString().split('T')[0]; return [ { id: 1, text: "The delivery was so fast! I got my order a day early.", sentiment: 'Positive', source: 'Website Review', topic: 'Shipping', date: d(0) }, { id: 2, text: "Your customer service agent was incredibly helpful.", sentiment: 'Positive', source: 'Support Chat', topic: 'Customer Service', date: d(0) }, { id: 3, text: "The app keeps crashing. Very frustrating!", sentiment: 'Negative', source: 'App Store', topic: 'App Experience', date: d(0) }, { id: 4, text: "Product quality is amazing, feels very durable.", sentiment: 'Positive', source: 'Website Review', topic: 'Product Quality', date: d(1) }, { id: 5, text: "It's an okay product. Does the job.", sentiment: 'Neutral', source: 'Website Review', topic: 'Product Quality', date: d(1) }, { id: 6, text: "Why is shipping so expensive?", sentiment: 'Negative', source: 'Twitter', topic: 'Shipping', date: d(2) }, ]; }; if (feedbackData.length === 0) feedbackData = getSampleData(); const saveState = () => localStorage.setItem('sa_feedback', JSON.stringify(feedbackData)); // --- CHART INSTANCES & UTILITIES --- let overallChart, trendChart, topicChart, sourceChart; const tabButtons = document.querySelectorAll('.sa-tab-button'); const tabContents = document.querySelectorAll('.sa-tab-content'); const nextBtn = document.getElementById('sa-next-btn'); const prevBtn = document.getElementById('sa-prev-btn'); // --- RENDER FUNCTIONS --- const renderAll = () => { try { renderKPIs(); renderOverallChart(); renderTrendChart(); renderTopicChart(); renderSourceChart(); renderFeedbackLog(); renderManageTab(); updateNavButtons(); } catch(error) { console.error("Dashboard rendering failed:", error); } }; const renderKPIs = () => { const positiveCount = feedbackData.filter(f => f.sentiment === 'Positive').length; const negativeCount = feedbackData.filter(f => f.sentiment === 'Negative').length; const total = feedbackData.length; document.getElementById('sa-total-kpi').textContent = total; document.getElementById('sa-positive-kpi').textContent = positiveCount; document.getElementById('sa-negative-kpi').textContent = negativeCount; }; const renderOverallChart = () => { const positiveCount = feedbackData.filter(f => f.sentiment === 'Positive').length; const negativeCount = feedbackData.filter(f => f.sentiment === 'Negative').length; const total = positiveCount + negativeCount; const netScore = total > 0 ? Math.round(((positiveCount - negativeCount) / total) * 100) : 0; const options = { chart: { type: 'radialBar', height: 300 }, series: [netScore], plotOptions: { radialBar: { startAngle: -135, endAngle: 135, hollow: { size: '70%' }, dataLabels: { name: { show: false }, value: { show: true, fontSize: '36px', formatter: val => `${val}` } } } }, stroke: { lineCap: 'round' }, colors: [netScore >= 0 ? 'var(--sa-positive-color)' : 'var(--sa-negative-color)'] }; if(overallChart) overallChart.destroy(); document.querySelector("#sa-overall-chart").innerHTML = ''; overallChart = new ApexCharts(document.querySelector("#sa-overall-chart"), options); overallChart.render(); }; const renderTrendChart = () => { const last7Days = Array.from({ length: 7 }, (_, i) => { const d = new Date('2025-07-09'); d.setDate(d.getDate() - i); return d.toISOString().split('T')[0]; }).reverse(); const positiveTrend = last7Days.map(day => feedbackData.filter(f => f.date === day && f.sentiment === 'Positive').length); const negativeTrend = last7Days.map(day => feedbackData.filter(f => f.date === day && f.sentiment === 'Negative').length); const options = { chart: { type: 'area', height: 300, toolbar: { show: false }, stacked:true }, series: [ { name: 'Positive', data: positiveTrend }, { name: 'Negative', data: negativeTrend } ], xaxis: { type: 'datetime', categories: last7Days }, dataLabels: { enabled: false }, stroke: { curve: 'smooth', width: 2 }, colors: ['var(--sa-positive-color)', 'var(--sa-negative-color)'], legend: { position: 'top' } }; if(trendChart) trendChart.destroy(); document.querySelector("#sa-trend-chart").innerHTML = ''; trendChart = new ApexCharts(document.querySelector("#sa-trend-chart"), options); trendChart.render(); }; const renderTopicChart = () => { const sentimentByTopic = feedbackData.reduce((acc, f) => { if (f.sentiment === 'Neutral') return acc; if (!acc[f.topic]) acc[f.topic] = { positive: 0, negative: 0 }; if (f.sentiment === 'Positive') acc[f.topic].positive++; else if (f.sentiment === 'Negative') acc[f.topic].negative++; return acc; }, {}); const netScores = Object.entries(sentimentByTopic).map(([topic, counts]) => { const total = counts.positive + counts.negative; const score = total > 0 ? ((counts.positive - counts.negative) / total) * 100 : 0; return { x: topic, y: score.toFixed(0) }; }); const options = { chart: { type: 'bar', height: 350, toolbar: { show: false } }, series: [{ name: 'Net Score', data: netScores.map(d => d.y) }], xaxis: { categories: netScores.map(d => d.x) }, plotOptions: { bar: { horizontal: true, distributed: true } }, colors: netScores.map(d => d.y >= 0 ? 'var(--sa-positive-color)' : 'var(--sa-negative-color)'), legend: { show: false } }; if(topicChart) topicChart.destroy(); document.querySelector("#sa-topic-chart").innerHTML = ''; topicChart = new ApexCharts(document.querySelector("#sa-topic-chart"), options); topicChart.render(); }; const renderSourceChart = () => { const feedbackBySource = feedbackData.reduce((acc, f) => { acc[f.source] = (acc[f.source] || 0) + 1; return acc; }, {}); const options = { chart: { type: 'donut', height: 350 }, series: Object.values(feedbackBySource), labels: Object.keys(feedbackBySource), legend: { position: 'bottom' } }; if(sourceChart) sourceChart.destroy(); document.querySelector("#sa-source-chart").innerHTML = ''; sourceChart = new ApexCharts(document.querySelector("#sa-source-chart"), options); sourceChart.render(); }; const renderFeedbackLog = () => { const tbody = document.getElementById('sa-log-tbody'); tbody.innerHTML = ''; feedbackData.sort((a,b)=> new Date(b.date) - new Date(a.date)).slice(0, 5).forEach(f => { const sentimentClass = `sentiment-${f.sentiment.toLowerCase()}`; tbody.innerHTML += `