Task Dependency Manager
Task Dependency Manager

Task List

"; document.getElementById('tdm-results-area').style.display = 'block'; return; } // Kahn's Algorithm for Topological Sort const inDegree = new Map(); const adjList = new Map(); // Stores tasks that depend on a given task tasks.forEach(task => { inDegree.set(task.id, 0); adjList.set(task.id, []); }); tasks.forEach(task => { task.prerequisites.forEach(prereqId => { // If task depends on prereqId, then an edge from prereqId to task.id if (adjList.has(prereqId)) { // prereqId must exist adjList.get(prereqId).push(task.id); inDegree.set(task.id, (inDegree.get(task.id) || 0) + 1); } else { // This case implies a prerequisite was selected that no longer exists or was never valid. // Should ideally be caught by tdm_updatePrerequisiteOptions logic ensuring valid IDs. console.warn(`Prerequisite ${prereqId} for task ${task.id} not found in defined tasks.`); } }); }); const queue = []; tasks.forEach(task => { if (inDegree.get(task.id) === 0) { queue.push(task.id); } }); const sortedOrder = []; const levelMap = new Map(); // To store level of each task let currentLevel = 0; while (queue.length > 0) { const tasksAtCurrentLevel = queue.length; if (tasksAtCurrentLevel === 0 && sortedOrder.length < tasks.length) break; // Cycle detected or disconnected for (let i = 0; i < tasksAtCurrentLevel; i++) { const u = queue.shift(); sortedOrder.push(u); levelMap.set(u, currentLevel); (adjList.get(u) || []).forEach(v_id => { inDegree.set(v_id, (inDegree.get(v_id) || 0) - 1); if (inDegree.get(v_id) === 0) { queue.push(v_id); } }); } currentLevel++; } const outputDiv = document.getElementById('tdm-analysis-output'); outputDiv.innerHTML = ''; // Clear previous results if (sortedOrder.length === tasks.length) { outputDiv.innerHTML += "

Valid task sequence found:

"; const ol = document.createElement('ol'); sortedOrder.forEach(taskId => { const taskObj = tasks.find(t => t.id === taskId); const li = document.createElement('li'); li.textContent = `${taskObj.id}: ${taskObj.name} (Level: ${levelMap.get(taskId)})`; ol.appendChild(li); }); outputDiv.appendChild(ol); tdm_analysisResultForPdf = { success: true, tasks, sortedOrder, levelMap, reportDate: new Date().toLocaleDateString() }; } else { outputDiv.innerHTML += "

Circular dependency detected or disconnected tasks found. Unable to determine a complete valid sequence.

"; let problematicTasksInfo = "

