Customer Support Channel Performance Dashboard

Customer Support Channel Performance Dashboard

Overall Support Performance Summary

Total Tickets

0

Avg. Resolution Time (Hours)

0

First Contact Resolution Rate

0%

Avg. CSAT Score

0

Tickets by Channel

Avg. Resolution Time by Channel (Hours)

FCR Rate by Channel

CSAT by Channel

Detailed Support Records

ID Customer Name Date Channel Issue Type Resolution Status Resolution Time (Hrs) FCR? CSAT Score Notes

No data to display.

'; return; } // Sort data by value descending for general charts, custom for specific types let sortedData = Object.entries(data); if (valueType === 'csat_score') { // Sort CSAT scores by value (e.g., higher CSAT first) sortedData.sort(([, valA], [, valB]) => valB - valA); } else { sortedData.sort(([, valA], [, valB]) => valB - valA); } // Determine the maximum value for scaling bars const maxValue = Math.max(...Object.values(data), 1); // Avoid division by zero if all values are 0 sortedData.forEach(([label, value]) => { const barWidth = (value / maxValue) * 100; // Scale based on max value for visual representation const percentageOfTotal = totalValue > 0 ? ((value / totalValue) * 100).toFixed(1) : 0; let displayValue; let barClass = 'bg-blue-300'; // Default bar color if (valueType === 'hours') { displayValue = `${value.toFixed(1)} Hrs`; } else if (valueType === 'percentage') { displayValue = `${value.toFixed(1)}%`; } else if (valueType === 'csat_score') { displayValue = `${value.toFixed(1)}`; // CSAT scores are averages if (value >= 4) barClass = 'csat-positive'; else if (value === 3) barClass = 'csat-neutral'; else if (value <= 2) barClass = 'csat-negative'; } else { // 'count' displayValue = `${value} Tickets`; } const barWrapper = document.createElement('div'); barWrapper.classList.add('chart-bar-wrapper'); barWrapper.innerHTML = ` ${label}:
${displayValue} (${percentageOfTotal}%)
`; targetElement.appendChild(barWrapper); }); } /** * Calculates and renders all distribution charts. */ function renderDistributionCharts() { // Tickets by Channel const ticketsByChannel = supportRecords.reduce((acc, record) => { acc[record.channel] = (acc[record.channel] || 0) + 1; return acc; }, {}); renderBarChart(ticketsByChannelChartElem, ticketsByChannel, 'count'); // Avg. Resolution Time by Channel const resolutionTimeByChannelSum = {}; const resolutionTimeByChannelCount = {}; supportRecords.forEach(record => { if (record.resolutionStatus === 'Resolved') { resolutionTimeByChannelSum[record.channel] = (resolutionTimeByChannelSum[record.channel] || 0) + record.resolutionTime; resolutionTimeByChannelCount[record.channel] = (resolutionTimeByChannelCount[record.channel] || 0) + 1; } }); const avgResolutionTimeByChannel = {}; for (const channel in resolutionTimeByChannelSum) { if (resolutionTimeByChannelCount[channel] > 0) { avgResolutionTimeByChannel[channel] = resolutionTimeByChannelSum[channel] / resolutionTimeByChannelCount[channel]; } else { avgResolutionTimeByChannel[channel] = 0; } } renderBarChart(avgResolutionTimeByChannelChartElem, avgResolutionTimeByChannel, 'hours'); // FCR Rate by Channel const fcrByChannelCount = {}; const totalTicketsByChannel = {}; supportRecords.forEach(record => { totalTicketsByChannel[record.channel] = (totalTicketsByChannel[record.channel] || 0) + 1; if (record.fcr === 'Yes') { fcrByChannelCount[record.channel] = (fcrByChannelCount[record.channel] || 0) + 1; } }); const fcrRateByChannel = {}; for (const channel in totalTicketsByChannel) { if (totalTicketsByChannel[channel] > 0) { fcrRateByChannel[channel] = (fcrByChannelCount[channel] || 0) / totalTicketsByChannel[channel] * 100; } else { fcrRateByChannel[channel] = 0; } } renderBarChart(fcrRateByChannelChartElem, fcrRateByChannel, 'percentage'); // CSAT by Channel const csatByChannelSum = {}; const csatByChannelCount = {}; supportRecords.forEach(record => { csatByChannelSum[record.channel] = (csatByChannelSum[record.channel] || 0) + record.csatScore; csatByChannelCount[record.channel] = (csatByChannelCount[record.channel] || 0) + 1; }); const avgCSATByChannel = {}; for (const channel in csatByChannelSum) { if (csatByChannelCount[channel] > 0) { avgCSATByChannel[channel] = csatByChannelSum[channel] / csatByChannelCount[channel]; } else { avgCSATByChannel[channel] = 0; } } renderBarChart(csatByChannelChartElem, avgCSATByChannel, 'csat_score'); } /** * Generates a PDF of the dashboard content. * Excludes non-essential UI elements like buttons and input forms. */ function generatePdf() { // Create a temporary div to hold only the content for PDF const pdfContentWrapper = document.createElement('div'); pdfContentWrapper.classList.add('pdf-content-wrapper'); // Apply PDF-specific styles // Add title const title = document.createElement('h2'); title.textContent = 'Customer Support Channel Performance Report'; title.classList.add('text-2xl', 'font-bold', 'mb-4'); pdfContentWrapper.appendChild(title); // Add summary cards const summarySection = document.createElement('div'); summarySection.classList.add('grid', 'grid-cols-4', 'gap-4', 'mb-6'); summarySection.innerHTML = `

