Customer Wait Times Dashboard

Customer Wait Times Dashboard

Wait Times Summary

All Wait Time Records

Record ID Service Point Date Time of Day Customers in Queue Avg. Wait Time (min) Resolution Time (min) Status

${item.title}

${item.value}

`; dashboardSummary.appendChild(card); }); // Populate wait times table (sorted by date descending) const sortedWaitTimeData = [...waitTimeData].sort((a, b) => new Date(b.date) - new Date(a.date)); sortedWaitTimeData.forEach(item => { const row = waitTimesTableBody.insertRow(); row.innerHTML = ` ${item.recordId} ${item.servicePoint} ${item.date} ${item.timeOfDay} ${item.customersInQueue} ${item.averageWaitTimeMinutes.toFixed(1)} ${item.resolutionTimeMinutes.toFixed(1)} ${item.status} `; }); } // --- Data Configuration Table Rendering and Editing --- /** * Renders the data in the configuration table. */ function renderConfigTable() { // Null check for config data table body if (!configDataTableBody) { console.error("Config data table body not found."); return; } configDataTableBody.innerHTML = ''; // Clear existing rows waitTimeData.forEach((item, index) => { const row = configDataTableBody.insertRow(); row.innerHTML = ` ${item.recordId} ${item.servicePoint} ${item.date} ${item.timeOfDay} ${item.customersInQueue} ${item.averageWaitTimeMinutes.toFixed(1)} ${item.resolutionTimeMinutes.toFixed(1)} ${item.status} `; }); } /** * Adds a new row or updates an existing row in the wait time data. */ window.addRow = function() { // Null checks for input fields if (!recordIdInput || !servicePointSelect || !recordDateInput || !timeOfDaySelect || !customersInQueueInput || !averageWaitTimeMinutesInput || !resolutionTimeMinutesInput || !recordStatusSelect) { console.error("One or more input fields not found."); return; } const newRecord = { recordId: recordIdInput.value.trim(), servicePoint: servicePointSelect.value.trim(), date: recordDateInput.value.trim(), timeOfDay: timeOfDaySelect.value.trim(), customersInQueue: parseInt(customersInQueueInput.value) || 0, averageWaitTimeMinutes: parseFloat(averageWaitTimeMinutesInput.value) || 0, resolutionTimeMinutes: parseFloat(resolutionTimeMinutesInput.value) || 0, status: recordStatusSelect.value.trim() }; // Simple validation if (!newRecord.recordId || !newRecord.servicePoint || !newRecord.date || newRecord.customersInQueue < 0 || newRecord.averageWaitTimeMinutes < 0 || newRecord.resolutionTimeMinutes < 0) { showModal("Please fill in all required fields (Record ID, Service Point, Date, positive numbers for metrics) with valid values."); return; } // Check for duplicate Record ID if adding a new row if (editingIndex === -1) { const isDuplicate = waitTimeData.some(record => record.recordId === newRecord.recordId); if (isDuplicate) { showModal(`Record ID "${newRecord.recordId}" already exists. Please use a unique ID.`); return; } waitTimeData.push(newRecord); } else { // Update existing row waitTimeData[editingIndex] = newRecord; editingIndex = -1; // Reset editing state updateRowButton.classList.add('hidden'); cancelEditButton.classList.add('hidden'); addRowButton.classList.remove('hidden'); recordIdInput.disabled = false; // Re-enable record ID for new entries } clearForm(); renderConfigTable(); updateDashboard(); // Keep dashboard in sync }; /** * Populates the form with data from the row to be edited. * @param {HTMLElement} button - The edit button clicked. */ window.editRow = function(button) { // Null checks for input fields and edit buttons if (!recordIdInput || !servicePointSelect || !recordDateInput || !timeOfDaySelect || !customersInQueueInput || !averageWaitTimeMinutesInput || !resolutionTimeMinutesInput || !recordStatusSelect || !addRowButton || !updateRowButton || !cancelEditButton) { console.error("One or more form/button elements not found for editing."); return; } const index = parseInt(button.dataset.index); editingIndex = index; const item = waitTimeData[index]; recordIdInput.value = item.recordId; servicePointSelect.value = item.servicePoint; recordDateInput.value = item.date; timeOfDaySelect.value = item.timeOfDay; customersInQueueInput.value = item.customersInQueue; averageWaitTimeMinutesInput.value = item.averageWaitTimeMinutes; resolutionTimeMinutesInput.value = item.resolutionTimeMinutes; recordStatusSelect.value = item.status; addRowButton.classList.add('hidden'); updateRowButton.classList.remove('hidden'); cancelEditButton.classList.remove('hidden'); recordIdInput.disabled = true; // Disable editing Record ID during update }; /** * Deletes a row from the wait time data. * @param {HTMLElement} button - The delete button clicked. */ window.deleteRow = function(button) { const index = parseInt(button.dataset.index); showModal("Are you sure you want to delete this wait time record?", true, (confirmed) => { if (confirmed) { waitTimeData.splice(index, 1); renderConfigTable(); updateDashboard(); // Keep dashboard in sync clearForm(); // Clear form if the deleted item was being edited editingIndex = -1; updateRowButton.classList.add('hidden'); cancelEditButton.classList.add('hidden'); addRowButton.classList.remove('hidden'); recordIdInput.disabled = false; // Re-enable record ID } }); }; /** * Clears the input form fields. */ function clearForm() { // Null checks for input fields if (!recordIdInput || !servicePointSelect || !recordDateInput || !timeOfDaySelect || !customersInQueueInput || !averageWaitTimeMinutesInput || !resolutionTimeMinutesInput || !recordStatusSelect) { console.error("One or more input fields not found for clearing."); return; } recordIdInput.value = ''; servicePointSelect.value = ''; // Reset to default "Select Service Point" recordDateInput.value = ''; timeOfDaySelect.value = 'Morning'; // Reset to default customersInQueueInput.value = ''; averageWaitTimeMinutesInput.value = ''; resolutionTimeMinutesInput.value = ''; recordStatusSelect.value = 'Resolved'; // Reset to default status } /** * Cancels the current edit operation and clears the form. */ window.cancelEdit = function() { editingIndex = -1; clearForm(); updateRowButton.classList.add('hidden'); cancelEditButton.classList.add('hidden'); addRowButton.classList.remove('hidden'); recordIdInput.disabled = false; // Re-enable record ID }; // --- PDF Download Functionality --- /** * Downloads the dashboard content as a PDF. */ window.downloadPdf = async function() { // Null check for dashboard content if (!dashboardTabContent) { console.error("Dashboard content for PDF not found."); return; } // Temporarily show the dashboard content if it's hidden, for accurate PDF generation const wasHidden = dashboardTabContent.classList.contains('hidden'); if (wasHidden) { dashboardTabContent.classList.remove('hidden'); } const { jsPDF } = window.jspdf; const doc = new jsPDF('p', 'pt', 'letter'); // 'p' for portrait, 'pt' for points, 'letter' size const element = dashboardTabContent; // The element to convert to PDF // Use html2canvas to render the HTML element to a canvas await html2canvas(element, { scale: 2, // Increase scale for better quality useCORS: true, // Important for images/external resources if any (though we don't use them here) logging: false // Disable logging for cleaner console }).then(canvas => { const imgData = canvas.toDataURL('image/png'); const imgWidth = 550; // A fixed width for the image in PDF const pageHeight = doc.internal.pageSize.height; const imgHeight = (canvas.height * imgWidth) / canvas.width; let heightLeft = imgHeight; let position = 30; // Initial Y position for content doc.setFontSize(22); doc.setTextColor(31, 41, 55); // Gray 900 equivalent doc.text("Customer Wait Times Dashboard", doc.internal.pageSize.getWidth() / 2, position, { align: "center" }); position += 30; // Move down for image doc.addImage(imgData, 'PNG', 30, position, imgWidth, imgHeight); heightLeft -= pageHeight; // If content overflows, add new pages while (heightLeft >= 0) { position = heightLeft - imgHeight + 30; // Adjust position for new page doc.addPage(); doc.addImage(imgData, 'PNG', 30, position, imgWidth, imgHeight); heightLeft -= pageHeight; } doc.save('Customer_Wait_Times_Dashboard.pdf'); }); // Restore hidden state if it was hidden initially if (wasHidden) { dashboardTabContent.classList.add('hidden'); } }; // --- Event Listeners --- if (addRowButton) { addRowButton.addEventListener('click', addRow); } else { console.error("Add Row Button not found."); } if (updateRowButton) { updateRowButton.addEventListener('click', addRow); // addRow function handles both add and update based on editingIndex } else { console.error("Update Row Button not found."); } if (cancelEditButton) { cancelEditButton.addEventListener('click', cancelEdit); } else { console.error("Cancel Edit Button not found."); } // Initial rendering and setup openTab('dashboard'); // Open dashboard by default updateDashboard(); // Populate dashboard with initial data renderConfigTable(); // Populate config table with initial data });
Scroll to Top