${objStatus.text}
${Math.round(objProgress)}%
${krHtml || '
`;
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');
});
No key results for this objective.
'}