345 lines
15 KiB
JavaScript
345 lines
15 KiB
JavaScript
// 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 = `
|
||
<div class="action-buttons" style="margin-bottom: 10px;">
|
||
<button class="action-btn sign-btn" onclick="signTask(${task.id}, ${currentUser.id})" title="Передать подписанту">
|
||
✍️ Подписать
|
||
</button>
|
||
</div>
|
||
`;
|
||
|
||
// Вставляем кнопки после заголовка
|
||
html = html.replace('<div class="tasks-type-header">',
|
||
'<div class="tasks-type-header">' + signButton);
|
||
} else {
|
||
// Добавляем кнопку в существующий контейнер
|
||
const signButton = `
|
||
<button class="action-btn sign-btn" onclick="signTask(${task.id}, ${currentUser.id})" title="Передать подписанту">
|
||
✍️ Подписать
|
||
</button>
|
||
`;
|
||
|
||
html = html.replace('</div><div class="tasks-type-description">',
|
||
signButton + '</div><div class="tasks-type-description">');
|
||
}
|
||
}
|
||
|
||
return html;
|
||
};
|
||
}
|
||
|
||
console.log('✅ Signature module loaded. User is signer:', isCurrentUserSigner);
|
||
}
|
||
|
||
// Запускаем инициализацию после загрузки DOM
|
||
if (document.readyState === 'loading') {
|
||
document.addEventListener('DOMContentLoaded', init);
|
||
} else {
|
||
init();
|
||
}
|
||
|
||
})(); |