Tasks not included in the valid sequence (potential cycle involvement or unresolvable dependencies):

    "; tasks.forEach(task => { if (!sortedOrder.includes(task.id)) { problematicTasksInfo += `
  • ${task.id}: ${task.name} (Still needs ${inDegree.get(task.id)} prerequisites)
  • `; } }); problematicTasksInfo += "
"; outputDiv.innerHTML += problematicTasksInfo; tdm_analysisResultForPdf = { success: false, tasks, sortedOrder, inDegree, reportDate: new Date().toLocaleDateString() }; } document.getElementById('tdm-results-area').style.display = 'block'; document.getElementById('tdm-download-pdf-button').disabled = false; } function tdm_clearAll() { document.getElementById('tdm-tasks-list').innerHTML = ''; tdm_taskCounter = 0; tdm_definedTasks = []; tdm_addTaskEntry(); // Add one default task tdm_disablePdfAndClearResults(); tdm_openTab({currentTarget: document.querySelector('.tdm-tab-button[onclick*="tdm-define-tab"]')}, 'tdm-define-tab'); } function tdm_downloadPDF() { if (!tpc_jsPDF_loaded_TDM || !tpc_autoTable_loaded_TDM) { alert("TDM Error: PDF libraries not fully loaded. Cannot generate PDF."); return; } if (!tdm_analysisResultForPdf) { alert("Please analyze dependencies first before downloading PDF."); return; } const { success, tasks, sortedOrder, levelMap, inDegree, reportDate } = tdm_analysisResultForPdf; const doc = new TPC_jsPDF_TDM(); const primaryColor = getComputedStyle(document.documentElement).getPropertyValue('--primary-color').trim(); const darkColor = getComputedStyle(document.documentElement).getPropertyValue('--dark-color').trim(); const dangerColor = getComputedStyle(document.documentElement).getPropertyValue('--danger-color').trim(); const successColor = getComputedStyle(document.documentElement).getPropertyValue('--accent-color').trim(); let finalY = 15; doc.setFontSize(18); doc.setTextColor(primaryColor); doc.text("Task Dependency Analysis Report", doc.internal.pageSize.getWidth() / 2, finalY, { align: 'center' }); finalY += 8; doc.setFontSize(10); doc.setTextColor(darkColor); doc.text(`Report Date: ${reportDate}`, doc.internal.pageSize.getWidth() / 2, finalY, { align: 'center'}); finalY += 10; doc.setFontSize(14); doc.setTextColor(primaryColor); doc.text("Defined Tasks & Prerequisites", 14, finalY); finalY += 7; const taskTableBody = tasks.map(t => [ t.id, t.name, t.prerequisites.join(', ') || 'None' ]); doc.autoTable({ startY: finalY, head: [['ID', 'Task Name', 'Prerequisites']], body: taskTableBody, theme: 'grid', headStyles: { fillColor: primaryColor, textColor: '#ffffff', fontSize: 10 }, styles: { fontSize: 9, cellPadding: 2, overflow: 'linebreak' } }); finalY = doc.lastAutoTable.finalY + 10; doc.setFontSize(14); doc.setTextColor(primaryColor); doc.text("Analysis Results", 14, finalY); finalY += 7; if (success) { doc.setFontSize(12); doc.setTextColor(successColor); doc.text("Status: Valid task sequence found.", 14, finalY); finalY += 6; doc.setTextColor(darkColor); doc.text("Execution Order (by Level):", 14, finalY); finalY += 6; const orderedTaskDetails = sortedOrder.map((taskId, index) => { const taskObj = tasks.find(t => t.id === taskId); return [`${index + 1}. ${taskId}`, taskObj.name, levelMap.get(taskId).toString()]; }); doc.autoTable({ startY: finalY, head: [['Order', 'Task Name', 'Level']], body: orderedTaskDetails, theme: 'grid', headStyles: {fillColor: successColor, textColor: '#ffffff', fontSize:10}, styles: { fontSize: 9, cellPadding: 2 } }); } else { doc.setFontSize(12); doc.setTextColor(dangerColor); doc.text("Status: Circular dependency detected or tasks are disconnected.", 14, finalY); finalY += 6; doc.setTextColor(darkColor); doc.text("Problematic tasks (not in valid sequence or with remaining dependencies):", 14, finalY); finalY += 6; let problematicDetails = []; tasks.forEach(task => { if (!sortedOrder || !sortedOrder.includes(task.id)) { // Ensure sortedOrder is defined problematicDetails.push([task.id, task.name, `Needs ${inDegree ? (inDegree.get(task.id) || 0) : 'N/A'} prereqs`]); } }); if (problematicDetails.length > 0) { doc.autoTable({ startY: finalY, head: [['ID', 'Task Name', 'Status']], body: problematicDetails, theme: 'grid', headStyles: {fillColor: dangerColor, textColor: '#ffffff', fontSize:10}, styles: { fontSize: 9, cellPadding: 2 } }); } else { doc.text("Could not identify specific problematic tasks (general graph error).", 14, finalY); } } doc.save(`Task_Dependency_Analysis_${new Date().toISOString().slice(0,10)}.pdf`); }
Scroll to Top