Files
minicrm/public/tasks.js
2026-02-24 23:08:35 +05:00

621 lines
21 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// tasks.js - Основные операции с задачами
let 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 || '';
// Формируем 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)}`);
}
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();
console.log(`Загружено ${tasks.length} задач`);
// Загружаем поля документа для задач типа "document"
await loadDocumentFieldsForTasks();
// Загружаем файлы для развернутых задач
await loadFilesForExpandedTasks();
// Обновляем отображение
const activeSection = document.querySelector('.section.active');
if (activeSection) {
const sectionId = activeSection.id;
if (sectionId === 'mytasks-section') {
filterMyTasks();
} else if (sectionId === 'runtasks-section') {
filterRunTasks();
} else {
renderTasks();
}
} else {
renderTasks();
}
} catch (error) {
console.error('Ошибка загрузки задач:', error);
const container = document.getElementById('tasks-list');
if (container) {
container.innerHTML = '<div class="error">Ошибка загрузки задач</div>';
}
}
}
// Новая функция для загрузки полей документов
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;