Tasks In Progress
${tasks.filter(t=>t.status==='In Progress').length}
Tasks Overdue
${overdueTasks.length}
`;
// Charts
renderProjectChart();
renderWorkloadChart();
// Upcoming Deadlines
const sevenDaysFromNow = new Date(today.getTime() + 7 * 864e5);
const upcoming = openTasks.filter(t => new Date(t.due) >= today && new Date(t.due) <= sevenDaysFromNow).sort((a,b) => a.due - b.due);
document.getElementById('fm-deadline-list').innerHTML = upcoming.map(t => `
${t.desc} ${new Date(t.due).toLocaleDateString()}`).join('') || '
No deadlines in the next 7 days.';
}
function renderManagementUI() {
// Tables
document.getElementById('fm-projects-tbody').innerHTML = projects.map(p => `
| ${p.name} |
`).join('');
document.getElementById('fm-team-tbody').innerHTML = teamMembers.map(m => `
| ${m.name} |
`).join('');
document.getElementById('fm-tasks-tbody').innerHTML = tasks.map(t => `
| ${t.desc} | ${projects.find(p=>p.id===t.projId).name} | ${teamMembers.find(m=>m.id===t.assigneeId).name} | ${t.status} | |
`).join('');
// Selects
document.getElementById('add-task-proj').innerHTML = projects.map(p => `
`).join('');
document.getElementById('add-task-assignee').innerHTML = teamMembers.map(m => `
`).join('');
}
// --- CHARTING FUNCTIONS ---
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 renderProjectChart() {
const statusByProject = projects.map(p => {
const projectTasks = tasks.filter(t => t.projId === p.id);
return {
ToDo: projectTasks.filter(t => t.status === 'To Do').length,
InProgress: projectTasks.filter(t => t.status === 'In Progress').length,
Completed: projectTasks.filter(t => t.status === 'Completed').length,
};
});
renderChart('fm-project-chart', 'bar', {
labels: projects.map(p => p.name),
datasets: [
{ label: 'To Do', data: statusByProject.map(p => p.ToDo), backgroundColor: '#94a3b8' },
{ label: 'In Progress', data: statusByProject.map(p => p.InProgress), backgroundColor: '#3b82f6' },
{ label: 'Completed', data: statusByProject.map(p => p.Completed), backgroundColor: '#16a34a' }
]
}, { responsive: true, maintainAspectRatio: false, scales: { x: { stacked: true }, y: { stacked: true, beginAtZero: true } } });
}
function renderWorkloadChart() {
const byAssignee = tasks.filter(t=>t.status !== 'Completed').reduce((acc, t) => {
const name = teamMembers.find(m => m.id === t.assigneeId).name;
acc[name] = (acc[name] || 0) + 1; return acc;
}, {});
renderChart('fm-workload-chart', 'doughnut', {
labels: Object.keys(byAssignee), datasets: [{ data: Object.values(byAssignee) }]
}, { responsive: true, maintainAspectRatio: false, plugins: { legend: { position: 'bottom' }, title: { display: true, text: 'Open Tasks per Team Member' } } });
}
// --- DATA ACTIONS ---
window.fmAddTask = function() {
tasks.push({
id: Date.now(),
desc: document.getElementById('add-task-desc').value,
projId: parseInt(document.getElementById('add-task-proj').value),
assigneeId: parseInt(document.getElementById('add-task-assignee').value),
priority: document.getElementById('add-task-priority').value,
due: new Date(document.getElementById('add-task-due').value),
status: 'To Do'
});
renderAll();
};
function deleteTask(taskId) {
tasks = tasks.filter(t => t.id !== taskId);
renderAll();
}
// --- GLOBAL & UTILITY ---
window.fmShowTab = id => {
document.querySelectorAll('.fm-main-tab-content, .fm-main-tab-button').forEach(el => el.classList.remove('active'));
document.getElementById(`fm-tab-${id}`)?.classList.add('active');
document.querySelector(`.fm-main-tab-button[onclick="fmShowTab('${id}')"]`)?.classList.add('active');
};
window.fmDownloadPDF = () => {
html2canvas(document.getElementById('fm-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_Dashboard.pdf');
});
};
initialize();
});