559 lines
20 KiB
JavaScript
559 lines
20 KiB
JavaScript
// 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)}`); // было creator_id, теперь creator
|
||
}
|
||
|
||
if (assigneeFilter) {
|
||
params.push(`assignee=${encodeURIComponent(assigneeFilter)}`); // было assignee_id, теперь assignee
|
||
}
|
||
|
||
if (deadlineFilter) {
|
||
params.push(`deadline=${encodeURIComponent(deadlineFilter)}`);
|
||
}
|
||
|
||
if (searchQuery) {
|
||
params.push(`search=${encodeURIComponent(searchQuery)}`);
|
||
}
|
||
|
||
if (typeFilter) {
|
||
params.push(`task_type=${encodeURIComponent(typeFilter)}`); // было type, теперь task_type
|
||
}
|
||
|
||
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} задач`);
|
||
|
||
// Обновляем отображение
|
||
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>';
|
||
}
|
||
}
|
||
}
|
||
|
||
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 = [];
|
||
}
|
||
}));
|
||
|
||
renderTasks();
|
||
} catch (error) {
|
||
console.error('Ошибка загрузки задач без срока:', error);
|
||
}
|
||
}
|
||
|
||
// удален async function createTask(event)
|
||
|
||
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;
|
||
}
|
||
|
||
// Используем editSelectedUsers
|
||
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;
|
||
|
||
// Устанавливаем дату по умолчанию (через 7 дней)
|
||
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;
|
||
}
|
||
|
||
// Используем copySelectedUsers
|
||
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;
|
||
// Пользователи с ролью 'tasks' могут редактировать любые задачи
|
||
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;
|
||
} |