Critical Blockers
${openBlockers.filter(b=>b.priority==='Critical').length}
Avg. Blocker Age
${avgAge.toFixed(1)} Days
Top Blocker Owner
${topOwner}
`;
}
const renderChart = (id, type, data, options) => {
const ctx = document.getElementById(id)?.getContext('2d'); if(!ctx) return;
if(charts[id]) charts[id].destroy();
charts[id] = new Chart(ctx, { type, data, options });
};
function renderCharts(openBlockers) {
const byPriority = openBlockers.reduce((acc,b)=>{acc[b.priority]=(acc[b.priority]||0)+1; return acc;},{});
renderChart('blocker-priority-chart', 'doughnut', { labels: Object.keys(byPriority), datasets: [{ data: Object.values(byPriority), backgroundColor: ['#dc2626','#f97316','#facc15','#3b82f6'] }] }, {responsive:true, maintainAspectRatio:false});
const byProject = openBlockers.reduce((acc,b)=>{acc[b.project]=(acc[b.project]||0)+1; return acc;},{});
renderChart('blocker-project-chart', 'bar', { labels: Object.keys(byProject), datasets: [{ label: '# of Blockers', data: Object.values(byProject), backgroundColor: 'rgba(124, 58, 237, 0.7)'}] }, {responsive:true, maintainAspectRatio:false});
}
function renderKanbanBoard(allBlockers) {
const columns = { Open: '', 'In Progress': '', Resolved: '' };
allBlockers.forEach(b => {
if(columns[b.status] !== undefined) {
columns[b.status] += `
${b.desc}
${b.project} | Owner: ${b.owner}`;
}
});
document.getElementById('kanban-open').innerHTML = columns['Open'];
document.getElementById('kanban-in-progress').innerHTML = columns['In Progress'];
document.getElementById('kanban-resolved').innerHTML = columns['Resolved'];
}
function addBlocker() {
blockers.push({
id: Date.now(),
desc: document.getElementById('add-desc').value,
project: document.getElementById('add-proj').value,
owner: 'Unassigned',
priority: document.getElementById('add-priority').value,
status: 'Open',
date: new Date().toISOString().split('T')[0]
});
render();
}
function blockerDownloadPDF() {
html2canvas(document.getElementById('blocker-pdf-content'), {scale: 2}).then(canvas => {
const pdf = new jspdf.jsPDF({orientation: 'landscape', unit: 'pt', format: 'a4'});
pdf.addImage(canvas.toDataURL('image/png'), 'PNG', 40, 40, pdf.internal.pageSize.getWidth() - 80, 0);
pdf.save('Project_Blocker_Dashboard.pdf');
});
}
initialize();
});