Task Dependency Dashboard
Project Timeline
Add New Task
Task List
| ID | Task | Dates | Dependencies | Actions |
|---|
No tasks to display. Please add tasks in the Data Configuration tab.
`; return; } // --- GANTT CHART LOGIC --- const sortedTasks = [...tasks].sort((a, b) => new Date(a.start) - new Date(b.start)); const projectStartDate = new Date(Math.min(...sortedTasks.map(t => new Date(t.start)))); const projectEndDate = new Date(Math.max(...sortedTasks.map(t => new Date(t.end)))); // Add some padding to the dates projectStartDate.setDate(projectStartDate.getDate() - 2); projectEndDate.setDate(projectEndDate.getDate() + 2); const totalDurationDays = (projectEndDate - projectStartDate) / (1000 * 60 * 60 * 24); // Render Timeline let currentDate = new Date(projectStartDate); while(currentDate <= projectEndDate) { const dayDiff = (currentDate - projectStartDate) / (1000 * 60 * 60 * 24); const leftPercent = (dayDiff / totalDurationDays) * 100; if (currentDate.getDate() === 1 || currentDate.getTime() === projectStartDate.getTime()) { const monthLabel = document.createElement('div'); monthLabel.className = 'absolute text-xs text-gray-500'; monthLabel.textContent = currentDate.toLocaleString('en-US', { month: 'short', year: '2-digit' }); monthLabel.style.left = `${leftPercent}%`; ganttChart.timeline.appendChild(monthLabel); } currentDate.setDate(currentDate.getDate() + 1); } // Render Tasks const taskPositions = {}; // Store { taskId: { y: yPos, startX: xPos, endX: xPos } } const rowHeight = 48; // h-8 (32px) + margin (16px) ganttChart.tasks.style.height = `${sortedTasks.length * rowHeight}px`; sortedTasks.forEach((task, index) => { const startDate = new Date(task.start); const endDate = new Date(task.end); const startOffsetDays = (startDate - projectStartDate) / (1000 * 60 * 60 * 24); const durationDays = (endDate - startDate) / (1000 * 60 * 60 * 24) + 1; const left = (startOffsetDays / totalDurationDays) * 100; const width = (durationDays / totalDurationDays) * 100; const top = index * rowHeight; const taskBar = document.createElement('div'); taskBar.className = 'gantt-task-bar'; taskBar.id = `task-bar-${task.id}`; taskBar.style.left = `${left}%`; taskBar.style.width = `${width}%`; taskBar.style.top = `${top}px`; taskBar.title = `${task.name} (${task.start} to ${task.end})`; taskBar.innerHTML = `${task.name}`; ganttChart.tasks.appendChild(taskBar); // Store position for drawing lines taskPositions[task.id] = { y: top + 16, // middle of the bar startX: left, endX: left + width }; }); // Render Dependency Lines sortedTasks.forEach(task => { if (task.dependencies.length > 0) { task.dependencies.forEach(depId => { const dependentPos = taskPositions[task.id]; const prerequisitePos = taskPositions[depId]; if (dependentPos && prerequisitePos) { const line = document.createElementNS('http://www.w3.org/2000/svg', 'path'); const fromX = prerequisitePos.endX; const fromY = prerequisitePos.y; const toX = dependentPos.startX; const toY = dependentPos.y; // Calculate path for a nice curve const controlX1 = fromX + (toX - fromX) * 0.5; const controlY1 = fromY; const controlX2 = fromX + (toX - fromX) * 0.5; const controlY2 = toY; const pathData = `M ${fromX}% ${fromY} C ${controlX1}% ${controlY1}, ${controlX2}% ${controlY2}, ${toX}% ${toY}`; line.setAttribute('d', pathData); line.setAttribute('class', 'gantt-dependency-line'); ganttChart.lines.appendChild(line); } }); } }); }; const renderAll = () => { renderTaskList(); renderDashboard(); }; // --- INITIALIZATION --- taskForm.addEventListener('submit', handleFormSubmit); switchTab('dashboard'); // Set initial tab renderAll(); });