Customer Education Dashboard
Key Education Performance Indicators
Education Insights
No insights generated yet. Please configure your data.
Data Configuration
Define your customer education metrics, targets, and actuals. Click 'Add Metric' to add more rows.
Customer Education Dashboard Report
Key Education Performance Indicators
Education Insights
No specific insights generated. Ensure your metrics have valid target and actual values.
'; } } educationInsights.innerHTML = insightsHtml; } // --- Event Handlers (Globally accessible for onclick attributes) --- /** * Switches between dashboard and data configuration tabs. * @param {string} tabName - 'dashboard' or 'dataConfig'. */ window.switchTab = function(tabName) { // Guard clauses for elements if (!dashboardTabBtn || !dataConfigTabBtn || !dashboardTabContent || !dataConfigTabContent || !prevTabBtn || !nextTabBtn) { console.error("One or more tab elements not found."); return; } currentTab = tabName; // Update button styles dashboardTabBtn.classList.remove('active'); dataConfigTabBtn.classList.remove('active'); dashboardTabContent.classList.add('hidden'); dataConfigTabContent.classList.add('hidden'); if (tabName === 'dashboard') { dashboardTabBtn.classList.add('active'); dashboardTabContent.classList.remove('hidden'); prevTabBtn.disabled = true; nextTabBtn.disabled = false; renderAllKpiCards(); // Re-render dashboard content when tab is active generateEducationInsights(); // Re-generate insights } else if (tabName === 'dataConfig') { dataConfigTabBtn.classList.add('active'); dataConfigTabContent.classList.remove('hidden'); prevTabBtn.disabled = false; nextTabBtn.disabled = true; renderAllMetricInputRows(); // Re-render input rows when tab is active } }; /** * Navigates between tabs using 'Next' and 'Previous' buttons. * @param {string} direction - 'prev' or 'next'. */ window.navigateTabs = function(direction) { if (direction === 'next') { if (currentTab === 'dashboard') { switchTab('dataConfig'); } } else if (direction === 'prev') { if (currentTab === 'dataConfig') { switchTab('dashboard'); } } }; /** * Adds a new empty metric row to the data configuration. */ if (addMetricBtn) { // Null check for addMetricBtn addMetricBtn.onclick = function() { const newMetricId = generateUniqueId(); const newMetric = { id: newMetricId, name: '', target: null, actual: null, unit: '' }; educationMetrics.push(newMetric); renderMetricInputRow(newMetric); showMessage('New metric row added.', 'info'); }; } /** * Updates a specific property of a metric in the educationMetrics array. * This function is called directly from input fields' oninput. * @param {string} id - The ID of the metric to update. * @param {string} property - The property to update ('name', 'target', 'actual', 'unit'). * @param {*} value - The new value for the property. */ window.updateMetricValue = function(id, property, value) { const metricIndex = educationMetrics.findIndex(m => m.id === id); if (metricIndex !== -1) { educationMetrics[metricIndex][property] = value; } }; /** * Removes a metric from the educationMetrics array and its corresponding input row. * @param {string} id - The ID of the metric to remove. */ window.removeMetric = function(id) { educationMetrics = educationMetrics.filter(m => m.id !== id); const metricRow = document.getElementById(`metric-row-${id}`); if (metricRow) { metricRow.remove(); showMessage('Metric removed successfully.', 'success'); } else { console.warn(`Metric row with ID ${id} not found for removal.`); } }; /** * Saves the data (which is already updated via oninput) and refreshes the dashboard. */ if (saveDataBtn) { // Null check for saveDataBtn saveDataBtn.onclick = function() { // Filter out any metrics that are completely empty or invalid educationMetrics = educationMetrics.filter(metric => metric.name.trim() !== '' || (typeof metric.target === 'number' && !isNaN(metric.target)) || (typeof metric.actual === 'number' && !isNaN(metric.actual)) ); // Ensure all numerical values are indeed numbers educationMetrics.forEach(metric => { metric.target = parseFloat(metric.target) || 0; metric.actual = parseFloat(metric.actual) || 0; }); renderAllKpiCards(); // Re-render dashboard generateEducationInsights(); // Re-generate insights showMessage('Data saved and dashboard updated!', 'success'); }; } /** * Handles the PDF download functionality. */ if (downloadPdfBtn) { // Null check for downloadPdfBtn downloadPdfBtn.onclick = async function() { if (!pdfContent || !pdfKpiMetrics || !pdfEducationInsights) { console.error("PDF content elements not found."); showMessage('Error: PDF content elements missing.', 'error'); return; } // Populate the hidden PDF content div with current dashboard data pdfKpiMetrics.innerHTML = `Key Education Performance Indicators
`; if (educationMetrics.length === 0) { pdfKpiMetrics.innerHTML += 'No KPI data available.
'; } else { let kpiTableHtml = `| Metric Name | Target Value | Actual Value | Unit | Progress (%) |
|---|---|---|---|---|
| ${metric.name || 'N/A'} | ${formatValue(metric.target, metric.unit)} | ${formatValue(metric.actual, metric.unit)} | ${metric.unit || 'N/A'} | ${progress.toFixed(2)}% |
No insights generated. Please configure your data.
'; } else { let overallProgress = 0; let metricsBelowTarget = []; let metricsAboveTarget = []; educationMetrics.forEach(metric => { const progress = calculateProgress(metric.actual, metric.target, metric.name); overallProgress += progress; if (metric.name.toLowerCase().includes('support ticket reduction')) { if (metric.actual < metric.target) { metricsBelowTarget.push({ name: metric.name, progress: progress.toFixed(2), type: 'reduction' }); } else { metricsAboveTarget.push({ name: metric.name, progress: progress.toFixed(2), type: 'reduction' }); } } else { if (progress < 100 && metric.actual < metric.target) { metricsBelowTarget.push({ name: metric.name, progress: progress.toFixed(2), type: 'direct' }); } else if (progress >= 100 && metric.actual >= metric.target) { metricsAboveTarget.push({ name: metric.name, progress: progress.toFixed(2), type: 'direct' }); } } }); const avgProgress = educationMetrics.length > 0 ? (overallProgress / educationMetrics.length).toFixed(2) : 0; insightsForPdf += `Overall Education Performance: The average progress across all defined metrics is ${avgProgress}%.
`; if (metricsAboveTarget.length > 0) { const directAbove = metricsAboveTarget.filter(m => m.type === 'direct').map(m => `${m.name} (${m.progress}%)`).join(', '); const reductionAbove = metricsAboveTarget.filter(m => m.type === 'reduction').map(m => `${m.name} (Achieved: ${formatValue(educationMetrics.find(em => em.name === m.name).actual, educationMetrics.find(em => em.name === m.name).unit)}, Target: ${formatValue(educationMetrics.find(em => em.name === m.name).target, educationMetrics.find(em => em.name === m.name).unit)})`).join(', '); if (directAbove) { insightsForPdf += `Strengths: Excellent performance observed in: ${directAbove}. Continue to build on these successful areas.
`; } if (reductionAbove) { insightsForPdf += `Positive Impact: Metrics showing strong positive impact (e.g., successful ticket reduction) include: ${reductionAbove}. This indicates effective education efforts.
`; } } if (metricsBelowTarget.length > 0) { const directBelow = metricsBelowTarget.filter(m => m.type === 'direct').map(m => `${m.name} (${m.progress}%)`).join(', '); const reductionBelow = metricsBelowTarget.filter(m => m.type === 'reduction').map(m => `${m.name} (Achieved: ${formatValue(educationMetrics.find(em => em.name === m.name).actual, educationMetrics.find(em => em.name === m.name).unit)}, Target: ${formatValue(educationMetrics.find(em => em.name === m.name).target, educationMetrics.find(em => em.name === m.name).unit)})`).join(', '); if (directBelow) { insightsForPdf += `Areas for Improvement: Metrics currently below target include: ${directBelow}. Focus efforts on these to improve educational outcomes.
`; } if (reductionBelow) { insightsForPdf += `Areas for Optimization: Metrics requiring further optimization (e.g., ticket reduction not meeting target) include: ${reductionBelow}. Review and refine education content or delivery for these areas.
`; } } insightsForPdf += `