// signature.js - Универсальный скрипт для подписания задач (function() { 'use strict'; // Конфигурация const CONFIG = { signerGroup: 'Подписант', apiEndpoint: '/api2/idusers', usersEndpoint: '/api/users', replaceEndpoint: '/api/tasks/${taskId}/replace-assignee', replaceAllEndpoint: '/api/tasks/${taskId}/replace-all-assignees', taskTypeDocument: 'document' // Тип задачи "Документ" }; // Текущий пользователь let currentUser = null; // Является ли текущий пользователь подписантом let isCurrentUserSigner = false; // Получение текущего пользователя async function getCurrentUser() { try { const response = await fetch('/api/user'); const data = await response.json(); if (data.user) { currentUser = data.user; ////console.log('✅ Signature: текущий пользователь', currentUser); // Проверяем, является ли пользователь подписантом await checkIfUserIsSigner(); } return currentUser; } catch (error) { console.error('❌ Signature: ошибка получения пользователя', error); return null; } } // Проверка, является ли текущий пользователь подписантом async function checkIfUserIsSigner() { if (!currentUser) return false; try { // Получаем всех подписантов const signers = await getSigners(); // Проверяем, есть ли текущий пользователь в списке подписантов isCurrentUserSigner = signers.some(signer => signer.id === currentUser.id || signer.login === currentUser.login || signer.name === currentUser.name ); ////console.log('✅ Signature: пользователь является подписантом?', isCurrentUserSigner); return isCurrentUserSigner; } catch (error) { console.error('❌ Signature: ошибка проверки подписанта', error); return false; } } // Получение подписантов async function getSigners() { try { const response = await fetch(CONFIG.apiEndpoint); if (!response.ok) return await getSignersFallback(); const data = await response.json(); const signers = data.filter(user => user.is_active && ( user.group_name?.toLowerCase().includes(CONFIG.signerGroup.toLowerCase()) || user.metadata?.groups?.some(g => g.toLowerCase().includes(CONFIG.signerGroup.toLowerCase())) || user.ldap_group?.toLowerCase().includes(CONFIG.signerGroup.toLowerCase()) ) ); return signers.map(s => ({ id: s.user_id, name: s.user_name || s.login || 'Неизвестно', login: s.user_login || s.login || '' })); } catch { return await getSignersFallback(); } } // Запасной метод получения подписантов async function getSignersFallback() { try { const res = await fetch(CONFIG.usersEndpoint); if (!res.ok) return []; const users = await res.json(); return users.filter(u => u.role === 'admin' || u.role === 'signer') .map(u => ({ id: u.id, name: u.name || u.login, login: u.login })); } catch { return []; } } // Основная функция подписания window.signTask = async function(taskId, userId) { // Если userId не передан, используем текущего пользователя const targetUserId = userId || currentUser?.id; if (!targetUserId) { alert('❌ Не удалось определить текущего пользователя'); return false; } try { const signers = await getSigners(); if (!signers.length) { alert('❌ Секретари не найдены в системе'); return false; } const names = signers.map(s => `• ${s.name} (${s.login})`).join('\n'); const msg = signers.length === 1 ? `✍️ Назначить подписанта?\n\n${names}` : `✍️ Назначить подписантов?\n\n${names}`; if (!confirm(msg)) return false; let response; if (signers.length > 1) { const url = CONFIG.replaceAllEndpoint.replace('${taskId}', taskId); response = await fetch(url, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ newAssigneeIds: signers.map(s => s.id) }) }); } else { const url = CONFIG.replaceEndpoint.replace('${taskId}', taskId); response = await fetch(url, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ oldAssigneeId: targetUserId, newAssigneeId: signers[0].id }) }); } if (response.ok) { alert(`✅ Задача ${signers.length > 1 ? 'назначена подписантам' : 'переназначена подписанту'}`); // Пробуем перезагрузить задачи разными способами if (window.TasksType && typeof window.TasksType.loadTasks === 'function') { window.TasksType.loadTasks(); } else if (window.loadTasks && typeof window.loadTasks === 'function') { window.loadTasks(); } else { // Если нет функций загрузки, просто обновляем страницу setTimeout(() => window.location.reload(), 1500); } return true; } else { const err = await response.json(); alert('❌ Ошибка: ' + (err.error || 'Неизвестная ошибка')); return false; } } catch (error) { console.error('❌ Signature error:', error); alert('❌ Сетевая ошибка: ' + error.message); return false; } }; // Обновление статуса задачи window.updateTaskStatus = async function(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) { if (window.TasksType && typeof window.TasksType.loadTasks === 'function') { window.TasksType.loadTasks(); } else if (window.loadTasks && typeof window.loadTasks === 'function') { window.loadTasks(); } return true; } else { const err = await response.json(); alert('❌ Ошибка: ' + (err.error || 'Неизвестная ошибка')); return false; } } catch (error) { console.error('❌ Status update error:', error); alert('❌ Сетевая ошибка'); return false; } }; // Получение типа задачи из элемента карточки function getTaskTypeFromCard(card) { // Ищем элемент с типом задачи const typeElement = card.querySelector('.task-type-badge, .task-type'); if (typeElement) { const classList = typeElement.className; if (classList.includes('document') || classList.includes('Документ')) { return 'document'; } } // Пробуем найти по тексту const cardText = card.textContent || ''; if (cardText.includes('Документ') || cardText.includes('document')) { return 'document'; } return null; } // Функция для добавления кнопок в существующие карточки задач window.addSignatureButtons = function() { // Показываем кнопку только если пользователь является подписантом if (!currentUser || isCurrentUserSigner) return; document.querySelectorAll('[data-task-id]').forEach(card => { // Проверяем, есть ли уже кнопка if (card.querySelector('.signature-btn')) return; // Проверяем тип задачи - только для документов const taskType = getTaskTypeFromCard(card); if (taskType !== CONFIG.taskTypeDocument) return; const taskId = card.dataset.taskId; // Ищем контейнер action-buttons const actionsContainer = card.querySelector('.action-buttons'); if (actionsContainer) { const btn = document.createElement('button'); btn.className = 'signature-btn action-btn sign-btn'; btn.innerHTML = '✍️ Подписать'; btn.onclick = (e) => { e.stopPropagation(); window.signTask(taskId, currentUser.id); }; btn.title = 'Передать подписанту'; actionsContainer.appendChild(btn); } }); }; // Наблюдатель за изменениями DOM для динамической добавления кнопок function observeDOM() { const observer = new MutationObserver((mutations) => { mutations.forEach((mutation) => { if (mutation.addedNodes.length) { window.addSignatureButtons(); } }); }); observer.observe(document.body, { childList: true, subtree: true }); } // Функция для получения отображаемого имени типа задачи function getTaskTypeDisplayName(type) { const typeMap = { 'document': 'Документ', 'task': 'Задача', 'bug': 'Ошибка', 'request': 'Заявка' }; return typeMap[type] || type; } // Инициализация при загрузке страницы async function init() { //console.log('🔄 Signature module initializing...'); // Получаем текущего пользователя и проверяем, является ли он подписантом await getCurrentUser(); // Добавляем кнопки на существующие карточки только если пользователь подписант setTimeout(() => { if (isCurrentUserSigner) { window.addSignatureButtons(); } }, 1000); // Запускаем наблюдение за DOM observeDOM(); // Перехватываем функцию рендеринга TasksType если она существует if (window.TasksType && window.TasksType.renderExpandedContent) { const originalRender = window.TasksType.renderExpandedContent; window.TasksType.renderExpandedContent = function(task) { let html = originalRender ? originalRender(task) : ''; // Добавляем кнопку подписания в развернутое содержимое только для документов и если пользователь подписант if (currentUser && isCurrentUserSigner && task.task_type === CONFIG.taskTypeDocument) { // Создаем контейнер action-buttons если его нет if (!html.includes('action-buttons')) { const signButton = `
`; // Вставляем кнопки после заголовка html = html.replace('
', '
' + signButton); } else { // Добавляем кнопку в существующий контейнер const signButton = ` `; html = html.replace('
', signButton + '
'); } } return html; }; } ////console.log('✅ Signature module loaded. User is signer:', isCurrentUserSigner); } // Запускаем инициализацию после загрузки DOM if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } })();