From eb28a10c5224b6f61674a29bc3c66b588dd4a22f Mon Sep 17 00:00:00 2001 From: kalugin66 Date: Wed, 4 Mar 2026 17:27:27 +0500 Subject: [PATCH] nav-task-actions --- public/auth.js | 3 +- public/index.html | 1 + public/nav-task-actions.js | 174 +++++++++++++++++++++++++++++++++++++ public/style.css | 51 +++++++++++ public/ui.js | 1 + 5 files changed, 229 insertions(+), 1 deletion(-) create mode 100644 public/nav-task-actions.js diff --git a/public/auth.js b/public/auth.js index e0819c6..80601c5 100644 --- a/public/auth.js +++ b/public/auth.js @@ -109,7 +109,8 @@ function reloadAllScripts() { 'navbar.js', 'chat-ui.js', 'loadMyCreatedTasks.js', - 'main.js' + 'main.js', + 'nav-task-actions.js' ]; // Удаляем существующие скрипты diff --git a/public/index.html b/public/index.html index ee2f6df..7ab9c23 100644 --- a/public/index.html +++ b/public/index.html @@ -429,6 +429,7 @@
Загрузка Канбан-доски...
+ \ No newline at end of file diff --git a/public/nav-task-actions.js b/public/nav-task-actions.js new file mode 100644 index 0000000..4591964 --- /dev/null +++ b/public/nav-task-actions.js @@ -0,0 +1,174 @@ +// nav-task-actions.js – выпадающее меню действий задачи +(function() { + 'use strict'; + + // Сбор доступных действий для конкретной задачи + function buildActionsForTask(task) { + console.log('buildActionsForTask called with task', 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 (!isDeleted && !isClosed) { + if (typeof openTaskChat === 'function') actions.push({ label: '💬 Чат', handler: () => openTaskChat(taskId) }); + if (typeof openAddFileModal === 'function') actions.push({ label: '📎 Добавить файл', handler: () => openAddFileModal(taskId) }); + } + + // Специальные пользователи + if (window.currentUser && window.currentUser.login === 'minicrm') { + if (typeof openEditModal === 'function') actions.push({ label: '✏️ Редактировать', handler: () => openEditModal(taskId) }); + } + if (window.currentUser && window.currentUser.login === 'kalugin.o') { + if (typeof openManageAssigneesModal === 'function') actions.push({ label: '👥 Управление исполнителями', handler: () => openManageAssigneesModal(taskId) }); + } + + // Администраторы и роль tasks + if (window.currentUser && (window.currentUser.role === 'admin' || (window.currentUser.role === 'tasks' && canEdit))) { + if (typeof assignAdd_openModal === 'function') actions.push({ label: '🧑‍💼➕ Добавить исполнителя', handler: () => assignAdd_openModal(taskId) }); + if (typeof assignRemove_openModal === 'function') actions.push({ label: '🧑‍💼❌ Удалить исполнителя', handler: () => assignRemove_openModal(taskId) }); + } + + // Копия – доступна всем, у кого есть кнопка + if (typeof openCopyModal === 'function') actions.push({ label: '📋 Создать копию', handler: () => openCopyModal(taskId) }); + + // Доработка для документа (исполнитель) + if (task.task_type === 'document' && userRole === 'Исполнитель') { + if (typeof openReworkModal === 'function') actions.push({ label: '🔄 Переделать документ', handler: () => openReworkModal(taskId) }); + } + + // Доработка и изменение срока для необычных задач (исполнитель) + if (!isDeleted && !isClosed && task.task_type !== 'regular' && + task.assignments && task.assignments.some(a => parseInt(a.user_id) === window.currentUser?.id)) { + if (typeof openReworkModal === 'function') actions.push({ label: '🔄 Доработка', handler: () => openReworkModal(taskId) }); + if (typeof openChangeDeadlineModal === 'function') actions.push({ label: '📅 Изменить срок', handler: () => openChangeDeadlineModal(taskId) }); + } + + // Закрытие (только minicrm) + if (window.currentUser && window.currentUser.login === 'minicrm') { + if (typeof closeTask === 'function') actions.push({ label: '🔒 Закрыть задачу', handler: () => closeTask(taskId) }); + } + + // Удаление (если есть права) + if (canEdit && !isDeleted && !isClosed) { + 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) }); + } + + console.log('Actions built:', actions); + return actions; + } + + // Рендеринг кнопки меню (без встроенного dropdown) + window.renderTaskActions = function(task) { + if (!task) return ''; + return ` +
+ +
+ `; + }; + + // Удалить текущее меню, если есть + function removeCurrentMenu() { + const existing = document.getElementById('task-action-menu'); + if (existing) existing.remove(); + } + + // Обработчик клика на кнопку меню + document.addEventListener('click', function(e) { + const menuBtn = e.target.closest('.task-actions-menu-btn'); + if (!menuBtn) return; + + console.log('Menu button clicked', menuBtn); + e.preventDefault(); + e.stopPropagation(); + + // Удаляем предыдущее меню + removeCurrentMenu(); + + 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 menu = document.createElement('div'); + menu.id = 'task-action-menu'; + menu.className = 'task-actions-dropdown'; + menu.style.position = 'absolute'; + menu.style.zIndex = '10000'; + menu.style.backgroundColor = 'white'; + menu.style.border = '1px solid #ccc'; + menu.style.borderRadius = '8px'; + menu.style.boxShadow = '0 4px 12px rgba(0,0,0,0.15)'; + menu.style.minWidth = '200px'; + menu.style.padding = '8px 0'; + + // Заполняем пунктами + actions.forEach(a => { + const item = document.createElement('div'); + item.className = 'menu-item'; + item.textContent = a.label; + item.style.padding = '10px 16px'; + item.style.cursor = 'pointer'; + item.style.fontSize = '14px'; + item.style.color = '#333'; + item.style.transition = 'background 0.2s'; + item.addEventListener('mouseenter', () => { item.style.backgroundColor = '#f8f9fa'; }); + item.addEventListener('mouseleave', () => { item.style.backgroundColor = 'transparent'; }); + item.addEventListener('click', (event) => { + event.stopPropagation(); + a.handler(); + removeCurrentMenu(); + }); + menu.appendChild(item); + }); + + // Позиционируем + const rect = menuBtn.getBoundingClientRect(); + menu.style.top = (rect.bottom + window.scrollY) + 'px'; + menu.style.left = (rect.left + window.scrollX) + 'px'; + + // Добавляем в body + document.body.appendChild(menu); + + // Закрытие при клике вне меню + setTimeout(() => { + document.addEventListener('click', function closeMenu(e) { + if (!menu.contains(e.target) && !menuBtn.contains(e.target)) { + removeCurrentMenu(); + document.removeEventListener('click', closeMenu); + } + }); + }, 0); + }); + + console.log('nav-task-actions.js loaded'); +})(); \ No newline at end of file diff --git a/public/style.css b/public/style.css index 30a921f..658ac96 100644 --- a/public/style.css +++ b/public/style.css @@ -4898,4 +4898,55 @@ button.btn-primary { .close:hover { color: #e74c3c; +} +.task-actions-menu-container { + position: relative; + display: inline-block; +} + +.task-actions-menu-btn { + background: linear-gradient(135deg, #6c757d, #5a6268); + color: white; + border: none; + border-radius: 5px; + padding: 6px 12px; + cursor: pointer; + font-size: 14px; + transition: background 0.2s; +} + +.task-actions-menu-btn:hover { + background: linear-gradient(135deg, #5a6268, #495057); +} + +.task-actions-dropdown { + position: absolute; + background: white; + border: 1px solid #dee2e6; + border-radius: 8px; + box-shadow: 0 4px 12px rgba(0,0,0,0.15); + z-index: 1000; + min-width: 200px; + max-width: 300px; + padding: 8px 0; + display: none; +} + +.task-actions-dropdown .menu-item { + padding: 10px 16px; + cursor: pointer; + font-size: 14px; + color: #333; + transition: background 0.2s; + white-space: nowrap; +} + +.task-actions-dropdown .menu-item:hover { + background-color: #f8f9fa; +} + +.task-actions-dropdown .menu-item.disabled { + color: #aaa; + cursor: default; + pointer-events: none; } \ No newline at end of file diff --git a/public/ui.js b/public/ui.js index 7b450f0..e4b0a43 100644 --- a/public/ui.js +++ b/public/ui.js @@ -168,6 +168,7 @@ if (statusFilter !== 'completed') {
${isExpanded ? `
+ ${isExpanded ? (window.renderTaskActions ? window.renderTaskActions(task) : '') : ''} ${!isDeleted && !isClosed ? `