`;
}
function renderConfiguration() {
renderProjectsList();
renderTeamsList();
renderTaskFormDropdowns();
}
function renderProjectsList() {
projectsList.innerHTML = state.projects.map(project => `
${project.name}
`).join('') || 'No projects created yet.
'; } function renderTeamsList() { teamsList.innerHTML = state.teams.map(team => `
${team.name}
`).join('') || 'No teams created yet.
'; } function renderTaskFormDropdowns() { taskProjectSelect.innerHTML = state.projects.map(p => ``).join(''); taskTeamSelect.innerHTML = state.teams.map(t => ``).join(''); } // SECTION: Event Handlers function handleTabClick(tabName) { state.activeTab = tabName; renderTabs(); } function handleProjectSelect(event) { state.selectedProjectId = parseInt(event.target.value); renderDashboard(); } function handleAddProject(event) { event.preventDefault(); const name = projectNameInput.value.trim(); if (name) { const newProject = { id: Date.now(), name }; state.projects.push(newProject); if (state.projects.length === 1) state.selectedProjectId = newProject.id; projectNameInput.value = ''; saveState(); render(); } } function handleDeleteProject(projectId) { state.projects = state.projects.filter(p => p.id !== projectId); state.tasks = state.tasks.filter(t => t.projectId !== projectId); if (state.selectedProjectId === projectId) { state.selectedProjectId = state.projects.length > 0 ? state.projects[0].id : null; } saveState(); render(); } function handleAddTeam(event) { event.preventDefault(); const name = teamNameInput.value.trim(); if (name) { state.teams.push({ id: Date.now(), name }); teamNameInput.value = ''; saveState(); renderConfiguration(); } } function handleDeleteTeam(teamId) { state.teams = state.teams.filter(t => t.id !== teamId); state.tasks.forEach(task => { if (task.teamId === teamId) task.teamId = null; }); saveState(); render(); } function handleAddTask(event) { event.preventDefault(); const name = taskNameInput.value.trim(); const projectId = parseInt(taskProjectSelect.value); const teamId = parseInt(taskTeamSelect.value); const dueDate = taskDueDateInput.value; const status = taskStatusSelect.value; if (name && projectId && teamId && dueDate && status) { state.tasks.push({ id: Date.now(), name, projectId, teamId, dueDate, status }); addTaskForm.reset(); saveState(); if (projectId === state.selectedProjectId) renderDashboard(); } else { alert("Please fill out all task fields."); } } /** * Generates and downloads a beautifully formatted PDF report. */ function generatePDF() { const { jsPDF } = window.jspdf; const doc = new jsPDF({ orientation: 'p', unit: 'mm', format: 'a4' }); if (!state.selectedProjectId) { alert("Please select a project to generate a report."); return; } // --- Data Gathering --- const selectedProject = state.projects.find(p => p.id === state.selectedProjectId); const tasksForProject = state.tasks.filter(t => t.projectId === state.selectedProjectId); const date = new Date().toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' }); // --- PDF Header --- doc.setFontSize(22); doc.setFont('helvetica', 'bold'); doc.text('Cross-Team Collaboration Report', 105, 20, { align: 'center' }); doc.setFontSize(16); doc.setFont('helvetica', 'normal'); doc.text(`Project: ${selectedProject.name}`, 105, 30, { align: 'center' }); doc.setFontSize(10); doc.setTextColor(150); doc.text(`Report Generated: ${date}`, 105, 35, { align: 'center' }); // --- Summary Section --- const totalTasks = tasksForProject.length; const todoCount = tasksForProject.filter(t => t.status === 'todo').length; const inProgressCount = tasksForProject.filter(t => t.status === 'inprogress').length; const doneCount = tasksForProject.filter(t => t.status === 'done').length; doc.setFontSize(14); doc.setFont('helvetica', 'bold'); doc.setTextColor(40); doc.text('Project Summary', 14, 55); doc.setLineWidth(0.5); doc.line(14, 57, 196, 57); // Underline doc.setFontSize(11); doc.setFont('helvetica', 'normal'); const summaryText = `Total Tasks: ${totalTasks} | To Do: ${todoCount} | In Progress: ${inProgressCount} | Done: ${doneCount}`; doc.text(summaryText, 14, 65); // --- Task Table using autoTable plugin --- const tableHead = [['Task Description', 'Assigned Team', 'Due Date', 'Status']]; const tableBody = tasksForProject.map(task => { const team = state.teams.find(t => t.id === task.teamId); const statusText = KANBAN_STATUSES[task.status]?.title || 'Unknown'; return [task.name, team ? team.name : 'Unassigned', task.dueDate, statusText]; }); doc.autoTable({ head: tableHead, body: tableBody, startY: 75, theme: 'grid', headStyles: { fillColor: [37, 99, 235], textColor: 255, fontStyle: 'bold' }, styles: { cellPadding: 2.5, fontSize: 10 }, alternateRowStyles: { fillColor: [241, 245, 249] }, didDrawPage: function (data) { // Footer with page numbers const pageCount = doc.internal.getNumberOfPages(); doc.setFontSize(8); doc.setTextColor(150); // FIXED: Use data.pageNumber instead of doc.internal.getPageInfo() doc.text('Page ' + data.pageNumber + ' of ' + pageCount, data.settings.margin.left, doc.internal.pageSize.height - 10); } }); // --- Save PDF --- const projectName = selectedProject.name.replace(/\s+/g, '_'); doc.save(`${projectName}_Report_${new Date().toISOString().slice(0, 10)}.pdf`); } // SECTION: Event Listeners if (tabDashboard) tabDashboard.addEventListener('click', () => handleTabClick('dashboard')); if (tabConfig) tabConfig.addEventListener('click', () => handleTabClick('config')); if (prevTabBtn) prevTabBtn.addEventListener('click', () => handleTabClick('dashboard')); if (nextTabBtn) nextTabBtn.addEventListener('click', () => handleTabClick('config')); if (projectSelector) projectSelector.addEventListener('change', handleProjectSelect); if (downloadPdfBtn) downloadPdfBtn.addEventListener('click', generatePDF); if (addProjectForm) addProjectForm.addEventListener('submit', handleAddProject); if (addTeamForm) addTeamForm.addEventListener('submit', handleAddTeam); if (addTaskForm) addTaskForm.addEventListener('submit', handleAddTask); if (projectsList) { projectsList.addEventListener('click', (e) => { if (e.target && e.target.classList.contains('delete-project-btn')) { const id = parseInt(e.target.dataset.id); if (confirm('Are you sure you want to delete this project and all its tasks?')) handleDeleteProject(id); } }); } if (teamsList) { teamsList.addEventListener('click', (e) => { if (e.target && e.target.classList.contains('delete-team-btn')) { const id = parseInt(e.target.dataset.id); if (confirm('Are you sure you want to delete this team? Tasks will be unassigned.')) handleDeleteTeam(id); } }); } // SECTION: Initial Application Load loadState(); render(); });