Prio: ${task.priority}
Due: ${task.dueDate ? new Date(task.dueDate+"T00:00:00Z").toLocaleDateString(undefined,{timeZone:'UTC'}) : 'N/A'}
`;
return card;
}
function ttd_changeTaskStatusFromCard(taskId, newStatus) {
const task = tasks_TTD.find(t => t.id === taskId);
if (task) {
task.status = newStatus;
ttd_renderDashboard(); // Re-render the current dashboard view
ttd_renderManageTasksTable(); // Also update the management table
}
}
function ttd_renderKanbanView(tasksToDisplay, parentElement) {
const board = document.createElement('div');
board.className = 'ttd-kanban-board';
KANBAN_STATUSES_TTD.forEach(status => {
const column = document.createElement('div');
column.className = 'ttd-kanban-column';
column.dataset.status = status; // For drop identification
column.style.backgroundColor = `var(--status-${status.toLowerCase().replace(/\s+/g, '')}-bg, var(--light-color))`;
column.innerHTML = `
${status} (${tasksToDisplay.filter(t => t.status === status).length})
`;
column.ondragover = (event) => { event.preventDefault(); column.classList.add('drag-over'); }; // Allow drop
column.ondragleave = () => { column.classList.remove('drag-over'); };
column.ondrop = (event) => {
event.preventDefault();
column.classList.remove('drag-over');
const droppedTaskId = event.dataTransfer.getData('text/plain');
const targetStatus = column.dataset.status;
const task = tasks_TTD.find(t => t.id === droppedTaskId);
if (task && task.status !== targetStatus) {
task.status = targetStatus;
ttd_renderDashboard(); // Re-render Kanban
ttd_renderManageTasksTable(); // Also update the management table
}
draggedTaskElement_TTD = null;
};
tasksToDisplay.filter(task => task.status === status).forEach(task => {
column.appendChild(ttd_createTaskCard(task));
});
board.appendChild(column);
});
parentElement.appendChild(board);
}
function ttd_renderTeamLoadView(tasksToDisplay, parentElement) {
const view = document.createElement('div');
view.className = 'ttd-team-load-view';
if (teamMembers_TTD.length === 0) {
view.innerHTML = "
No team members defined. Add members in the Setup tab.
";
parentElement.appendChild(view); return;
}
teamMembers_TTD.forEach(member => {
const memberCol = document.createElement('div');
memberCol.className = 'ttd-member-column';
const memberTasks = tasksToDisplay.filter(t => t.assignedTo === member.id);
memberCol.innerHTML = `
${member.name} (${memberTasks.length} tasks)
`;
memberTasks.forEach(task => {
memberCol.appendChild(ttd_createTaskCard(task)); // Re-use task card style
});
if(memberTasks.length === 0) memberCol.innerHTML += '
No tasks assigned.
';
view.appendChild(memberCol);
});
// Unassigned tasks
const unassignedTasks = tasksToDisplay.filter(t => !t.assignedTo || t.assignedTo === "");
if(unassignedTasks.length > 0){
const unassignedCol = document.createElement('div');
unassignedCol.className = 'ttd-member-column';
unassignedCol.innerHTML = `
Unassigned (${unassignedTasks.length} tasks)
`;
unassignedTasks.forEach(task => unassignedCol.appendChild(ttd_createTaskCard(task)));
view.appendChild(unassignedCol);
}
parentElement.appendChild(view);
}
function ttd_renderMasterListView(tasksToDisplay, parentElement) {
const tableContainer = document.createElement('div');
tableContainer.style.overflowX = 'auto';
const table = document.createElement('table');
table.className = 'ttd-master-list-table';
table.innerHTML = `
| Name | Assigned To | Status | Priority |
Due Date | Effort | Category |
`;
const tbody = table.querySelector('tbody');
if (tasksToDisplay.length === 0) {
tbody.innerHTML = '
| No tasks to display. |
';
} else {
tasksToDisplay.forEach(task => {
const row = tbody.insertRow();
row.insertCell().textContent = task.name;
const assignee = teamMembers_TTD.find(m => m.id === task.assignedTo);
row.insertCell().textContent = assignee ? assignee.name : 'Unassigned';
row.insertCell().textContent = task.status;
row.insertCell().textContent = task.priority;
row.insertCell().textContent = task.dueDate ? new Date(task.dueDate+"T00:00:00Z").toLocaleDateString(undefined,{timeZone:'UTC'}) : 'N/A';
row.insertCell().textContent = task.effort ? `${task.effort} ${task.effortUnit}` : 'N/A';
const category = taskCategories_TTD.find(c => c.id === task.category);
row.insertCell().textContent = category ? category.name : 'N/A';
});
}
tableContainer.appendChild(table);
parentElement.appendChild(tableContainer);
}
// --- PDF and Data Clearing ---
function ttd_clearAllData() {
if (confirm("Are you sure you want to clear ALL data (team, categories, tasks)?")) {
teamMembers_TTD = []; memberIdCounter_TTD = 0;
taskCategories_TTD = []; categoryIdCounter_TTD = 0;
tasks_TTD = []; taskIdCounter_TTD = 0;
ttd_renderMembersList(); ttd_renderCategoriesList();
ttd_updateFilterDropdowns(); ttd_updateAssigneeDropdowns(); ttd_updateCategoryDropdowns();
ttd_clearTaskForm();
ttd_renderManageTasksTable();
ttd_renderDashboard();
alert("All data cleared.");
}
}
function ttd_downloadPDF() {
if (!tpc_jsPDF_loaded_TTD || !tpc_autoTable_loaded_TTD) { alert("TTD Error: PDF libraries not loaded."); return; }
const filteredTasks = ttd_getFilteredTasks(); // Get currently filtered tasks for the report
const doc = new TPC_jsPDF_TTD();
const primaryColor = getComputedStyle(document.documentElement).getPropertyValue('--primary-color').trim();
const darkColor = getComputedStyle(document.documentElement).getPropertyValue('--dark-color').trim();
let finalY = 15;
const reportDateStr = new Date().toLocaleDateString();
const today = new Date(); today.setHours(0,0,0,0);
doc.setFontSize(18); doc.setTextColor(primaryColor);
doc.text("Team Task Dashboard Report", doc.internal.pageSize.getWidth() / 2, finalY, { align: 'center' });
finalY += 8; doc.setFontSize(10); doc.setTextColor(darkColor);
doc.text(`Report Date: ${reportDateStr}`, doc.internal.pageSize.getWidth() / 2, finalY, { align: 'center'});
finalY += 12;
// Summary Stats for PDF (based on filtered tasks)
doc.setFontSize(14); doc.setTextColor(primaryColor);
doc.text("Summary Statistics (Based on Current Filters)", 14, finalY); finalY += 7;
const totalTasks = filteredTasks.length;
const statusCounts = KANBAN_STATUSES_TTD.reduce((acc, status) => { acc[status] = 0; return acc; }, {});
let overdueCount = 0;
filteredTasks.forEach(task => {
if (statusCounts.hasOwnProperty(task.status)) statusCounts[task.status]++;
if (task.dueDate && new Date(task.dueDate+"T00:00:00Z") < today && task.status !== 'Done') overdueCount++;
});
let summaryText = `Total Tasks: ${totalTasks} | Overdue: ${overdueCount} | `;
KANBAN_STATUSES_TTD.forEach(s => summaryText += `${s}: ${statusCounts[s]} | `);
doc.setFontSize(9); doc.setTextColor(darkColor);
doc.text(summaryText.slice(0,-3), 14, finalY, {maxWidth: doc.internal.pageSize.getWidth() - 28});
finalY += (Math.ceil(doc.getTextDimensions(summaryText).h / (doc.internal.pageSize.getWidth() - 28)) * 4) + 8 ;
doc.setFontSize(14); doc.setTextColor(primaryColor);
doc.text("Filtered Task List", 14, finalY); finalY += 7;
if (filteredTasks.length > 0) {
const taskTableBody = filteredTasks.map(task => {
const assignee = teamMembers_TTD.find(m => m.id === task.assignedTo);
const category = taskCategories_TTD.find(c => c.id === task.category);
return [
task.name,
assignee ? assignee.name : 'Unassigned',
task.status, task.priority,
task.dueDate ? new Date(task.dueDate+"T00:00:00Z").toLocaleDateString(undefined,{timeZone:'UTC'}) : 'N/A',
task.effort ? `${task.effort} ${task.effortUnit}` : 'N/A',
category ? category.name : 'N/A'
];
});
doc.autoTable({
startY: finalY,
head: [['Name', 'Assigned', 'Status', 'Priority', 'Due', 'Effort', 'Category']],
body: taskTableBody, theme: 'grid',
headStyles: { fillColor: primaryColor, textColor: '#ffffff', fontSize: 9 },
styles: { fontSize: 8, cellPadding: 1.5, overflow: 'linebreak' }
});
} else {
doc.setFontSize(10); doc.setTextColor(darkColor); doc.text("No tasks match current filters.", 14, finalY);
}
doc.save(`Team_Task_Dashboard_${new Date().toISOString().slice(0,10)}.pdf`);
}