// tasks.js - Основные операции с задачами let tasks = []; // локальная переменная window.tasks = tasks; // делаем доступной глобально let expandedTasks = new Set(); let showingTasksWithoutDate = false; // Функция загрузки задач с фильтрацией на сервере async function loadTasks() { try { // Получаем значения фильтров const statusFilter = document.getElementById('status-filter')?.value || 'active,in_progress,assigned,overdue,rework'; const creatorFilter = document.getElementById('creator-filter')?.value || ''; const assigneeFilter = document.getElementById('assignee-filter')?.value || ''; const deadlineFilter = document.getElementById('deadline-filter')?.value || ''; const searchQuery = document.getElementById('search-tasks')?.value || ''; const typeFilter = document.getElementById('type-filter')?.value || ''; // ===== ДОБАВЛЕНО: получаем текущий вид из глобальной переменной ===== const view = window.currentTaskView || 'all'; // 'all', 'my_assigned', 'assigned_to_me' // Формируем URL с параметрами let url = '/api/tasks?'; const params = []; if (statusFilter !== 'all') { params.push(`status=${encodeURIComponent(statusFilter)}`); } if (creatorFilter) { params.push(`creator=${encodeURIComponent(creatorFilter)}`); } if (assigneeFilter) { params.push(`assignee=${encodeURIComponent(assigneeFilter)}`); } if (deadlineFilter) { params.push(`deadline=${encodeURIComponent(deadlineFilter)}`); } if (searchQuery) { params.push(`search=${encodeURIComponent(searchQuery)}`); } if (typeFilter) { params.push(`task_type=${encodeURIComponent(typeFilter)}`); } if (view !== 'all') { params.push(`view=${encodeURIComponent(view)}`); } url += params.join('&'); ////console.log('Загрузка задач с фильтрами:', url); const response = await fetch(url); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } tasks = await response.json(); window.tasks = tasks; // синхронизируем глобальную переменную //console.log(`Загружено ${tasks.length} задач`); // Загружаем поля документа для задач типа "document" await loadDocumentFieldsForTasks(); // Загружаем файлы для развернутых задач await loadFilesForExpandedTasks(); // Обновляем отображение в зависимости от активной секции renderTasksForActiveSection(); } catch (error) { console.error('Ошибка загрузки задач:', error); const container = document.getElementById('tasks-list'); if (container) { container.innerHTML = '
Ошибка загрузки задач
'; } } } // Новая функция для определения активной секции и рендеринга function renderTasksForActiveSection() { const activeSection = document.querySelector('.section.active'); if (!activeSection) { renderTasks(); // по умолчанию return; } const sectionId = activeSection.id; if (sectionId === 'mytasks-section') { if (typeof renderMyTasks === 'function') { renderMyTasks(); } else { renderTasksInContainer('mytasks-list', tasks); } } else if (sectionId === 'runtasks-section') { if (typeof renderRunTasks === 'function') { renderRunTasks(); } else { renderTasksInContainer('runtasks-list', tasks); } } else if (sectionId === 'tasks-section') { renderTasks(); } else { renderTasks(); } } // Новая функция для загрузки полей документов async function loadDocumentFieldsForTasks() { const documentTasks = tasks.filter(task => task.task_type === 'document'); if (documentTasks.length === 0) { return; } //console.log(`Загрузка полей документов для ${documentTasks.length} задач`); // Загружаем поля для каждой задачи for (const task of documentTasks) { try { const docResponse = await fetch(`/api/tasks/${task.id}/document-fields`); if (docResponse.ok) { const docData = await docResponse.json(); task.document_fields = docData.data || {}; //console.log(`✅ Загружены поля для задачи ${task.id}:`, task.document_fields); } else { task.document_fields = {}; } } catch (error) { console.error(`Ошибка загрузки полей документа для задачи ${task.id}:`, error); task.document_fields = {}; } } } // Новая функция для загрузки файлов развернутых задач async function loadFilesForExpandedTasks() { const expandedTasksArray = tasks.filter(task => expandedTasks.has(task.id)); if (expandedTasksArray.length === 0) { return; } await Promise.all(expandedTasksArray.map(async (task) => { try { const filesResponse = await fetch(`/api/tasks/${task.id}/files`); if (filesResponse.ok) { task.files = await filesResponse.json(); } else { task.files = []; } } catch (error) { console.error(`Ошибка загрузки файлов для задачи ${task.id}:`, error); task.files = []; } })); } function showTasksWithoutDate() { showingTasksWithoutDate = true; const btn = document.getElementById('tasks-no-date-btn'); if (btn) btn.classList.add('active'); loadTasksWithoutDate(); } function resetAllFilters() { document.getElementById('status-filter').value = 'active,in_progress,assigned,overdue,rework'; document.getElementById('creator-filter').value = ''; document.getElementById('assignee-filter').value = ''; document.getElementById('deadline-filter').value = ''; document.getElementById('type-filter').value = ''; document.getElementById('search-tasks').value = ''; loadTasks(); } async function loadTasksWithoutDate() { try { const response = await fetch('/api/tasks'); if (!response.ok) throw new Error('Ошибка загрузки задач'); const allTasks = await response.json(); tasks = allTasks.filter(task => { const hasTaskDueDate = !task.due_date; const hasAssignmentDueDates = task.assignments && task.assignments.every(assignment => !assignment.due_date); return hasTaskDueDate && hasAssignmentDueDates; }); // Загружаем файлы для всех задач await Promise.all(tasks.map(async (task) => { try { const filesResponse = await fetch(`/api/tasks/${task.id}/files`); if (filesResponse.ok) { task.files = await filesResponse.json(); } else { task.files = []; } } catch (error) { console.error(`Ошибка загрузки файлов для задачи ${task.id}:`, error); task.files = []; } })); // Загружаем поля документов await loadDocumentFieldsForTasks(); renderTasks(); } catch (error) { console.error('Ошибка загрузки задач без срока:', error); } } async function openEditModal(taskId) { try { const response = await fetch(`/api/tasks/${taskId}`); if (!response.ok) { if (response.status === 404) { alert('Задача не найдена или у вас нет прав доступа'); } throw new Error('Ошибка загрузки задачи'); } const task = await response.json(); if (!canUserEditTask(task)) { alert('У вас нет прав для редактирования этой задачи'); return; } document.getElementById('edit-task-id').value = task.id; document.getElementById('edit-title').value = task.title; document.getElementById('edit-description').value = task.description || ''; document.getElementById('edit-due-date').value = task.due_date ? formatDateTimeForInput(task.due_date) : ''; // Устанавливаем выбранных пользователей editSelectedUsers = task.assignments ? task.assignments.map(a => a.user_id) : []; renderEditUsersChecklist(users); // Показываем существующие файлы currentEditTaskFiles = task.files || []; updateEditFileList(); document.getElementById('edit-task-modal').style.display = 'block'; } catch (error) { console.error('Ошибка:', error); alert('Ошибка загрузки задачи'); } } function closeEditModal() { document.getElementById('edit-task-modal').style.display = 'none'; document.getElementById('edit-file-list').innerHTML = ''; document.getElementById('edit-user-search').value = ''; editSelectedUsers = []; currentEditTaskFiles = []; filterEditUsers(); } async function updateTask(event) { event.preventDefault(); const taskId = document.getElementById('edit-task-id').value; const title = document.getElementById('edit-title').value; const description = document.getElementById('edit-description').value; const dueDate = document.getElementById('edit-due-date').value; if (!dueDate) { alert('Дата и время выполнения обязательны'); return; } const assignedUserIds = editSelectedUsers; const formData = new FormData(); formData.append('title', title); formData.append('description', description); formData.append('assignedUsers', JSON.stringify(assignedUserIds)); formData.append('dueDate', dueDate); const files = document.getElementById('edit-files').files; for (let i = 0; i < files.length; i++) { formData.append('files', files[i]); } try { const response = await fetch(`/api/tasks/${taskId}`, { method: 'PUT', body: formData }); if (response.ok) { alert('Задача успешно обновлена!'); closeEditModal(); loadTasks(); loadActivityLogs(); } else { const error = await response.json(); alert(error.error || 'Ошибка обновления задачи'); } } catch (error) { console.error('Ошибка:', error); alert('Ошибка обновления задачи'); } } function openCopyModal(taskId) { document.getElementById('copy-task-id').value = taskId; const defaultDate = new Date(); defaultDate.setDate(defaultDate.getDate() + 7); document.getElementById('copy-due-date').value = defaultDate.toISOString().substring(0, 16); copySelectedUsers = []; renderCopyUsersChecklist(users); document.getElementById('copy-task-modal').style.display = 'block'; } function closeCopyModal() { document.getElementById('copy-task-modal').style.display = 'none'; document.getElementById('copy-user-search').value = ''; copySelectedUsers = []; filterCopyUsers(); } async function copyTask(event) { event.preventDefault(); const taskId = document.getElementById('copy-task-id').value; const dueDate = document.getElementById('copy-due-date').value; if (!dueDate) { alert('Дата и время выполнения обязательны для копии задачи'); return; } const assignedUserIds = copySelectedUsers; if (assignedUserIds.length === 0) { alert('Выберите хотя бы одного исполнителя для копии задачи'); return; } try { const response = await fetch(`/api/tasks/${taskId}/copy`, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ assignedUsers: assignedUserIds, dueDate: dueDate }) }); if (response.ok) { alert('Копия задачи успешно создана!'); closeCopyModal(); loadTasks(); loadActivityLogs(); } else { const error = await response.json(); alert(error.error || 'Ошибка создания копии задачи'); } } catch (error) { console.error('Ошибка:', error); alert('Ошибка создания копии задачи'); } } async function closeTask(taskId) { if (!confirm('Вы уверены, что хотите закрыть эту задачу? Исполнители больше не будут видеть её.')) { return; } try { const response = await fetch(`/api/tasks/${taskId}/close`, { method: 'POST' }); if (response.ok) { alert('Задача закрыта!'); loadTasks(); loadActivityLogs(); } else { const error = await response.json(); alert(error.error || 'Ошибка закрытия задачи'); } } catch (error) { console.error('Ошибка:', error); alert('Ошибка закрытия задачи'); } } async function reopenTask(taskId) { try { const response = await fetch(`/api/tasks/${taskId}/reopen`, { method: 'POST' }); if (response.ok) { alert('Задача открыта!'); loadTasks(); loadActivityLogs(); } else { const error = await response.json(); alert(error.error || 'Ошибка открытия задачи'); } } catch (error) { console.error('Ошибка:', error); alert('Ошибка открытия задачи'); } } async function deleteTask(taskId) { if (!confirm('Вы уверены, что хотите удалить эту задачу?')) { return; } try { const response = await fetch(`/api/tasks/${taskId}`, { method: 'DELETE' }); if (response.ok) { alert('Задача удалена!'); loadTasks(); loadActivityLogs(); } else { const error = await response.json(); alert(error.error || 'Ошибка удаления задачи'); } } catch (error) { console.error('Ошибка:', error); alert('Ошибка удаления задачи'); } } async function restoreTask(taskId) { try { const response = await fetch(`/api/tasks/${taskId}/restore`, { method: 'POST' }); if (response.ok) { alert('Задача восстановлена!'); loadTasks(); loadActivityLogs(); } else { const error = await response.json(); alert(error.error || 'Ошибка восстановления задачи'); } } catch (error) { console.error('Ошибка:', error); alert('Ошибка восстановления задачи'); } } function openEditAssignmentModal(taskId, userId) { const task = tasks.find(t => t.id === taskId); if (!task) return; const assignment = task.assignments.find(a => a.user_id === userId); if (!assignment) return; document.getElementById('edit-assignment-task-id').value = taskId; document.getElementById('edit-assignment-user-id').value = userId; document.getElementById('edit-assignment-due-date').value = assignment.due_date ? formatDateTimeForInput(assignment.due_date) : ''; document.getElementById('edit-assignment-modal').style.display = 'block'; } function closeEditAssignmentModal() { document.getElementById('edit-assignment-modal').style.display = 'none'; } async function updateAssignment(event) { event.preventDefault(); const taskId = document.getElementById('edit-assignment-task-id').value; const userId = document.getElementById('edit-assignment-user-id').value; const dueDate = document.getElementById('edit-assignment-due-date').value; if (!dueDate) { alert('Дата и время выполнения обязательны'); return; } try { const response = await fetch(`/api/tasks/${taskId}/assignment/${userId}`, { method: 'PUT', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ dueDate: dueDate }) }); if (response.ok) { alert('Сроки исполнителя обновлены!'); closeEditAssignmentModal(); loadTasks(); loadActivityLogs(); } else { const error = await response.json(); alert(error.error || 'Ошибка обновления сроков'); } } catch (error) { console.error('Ошибка:', error); alert('Ошибка обновления сроков'); } } function openReworkModal(taskId) { document.getElementById('rework-task-id').value = taskId; document.getElementById('rework-task-modal').style.display = 'block'; } function closeReworkModal() { document.getElementById('rework-task-modal').style.display = 'none'; document.getElementById('rework-comment').value = ''; } async function sendForRework(event) { event.preventDefault(); const taskId = document.getElementById('rework-task-id').value; const comment = document.getElementById('rework-comment').value; try { const response = await fetch(`/api/tasks/${taskId}/rework`, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ comment }) }); if (response.ok) { alert('Задача возвращена на доработку!'); closeReworkModal(); loadTasks(); loadActivityLogs(); } else { const error = await response.json(); alert(error.error || 'Ошибка возврата задачи на доработку'); } } catch (error) { console.error('Ошибка:', error); alert('Ошибка возврата задачи на доработку'); } } async function updateStatus(taskId, userId, status) { try { const response = await fetch(`/api/tasks/${taskId}/status`, { method: 'PUT', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ userId, status }) }); if (response.ok) { loadTasks(); loadActivityLogs(); } else { const error = await response.json(); alert(error.error || 'Ошибка обновления статуса'); } } catch (error) { console.error('Ошибка:', error); alert('Ошибка обновления статуса'); } } function canUserEditTask(task) { if (!currentUser) return false; if (currentUser.role === 'admin') return true; if (currentUser.role === 'tasks') return true; if (parseInt(task.created_by) === currentUser.id) { if (task.assignments && task.assignments.length > 0) { const assignedToOthers = task.assignments.some(assignment => parseInt(assignment.user_id) !== currentUser.id ); if (assignedToOthers) { return true; } } return true; } if (task.assignments) { const isExecutor = task.assignments.some(assignment => parseInt(assignment.user_id) === currentUser.id ); if (isExecutor) { return false; } } return false; } function canUserAddFilesToTask(task) { if (!currentUser) return false; if (currentUser.role === 'admin') return true; if (parseInt(task.created_by) === currentUser.id) return true; if (task.assignments) { const isExecutor = task.assignments.some(assignment => parseInt(assignment.user_id) === currentUser.id ); return isExecutor; } return false; } // Добавляем отладочную функцию function debugDocumentFields() { console.log('=== ОТЛАДКА ПОЛЕЙ ДОКУМЕНТОВ ==='); const documentTasks = tasks.filter(task => task.task_type === 'document'); console.log(`Найдено ${documentTasks.length} задач типа "document"`); documentTasks.forEach(task => { console.log(`Задача ${task.id}:`, { title: task.title, document_fields: task.document_fields || 'НЕТ ПОЛЕЙ' }); }); console.log('================================'); } // Вызываем отладку после загрузки (можно вызвать вручную из консоли) window.debugDocumentFields = debugDocumentFields; window.loadTasks = loadTasks; window.updateAssignment = updateAssignment; window.renderTasksForActiveSection = renderTasksForActiveSection;