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 |
|---|
Add/Edit Support Records
Manage Existing Records
| ID | Customer Name | Date | Channel | Issue Type | Resolution Status | Resolution Time (Hrs) | FCR? | CSAT Score | Notes | Actions |
|---|
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}
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 |
|---|
