COBIT Dashboard

No objectives added yet. Use the form above to get started.

'; pdfActionContainer.style.display = 'none'; return; } pdfActionContainer.style.display = 'block'; objectives.forEach(obj => { const target = parseFloat(obj.targetValue) || 0; const current = parseFloat(obj.currentValue) || 0; let progress = target > 0 ? Math.max(0, Math.min(100, (current / target) * 100)) : 0; const card = document.createElement('div'); card.className = 'cobit-card'; card.dataset.id = obj.id; card.innerHTML = ` ${obj.cobitDomain}

${escapeHtml(obj.objectiveName)}

Metric: ${escapeHtml(obj.keyMetric)}

Values: ${current} / ${target}

${progress.toFixed(1)}%
`; dashboardOutput.appendChild(card); }); } /** * Handles form submission for adding or updating objectives. */ function handleFormSubmit(event) { event.preventDefault(); const id = objectiveIdInput.value; const newObjective = { objectiveName: objectiveNameInput.value.trim(), cobitDomain: cobitDomainInput.value, keyMetric: keyMetricInput.value.trim(), targetValue: parseFloat(targetValueInput.value), currentValue: parseFloat(currentValueInput.value) }; if (id) { // Update existing const index = objectives.findIndex(obj => obj.id === id); if (index !== -1) { objectives[index] = { ...objectives[index], ...newObjective }; } } else { // Add new newObjective.id = `obj_${Date.now()}`; objectives.push(newObjective); } form.reset(); objectiveIdInput.value = ''; formSubmitBtn.textContent = 'Add Objective'; formSubmitBtn.classList.add('primary'); formSubmitBtn.classList.remove('secondary'); renderDashboard(); } /** * Handles clicks on Edit and Delete buttons using event delegation. */ function handleDashboardClick(event) { const target = event.target; const card = target.closest('.cobit-card'); if (!card) return; const id = card.dataset.id; if (target.classList.contains('edit-btn')) { const objToEdit = objectives.find(obj => obj.id === id); if (objToEdit) { objectiveIdInput.value = objToEdit.id; objectiveNameInput.value = objToEdit.objectiveName; cobitDomainInput.value = objToEdit.cobitDomain; keyMetricInput.value = objToEdit.keyMetric; targetValueInput.value = objToEdit.targetValue; currentValueInput.value = objToEdit.currentValue; formSubmitBtn.textContent = 'Update Objective'; formSubmitBtn.classList.remove('primary'); formSubmitBtn.classList.add('secondary'); form.scrollIntoView({ behavior: 'smooth' }); objectiveNameInput.focus(); } } else if (target.classList.contains('delete-btn')) { if (confirm('Are you sure you want to delete this objective?')) { objectives = objectives.filter(obj => obj.id !== id); renderDashboard(); } } } /** * Generates and downloads a PDF report of the dashboard. */ function generatePdf() { const doc = new jsPDF(); doc.setFontSize(18); doc.text('COBIT Dashboard Report', 14, 22); doc.setFontSize(11); doc.setTextColor(100); doc.text(`Report generated on: ${new Date().toLocaleDateString()}`, 14, 30); const tableColumns = ['Objective', 'Domain', 'Metric', 'Target', 'Current', 'Progress (%)']; const tableRows = []; objectives.forEach(obj => { const target = parseFloat(obj.targetValue) || 0; const current = parseFloat(obj.currentValue) || 0; let progress = target > 0 ? Math.max(0, Math.min(100, (current / target) * 100)) : 0; const rowData = [ obj.objectiveName, obj.cobitDomain, obj.keyMetric, target, current, `${progress.toFixed(1)}%` ]; tableRows.push(rowData); }); doc.autoTable({ head: [tableColumns], body: tableRows, startY: 35, theme: 'grid', headStyles: { fillColor: [37, 99, 235] }, // blue-600 styles: { fontSize: 9, cellPadding: 2.5 }, columnStyles: { 0: { cellWidth: 'auto' }, // Objective 1: { cellWidth: 20 }, // Domain 2: { cellWidth: 'auto' }, // Metric 3: { halign: 'right' }, // Target 4: { halign: 'right' }, // Current 5: { halign: 'right' } // Progress } }); const pageCount = doc.internal.getNumberOfPages(); for(let i = 1; i <= pageCount; i++) { doc.setPage(i); doc.setFontSize(8); doc.text(`Page ${i} of ${pageCount}`, doc.internal.pageSize.width - 25, doc.internal.pageSize.height - 10); } doc.save('COBIT_Dashboard_Report.pdf'); } /** * Escapes HTML to prevent XSS. */ function escapeHtml(unsafe) { if (typeof unsafe !== 'string') return unsafe; return unsafe .replace(/&/g, "&") .replace(//g, ">") .replace(/"/g, """) .replace(/'/g, "'"); } // --- Event Listeners --- form.addEventListener('submit', handleFormSubmit); dashboardOutput.addEventListener('click', handleDashboardClick); downloadPdfBtn.addEventListener('click', generatePdf); // --- Initial Render --- renderDashboard(); });
Scroll to Top