Online Multi-Team Synchronization Panel

Online Multi-Team Synchronization Panel

Project Timelines & Status

Status: ${status.label}

${latestUpdate ? latestUpdate.text : 'No updates logged for this date.'}

`; }).join('') || '

No projects for this team.

'}
`; }).join(''); } function renderLogForm() { logTeamSelect.innerHTML = state.teams.map(t => ``).join(''); handleLogTeamChange(); // Populate projects for the default selected team } function handleLogTeamChange() { const teamId = parseInt(logTeamSelect.value); logProjectSelect.innerHTML = state.projects.filter(p => p.teamId === teamId).map(p => ``).join('') || ''; } function renderConfiguration() { // Teams teamsList.innerHTML = state.teams.map(t => `
${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(); });
Scroll to Top