реквизиты
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
// document-fields.js - Скрипт для управления полями документа в задачах
|
// document-fields.js - Скрипт для управления полями документа в задачах
|
||||||
|
// Показывает кнопку "Реквизиты" только для задач с типом "document" и только когда задача раскрыта
|
||||||
|
|
||||||
(function() {
|
(function() {
|
||||||
'use strict';
|
'use strict';
|
||||||
@@ -202,6 +203,9 @@
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-size: 0.9em;
|
font-size: 0.9em;
|
||||||
transition: background-color 0.2s;
|
transition: background-color 0.2s;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.document-fields-btn-icon:hover {
|
.document-fields-btn-icon:hover {
|
||||||
@@ -216,6 +220,11 @@
|
|||||||
background-color: #7B1FA2;
|
background-color: #7B1FA2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.document-fields-btn-icon.document.loading {
|
||||||
|
opacity: 0.7;
|
||||||
|
cursor: wait;
|
||||||
|
}
|
||||||
|
|
||||||
.document-fields-badge {
|
.document-fields-badge {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
padding: 2px 8px;
|
padding: 2px 8px;
|
||||||
@@ -225,6 +234,22 @@
|
|||||||
color: #1976d2;
|
color: #1976d2;
|
||||||
margin-left: 8px;
|
margin-left: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.document-fields-placeholder {
|
||||||
|
display: inline-block;
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
border: 2px solid #f3f3f3;
|
||||||
|
border-top: 2px solid #9C27B0;
|
||||||
|
border-radius: 50%;
|
||||||
|
animation: spin 1s linear infinite;
|
||||||
|
margin-right: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes spin {
|
||||||
|
0% { transform: rotate(0deg); }
|
||||||
|
100% { transform: rotate(360deg); }
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
`
|
`
|
||||||
};
|
};
|
||||||
@@ -232,14 +257,32 @@
|
|||||||
// Текущий пользователь
|
// Текущий пользователь
|
||||||
let currentUser = null;
|
let currentUser = null;
|
||||||
|
|
||||||
|
// Кэш для типов задач
|
||||||
|
const taskTypeCache = new Map();
|
||||||
|
|
||||||
|
// Множество задач, для которых уже выполняется проверка
|
||||||
|
const pendingChecks = new Set();
|
||||||
|
|
||||||
|
// Селекторы для поиска раскрытых задач
|
||||||
|
const EXPANDED_TASK_SELECTORS = [
|
||||||
|
'.task-card.expanded',
|
||||||
|
'.task-item.expanded',
|
||||||
|
'[data-expanded="true"]',
|
||||||
|
'.task-details',
|
||||||
|
'.task-content.expanded'
|
||||||
|
];
|
||||||
|
|
||||||
// Получение текущего пользователя
|
// Получение текущего пользователя
|
||||||
async function getCurrentUser() {
|
async function getCurrentUser() {
|
||||||
try {
|
try {
|
||||||
const response = await fetch('/api/user');
|
const response = await fetch('/api/user');
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`HTTP error! status: ${response.status}`);
|
||||||
|
}
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
if (data.user) {
|
if (data.user) {
|
||||||
currentUser = data.user;
|
currentUser = data.user;
|
||||||
console.log('✅ DocumentFields: текущий пользователь', currentUser);
|
console.log('✅ DocumentFields: текущий пользователь', currentUser.login);
|
||||||
}
|
}
|
||||||
return currentUser;
|
return currentUser;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -248,9 +291,88 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Получение типа задачи по ID
|
||||||
|
async function getTaskType(taskId) {
|
||||||
|
// Проверяем кэш
|
||||||
|
if (taskTypeCache.has(taskId)) {
|
||||||
|
return taskTypeCache.get(taskId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Проверяем, не выполняется ли уже проверка для этой задачи
|
||||||
|
if (pendingChecks.has(taskId)) {
|
||||||
|
// Ждем завершения проверки
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
const checkInterval = setInterval(() => {
|
||||||
|
if (taskTypeCache.has(taskId)) {
|
||||||
|
clearInterval(checkInterval);
|
||||||
|
resolve(taskTypeCache.get(taskId));
|
||||||
|
}
|
||||||
|
}, 100);
|
||||||
|
|
||||||
|
// Таймаут на случай ошибки
|
||||||
|
setTimeout(() => {
|
||||||
|
clearInterval(checkInterval);
|
||||||
|
resolve('regular');
|
||||||
|
}, 5000);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pendingChecks.add(taskId);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(`/api/tasks/${taskId}/type`);
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
if (response.status === 404) {
|
||||||
|
taskTypeCache.set(taskId, 'regular');
|
||||||
|
return 'regular';
|
||||||
|
}
|
||||||
|
throw new Error(`HTTP error! status: ${response.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
const taskType = data.task_type || 'regular';
|
||||||
|
|
||||||
|
// Сохраняем в кэш
|
||||||
|
taskTypeCache.set(taskId, taskType);
|
||||||
|
|
||||||
|
return taskType;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`❌ Ошибка получения типа задачи ${taskId}:`, error);
|
||||||
|
taskTypeCache.set(taskId, 'regular');
|
||||||
|
return 'regular';
|
||||||
|
} finally {
|
||||||
|
pendingChecks.delete(taskId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Проверка, раскрыта ли задача
|
||||||
|
function isTaskExpanded(taskCard) {
|
||||||
|
// Проверяем по различным селекторам
|
||||||
|
for (const selector of EXPANDED_TASK_SELECTORS) {
|
||||||
|
if (taskCard.matches(selector) || taskCard.querySelector(selector)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Проверяем наличие классов раскрытия
|
||||||
|
if (taskCard.classList.contains('expanded') ||
|
||||||
|
taskCard.classList.contains('open') ||
|
||||||
|
taskCard.classList.contains('active')) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Проверяем атрибуты
|
||||||
|
if (taskCard.getAttribute('data-expanded') === 'true' ||
|
||||||
|
taskCard.getAttribute('aria-expanded') === 'true') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Создание модального окна
|
// Создание модального окна
|
||||||
function createModal() {
|
function createModal() {
|
||||||
// Добавляем стили
|
|
||||||
if (!document.getElementById('document-fields-styles')) {
|
if (!document.getElementById('document-fields-styles')) {
|
||||||
const styleElement = document.createElement('div');
|
const styleElement = document.createElement('div');
|
||||||
styleElement.id = 'document-fields-styles';
|
styleElement.id = 'document-fields-styles';
|
||||||
@@ -258,7 +380,6 @@
|
|||||||
document.head.appendChild(styleElement);
|
document.head.appendChild(styleElement);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Проверяем, существует ли уже модальное окно
|
|
||||||
if (document.getElementById(CONFIG.modalId)) {
|
if (document.getElementById(CONFIG.modalId)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -313,6 +434,14 @@
|
|||||||
|
|
||||||
// Открытие модального окна
|
// Открытие модального окна
|
||||||
async function openModal(taskId) {
|
async function openModal(taskId) {
|
||||||
|
// Проверяем тип задачи
|
||||||
|
const taskType = await getTaskType(taskId);
|
||||||
|
|
||||||
|
if (taskType !== 'document') {
|
||||||
|
alert('Эта функция доступна только для задач типа "Документ"');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const modal = document.getElementById(CONFIG.modalId);
|
const modal = document.getElementById(CONFIG.modalId);
|
||||||
if (!modal) {
|
if (!modal) {
|
||||||
createModal();
|
createModal();
|
||||||
@@ -324,28 +453,23 @@
|
|||||||
const errorDiv = document.getElementById('documentFieldsError');
|
const errorDiv = document.getElementById('documentFieldsError');
|
||||||
const messageDiv = document.getElementById('documentFieldsMessage');
|
const messageDiv = document.getElementById('documentFieldsMessage');
|
||||||
|
|
||||||
// Скрываем предыдущие сообщения
|
if (errorDiv) errorDiv.style.display = 'none';
|
||||||
errorDiv.style.display = 'none';
|
if (messageDiv) messageDiv.style.display = 'none';
|
||||||
messageDiv.style.display = 'none';
|
|
||||||
|
|
||||||
// Показываем загрузку
|
if (form) form.style.display = 'none';
|
||||||
form.style.display = 'none';
|
if (loading) loading.style.display = 'block';
|
||||||
loading.style.display = 'block';
|
|
||||||
|
|
||||||
// Активируем модальное окно
|
|
||||||
modalElement.classList.add('active');
|
modalElement.classList.add('active');
|
||||||
|
|
||||||
// Устанавливаем ID задачи
|
const taskIdSpan = document.getElementById('documentTaskId');
|
||||||
document.getElementById('documentTaskId').textContent = taskId;
|
if (taskIdSpan) taskIdSpan.textContent = taskId;
|
||||||
|
|
||||||
// Устанавливаем автора (логин текущего пользователя)
|
|
||||||
const authorInput = document.getElementById('documentAuthor');
|
const authorInput = document.getElementById('documentAuthor');
|
||||||
if (currentUser) {
|
if (authorInput && currentUser) {
|
||||||
authorInput.value = currentUser.login || '';
|
authorInput.value = currentUser.login || '';
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Загружаем текущие значения
|
|
||||||
const response = await fetch(`/api/tasks/${taskId}/document-fields`);
|
const response = await fetch(`/api/tasks/${taskId}/document-fields`);
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error('Ошибка загрузки данных');
|
throw new Error('Ошибка загрузки данных');
|
||||||
@@ -354,10 +478,13 @@
|
|||||||
const result = await response.json();
|
const result = await response.json();
|
||||||
|
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
document.getElementById('documentNumber').value = result.data.document_n || '';
|
const numberInput = document.getElementById('documentNumber');
|
||||||
document.getElementById('documentDate').value = result.data.document_d || '';
|
const dateInput = document.getElementById('documentDate');
|
||||||
// Автор из БД может отличаться от текущего пользователя
|
|
||||||
if (result.data.document_a && !authorInput.value) {
|
if (numberInput) numberInput.value = result.data.document_n || '';
|
||||||
|
if (dateInput) dateInput.value = result.data.document_d || '';
|
||||||
|
|
||||||
|
if (result.data.document_a && authorInput && !authorInput.value) {
|
||||||
authorInput.value = result.data.document_a;
|
authorInput.value = result.data.document_a;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -365,9 +492,8 @@
|
|||||||
console.error('❌ Ошибка загрузки полей документа:', error);
|
console.error('❌ Ошибка загрузки полей документа:', error);
|
||||||
showError('Ошибка загрузки данных: ' + error.message);
|
showError('Ошибка загрузки данных: ' + error.message);
|
||||||
} finally {
|
} finally {
|
||||||
// Скрываем загрузку, показываем форму
|
if (loading) loading.style.display = 'none';
|
||||||
loading.style.display = 'none';
|
if (form) form.style.display = 'block';
|
||||||
form.style.display = 'block';
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -376,12 +502,22 @@
|
|||||||
const modal = document.getElementById(CONFIG.modalId);
|
const modal = document.getElementById(CONFIG.modalId);
|
||||||
if (modal) {
|
if (modal) {
|
||||||
modal.classList.remove('active');
|
modal.classList.remove('active');
|
||||||
|
|
||||||
|
const numberInput = document.getElementById('documentNumber');
|
||||||
|
const dateInput = document.getElementById('documentDate');
|
||||||
|
const authorInput = document.getElementById('documentAuthor');
|
||||||
|
|
||||||
|
if (numberInput) numberInput.value = '';
|
||||||
|
if (dateInput) dateInput.value = '';
|
||||||
|
if (authorInput) authorInput.value = '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Показать ошибку
|
// Показать ошибку
|
||||||
function showError(message) {
|
function showError(message) {
|
||||||
const errorDiv = document.getElementById('documentFieldsError');
|
const errorDiv = document.getElementById('documentFieldsError');
|
||||||
|
if (!errorDiv) return;
|
||||||
|
|
||||||
errorDiv.textContent = message;
|
errorDiv.textContent = message;
|
||||||
errorDiv.style.display = 'block';
|
errorDiv.style.display = 'block';
|
||||||
|
|
||||||
@@ -393,6 +529,8 @@
|
|||||||
// Показать успех
|
// Показать успех
|
||||||
function showSuccess(message) {
|
function showSuccess(message) {
|
||||||
const messageDiv = document.getElementById('documentFieldsMessage');
|
const messageDiv = document.getElementById('documentFieldsMessage');
|
||||||
|
if (!messageDiv) return;
|
||||||
|
|
||||||
messageDiv.textContent = message;
|
messageDiv.textContent = message;
|
||||||
messageDiv.style.display = 'block';
|
messageDiv.style.display = 'block';
|
||||||
|
|
||||||
@@ -404,9 +542,8 @@
|
|||||||
|
|
||||||
// Валидация даты
|
// Валидация даты
|
||||||
function validateDate(dateStr) {
|
function validateDate(dateStr) {
|
||||||
if (!dateStr) return true; // Пустая дата допустима
|
if (!dateStr) return true;
|
||||||
|
|
||||||
// Проверка формата ДД.ММ.ГГГГ
|
|
||||||
const datePattern = /^(\d{2})\.(\d{2})\.(\d{4})$/;
|
const datePattern = /^(\d{2})\.(\d{2})\.(\d{4})$/;
|
||||||
if (!datePattern.test(dateStr)) {
|
if (!datePattern.test(dateStr)) {
|
||||||
return 'Неверный формат даты. Используйте ДД.ММ.ГГГГ';
|
return 'Неверный формат даты. Используйте ДД.ММ.ГГГГ';
|
||||||
@@ -424,12 +561,16 @@
|
|||||||
|
|
||||||
// Сохранение полей
|
// Сохранение полей
|
||||||
async function saveFields() {
|
async function saveFields() {
|
||||||
const taskId = document.getElementById('documentTaskId').textContent;
|
const taskId = document.getElementById('documentTaskId')?.textContent;
|
||||||
const document_n = document.getElementById('documentNumber').value.trim();
|
if (!taskId) {
|
||||||
let document_d = document.getElementById('documentDate').value.trim();
|
showError('ID задачи не найден');
|
||||||
const document_a = document.getElementById('documentAuthor').value.trim() || currentUser?.login;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const document_n = document.getElementById('documentNumber')?.value.trim() || '';
|
||||||
|
let document_d = document.getElementById('documentDate')?.value.trim() || '';
|
||||||
|
const document_a = document.getElementById('documentAuthor')?.value.trim() || currentUser?.login;
|
||||||
|
|
||||||
// Валидация даты
|
|
||||||
if (document_d) {
|
if (document_d) {
|
||||||
const dateValidation = validateDate(document_d);
|
const dateValidation = validateDate(document_d);
|
||||||
if (dateValidation !== true) {
|
if (dateValidation !== true) {
|
||||||
@@ -438,8 +579,9 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Показываем загрузку
|
|
||||||
const saveBtn = document.querySelector('.document-fields-btn.save');
|
const saveBtn = document.querySelector('.document-fields-btn.save');
|
||||||
|
if (!saveBtn) return;
|
||||||
|
|
||||||
const originalText = saveBtn.textContent;
|
const originalText = saveBtn.textContent;
|
||||||
saveBtn.textContent = 'Сохранение...';
|
saveBtn.textContent = 'Сохранение...';
|
||||||
saveBtn.disabled = true;
|
saveBtn.disabled = true;
|
||||||
@@ -461,8 +603,6 @@
|
|||||||
|
|
||||||
if (response.ok && result.success) {
|
if (response.ok && result.success) {
|
||||||
showSuccess('✅ Поля документа сохранены');
|
showSuccess('✅ Поля документа сохранены');
|
||||||
|
|
||||||
// Обновляем отображение в карточке задачи, если есть
|
|
||||||
updateTaskCardDisplay(taskId, result.data);
|
updateTaskCardDisplay(taskId, result.data);
|
||||||
} else {
|
} else {
|
||||||
showError(result.error || 'Ошибка сохранения');
|
showError(result.error || 'Ошибка сохранения');
|
||||||
@@ -481,14 +621,12 @@
|
|||||||
const taskCards = document.querySelectorAll(`[data-task-id="${taskId}"]`);
|
const taskCards = document.querySelectorAll(`[data-task-id="${taskId}"]`);
|
||||||
|
|
||||||
taskCards.forEach(card => {
|
taskCards.forEach(card => {
|
||||||
// Ищем или создаем контейнер для полей документа
|
|
||||||
let docContainer = card.querySelector('.document-fields-display');
|
let docContainer = card.querySelector('.document-fields-display');
|
||||||
|
|
||||||
if (!docContainer) {
|
if (!docContainer) {
|
||||||
docContainer = document.createElement('div');
|
docContainer = document.createElement('div');
|
||||||
docContainer.className = 'document-fields-display';
|
docContainer.className = 'document-fields-display';
|
||||||
|
|
||||||
// Вставляем после заголовка или в подходящее место
|
|
||||||
const header = card.querySelector('.task-header');
|
const header = card.querySelector('.task-header');
|
||||||
if (header) {
|
if (header) {
|
||||||
header.after(docContainer);
|
header.after(docContainer);
|
||||||
@@ -497,7 +635,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Формируем HTML для отображения
|
|
||||||
let displayHTML = '<div style="margin: 8px 0; padding: 5px; background-color: #f5f5f5; border-radius: 4px; font-size: 0.9em;">';
|
let displayHTML = '<div style="margin: 8px 0; padding: 5px; background-color: #f5f5f5; border-radius: 4px; font-size: 0.9em;">';
|
||||||
|
|
||||||
if (data.document_n) {
|
if (data.document_n) {
|
||||||
@@ -522,20 +659,37 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Добавление кнопки в карточки задач
|
// Добавление кнопки в раскрытую задачу
|
||||||
function addDocumentFieldsButtons() {
|
async function addButtonToExpandedTask(taskCard) {
|
||||||
if (!currentUser) return;
|
if (!currentUser) return;
|
||||||
|
|
||||||
document.querySelectorAll('[data-task-id]').forEach(card => {
|
const taskId = taskCard.dataset.taskId;
|
||||||
// Проверяем, есть ли уже кнопка
|
if (!taskId) return;
|
||||||
if (card.querySelector('.document-fields-btn-icon')) return;
|
|
||||||
|
|
||||||
const taskId = card.dataset.taskId;
|
// Проверяем, есть ли уже кнопка
|
||||||
|
if (taskCard.querySelector('.document-fields-btn-icon')) return;
|
||||||
|
|
||||||
// Ищем контейнер action-buttons
|
// Проверяем, раскрыта ли задача
|
||||||
const actionsContainer = card.querySelector('.action-buttons, .task-actions');
|
if (!isTaskExpanded(taskCard)) return;
|
||||||
|
|
||||||
if (actionsContainer) {
|
// Показываем индикатор загрузки
|
||||||
|
const actionsContainer = taskCard.querySelector('.action-buttons, .task-actions');
|
||||||
|
if (!actionsContainer) return;
|
||||||
|
|
||||||
|
const loadingIndicator = document.createElement('span');
|
||||||
|
loadingIndicator.className = 'document-fields-placeholder';
|
||||||
|
loadingIndicator.title = 'Проверка типа задачи...';
|
||||||
|
actionsContainer.appendChild(loadingIndicator);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Получаем тип задачи
|
||||||
|
const taskType = await getTaskType(taskId);
|
||||||
|
|
||||||
|
// Удаляем индикатор загрузки
|
||||||
|
loadingIndicator.remove();
|
||||||
|
|
||||||
|
// Добавляем кнопку только для задач с типом "document"
|
||||||
|
if (taskType === 'document') {
|
||||||
const btn = document.createElement('button');
|
const btn = document.createElement('button');
|
||||||
btn.className = 'document-fields-btn-icon document';
|
btn.className = 'document-fields-btn-icon document';
|
||||||
btn.innerHTML = '📄 Реквизиты';
|
btn.innerHTML = '📄 Реквизиты';
|
||||||
@@ -545,57 +699,107 @@
|
|||||||
};
|
};
|
||||||
btn.title = 'Редактировать реквизиты документа';
|
btn.title = 'Редактировать реквизиты документа';
|
||||||
actionsContainer.appendChild(btn);
|
actionsContainer.appendChild(btn);
|
||||||
|
|
||||||
|
console.log(`✅ Добавлена кнопка для раскрытой задачи ${taskId} (тип: document)`);
|
||||||
}
|
}
|
||||||
});
|
} catch (error) {
|
||||||
|
console.error(`❌ Ошибка проверки задачи ${taskId}:`, error);
|
||||||
|
loadingIndicator.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Обработчик раскрытия задачи
|
||||||
|
function handleTaskExpand(mutations) {
|
||||||
|
for (const mutation of mutations) {
|
||||||
|
if (mutation.type === 'attributes' &&
|
||||||
|
(mutation.attributeName === 'class' || mutation.attributeName === 'data-expanded')) {
|
||||||
|
|
||||||
|
const target = mutation.target;
|
||||||
|
if (target.dataset?.taskId && isTaskExpanded(target)) {
|
||||||
|
addButtonToExpandedTask(target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mutation.type === 'childList' && mutation.addedNodes.length) {
|
||||||
|
for (const node of mutation.addedNodes) {
|
||||||
|
if (node.nodeType === 1) { // Element node
|
||||||
|
// Проверяем сам элемент
|
||||||
|
if (node.dataset?.taskId && isTaskExpanded(node)) {
|
||||||
|
addButtonToExpandedTask(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Проверяем дочерние элементы
|
||||||
|
const expandedTasks = node.querySelectorAll('[data-task-id]');
|
||||||
|
expandedTasks.forEach(task => {
|
||||||
|
if (isTaskExpanded(task)) {
|
||||||
|
addButtonToExpandedTask(task);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Наблюдатель за изменениями DOM
|
// Наблюдатель за изменениями DOM
|
||||||
function observeDOM() {
|
function observeDOM() {
|
||||||
const observer = new MutationObserver((mutations) => {
|
// Наблюдатель за атрибутами и появлением новых элементов
|
||||||
mutations.forEach((mutation) => {
|
const observer = new MutationObserver(handleTaskExpand);
|
||||||
if (mutation.addedNodes.length) {
|
|
||||||
window.documentFields.addButtons();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
observer.observe(document.body, {
|
observer.observe(document.body, {
|
||||||
|
attributes: true,
|
||||||
|
attributeFilter: ['class', 'data-expanded', 'aria-expanded'],
|
||||||
childList: true,
|
childList: true,
|
||||||
subtree: true
|
subtree: true
|
||||||
});
|
});
|
||||||
|
|
||||||
|
console.log('👀 Наблюдатель за раскрытием задач запущен');
|
||||||
|
|
||||||
|
// Также проверяем уже раскрытые задачи при загрузке
|
||||||
|
setTimeout(() => {
|
||||||
|
document.querySelectorAll('[data-task-id]').forEach(task => {
|
||||||
|
if (isTaskExpanded(task)) {
|
||||||
|
addButtonToExpandedTask(task);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, 2000);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Очистка кэша
|
||||||
|
function clearCache() {
|
||||||
|
taskTypeCache.clear();
|
||||||
|
pendingChecks.clear();
|
||||||
|
console.log('🗑️ Кэш типов задач очищен');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Инициализация
|
// Инициализация
|
||||||
async function init() {
|
async function init() {
|
||||||
console.log('🔄 DocumentFields module initializing...');
|
console.log('🔄 DocumentFields module initializing...');
|
||||||
|
|
||||||
// Получаем текущего пользователя
|
try {
|
||||||
await getCurrentUser();
|
await getCurrentUser();
|
||||||
|
createModal();
|
||||||
|
observeDOM();
|
||||||
|
|
||||||
// Создаем модальное окно
|
console.log('✅ DocumentFields module loaded');
|
||||||
createModal();
|
} catch (error) {
|
||||||
|
console.error('❌ Ошибка инициализации:', error);
|
||||||
// Добавляем кнопки на существующие карточки
|
}
|
||||||
setTimeout(() => {
|
|
||||||
window.documentFields.addButtons();
|
|
||||||
}, 1000);
|
|
||||||
|
|
||||||
// Запускаем наблюдение за DOM
|
|
||||||
observeDOM();
|
|
||||||
|
|
||||||
console.log('✅ DocumentFields module loaded');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Экспортируем функции в глобальную область
|
// Экспортируем функции
|
||||||
window.documentFields = {
|
window.documentFields = {
|
||||||
openModal,
|
openModal,
|
||||||
closeModal,
|
closeModal,
|
||||||
saveFields,
|
saveFields,
|
||||||
addButtons: addDocumentFieldsButtons,
|
getTaskType,
|
||||||
init
|
clearCache,
|
||||||
|
init,
|
||||||
|
addButtonToExpandedTask,
|
||||||
|
isTaskExpanded
|
||||||
};
|
};
|
||||||
|
|
||||||
// Запускаем инициализацию после загрузки DOM
|
// Запускаем инициализацию
|
||||||
if (document.readyState === 'loading') {
|
if (document.readyState === 'loading') {
|
||||||
document.addEventListener('DOMContentLoaded', init);
|
document.addEventListener('DOMContentLoaded', init);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -1934,6 +1934,41 @@ app.get('/api/tasks/:taskId/document-fields', requireAuth, (req, res) => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
// API для получения типа задачи
|
||||||
|
app.get('/api/tasks/:taskId/type', requireAuth, (req, res) => {
|
||||||
|
const { taskId } = req.params;
|
||||||
|
const userId = req.session.user.id;
|
||||||
|
|
||||||
|
// Проверяем доступ к задаче
|
||||||
|
const { checkTaskAccess } = require('./database');
|
||||||
|
|
||||||
|
checkTaskAccess(userId, taskId, (err, hasAccess) => {
|
||||||
|
if (err || !hasAccess) {
|
||||||
|
return res.status(404).json({ error: 'Задача не найдена или у вас нет прав доступа' });
|
||||||
|
}
|
||||||
|
|
||||||
|
db.get(
|
||||||
|
"SELECT task_type FROM tasks WHERE id = ?",
|
||||||
|
[taskId],
|
||||||
|
(err, row) => {
|
||||||
|
if (err) {
|
||||||
|
console.error('❌ Ошибка получения типа задачи:', err);
|
||||||
|
return res.status(500).json({ error: err.message });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!row) {
|
||||||
|
return res.status(404).json({ error: 'Задача не найдена' });
|
||||||
|
}
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
success: true,
|
||||||
|
task_type: row.task_type || 'regular',
|
||||||
|
taskId: taskId
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = { setupTaskEndpoints,getApproverUsers };
|
module.exports = { setupTaskEndpoints,getApproverUsers };
|
||||||
Reference in New Issue
Block a user