Total Tickets

${totalTicketsCountElem.textContent}

Avg. Resolution Time (Hours)

${avgResolutionTimeElem.textContent}

First Contact Resolution Rate

${fcrRateElem.textContent}

Avg. CSAT Score

${avgCSATScoreElem.textContent}

`; pdfContentWrapper.appendChild(summarySection); // Add distribution charts const chartsSection = document.createElement('div'); chartsSection.classList.add('grid', 'grid-cols-2', 'gap-6', 'mb-6'); chartsSection.innerHTML = `

Tickets by Channel

Avg. Resolution Time by Channel (Hours)

FCR Rate by Channel

CSAT by Channel

`; pdfContentWrapper.appendChild(chartsSection); // Populate PDF charts (re-use logic but target PDF elements) const ticketsByChannel = supportRecords.reduce((acc, record) => { acc[record.channel] = (acc[record.channel] || 0) + 1; return acc; }, {}); renderBarChart(chartsSection.querySelector('#pdfTicketsByChannelChart'), ticketsByChannel, 'count'); const resolutionTimeByChannelSum = {}; const resolutionTimeByChannelCount = {}; supportRecords.forEach(record => { if (record.resolutionStatus === 'Resolved') { resolutionTimeByChannelSum[record.channel] = (resolutionTimeByChannelSum[record.channel] || 0) + record.resolutionTime; resolutionTimeByChannelCount[record.channel] = (resolutionTimeByChannelCount[record.channel] || 0) + 1; } }); const avgResolutionTimeByChannel = {}; for (const channel in resolutionTimeByChannelSum) { if (resolutionTimeByChannelCount[channel] > 0) { avgResolutionTimeByChannel[channel] = resolutionTimeByChannelSum[channel] / resolutionTimeByChannelCount[channel]; } else { avgResolutionTimeByChannel[channel] = 0; } } renderBarChart(chartsSection.querySelector('#pdfAvgResolutionTimeByChannelChart'), avgResolutionTimeByChannel, 'hours'); const fcrByChannelCount = {}; const totalTicketsByChannel = {}; supportRecords.forEach(record => { totalTicketsByChannel[record.channel] = (totalTicketsByChannel[record.channel] || 0) + 1; if (record.fcr === 'Yes') { fcrByChannelCount[record.channel] = (fcrByChannelCount[record.channel] || 0) + 1; } }); const fcrRateByChannel = {}; for (const channel in totalTicketsByChannel) { if (totalTicketsByChannel[channel] > 0) { fcrRateByChannel[channel] = (fcrByChannelCount[channel] || 0) / totalTicketsByChannel[channel] * 100; } else { fcrRateByChannel[channel] = 0; } } renderBarChart(chartsSection.querySelector('#pdfFcrRateByChannelChart'), fcrRateByChannel, 'percentage'); const csatByChannelSum = {}; const csatByChannelCount = {}; supportRecords.forEach(record => { csatByChannelSum[record.channel] = (csatByChannelSum[record.channel] || 0) + record.csatScore; csatByChannelCount[record.channel] = (csatByChannelCount[record.channel] || 0) + 1; }); const avgCSATByChannel = {}; for (const channel in csatByChannelSum) { if (csatByChannelCount[channel] > 0) { avgCSATByChannel[channel] = csatByChannelSum[channel] / csatByChannelCount[channel]; } else { avgCSATByChannel[channel] = 0; } } renderBarChart(chartsSection.querySelector('#pdfCsatByChannelChart'), avgCSATByChannel, 'csat_score'); // Add detailed support records list table const supportListSection = document.createElement('div'); supportListSection.innerHTML = `

