// nav-task-actions.js – модальное окно с действиями задачи (сетка 3 колонки, центрированное) (function() { 'use strict'; const LOG_ENABLED = 0; function log(...args) { if (LOG_ENABLED) console.log(...args); } // ========== ВСПОМОГАТЕЛЬНЫЕ ФУНКЦИИ ДЛЯ РЕКВИЗИТОВ ========== function escapeHtml(str) { if (!str) return ''; return String(str).replace(/[&<>]/g, function(m) { if (m === '&') return '&'; if (m === '<') return '<'; if (m === '>') return '>'; return m; }); } function updateTaskCardDisplay(taskId, data) { const taskCards = document.querySelectorAll(`[data-task-id="${taskId}"]`); taskCards.forEach(card => { let docContainer = card.querySelector('.document-fields-display'); if (!docContainer) { docContainer = document.createElement('div'); docContainer.className = 'document-fields-display'; const header = card.querySelector('.task-header'); if (header) header.after(docContainer); else card.prepend(docContainer); } let html = '
'; if (data.document_n) html += `№: ${escapeHtml(data.document_n)}`; if (data.document_d) html += `Дата: ${escapeHtml(data.document_d)}`; if (data.document_a) html += `Автор: ${escapeHtml(data.document_a)}`; if (!data.document_n && !data.document_d) html += 'Нет данных документа'; html += '
'; docContainer.innerHTML = html; }); } async function openDocumentFieldsModal(taskId) { const existing = document.getElementById('doc-fields-modal'); if (existing) existing.remove(); // Загружаем текущие поля let fields = { document_n: '', document_d: '', document_a: '' }; try { const response = await fetch(`/api/tasks/${taskId}/document-fields`); if (response.ok) { const result = await response.json(); fields = result.data || {}; } } catch (error) { console.error('Ошибка загрузки реквизитов:', error); } // Преобразуем дату из ДД.ММ.ГГГГ в YYYY-MM-DD для input type="date" let dateValue = ''; if (fields.document_d) { const parts = fields.document_d.split('.'); if (parts.length === 3) { dateValue = `${parts[2]}-${parts[1]}-${parts[0]}`; } } // Проверяем права на редактирование let canEdit = false; const hasFields = !!(fields.document_n || fields.document_d); if (hasFields) { canEdit = fields.document_a === currentUser?.login; } else { try { const groupsRes = await fetch(`/api2/idusers/user/${currentUser.id}/groups`); if (groupsRes.ok) { const groups = await groupsRes.json(); canEdit = groups.some(g => g === 'Подписант' || g === 'Секретарь' || g.includes('Подписант') || g.includes('Секретарь')); } } catch (error) { console.error('Ошибка проверки групп:', error); } } // Создаём модальное окно const modal = document.createElement('div'); modal.id = 'doc-fields-modal'; modal.className = 'modal'; modal.style.display = 'flex'; modal.style.alignItems = 'center'; modal.style.justifyContent = 'center'; modal.style.zIndex = '10001'; modal.innerHTML = ` `; document.body.appendChild(modal); window.closeDocFieldsModal = function() { const m = document.getElementById('doc-fields-modal'); if (m) m.remove(); delete window.closeDocFieldsModal; }; modal.addEventListener('click', (e) => { if (e.target === modal) window.closeDocFieldsModal(); }); if (canEdit) { const saveBtn = document.getElementById('save-doc-fields'); saveBtn.addEventListener('click', async () => { const number = document.getElementById('doc-number').value.trim(); const dateInput = document.getElementById('doc-date'); let formattedDate = ''; if (dateInput.value) { // Преобразуем YYYY-MM-DD в ДД.ММ.ГГГГ const parts = dateInput.value.split('-'); if (parts.length === 3) { formattedDate = `${parts[2]}.${parts[1]}.${parts[0]}`; } } const author = document.getElementById('doc-author').value.trim() || currentUser?.login; saveBtn.disabled = true; saveBtn.textContent = 'Сохранение...'; try { const response = await fetch(`/api/tasks/${taskId}/document-fields`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ document_n: number || null, document_d: formattedDate || null, document_a: author }) }); const result = await response.json(); if (response.ok && result.success) { alert('✅ Реквизиты сохранены'); window.closeDocFieldsModal(); const task = window.tasks?.find(t => t.id == taskId); if (task) task.document_fields = result.data; updateTaskCardDisplay(taskId, result.data); if (typeof loadTasks === 'function') loadTasks(); } else { alert('❌ Ошибка: ' + (result.error || 'Неизвестная ошибка')); } } catch (error) { console.error('Ошибка сохранения:', error); alert('Сетевая ошибка'); } finally { saveBtn.disabled = false; saveBtn.textContent = 'Сохранить'; } }); } } // ========== ОСНОВНАЯ ЛОГИКА МЕНЮ ДЕЙСТВИЙ ========== // Сбор доступных действий для конкретной задачи function buildActionsForTask(task) { const taskId = task.id; const isDeleted = task.status === 'deleted'; const isClosed = task.closed_at !== null; const userRole = window.getUserRoleInTask ? window.getUserRoleInTask(task) : 'Наблюдатель'; const canEdit = window.canUserEditTask ? window.canUserEditTask(task) : false; const actions = []; // ==== ДОБАВЛЕННЫЙ БЛОК: кнопки для текущего исполнителя if (currentUser && !isDeleted && !isClosed) { const myAssignment = task.assignments?.find(a => parseInt(a.user_id) === currentUser.id); if (myAssignment) { if (myAssignment.status === 'assigned') { actions.push({ label: '▶️ Приступить', handler: () => window.updateStatus(taskId, currentUser.id, 'in_progress'), primary: true }); } if (['in_progress', 'overdue', 'rework'].includes(myAssignment.status)) { const isDocumentTask = task.task_type === 'document'; const handler = isDocumentTask ? () => window.openDocumentCompleteModal(taskId, currentUser.id) : () => window.updateStatus(taskId, currentUser.id, 'completed'); actions.push({ label: '✅ Выполнено', handler: handler, primary: true }); } } } // Копия – доступна всем, у кого есть кнопка if (typeof openCopyModal === 'function') { actions.push({ label: '📋 Создать копию', handler: () => openCopyModal(taskId), primary: true }); } // Действия для активных (не удалённых, не закрытых) задач if (!isDeleted && !isClosed && currentUser.login === 'minicrm') { if (typeof openTaskChat === 'function') { actions.push({ label: '💬 Чат', handler: () => openTaskChat(taskId) }); } if (typeof openAddFileModal === 'function') { actions.push({ label: '📎 Добавить файл', handler: () => openAddFileModal(taskId) }); } } // Специальные пользователи if (currentUser && currentUser.login === 'minicrm') { if (typeof openEditModal === 'function') { actions.push({ label: '✏️ Редактировать', handler: () => openEditModal(taskId) }); } if (typeof openManageAssigneesModal === 'function') { actions.push({ label: '👥 Управление исполнителями', handler: () => openManageAssigneesModal(taskId) }); } } // Подписание (только для документов, если текущий пользователь - исполнитель) if (!isDeleted && !isClosed && task.task_type === 'document' && currentUser && task.assignments?.some(a => parseInt(a.user_id) === currentUser.id)) { if (typeof window.signTask === 'function') { actions.push({ label: '✍️ Подписать', handler: () => window.signTask(taskId, currentUser.id), admin: true }); } } // Реквизиты документа (для задач типа document) if (!isDeleted && !isClosed && task.task_type === 'document' && currentUser && task.assignments?.some(a => parseInt(a.user_id) === currentUser.id)) { actions.push({ label: '📄 Реквизиты', handler: () => openDocumentFieldsModal(taskId), admin: false }); } // Администраторы и роль tasks if (currentUser && (currentUser.role === 'admin' || (currentUser.role === 'tasks' && canEdit))) { if (typeof assignAdd_openModal === 'function') { actions.push({ label: '🧑‍💼➕ Добавить исполнителя', handler: () => assignAdd_openModal(taskId), admin: true }); } if (typeof assignRemove_openModal === 'function') { actions.push({ label: '🧑‍💼❌ Удалить исполнителя', handler: () => assignRemove_openModal(taskId), admin: true }); } if (currentUser && (currentUser.role === 'admin' && currentUser.login === 'minicrm')) { if (typeof openReworkModal === 'function') { actions.push({ label: '🔄 Переделать документ', handler: () => openReworkModal(taskId) }); } } } // Кнопка "Создать ознакомление" для админов и tasks if (currentUser && (currentUser.role === 'admin' || currentUser.role === 'tasks')) { actions.push({ label: '📖 Создать ознакомление', handler: () => openAcquaintanceModal(task.id), primary: true }); } // Доработка и изменение срока для необычных задач (исполнитель) if (!isDeleted && !isClosed && task.task_type !== 'regular' && task.assignments && task.assignments.some(a => parseInt(a.user_id) === currentUser?.id)) { if (typeof openReworkModal === 'function') { actions.push({ label: '🔄 Доработка', handler: () => openReworkModal(taskId), primary_task: true }); } if (typeof openChangeDeadlineModal === 'function') { actions.push({ label: '📅 Изменить срок', handler: () => openChangeDeadlineModal(taskId), primary_task: true }); } } if (!isDeleted && !isClosed && task.task_type == 'regular') { if (typeof closeTask === 'function') { actions.push({ label: '🔒 Закрыть задачу', handler: () => closeTask(taskId), admin: true }); } } if (canEdit && !isDeleted && !isClosed && currentUser.role === 'admin') { if (typeof deleteTask === 'function') actions.push({ label: '🗑️ Удалить', handler: () => deleteTask(taskId) }); } if (currentUser && currentUser.login === 'minicrm') { if (typeof closeTask === 'function') actions.push({ label: '🔒 Закрыть задачу', handler: () => closeTask(taskId) }); if (canEdit && !isDeleted && !isClosed && currentUser.role === 'admin') { if (typeof deleteTask === 'function') actions.push({ label: '🗑️ Удалить', handler: () => deleteTask(taskId) }); } if (isClosed && canEdit) { if (typeof reopenTask === 'function') actions.push({ label: '🔓 Открыть задачу', handler: () => reopenTask(taskId) }); } if (isDeleted && window.currentUser?.role === 'admin') { if (typeof restoreTask === 'function') actions.push({ label: '↶ Восстановить', handler: () => restoreTask(taskId) }); } } return actions; } // Рендеринг кнопки меню window.renderTaskActions = function(task) { if (!task) return ''; return `
`; }; // Удалить модальное окно, если оно есть function removeModal() { const existing = document.getElementById('task-action-modal'); if (existing) existing.remove(); } // Обработчик клика на кнопку меню document.addEventListener('click', function(e) { const menuBtn = e.target.closest('.task-actions-menu-btn'); if (!menuBtn) return; e.preventDefault(); e.stopPropagation(); removeModal(); const taskId = menuBtn.dataset.taskId; if (!taskId) { console.error('No task id found on button'); return; } const task = window.tasks?.find(t => t.id == taskId); if (!task) { console.error('Task not found for id', taskId); return; } const actions = buildActionsForTask(task); if (actions.length === 0) return; const primaryActions = actions.filter(a => a.primary); const adminActions = actions.filter(a => a.admin && !a.primary); const taskActions = actions.filter(a => a.primary_task && !a.primary && !a.primary); const userActions = actions.filter(a => !a.primary && !a.admin && !a.primary_task); const overlay = document.createElement('div'); overlay.id = 'task-action-modal'; overlay.style.position = 'fixed'; overlay.style.top = '0'; overlay.style.left = '0'; overlay.style.width = '100%'; overlay.style.height = '100%'; overlay.style.backgroundColor = 'rgba(0,0,0,0.5)'; overlay.style.zIndex = '10000'; overlay.style.display = 'flex'; overlay.style.alignItems = 'center'; overlay.style.justifyContent = 'center'; const modal = document.createElement('div'); modal.style.backgroundColor = 'white'; modal.style.borderRadius = '12px'; modal.style.padding = '25px'; modal.style.maxWidth = '500px'; modal.style.width = '90%'; modal.style.boxShadow = '0 10px 40px rgba(0,0,0,0.2)'; modal.style.position = 'relative'; modal.style.maxHeight = '85vh'; modal.style.overflowY = 'auto'; const header = document.createElement('div'); header.style.display = 'flex'; header.style.justifyContent = 'space-between'; header.style.alignItems = 'center'; header.style.marginBottom = '20px'; header.style.paddingBottom = '10px'; header.style.borderBottom = '1px solid #eee'; const title = document.createElement('h3'); title.style.margin = '0'; title.style.fontSize = '18px'; title.style.color = '#333'; title.style.flex = '1'; title.style.textAlign = 'center'; title.textContent = `Действия для задачи #${taskId}`; const closeBtn = document.createElement('span'); closeBtn.innerHTML = '×'; closeBtn.style.fontSize = '28px'; closeBtn.style.fontWeight = 'bold'; closeBtn.style.cursor = 'pointer'; closeBtn.style.color = '#999'; closeBtn.style.transition = 'color 0.2s'; closeBtn.onmouseover = () => closeBtn.style.color = '#e74c3c'; closeBtn.onmouseout = () => closeBtn.style.color = '#999'; closeBtn.onclick = () => removeModal(); header.appendChild(title); header.appendChild(closeBtn); modal.appendChild(header); // БЛОК: primary actions if (primaryActions.length > 0) { const primaryContainer = document.createElement('div'); primaryContainer.style.marginBottom = '20px'; primaryActions.forEach(a => { const btn = document.createElement('button'); btn.textContent = a.label; btn.style.cssText = ` width: 100%; padding: 14px; background-color: #27ae60; color: white; border: none; border-radius: 8px; font-size: 16px; font-weight: 500; cursor: pointer; transition: background-color 0.2s; margin-bottom: 10px; `; btn.onmouseover = () => { btn.style.backgroundColor = '#229954'; }; btn.onmouseout = () => { btn.style.backgroundColor = '#27ae60'; }; btn.onclick = (event) => { event.stopPropagation(); a.handler(); removeModal(); }; primaryContainer.appendChild(btn); }); modal.appendChild(primaryContainer); } // БЛОК: taskActions if (taskActions.length > 0) { const taskContainer = document.createElement('div'); taskContainer.style.marginBottom = '20px'; taskActions.forEach(a => { const btn = document.createElement('button'); btn.textContent = a.label; btn.style.cssText = ` width: 100%; padding: 14px; background-color: #dfc63cff; color: white; border: none; border-radius: 8px; font-size: 16px; font-weight: 500; cursor: pointer; transition: background-color 0.2s; margin-bottom: 10px; `; btn.onmouseover = () => btn.style.backgroundColor = '#dfc63cff'; btn.onmouseout = () => btn.style.backgroundColor = '#dfc63cff'; btn.onclick = (event) => { event.stopPropagation(); a.handler(); removeModal(); }; taskContainer.appendChild(btn); }); modal.appendChild(taskContainer); } // БЛОК: admin if (adminActions.length > 0) { const adminContainer = document.createElement('div'); adminContainer.style.marginBottom = '20px'; adminActions.forEach(a => { const btn = document.createElement('button'); btn.textContent = a.label; btn.style.cssText = ` width: 100%; padding: 14px; background-color: #e74c3c; color: white; border: none; border-radius: 8px; font-size: 16px; font-weight: 500; cursor: pointer; transition: background-color 0.2s; margin-bottom: 10px; `; btn.onmouseover = () => btn.style.backgroundColor = '#c0392b'; btn.onmouseout = () => btn.style.backgroundColor = '#e74c3c'; btn.onclick = (event) => { event.stopPropagation(); a.handler(); removeModal(); }; adminContainer.appendChild(btn); }); modal.appendChild(adminContainer); } // БЛОК: остальные (user) if (userActions.length > 0) { const grid = document.createElement('div'); grid.style.cssText = ` display: flex; flex-wrap: wrap; gap: 10px; justify-content: center; `; userActions.forEach(a => { const btn = document.createElement('button'); btn.textContent = a.label; btn.style.cssText = ` flex: 0 0 calc(33.333% - 10px); min-width: 120px; padding: 12px 8px; border: none; border-radius: 8px; background-color: #3498db; color: white; font-size: 14px; font-weight: 500; cursor: pointer; transition: background-color 0.2s; `; btn.onmouseover = () => btn.style.backgroundColor = '#2980b9'; btn.onmouseout = () => btn.style.backgroundColor = '#3498db'; btn.onclick = (event) => { event.stopPropagation(); a.handler(); removeModal(); }; grid.appendChild(btn); }); modal.appendChild(grid); } overlay.appendChild(modal); document.body.appendChild(overlay); overlay.addEventListener('click', function(event) { if (event.target === overlay) { removeModal(); } }); }); // Экспортируем функции для использования в HTML (например, closeDocFieldsModal) window.closeDocFieldsModal = function() { const m = document.getElementById('doc-fields-modal'); if (m) m.remove(); }; //console.log('nav-task-actions.js loaded (centered modal)'); })();