OKR Dashboard

Objectives and Key Results (OKR) Dashboard

Track progress towards your most important goals.

Overall Progress

0%

Objectives

0

On Track

0

At Risk

0

Objectives

Objective Progress

Status Distribution

Owner: ${obj.owner}

${objStatus.text}

${Math.round(objProgress)}%

${krHtml || '

No key results for this objective.

'}
`; okrListContainer.appendChild(objectiveElement); }); }; const renderObjectiveProgressChart = () => { const ctx = document.getElementById('objectiveProgressChart')?.getContext('2d'); if (!ctx) return; if (charts.objectiveProgressChart) charts.objectiveProgressChart.destroy(); charts.objectiveProgressChart = new Chart(ctx, { type: 'bar', data: { labels: objectives.map(o => o.title), datasets: [{ label: 'Progress', data: objectives.map(o => calculateObjectiveProgress(o)), backgroundColor: 'rgba(0, 115, 192, 0.7)', }] }, options: { indexAxis: 'y', responsive: true, maintainAspectRatio: false, plugins: { legend: { display: false } }, scales: { x: { beginAtZero: true, max: 100 } } } }); }; const renderStatusDistributionChart = () => { const ctx = document.getElementById('statusDistributionChart')?.getContext('2d'); if (!ctx) return; if (charts.statusDistributionChart) charts.statusDistributionChart.destroy(); const statuses = objectives.map(o => getStatus(calculateObjectiveProgress(o)).text); const statusCounts = { 'On Track': 0, 'At Risk': 0, 'Off Track': 0 }; statuses.forEach(s => statusCounts[s]++); charts.statusDistributionChart = new Chart(ctx, { type: 'doughnut', data: { labels: Object.keys(statusCounts), datasets: [{ data: Object.values(statusCounts), backgroundColor: ['#10b981', '#f59e0b', '#ef4444'], }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { position: 'bottom' } } } }); }; const renderDataConfig = () => { const parentObjectiveSelect = document.getElementById('parentObjective'); parentObjectiveSelect.innerHTML = objectives.length > 0 ? objectives.map(o => ``).join('') : ''; }; // --- EVENT HANDLERS --- document.getElementById('add-objective-form').addEventListener('submit', (e) => { e.preventDefault(); const form = e.target; objectives.push({ id: Date.now(), title: form.objectiveTitle.value, owner: form.objectiveOwner.value, keyResults: [] }); form.reset(); renderAll(); }); document.getElementById('add-kr-form').addEventListener('submit', (e) => { e.preventDefault(); const form = e.target; const parentId = parseInt(form.parentObjective.value); const parentObj = objectives.find(o => o.id === parentId); if (parentObj) { parentObj.keyResults.push({ id: Date.now(), title: form.krTitle.value, start: parseFloat(form.krStart.value), current: parseFloat(form.krCurrent.value), target: parseFloat(form.krTarget.value) }); form.reset(); form.krStart.value = 0; form.krCurrent.value = 0; renderAll(); alert('Key Result added successfully!'); } else { alert('Could not find the selected objective.'); } }); if (pdfDownloadBtn) { pdfDownloadBtn.addEventListener('click', function () { const { jsPDF } = window.jspdf; const doc = new jsPDF({ orientation: 'p', unit: 'pt', format: 'a4' }); const contentToPrint = document.getElementById('dashboard-content'); if (!contentToPrint) return; html2canvas(contentToPrint, { scale: 2, useCORS: true, onclone: (doc) => { Chart.defaults.animation = false; } }) .then(canvas => { Chart.defaults.animation = true; const imgData = canvas.toDataURL('image/png'); const imgProps = doc.getImageProperties(imgData); const pdfWidth = doc.internal.pageSize.getWidth() - 40; const pdfHeight = (imgProps.height * pdfWidth) / imgProps.width; doc.text(`OKR Dashboard Report - ${new Date().toLocaleDateString()}`, 20, 20); doc.addImage(imgData, 'PNG', 20, 40, pdfWidth, pdfHeight); doc.save(`okr-dashboard-report.pdf`); }).catch(err => { Chart.defaults.animation = true; console.error("PDF generation error:", err); }); }); } // --- INITIALIZATION --- renderAll(); showTab('dashboard'); });
Scroll to Top