No projects for this team.
'}${t.name}
`).join('');
// Members
memberTeamSelect.innerHTML = state.teams.map(t => ``).join('');
membersList.innerHTML = state.members.map(m => `${m.name}
`).join('');
// Projects
projectTeamSelect.innerHTML = state.teams.map(t => ``).join('');
projectsList.innerHTML = state.projects.map(p => `${p.name}
`).join('');
}
// SECTION: Event Handlers
function handleTabClick(tabName) { state.activeTab = tabName; render(); }
function handleNavClick(dir) { const i = TABS.indexOf(state.activeTab); const newI = i + dir; if (newI >= 0 && newI < TABS.length) { state.activeTab = TABS[newI]; render(); } }
function handleAddTeam(e) { e.preventDefault(); const name = teamNameInput.value.trim(); if(name){ state.teams.push({id: Date.now(), name}); teamNameInput.value = ''; saveState(); render(); } }
function handleDelete(type, id) { if(confirm(`Delete this ${type}?`)){ state[type+'s'] = state[type+'s'].filter(item => item.id !== id); saveState(); render(); } }
function handleAddMember(e) { e.preventDefault(); const name = memberNameInput.value.trim(); const teamId = parseInt(memberTeamSelect.value); if(name && teamId){ state.members.push({id: Date.now(), name, teamId}); memberNameInput.value = ''; saveState(); render(); } }
function handleAddProject(e) { e.preventDefault(); const name = projectNameInput.value.trim(); const teamId = parseInt(projectTeamSelect.value); const startDate = projectStartInput.value; const endDate = projectEndInput.value; if(name && teamId && startDate && endDate){ state.projects.push({id: Date.now(), name, teamId, startDate, endDate}); addProjectForm.reset(); saveState(); render(); } }
function handleLogUpdate(e) {
e.preventDefault();
const update = { id: Date.now(), projectId: parseInt(logProjectSelect.value), date: toISODateString(new Date()), status: logStatusSelect.value, text: logUpdateText.value };
state.updates.push(update);
saveState();
alert('Update logged!');
state.activeTab = 'dashboard';
render();
}
function generatePDF() {
const { jsPDF } = window.jspdf;
const doc = new jsPDF({ orientation: 'p', unit: 'mm', format: 'a4' });
const selectedDate = new Date(dateFilter.value);
doc.setFontSize(22); doc.setFont('helvetica', 'bold'); doc.text('Multi-Team Synchronization Report', 105, 20, { align: 'center' });
doc.setFontSize(10); doc.setTextColor(150); doc.text(`As of: ${selectedDate.toLocaleDateString()}`, 105, 27, { align: 'center' });
let yPos = 40;
state.teams.forEach(team => {
if (yPos > 260) { doc.addPage(); yPos = 20; }
doc.setFontSize(16); doc.setFont('helvetica', 'bold'); doc.text(team.name, 14, yPos);
yPos += 8;
const projectsForTeam = state.projects.filter(p => p.teamId === team.id);
const tableBody = projectsForTeam.map(project => {
const latestUpdate = state.updates.filter(u => u.projectId === project.id && new Date(u.date) <= selectedDate).sort((a, b) => new Date(b.date) - new Date(a.date))[0];
const status = latestUpdate ? STATUS_MAP[latestUpdate.status].label : 'No Status';
return [project.name, status, latestUpdate ? latestUpdate.text : 'N/A'];
});
doc.autoTable({
head: [['Project', 'Status', 'Latest Update']],
body: tableBody, startY: yPos, theme: 'grid',
headStyles: { fillColor: [37, 99, 235] },
didDrawPage: data => { doc.setFontSize(8); doc.setTextColor(150); doc.text('Page ' + data.pageNumber, data.settings.margin.left, doc.internal.pageSize.height - 10); }
});
yPos = doc.autoTable.previous.finalY + 15;
});
doc.save(`Sync_Report_${toISODateString(selectedDate)}.pdf`);
}
// SECTION: Event Listeners
Object.keys(tabButtons).forEach(id => tabButtons[id]?.addEventListener('click', () => handleTabClick(id)));
prevTabBtn?.addEventListener('click', () => handleNavClick(-1));
nextTabBtn?.addEventListener('click', () => handleNavClick(1));
dateFilter?.addEventListener('change', e => { state.selectedDate = new Date(e.target.value); renderDashboard(); });
downloadPdfBtn?.addEventListener('click', generatePDF);
logUpdateForm?.addEventListener('submit', handleLogUpdate);
logTeamSelect?.addEventListener('change', handleLogTeamChange);
addTeamForm?.addEventListener('submit', handleAddTeam);
addMemberForm?.addEventListener('submit', handleAddMember);
addProjectForm?.addEventListener('submit', handleAddProject);
teamsList?.addEventListener('click', e => { if(e.target.classList.contains('delete-team-btn')) handleDelete('team', parseInt(e.target.dataset.id)); });
membersList?.addEventListener('click', e => { if(e.target.classList.contains('delete-member-btn')) handleDelete('member', parseInt(e.target.dataset.id)); });
projectsList?.addEventListener('click', e => { if(e.target.classList.contains('delete-project-btn')) handleDelete('project', parseInt(e.target.dataset.id)); });
// SECTION: Initial Load
loadState();
render();
});
