// Sub-task Management Functions // Global variable to track subtasks let currentSubtasks = []; // Initialize subtasks when loading a task function initializeSubtasks(taskId) { currentSubtasks = []; const subtasksContainer = document.getElementById('subtasks-container'); if (!subtasksContainer) return; subtasksContainer.innerHTML = '
Loading subtasks...
'; if (taskId) { // Fetch existing subtasks fetch(`/api/tasks/${taskId}`) .then(response => response.json()) .then(task => { if (task.subtasks) { currentSubtasks = task.subtasks; renderSubtasks(); } else { subtasksContainer.innerHTML = '

No subtasks yet

'; } }) .catch(error => { console.error('Error loading subtasks:', error); subtasksContainer.innerHTML = '

Error loading subtasks

'; }); } else { renderSubtasks(); } } // Render subtasks in the modal function renderSubtasks() { const container = document.getElementById('subtasks-container'); if (!container) return; if (currentSubtasks.length === 0) { container.innerHTML = '

No subtasks yet

'; return; } container.innerHTML = currentSubtasks.map((subtask, index) => `
`).join(''); } // Add a new subtask function addSubtask() { const newSubtask = { name: '', status: 'TODO', priority: 'MEDIUM', assigned_to_id: null, isNew: true }; currentSubtasks.push(newSubtask); renderSubtasks(); // Focus on the new subtask input setTimeout(() => { const inputs = document.querySelectorAll('.subtask-name'); if (inputs.length > 0) { inputs[inputs.length - 1].focus(); } }, 50); } // Remove a subtask function removeSubtask(index) { const subtask = currentSubtasks[index]; if (subtask.id) { // If it has an ID, it exists in the database if (confirm('Are you sure you want to delete this subtask?')) { fetch(`/api/subtasks/${subtask.id}`, { method: 'DELETE', headers: { 'Content-Type': 'application/json' } }) .then(response => { if (response.ok) { currentSubtasks.splice(index, 1); renderSubtasks(); showNotification('Subtask deleted successfully', 'success'); } else { throw new Error('Failed to delete subtask'); } }) .catch(error => { console.error('Error deleting subtask:', error); showNotification('Error deleting subtask', 'error'); }); } } else { // Just remove from array if not saved yet currentSubtasks.splice(index, 1); renderSubtasks(); } } // Update subtask name function updateSubtaskName(index, name) { currentSubtasks[index].name = name; } // Update subtask priority function updateSubtaskPriority(index, priority) { currentSubtasks[index].priority = priority; } // Update subtask assignee function updateSubtaskAssignee(index, assigneeId) { currentSubtasks[index].assigned_to_id = assigneeId || null; } // Toggle subtask status function toggleSubtaskStatus(index) { const subtask = currentSubtasks[index]; const newStatus = subtask.status === 'DONE' ? 'TODO' : 'DONE'; if (subtask.id) { // Update in database fetch(`/api/subtasks/${subtask.id}`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ status: newStatus }) }) .then(response => response.json()) .then(updatedSubtask => { currentSubtasks[index] = updatedSubtask; renderSubtasks(); updateTaskProgress(); }) .catch(error => { console.error('Error updating subtask status:', error); showNotification('Error updating subtask status', 'error'); renderSubtasks(); // Re-render to revert checkbox }); } else { currentSubtasks[index].status = newStatus; } } // Save all subtasks for a task function saveSubtasks(taskId) { const promises = []; currentSubtasks.forEach(subtask => { if (subtask.isNew && subtask.name.trim()) { // Create new subtask promises.push( fetch('/api/subtasks', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ task_id: taskId, name: subtask.name, priority: subtask.priority, assigned_to_id: subtask.assigned_to_id, status: subtask.status }) }) .then(response => response.json()) ); } else if (subtask.id && !subtask.isNew) { // Update existing subtask promises.push( fetch(`/api/subtasks/${subtask.id}`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ name: subtask.name, priority: subtask.priority, assigned_to_id: subtask.assigned_to_id, status: subtask.status }) }) .then(response => response.json()) ); } }); return Promise.all(promises); } // Update task progress based on subtasks function updateTaskProgress() { const taskId = document.getElementById('task-id').value; if (!taskId) return; // Refresh the task card in the board if (typeof refreshTaskCard === 'function') { refreshTaskCard(taskId); } } // Render assignee options function renderAssigneeOptions(selectedId) { // This should be populated from the global team members list const teamMembers = window.teamMembers || []; return teamMembers.map(member => `` ).join(''); } // Helper function to escape HTML function escapeHtml(unsafe) { return unsafe .replace(/&/g, "&") .replace(//g, ">") .replace(/"/g, """) .replace(/'/g, "'"); } // Display subtasks in task cards function renderSubtasksInCard(subtasks) { if (!subtasks || subtasks.length === 0) return ''; const completedCount = subtasks.filter(s => s.status === 'COMPLETED').length; const totalCount = subtasks.length; const percentage = Math.round((completedCount / totalCount) * 100); return `
${completedCount}/${totalCount} subtasks
`; } // Add subtask styles const subtaskStyles = ` `; // Inject styles when document is ready if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', function() { document.head.insertAdjacentHTML('beforeend', subtaskStyles); }); } else { document.head.insertAdjacentHTML('beforeend', subtaskStyles); }