nav-task-actions
This commit is contained in:
@@ -109,7 +109,8 @@ function reloadAllScripts() {
|
|||||||
'navbar.js',
|
'navbar.js',
|
||||||
'chat-ui.js',
|
'chat-ui.js',
|
||||||
'loadMyCreatedTasks.js',
|
'loadMyCreatedTasks.js',
|
||||||
'main.js'
|
'main.js',
|
||||||
|
'nav-task-actions.js'
|
||||||
];
|
];
|
||||||
|
|
||||||
// Удаляем существующие скрипты
|
// Удаляем существующие скрипты
|
||||||
|
|||||||
@@ -429,6 +429,7 @@
|
|||||||
<div class="loading">Загрузка Канбан-доски...</div>
|
<div class="loading">Загрузка Канбан-доски...</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<script src="nav-task-actions.js"></script>
|
||||||
<script src="loading-end.js"></script>
|
<script src="loading-end.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
174
public/nav-task-actions.js
Normal file
174
public/nav-task-actions.js
Normal file
@@ -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 `
|
||||||
|
<div class="task-actions-menu-container">
|
||||||
|
<button class="task-actions-menu-btn" data-task-id="${task.id}">⋮ Действия</button>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Удалить текущее меню, если есть
|
||||||
|
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');
|
||||||
|
})();
|
||||||
@@ -4898,4 +4898,55 @@ button.btn-primary {
|
|||||||
|
|
||||||
.close:hover {
|
.close:hover {
|
||||||
color: #e74c3c;
|
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;
|
||||||
}
|
}
|
||||||
@@ -168,6 +168,7 @@ if (statusFilter !== 'completed') {
|
|||||||
<div class="task-content ${isExpanded ? 'expanded' : ''}">
|
<div class="task-content ${isExpanded ? 'expanded' : ''}">
|
||||||
${isExpanded ? `
|
${isExpanded ? `
|
||||||
<div class="task-actions">
|
<div class="task-actions">
|
||||||
|
${isExpanded ? (window.renderTaskActions ? window.renderTaskActions(task) : '') : ''}
|
||||||
${!isDeleted && !isClosed ? `
|
${!isDeleted && !isClosed ? `
|
||||||
<button class="copy-btn" onclick="openTaskChat(${task.id})" title="Открыть чат">💬</button>
|
<button class="copy-btn" onclick="openTaskChat(${task.id})" title="Открыть чат">💬</button>
|
||||||
<button class="add-file-btn" onclick="openAddFileModal(${task.id})" title="Добавить файл">📎</button>
|
<button class="add-file-btn" onclick="openAddFileModal(${task.id})" title="Добавить файл">📎</button>
|
||||||
|
|||||||
Reference in New Issue
Block a user