Tasks: ${completedTasks} / ${projectTasks.length}
${progress}% Complete
`;
dashboardGrid.appendChild(card);
});
};
const renderProjectsTab = () => {
// Populate selector
projectSelector.innerHTML = state.projects.map(p => `
`).join('');
if (state.selectedProjectId) {
projectSelector.value = state.selectedProjectId;
} else if (state.projects.length > 0) {
state.selectedProjectId = state.projects[0].id;
projectSelector.value = state.selectedProjectId;
}
// Render tasks for selected project
renderTaskTable();
};
const renderTaskTable = () => {
tasksTableBody.innerHTML = '';
const selectedProject = state.projects.find(p => p.id == state.selectedProjectId);
projectReportTitle.textContent = selectedProject ? `Task List for: ${selectedProject.name}` : 'No Project Selected';
if (!state.selectedProjectId) return;
const projectTasks = state.tasks.filter(t => t.projectId == state.selectedProjectId);
if (projectTasks.length === 0) {
tasksTableBody.innerHTML = `
| No tasks for this project. |
`;
return;
}
projectTasks.forEach(task => {
const teamMember = state.team.find(tm => tm.id === task.teamMemberId)?.name || 'Unassigned';
const statusClass = task.status === 'Done' ? 'status-done' : task.status === 'In Progress' ? 'status-inprogress' : 'status-todo';
const row = document.createElement('tr');
row.className = 'bg-white border-b';
row.innerHTML = `
${task.name} |
${teamMember} |
${task.dueDate} |
${task.status} |
`;
tasksTableBody.appendChild(row);
});
};
const renderConfigTab = () => {
// Render Projects
configContainers.projects.innerHTML = '';
state.projects.forEach(p => addConfigRow('project', p));
// Render Team
configContainers.team.innerHTML = '';
state.team.forEach(tm => addConfigRow('team', tm));
// Render Tasks
configContainers.tasks.innerHTML = '';
state.tasks.forEach(t => addConfigRow('task', t));
};
const addConfigRow = (type, data = {}) => {
const container = configContainers[type];
const newRow = document.createElement('div');
newRow.className = 'flex items-center space-x-2 config-row';
let content = '';
switch (type) {
case 'project':
content = `
`;
break;
case 'team':
content = `
`;
break;
case 'task':
const projectOptions = state.projects.map(p => `
`).join('');
const teamOptions = state.team.map(tm => `
`).join('');
content = `
`;
break;
}
newRow.innerHTML = content + `
`;
newRow.dataset.id = data.id || Date.now();
container.appendChild(newRow);
newRow.querySelector('.remove-row-btn').addEventListener('click', () => newRow.remove());
};
// --- DATA & LOGIC FUNCTIONS ---
const saveConfiguration = () => {
// Save Projects
state.projects = Array.from(configContainers.projects.querySelectorAll('.config-row')).map((row, i) => ({
id: i + 1,
name: row.querySelector('.data-name').value.trim()
})).filter(p => p.name);
// Save Team
state.team = Array.from(configContainers.team.querySelectorAll('.config-row')).map((row, i) => ({
id: i + 1,
name: row.querySelector('.data-name').value.trim()
})).filter(tm => tm.name);
// Save Tasks
state.tasks = Array.from(configContainers.tasks.querySelectorAll('.config-row')).map((row, i) => ({
id: i + 1,
name: row.querySelector('.data-name').value.trim(),
projectId: parseInt(row.querySelector('.data-project-id').value),
teamMemberId: parseInt(row.querySelector('.data-team-id').value),
dueDate: row.querySelector('.data-due-date').value,
status: row.querySelector('.data-status').value
})).filter(t => t.name);
alert('Configuration saved successfully!');
renderAll();
};
const switchTab = (tabName) => {
state.activeTab = tabName;
Object.values(tabs).forEach(tab => {
tab.btn.classList.remove('active');
tab.panel.classList.add('hidden');
});
tabs[tabName].btn.classList.add('active');
tabs[tabName].panel.classList.remove('hidden');
updateNavButtons();
};
const updateNavButtons = () => {
const tabKeys = Object.keys(tabs);
const currentIndex = tabKeys.indexOf(state.activeTab);
navButtons.prev.disabled = currentIndex === 0;
navButtons.next.disabled = currentIndex === tabKeys.length - 1;
};
const handlePdfDownload = () => {
const { jsPDF } = window.jspdf;
const pdfContent = document.getElementById('pdf-export-area');
html2canvas(pdfContent, { scale: 2, useCORS: true }).then(canvas => {
const imgData = canvas.toDataURL('image/png');
const pdf = new jsPDF({
orientation: 'portrait',
unit: 'pt',
format: 'a4'
});
const imgProps = pdf.getImageProperties(imgData);
const pdfWidth = pdf.internal.pageSize.getWidth();
const pdfHeight = (imgProps.height * pdfWidth) / imgProps.width;
const margin = 40;
pdf.addImage(imgData, 'PNG', margin, margin, pdfWidth - (margin * 2), pdfHeight);
pdf.save('project-report.pdf');
});
};
const renderAll = () => {
renderDashboard();
renderProjectsTab();
renderConfigTab();
};
// --- EVENT LISTENERS ---
Object.keys(tabs).forEach(tabName => {
tabs[tabName].btn.addEventListener('click', () => switchTab(tabName));
});
navButtons.next.addEventListener('click', () => {
const tabKeys = Object.keys(tabs);
const currentIndex = tabKeys.indexOf(state.activeTab);
if (currentIndex < tabKeys.length - 1) switchTab(tabKeys[currentIndex + 1]);
});
navButtons.prev.addEventListener('click', () => {
const tabKeys = Object.keys(tabs);
const currentIndex = tabKeys.indexOf(state.activeTab);
if (currentIndex > 0) switchTab(tabKeys[currentIndex - 1]);
});
addButtons.project.addEventListener('click', () => addConfigRow('project'));
addButtons.team.addEventListener('click', () => addConfigRow('team'));
addButtons.task.addEventListener('click', () => addConfigRow('task'));
saveConfigBtn.addEventListener('click', saveConfiguration);
projectSelector.addEventListener('change', (e) => {
state.selectedProjectId = e.target.value;
renderTaskTable();
});
downloadPdfBtn.addEventListener('click', handlePdfDownload);
// --- INITIALIZATION ---
const init = () => {
// Sample Data
state.projects = [
{ id: 1, name: 'Website Redesign' },
{ id: 2, name: 'Q3 Marketing Campaign' },
{ id: 3, name: 'Mobile App Launch' }
];
state.team = [
{ id: 1, name: 'John Smith' },
{ id: 2, name: 'Emily Jones' },
{ id: 3, name: 'Michael Williams' }
];
state.tasks = [
{ id: 1, name: 'Develop new homepage mockup', projectId: 1, teamMemberId: 2, dueDate: '2025-09-15', status: 'In Progress' },
{ id: 2, name: 'Create social media content calendar', projectId: 2, teamMemberId: 2, dueDate: '2025-08-30', status: 'Done' },
{ id: 3, name: 'Finalize API documentation', projectId: 3, teamMemberId: 1, dueDate: '2025-09-05', status: 'In Progress' },
{ id: 4, name: 'User testing for checkout flow', projectId: 1, teamMemberId: 3, dueDate: '2025-09-20', status: 'To Do' },
{ id: 5, name: 'Launch PPC ads for campaign', projectId: 2, teamMemberId: 3, dueDate: '2025-09-01', status: 'To Do' },
];
state.selectedProjectId = state.projects[0]?.id;
renderAll();
switchTab('dashboard');
};
init();
});