Detailed Support Records

${dashboardSupportTableBody.innerHTML}
ID Customer Name Date Channel Issue Type Resolution Status Resolution Time (Hrs) FCR? CSAT Score Notes
`; pdfContentWrapper.appendChild(supportListSection); // Options for html2pdf const options = { margin: 10, filename: 'Customer_Support_Dashboard.pdf', image: { type: 'jpeg', quality: 0.98 }, html2canvas: { scale: 2, logging: true, dpi: 192, letterRendering: true }, jsPDF: { unit: 'mm', format: 'a4', orientation: 'portrait' }, pagebreak: { mode: ['avoid-all', 'css', 'legacy'] } }; // Generate PDF from the temporary content wrapper html2pdf().from(pdfContentWrapper).set(options).save(); // Clean up the temporary div (optional, as it's not appended to the DOM) pdfContentWrapper.remove(); } /** * Loads initial sample data for the dashboard. * Relevant to USA context. */ function loadSampleData() { supportRecords = [ { id: 'TKT001', customerName: 'Alice Brown', recordDate: '2024-07-01', channel: 'Phone', issueType: 'Billing Inquiry', resolutionStatus: 'Resolved', resolutionTime: 0.5, fcr: 'Yes', csatScore: 5, notes: 'Customer satisfied with quick resolution.' }, { id: 'TKT002', customerName: 'Bob White', recordDate: '2024-07-01', channel: 'Email', issueType: 'Technical Issue', resolutionStatus: 'Resolved', resolutionTime: 4.2, fcr: 'No', csatScore: 4, notes: 'Required backend team assistance.' }, { id: 'TKT003', customerName: 'Charlie Green', recordDate: '2024-07-02', channel: 'Chat', issueType: 'Feature Request', resolutionStatus: 'Open', resolutionTime: 1.0, fcr: 'No', csatScore: 3, notes: 'Forwarded to product team.' }, { id: 'TKT004', customerName: 'Diana Prince', recordDate: '2024-07-02', channel: 'Phone', issueType: 'Account Access', resolutionStatus: 'Resolved', resolutionTime: 0.3, fcr: 'Yes', csatScore: 5, notes: 'Password reset successful.' }, { id: 'TKT005', customerName: 'Ethan Hunt', recordDate: '2024-07-03', channel: 'Social Media', issueType: 'Product Complaint', resolutionStatus: 'Escalated', resolutionTime: 2.0, fcr: 'No', csatScore: 2, notes: 'Negative public comment, escalated to PR.' }, { id: 'TKT006', customerName: 'Fiona Davis', recordDate: '2024-07-03', channel: 'Email', issueType: 'Refund Request', resolutionStatus: 'Resolved', resolutionTime: 1.5, fcr: 'Yes', csatScore: 4, notes: 'Refund processed as per policy.' }, { id: 'TKT007', customerName: 'George Wilson', recordDate: '2024-07-04', channel: 'Phone', issueType: 'Service Interruption', resolutionStatus: 'Resolved', resolutionTime: 0.8, fcr: 'Yes', csatScore: 5, notes: 'Service restored, customer happy.' }, { id: 'TKT008', customerName: 'Hannah Miller', recordDate: '2024-07-04', channel: 'Chat', issueType: 'Login Issue', resolutionStatus: 'Resolved', resolutionTime: 0.2, fcr: 'Yes', csatScore: 5, notes: 'Quick fix provided.' }, { id: 'TKT009', customerName: 'Ivan Rodriguez', recordDate: '2024-07-05', channel: 'Email', issueType: 'Bug Report', resolutionStatus: 'Open', resolutionTime: 3.0, fcr: 'No', csatScore: 3, notes: 'Detailed bug report, sent to dev team.' } ]; }
Scroll to Top