Files
minicrm/public/nav-task-actions.js
2026-03-18 09:58:11 +05:00

426 lines
18 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.
// nav-task-actions.js модальное окно с действиями задачи (сетка 3 колонки, центрированное)
(function() {
'use strict';
// Управление логированием: установите window.navTaskActionsLog = 1 для включения логов
const LOG_ENABLED = 0;
function log(...args) {
if (LOG_ENABLED) console.log(...args);
}
log('currentUser:', currentUser);
//log('currentUser.role:', currentUser.role);
// Сбор доступных действий для конкретной задачи
function buildActionsForTask(task) {
//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 (currentUser && !isDeleted && !isClosed) {
const myAssignment = task.assignments?.find(a => parseInt(a.user_id) === currentUser.id);
if (myAssignment) {
// Кнопка "Приступить" (если статус 'assigned')
if (myAssignment.status === 'assigned') {
actions.push({
label: '▶️ Приступить',
handler: () => window.updateStatus(taskId, currentUser.id, 'in_progress'),
primary: true
});
}
// Кнопка "Выполнено" (если статус 'in_progress', 'overdue' или 'rework')
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
});
log('nav-task-actions assignRemove_openModal yes');
} else {
log('nav-task-actions assignRemove_openModal not');
}
// Действия для активных (не удалённых, не закрытых) задач
if (!isDeleted && !isClosed && currentUser.login === 'minicrm') {
if (typeof openTaskChat === 'function') {
actions.push({ label: '💬 Чат', handler: () => openTaskChat(taskId) });
log('nav-task-actions openTaskChat yes');
} else {
log('nav-task-actions openTaskChat not');
}
if (typeof openAddFileModal === 'function') {
actions.push({ label: '📎 Добавить файл', handler: () => openAddFileModal(taskId) });
log('nav-task-actions openAddFileModal yes');
} else {
log('nav-task-actions openAddFileModal not');
}
}
// Специальные пользователи
if (currentUser && currentUser.login === 'minicrm') {
if (typeof openEditModal === 'function') {
actions.push({ label: '✏️ Редактировать', handler: () => openEditModal(taskId) });
log('nav-task-actions openEditModal yes');
} else {
log('nav-task-actions openEditModal not');
}
////}
//if (currentUser && currentUser.login === 'kalugin.o') {
if (typeof openManageAssigneesModal === 'function') {
actions.push({ label: '👥 Управление исполнителями', handler: () => openManageAssigneesModal(taskId) });
log('nav-task-actions openManageAssigneesModal yes');
} else {
log('nav-task-actions openManageAssigneesModal not');
}
}
// Подписание (только для документов, если текущий пользователь - исполнитель)
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 // primary_task
});
}
}
// ${currentUser && currentUser.role === 'tasks' && canEdit || currentUser.role === 'admin'
// Администраторы и роль tasks
if (currentUser && (currentUser.role === 'admin' || (currentUser.role === 'tasks' && canEdit))) {
if (typeof assignAdd_openModal === 'function') {
actions.push({ label: '🧑‍💼➕ Добавить исполнителя',
handler: () => assignAdd_openModal(taskId),
admin: true
});
log('nav-task-actions assignAdd_openModal yes');
} else {
log('nav-task-actions assignAdd_openModal not');
}
if (typeof assignRemove_openModal === 'function') {
actions.push({ label: '🧑‍💼❌ Удалить исполнителя',
handler: () => assignRemove_openModal(taskId),
admin: true
});
log('nav-task-actions assignRemove_openModal yes');
} else {
log('nav-task-actions assignRemove_openModal not');
}
//}
// Доработка для документа (исполнитель)
if (currentUser && (currentUser.role === 'admin' && currentUser.login === 'minicrm')) {
if (typeof openReworkModal === 'function') {
actions.push({ label: '🔄 Переделать документ', handler: () => openReworkModal(taskId) });
log('nav-task-actions assignRemove_openModal yes');
} else {
log('nav-task-actions assignRemove_openModal not');
}
}
}
// Доработка и изменение срока для необычных задач (исполнитель)
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});
log('nav-task-actions openReworkModal yes');
} else {log('nav-task-actions openReworkModal not');}
if (typeof openChangeDeadlineModal === 'function') { actions.push({ label: '📅 Изменить срок', handler: () => openChangeDeadlineModal(taskId),primary_task: true });
log('nav-task-actions openChangeDeadlineModal yes');
} else {log('nav-task-actions openChangeDeadlineModal not');}
}
if (!isDeleted && !isClosed && task.task_type == 'regular') {
if (typeof closeTask === 'function') { actions.push({ label: '🔒 Закрыть задачу', handler: () => closeTask(taskId),admin: true });
log('nav-task-actions closeTask yes');
} else {log('nav-task-actions closeTask not');}
}
if (canEdit && !isDeleted && !isClosed && currentUser.role === 'admin') {
if (typeof deleteTask === 'function') actions.push({ label: '🗑️ Удалить', handler: () => deleteTask(taskId) });
}
if (currentUser && currentUser.login === 'minicrm') {
// Закрытие (только minicrm)
//if (window.currentUser && window.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) });
}
}
//log('Actions built:', actions);
return actions;
}
// Рендеринг кнопки меню
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 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;
//log('Menu button clicked', menuBtn);
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;
// primary
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 = '&times;';
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();
}
});
});
//console.log('nav-task-actions.js loaded (centered modal)');
})();