Monthly Closing Dashboard

Monthly Closing for June 2024

Track tasks, monitor KPIs, and manage your month-end process.

Overall Progress

0%

Key Performance Indicators

Closing Checklist

Task Description Status

Notes & Observations

Customize KPIs

Customize Tasks

${valueFormatted}

`; kpiCardsContainer.innerHTML += card; }); }; const renderTasks = () => { taskListBody.innerHTML = ''; state.tasks.forEach(task => { const statusColors = { "Not Started": "bg-red-100 text-red-800", "In Progress": "bg-yellow-100 text-yellow-800", "Completed": "bg-green-100 text-green-800" }; const row = ` ${task.description} ${task.status} `; taskListBody.innerHTML += row; }); // Add event listeners to the new status dropdowns document.querySelectorAll('.status-select').forEach(select => { select.addEventListener('change', handleStatusChange); }); }; const updateProgress = () => { const completedTasks = state.tasks.filter(t => t.status === 'Completed').length; const totalTasks = state.tasks.length; const percentage = totalTasks > 0 ? Math.round((completedTasks / totalTasks) * 100) : 0; progressBar.style.width = `${percentage}%`; progressBar.textContent = `${percentage}%`; }; const renderNotes = () => { notesArea.value = state.notes; }; const renderKpiConfig = () => { kpiConfigContainer.innerHTML = ''; state.kpis.forEach((kpi, index) => { const configItem = `
${kpi.prefix || ''}
`; kpiConfigContainer.innerHTML += configItem; }); document.querySelectorAll('.kpi-config-input').forEach(input => { input.addEventListener('change', handleKpiChange); }); }; const renderTaskConfig = () => { taskConfigContainer.innerHTML = ''; state.tasks.forEach((task, index) => { const configItem = `
`; taskConfigContainer.innerHTML += configItem; }); document.querySelectorAll('.task-config-input').forEach(input => { input.addEventListener('change', handleTaskDescriptionChange); }); document.querySelectorAll('.remove-task-btn').forEach(button => { button.addEventListener('click', handleRemoveTask); }); }; // --- EVENT HANDLERS --- const handleStatusChange = (e) => { const taskId = parseInt(e.target.dataset.taskId, 10); const newStatus = e.target.value; const task = state.tasks.find(t => t.id === taskId); if (task) { task.status = newStatus; renderTasks(); // Re-render to update colors updateProgress(); } }; const handleTitleChange = (e) => { state.title = e.target.textContent; }; const handleNotesChange = (e) => { state.notes = e.target.value; }; const handleKpiChange = (e) => { const kpiIndex = parseInt(e.target.dataset.kpiIndex, 10); const newValue = parseFloat(e.target.value); if (!isNaN(newValue)) { state.kpis[kpiIndex].value = newValue; renderKpis(); } }; const handleTaskDescriptionChange = (e) => { const taskIndex = parseInt(e.target.dataset.taskIndex, 10); state.tasks[taskIndex].description = e.target.value; renderTasks(); }; const handleRemoveTask = (e) => { const taskIndex = parseInt(e.currentTarget.dataset.taskIndex, 10); state.tasks.splice(taskIndex, 1); renderAll(); // Re-render everything after removal }; const handleAddTask = () => { const newId = state.tasks.length > 0 ? Math.max(...state.tasks.map(t => t.id)) + 1 : 1; state.tasks.push({ id: newId, description: "New Task", status: "Not Started" }); renderAll(); }; const handleDownloadPdf = () => { const { jsPDF } = window.jspdf; const pdfContent = document.getElementById('pdf-content'); // Temporarily show print-friendly status for capture const printOnlyElements = pdfContent.querySelectorAll('.print-only'); const noPrintElements = pdfContent.querySelectorAll('.no-print, .status-select'); printOnlyElements.forEach(el => el.style.display = 'inline-flex'); noPrintElements.forEach(el => el.style.display = 'none'); html2canvas(pdfContent, { scale: 2, // Higher scale for better quality useCORS: true, backgroundColor: '#ffffff' }).then(canvas => { // Revert visibility changes after capture printOnlyElements.forEach(el => el.style.display = 'none'); noPrintElements.forEach(el => el.style.display = ''); const imgData = canvas.toDataURL('image/png'); const pdf = new jsPDF({ orientation: 'p', unit: 'mm', format: 'a4' }); const pdfWidth = pdf.internal.pageSize.getWidth(); const pdfHeight = pdf.internal.pageSize.getHeight(); const canvasWidth = canvas.width; const canvasHeight = canvas.height; const ratio = canvasWidth / canvasHeight; const imgWidth = pdfWidth - 20; // with margin const imgHeight = imgWidth / ratio; let heightLeft = imgHeight; let position = 10; // top margin pdf.addImage(imgData, 'PNG', 10, position, imgWidth, imgHeight); heightLeft -= (pdfHeight - 20); while (heightLeft > 0) { position = -heightLeft - 10; pdf.addPage(); pdf.addImage(imgData, 'PNG', 10, position, imgWidth, imgHeight); heightLeft -= (pdfHeight - 20); } pdf.save(`${state.title.replace(/ /g, '_')}_Dashboard.pdf`); }).catch(err => { console.error("Error generating PDF:", err); // Revert visibility changes even if there's an error printOnlyElements.forEach(el => el.style.display = 'none'); noPrintElements.forEach(el => el.style.display = ''); }); }; // --- TABBING LOGIC --- let currentTabIndex = 0; const updateTabButtons = () => { prevTabBtn.disabled = currentTabIndex === 0; nextTabBtn.disabled = currentTabIndex === tabContents.length - 1; }; const switchTab = (index) => { tabButtons.forEach(btn => btn.classList.remove('active')); tabContents.forEach(content => content.classList.remove('active')); tabButtons[index].classList.add('active'); tabContents[index].classList.add('active'); currentTabIndex = index; updateTabButtons(); }; tabButtons.forEach((button, index) => { button.addEventListener('click', () => switchTab(index)); }); prevTabBtn.addEventListener('click', () => { if (currentTabIndex > 0) { switchTab(currentTabIndex - 1); } }); nextTabBtn.addEventListener('click', () => { if (currentTabIndex < tabContents.length - 1) { switchTab(currentTabIndex + 1); } }); // --- INITIALIZATION --- // Null checks for essential elements if (dashboardTitle && kpiCardsContainer && taskListBody && progressBar && notesArea && kpiConfigContainer && taskConfigContainer && addTaskBtn && downloadPdfBtn) { // Add event listeners dashboardTitle.addEventListener('blur', handleTitleChange); notesArea.addEventListener('change', handleNotesChange); addTaskBtn.addEventListener('click', handleAddTask); downloadPdfBtn.addEventListener('click', handleDownloadPdf); // Initial render renderAll(); updateTabButtons(); } else { console.error("One or more essential elements could not be found in the DOM."); } });
Scroll to Top