`;
tasksListEl.appendChild(taskEl);
});
}
function sptsRenderTaskSummary() {
let totalTasks = 0;
let todoTasks = 0;
let inProgressTasks = 0;
let completedTasks = 0;
sptsProject.phases.forEach(phase => {
if (phase.tasks) {
totalTasks += phase.tasks.length;
phase.tasks.forEach(task => {
if (task.status === 'To Do') todoTasks++;
else if (task.status === 'In Progress') inProgressTasks++;
else if (task.status === 'Completed') completedTasks++;
});
}
});
document.getElementById('sptsTotalTasksCount').textContent = totalTasks;
document.getElementById('sptsTodoTasksCount').textContent = todoTasks;
document.getElementById('sptsInProgressTasksCount').textContent = inProgressTasks;
document.getElementById('sptsCompletedTasksCount').textContent = completedTasks;
// Optionally, render a full list of tasks in the summary tab too
// const allTasksListEl = document.getElementById('sptsAllTasksList');
// allTasksListEl.innerHTML = ''; // Clear previous list
// ... logic to build and append task items similar to phase view ...
}
// --- MODAL HANDLING ---
function sptsOpenModal(modalId) { document.getElementById(modalId).style.display = 'block'; }
function sptsCloseModal(modalId) { document.getElementById(modalId).style.display = 'none'; }
window.onclick = function(event) { // Close modals if clicked outside
if (event.target == sptsPhaseModal) sptsCloseModal('sptsPhaseModal');
if (event.target == sptsTaskModal) sptsCloseModal('sptsTaskModal');
}
// --- PHASE CRUD ---
window.sptsOpenPhaseModal = function(phaseIdToEdit = null) {
sptsPhaseForm.reset();
if (phaseIdToEdit) {
const phase = sptsProject.phases.find(p => p.id === phaseIdToEdit);
if (phase) {
sptsPhaseModalTitle.textContent = 'Edit Phase';
sptsPhaseIdInput.value = phase.id;
sptsPhaseNameInput.value = phase.name;
}
} else {
sptsPhaseModalTitle.textContent = 'Add New Phase';
sptsPhaseIdInput.value = '';
}
sptsOpenModal('sptsPhaseModal');
}
sptsPhaseForm.addEventListener('submit', function(e) {
e.preventDefault();
const phaseId = sptsPhaseIdInput.value;
const phaseName = sptsPhaseNameInput.value.trim();
if (!phaseName) { alert('Phase name cannot be empty.'); return; }
if (phaseId) { // Editing existing phase
const phase = sptsProject.phases.find(p => p.id === phaseId);
if (phase) phase.name = phaseName;
} else { // Adding new phase
const newPhase = {
id: 'spts-phase-' + Date.now(),
name: phaseName,
tasks: []
};
sptsProject.phases.push(newPhase);
}
sptsSaveData();
sptsRenderPhases();
sptsCloseModal('sptsPhaseModal');
});
window.sptsDeletePhase = function(phaseId) {
if (confirm('Are you sure you want to delete this phase and all its tasks?')) {
sptsProject.phases = sptsProject.phases.filter(p => p.id !== phaseId);
sptsSaveData();
sptsRenderPhases();
}
}
// --- TASK CRUD ---
window.sptsOpenTaskModal = function(phaseId, taskIdToEdit = null) {
sptsTaskForm.reset();
sptsTaskPhaseIdInput.value = phaseId;
const phase = sptsProject.phases.find(p => p.id === phaseId);
if (!phase) return;
if (taskIdToEdit) {
const task = phase.tasks.find(t => t.id === taskIdToEdit);
if (task) {
sptsTaskModalTitle.textContent = `Edit Task in ${phase.name}`;
sptsTaskIdInput.value = task.id;
sptsTaskNameInput.value = task.name;
sptsTaskDescriptionInput.value = task.description || '';
sptsTaskEffortInput.value = task.effort || '';
sptsTaskAssigneeInput.value = task.assignee || '';
sptsTaskDueDateInput.value = task.dueDate || '';
sptsTaskStatusInput.value = task.status || 'To Do';
}
} else {
sptsTaskModalTitle.textContent = `Add New Task to ${phase.name}`;
sptsTaskIdInput.value = '';
}
sptsOpenModal('sptsTaskModal');
}
sptsTaskForm.addEventListener('submit', function(e) {
e.preventDefault();
const phaseId = sptsTaskPhaseIdInput.value;
const taskId = sptsTaskIdInput.value;
const taskName = sptsTaskNameInput.value.trim();
if (!taskName) { alert('Task name cannot be empty.'); return; }
const phase = sptsProject.phases.find(p => p.id === phaseId);
if (!phase) return;
const taskData = {
name: taskName,
description: sptsTaskDescriptionInput.value.trim(),
effort: sptsTaskEffortInput.value ? parseInt(sptsTaskEffortInput.value) : null,
assignee: sptsTaskAssigneeInput.value.trim(),
dueDate: sptsTaskDueDateInput.value,
status: sptsTaskStatusInput.value
};
if (taskId) { // Editing existing task
const taskIndex = phase.tasks.findIndex(t => t.id === taskId);
if (taskIndex > -1) {
phase.tasks[taskIndex] = { ...phase.tasks[taskIndex], ...taskData };
}
} else { // Adding new task
const newTask = {
id: 'spts-task-' + Date.now(),
...taskData
};
phase.tasks.push(newTask);
}
sptsSaveData();
sptsRenderTasksForPhase(phaseId);
sptsCloseModal('sptsTaskModal');
});
window.sptsDeleteTask = function(phaseId, taskId) {
if (confirm('Are you sure you want to delete this task?')) {
const phase = sptsProject.phases.find(p => p.id === phaseId);
if (phase) {
phase.tasks = phase.tasks.filter(t => t.id !== taskId);
sptsSaveData();
sptsRenderTasksForPhase(phaseId);
}
}
}
// --- PDF GENERATION ---
sptsDownloadPdfButton.addEventListener('click', function() {
const doc = new jsPDF();
const primaryColor = getComputedStyle(document.documentElement).getPropertyValue('--spts-primary-color').trim();
const secondaryColor = getComputedStyle(document.documentElement).getPropertyValue('--spts-secondary-color').trim();
const textColor = getComputedStyle(document.documentElement).getPropertyValue('--spts-text-color').trim();
doc.setFontSize(18);
doc.setTextColor(primaryColor);
doc.text(`Project Breakdown: ${sptsProject.name || 'Untitled Project'}`, 14, 22);
doc.setFontSize(11);
doc.setTextColor(100);
doc.text(`Report generated on: ${new Date().toLocaleDateString()}`, 14, 30);
let yPos = 40;
// Project Details
doc.setFontSize(12);
doc.setTextColor(textColor);
if(sptsProject.goal) { doc.text(`Goal: ${sptsProject.goal}`, 14, yPos); yPos += 7; }
if(sptsProject.startDate) { doc.text(`Start Date: ${new Date(sptsProject.startDate + 'T00:00:00').toLocaleDateString()}`, 14, yPos); yPos += 7; }
if(sptsProject.endDate) { doc.text(`End Date: ${new Date(sptsProject.endDate + 'T00:00:00').toLocaleDateString()}`, 14, yPos); yPos += 10; }
sptsProject.phases.forEach((phase, index) => {
if (yPos > 260 && index > 0) { // Add new page if content is too long
doc.addPage();
yPos = 20;
}
doc.setFontSize(14);
doc.setTextColor(secondaryColor);
doc.text(`Phase: ${phase.name}`, 14, yPos);
yPos += 8;
if (phase.tasks && phase.tasks.length > 0) {
const tableColumn = ["#", "Task Name", "Description", "Effort (h)", "Assignee", "Due Date", "Status"];
const tableRows = [];
phase.tasks.forEach((task, taskIndex) => {
const taskData = [
taskIndex + 1,
task.name || "N/A",
task.description || "N/A",
task.effort !== null ? task.effort : "N/A",
task.assignee || "N/A",
task.dueDate ? new Date(task.dueDate + 'T00:00:00').toLocaleDateString() : "N/A",
task.status || "N/A"
];
tableRows.push(taskData);
});
doc.autoTable({
head: [tableColumn],
body: tableRows,
startY: yPos,
theme: 'grid',
headStyles: { fillColor: secondaryColor, textColor: '#ffffff' },
styles: { font: 'Arial', fontSize: 8, cellPadding: 1.5, overflow: 'linebreak' },
columnStyles: {
0: { cellWidth: 8 }, // #
1: { cellWidth: 35 }, // Task Name
2: { cellWidth: 50 }, // Description
3: { cellWidth: 15 }, // Effort
4: { cellWidth: 25 }, // Assignee
5: { cellWidth: 20 }, // Due Date
6: { cellWidth: 20 } // Status
},
didDrawPage: function (data) { // Handle page breaks if table is very long
yPos = data.cursor.y + 15; // Update yPos for next element
}
});
yPos = doc.lastAutoTable.finalY + 10; // Ensure yPos is updated after table
} else {
doc.setFontSize(10);
doc.setTextColor(textColor);
doc.text("No tasks in this phase.", 16, yPos);
yPos += 10;
}
});
// Summary in PDF
if (yPos > 250) { doc.addPage(); yPos = 20;}
doc.setFontSize(14);
doc.setTextColor(primaryColor);
doc.text("Task Summary", 14, yPos);
yPos += 8;
doc.setFontSize(10);
doc.setTextColor(textColor);
let totalTasks = 0, todo = 0, inProgress = 0, completed = 0;
sptsProject.phases.forEach(p => p.tasks.forEach(t => {
totalTasks++;
if(t.status === 'To Do') todo++;
else if(t.status === 'In Progress') inProgress++;
else if(t.status === 'Completed') completed++;
}));
doc.text(`Total Tasks: ${totalTasks}`, 14, yPos); yPos += 6;
doc.text(`To Do: ${todo}`, 14, yPos); yPos += 6;
doc.text(`In Progress: ${inProgress}`, 14, yPos); yPos += 6;
doc.text(`Completed: ${completed}`, 14, yPos);
doc.save(`Project_Task_Breakdown_${(sptsProject.name || 'Export').replace(/\s+/g, '_')}_${new Date().toISOString().slice(0,10)}.pdf`);
});
// --- INITIALIZATION ---
document.addEventListener('DOMContentLoaded', () => {
sptsLoadProject();
// Activate the first tab
if (document.querySelector('#smartProjectTaskSplitterTool .spts-tab-button')) {
document.querySelector('#smartProjectTaskSplitterTool .spts-tab-button').click();
}
});