795 lines
31 KiB
JavaScript
795 lines
31 KiB
JavaScript
// users.js - Управление пользователями и пользовательскими списками
|
||
|
||
let users = [];
|
||
let allUsers = [];
|
||
let usersLoadingPromise = null;
|
||
let filteredUsers = [];
|
||
let selectedUsers = [];
|
||
let editSelectedUsers = [];
|
||
let copySelectedUsers = [];
|
||
let acquaintanceSelectedUsers = []; // исполнители для ознакомления
|
||
let acquaintanceSelectedAuthor = null; // выбранный автор для ознакомления
|
||
|
||
// Переменные для пользовательских списков
|
||
let userLists = [];
|
||
let isUserListsLoading = false;
|
||
let currentEditingListId = null;
|
||
|
||
// Кэш групп пользователей
|
||
let userGroupsCache = {};
|
||
|
||
let isUsersLoading = false;
|
||
|
||
// ==================== ЗАГРУЗКА ПОЛЬЗОВАТЕЛЕЙ ====================
|
||
|
||
async function loadUsers() {
|
||
// Если загрузка уже идёт, возвращаем существующий промис
|
||
if (usersLoadingPromise) return usersLoadingPromise;
|
||
|
||
usersLoadingPromise = (async () => {
|
||
try {
|
||
const response = await fetch('/api/users');
|
||
const allUsersData = await response.json();
|
||
allUsers = allUsersData;
|
||
users = filterAssignableUsers(allUsersData);
|
||
filteredUsers = [...users];
|
||
renderUsersChecklist();
|
||
renderEditUsersChecklist();
|
||
renderCopyUsersChecklist();
|
||
populateFilterDropdowns();
|
||
// Загружаем пользовательские списки (не ждём)
|
||
loadUserLists();
|
||
} catch (error) {
|
||
console.error('Ошибка загрузки пользователей:', error);
|
||
} finally {
|
||
usersLoadingPromise = null;
|
||
}
|
||
})();
|
||
|
||
return usersLoadingPromise;
|
||
}
|
||
|
||
// ==================== ПОЛУЧЕНИЕ ГРУПП ПОЛЬЗОВАТЕЛЯ ====================
|
||
|
||
async function getUserGroups(userId) {
|
||
if (userGroupsCache[userId]) {
|
||
return userGroupsCache[userId];
|
||
}
|
||
|
||
try {
|
||
const response = await fetch(`/api2/idusers/user/${userId}/groups`);
|
||
if (!response.ok) return [];
|
||
|
||
const groups = await response.json();
|
||
userGroupsCache[userId] = groups || [];
|
||
return userGroupsCache[userId];
|
||
} catch (error) {
|
||
console.error('Ошибка получения групп пользователя:', error);
|
||
return [];
|
||
}
|
||
}
|
||
|
||
// ==================== ФИЛЬТРАЦИЯ ПОЛЬЗОВАТЕЛЕЙ ПО ПРАВАМ ====================
|
||
|
||
function filterAssignableUsers(allUsers, taskType = 'regular') {
|
||
if (!currentUser) return [];
|
||
|
||
// Для задач типа "document" – только секретари (асинхронно не получится здесь, но мы фильтруем позже)
|
||
// В текущей реализации эта функция вызывается синхронно, поэтому для специальных типов фильтрация будет в filterUsers
|
||
// Здесь оставляем базовую фильтрацию по ролям
|
||
|
||
// Администратор видит всех, кроме себя
|
||
if (currentUser.role === 'admin') {
|
||
return allUsers.filter(user => user.id !== currentUser.id);
|
||
}
|
||
if (currentUser.role === 'secretary') {
|
||
return allUsers.filter(user => user.id !== currentUser.id);
|
||
}
|
||
if (currentUser.role === 'ithelp') {
|
||
return allUsers.filter(user =>
|
||
(user.role === 'teacher' || user.role === 'tasks' || user.role === 'help' || user.role === 'request' || user.role === 'ithelp') &&
|
||
user.id !== currentUser.id
|
||
);
|
||
}
|
||
if (currentUser.role === 'request') {
|
||
return allUsers.filter(user =>
|
||
(user.role === 'teacher' || user.role === 'tasks' || user.role === 'help' || user.role === 'request' || user.role === 'ithelp') &&
|
||
user.id !== currentUser.id
|
||
);
|
||
}
|
||
if (currentUser.role === 'help') {
|
||
return allUsers.filter(user =>
|
||
(user.role === 'teacher' || user.role === 'tasks' || user.role === 'help' || user.role === 'request' || user.role === 'ithelp') &&
|
||
user.id !== currentUser.id
|
||
);
|
||
}
|
||
if (currentUser.role === 'tasks') {
|
||
return allUsers.filter(user =>
|
||
(user.role === 'teacher' || user.role === 'tasks' || user.role === 'help' || user.role === 'request' || user.role === 'ithelp') &&
|
||
user.id !== currentUser.id
|
||
);
|
||
}
|
||
if (currentUser.role === 'teacher') {
|
||
return allUsers.filter(user =>
|
||
(user.role === 'help' || user.role === 'request' || user.role === 'ithelp') &&
|
||
user.id !== currentUser.id
|
||
);
|
||
}
|
||
|
||
return [];
|
||
}
|
||
|
||
// ==================== ЗАПОЛНЕНИЕ ВЫПАДАЮЩИХ СПИСКОВ ФИЛЬТРОВ ====================
|
||
|
||
function populateFilterDropdowns() {
|
||
const creatorFilter = document.getElementById('creator-filter');
|
||
const assigneeFilter = document.getElementById('assignee-filter');
|
||
|
||
if (creatorFilter) {
|
||
creatorFilter.innerHTML = '<option value="">Все заказчики</option>';
|
||
}
|
||
if (assigneeFilter) {
|
||
assigneeFilter.innerHTML = '<option value="">Все исполнители</option>';
|
||
}
|
||
|
||
users.forEach(user => {
|
||
const option = document.createElement('option');
|
||
option.value = user.id;
|
||
option.textContent = `${user.name} (${user.login})`;
|
||
|
||
if (creatorFilter) creatorFilter.appendChild(option.cloneNode(true));
|
||
if (assigneeFilter) assigneeFilter.appendChild(option.cloneNode(true));
|
||
});
|
||
}
|
||
|
||
// ==================== ФИЛЬТРАЦИЯ ПРИ ПОИСКЕ (С УЧЁТОМ ТИПА ЗАДАЧИ) ====================
|
||
|
||
async function filterUsers() {
|
||
const search = document.getElementById('user-search')?.value.toLowerCase() || '';
|
||
const taskType = document.getElementById('task-type')?.value || 'regular';
|
||
|
||
isUsersLoading = true;
|
||
renderUsersChecklist(); // Показываем загрузку
|
||
|
||
try {
|
||
// Сначала фильтруем по поиску
|
||
let tempFiltered = users.filter(user =>
|
||
user.name.toLowerCase().includes(search) ||
|
||
user.login.toLowerCase().includes(search) ||
|
||
(user.email && user.email.toLowerCase().includes(search))
|
||
);
|
||
|
||
// Если тип задачи требует специальной группы, фильтруем по группам
|
||
const specialTypes = ['document', 'it', 'ahch', 'psychologist', 'speech_therapist', 'Social_educator', 'hr', 'certificate', 'e_journal'];
|
||
if (specialTypes.includes(taskType)) {
|
||
const groupNames = {
|
||
'document': 'Секретарь',
|
||
'it': 'ИТ специалист',
|
||
'ahch': 'АХЧ',
|
||
'psychologist': 'психолог',
|
||
'speech_therapist': 'логопед',
|
||
'Social_educator': 'Социальный педагог',
|
||
'hr': 'Диспетчер',
|
||
'certificate': 'Администрация',
|
||
'e_journal': 'Админ ЭЖ'
|
||
};
|
||
const targetGroup = groupNames[taskType];
|
||
|
||
filteredUsers = [];
|
||
for (const user of tempFiltered) {
|
||
const groups = await getUserGroups(user.id);
|
||
const hasTargetGroup = groups.some(group =>
|
||
group.name === targetGroup ||
|
||
(typeof group === 'string' && group.includes(targetGroup))
|
||
);
|
||
if (hasTargetGroup) {
|
||
filteredUsers.push(user);
|
||
}
|
||
}
|
||
} else {
|
||
filteredUsers = tempFiltered;
|
||
}
|
||
} catch (error) {
|
||
console.error('Ошибка фильтрации пользователей:', error);
|
||
filteredUsers = [];
|
||
} finally {
|
||
isUsersLoading = false;
|
||
renderUsersChecklist();
|
||
}
|
||
}
|
||
|
||
async function filterEditUsers() {
|
||
const search = document.getElementById('edit-user-search')?.value.toLowerCase() || '';
|
||
const taskId = document.getElementById('edit-task-id')?.value;
|
||
if (!taskId) return;
|
||
|
||
const task = window.tasks?.find(t => t.id == taskId);
|
||
const taskType = task ? task.task_type : 'regular';
|
||
|
||
let filtered = users.filter(user =>
|
||
user.name.toLowerCase().includes(search) ||
|
||
user.login.toLowerCase().includes(search) ||
|
||
(user.email && user.email.toLowerCase().includes(search))
|
||
);
|
||
|
||
if (taskType === 'document') {
|
||
const filteredByGroup = [];
|
||
for (const user of filtered) {
|
||
const groups = await getUserGroups(user.id);
|
||
const hasSecretaryGroup = groups.some(group =>
|
||
group.name === 'Секретарь' ||
|
||
(typeof group === 'string' && group.includes('Секретарь'))
|
||
);
|
||
if (hasSecretaryGroup) filteredByGroup.push(user);
|
||
}
|
||
filtered = filteredByGroup;
|
||
}
|
||
|
||
renderEditUsersChecklist(filtered);
|
||
}
|
||
|
||
async function filterCopyUsers() {
|
||
const search = document.getElementById('copy-user-search')?.value.toLowerCase() || '';
|
||
const taskId = document.getElementById('copy-task-id')?.value;
|
||
if (!taskId) return;
|
||
|
||
const task = window.tasks?.find(t => t.id == taskId);
|
||
const taskType = task ? task.task_type : 'regular';
|
||
|
||
let filtered = users.filter(user =>
|
||
user.name.toLowerCase().includes(search) ||
|
||
user.login.toLowerCase().includes(search) ||
|
||
(user.email && user.email.toLowerCase().includes(search))
|
||
);
|
||
|
||
if (taskType === 'document') {
|
||
const filteredByGroup = [];
|
||
for (const user of filtered) {
|
||
const groups = await getUserGroups(user.id);
|
||
const hasSecretaryGroup = groups.some(group =>
|
||
group.name === 'Секретарь' ||
|
||
(typeof group === 'string' && group.includes('Секретарь'))
|
||
);
|
||
if (hasSecretaryGroup) filteredByGroup.push(user);
|
||
}
|
||
filtered = filteredByGroup;
|
||
}
|
||
|
||
renderCopyUsersChecklist(filtered);
|
||
}
|
||
|
||
// ==================== РЕНДЕРИНГ ЧЕКБОКСОВ ПОЛЬЗОВАТЕЛЕЙ ====================
|
||
|
||
function renderUsersChecklist() {
|
||
const container = document.getElementById('users-checklist');
|
||
if (!container) return;
|
||
|
||
// Создаём структуру с двумя колонками, если её ещё нет
|
||
if (!container.querySelector('.users-two-columns')) {
|
||
container.innerHTML = `
|
||
<div class="users-two-columns" style="display: flex; gap: 20px;">
|
||
<div class="left-column" style="flex: 1; min-width: 0;"></div>
|
||
<div class="right-column" style="flex: 1; min-width: 0;"></div>
|
||
</div>
|
||
`;
|
||
}
|
||
|
||
const leftCol = container.querySelector('.left-column');
|
||
const rightCol = container.querySelector('.right-column');
|
||
|
||
// Левая колонка – чекбоксы пользователей
|
||
if (isUsersLoading) {
|
||
leftCol.innerHTML = '<div class="loading-spinner">⏳ Загрузка пользователей...</div>';
|
||
} else if (!filteredUsers || filteredUsers.length === 0) {
|
||
leftCol.innerHTML = '<div class="no-users">Нет доступных пользователей</div>';
|
||
} else {
|
||
leftCol.innerHTML = filteredUsers
|
||
.filter(user => user.id !== currentUser?.id)
|
||
.map(user => `
|
||
<div class="checkbox-item">
|
||
<label>
|
||
<input type="checkbox" name="assignedUsers" value="${user.id}"
|
||
onchange="toggleUserSelection(this, ${user.id})"
|
||
${selectedUsers.includes(user.id) ? 'checked' : ''}>
|
||
${escapeHtml(user.name)}
|
||
${getUserTypeLabel(user, document.getElementById('task-type')?.value)}
|
||
</label>
|
||
</div>
|
||
`).join('');
|
||
}
|
||
|
||
// Правая колонка – панель списков пользователя
|
||
if (!document.getElementById('user-lists-panel')) {
|
||
const panel = document.createElement('div');
|
||
panel.id = 'user-lists-panel';
|
||
rightCol.appendChild(panel);
|
||
}
|
||
renderUserListsPanel();
|
||
}
|
||
|
||
function renderEditUsersChecklist(filtered = users) {
|
||
const container = document.getElementById('edit-users-checklist');
|
||
if (!container) return;
|
||
|
||
container.innerHTML = filtered
|
||
.filter(user => user.id !== currentUser?.id)
|
||
.map(user => `
|
||
<div class="checkbox-item">
|
||
<label>
|
||
<input type="checkbox" name="assignedUsers" value="${user.id}"
|
||
onchange="toggleEditUserSelection(this, ${user.id})"
|
||
${editSelectedUsers.includes(user.id) ? 'checked' : ''}>
|
||
${escapeHtml(user.name)} (${user.email})
|
||
${user.auth_type === 'ldap' ? '<small style="color: #666;"> - LDAP</small>' : ''}
|
||
</label>
|
||
</div>
|
||
`).join('');
|
||
}
|
||
|
||
function renderCopyUsersChecklist(filtered = users) {
|
||
const container = document.getElementById('copy-users-checklist');
|
||
if (!container) return;
|
||
|
||
container.innerHTML = filtered
|
||
.filter(user => user.id !== currentUser?.id)
|
||
.map(user => `
|
||
<div class="checkbox-item">
|
||
<label>
|
||
<input type="checkbox" name="assignedUsers" value="${user.id}"
|
||
onchange="toggleCopyUserSelection(this, ${user.id})"
|
||
${copySelectedUsers.includes(user.id) ? 'checked' : ''}>
|
||
${escapeHtml(user.name)} (${user.email})
|
||
${user.auth_type === 'ldap' ? '<small style="color: #666;"> - LDAP</small>' : ''}
|
||
</label>
|
||
</div>
|
||
`).join('');
|
||
}
|
||
|
||
// ==================== УПРАВЛЕНИЕ ВЫБРАННЫМИ ПОЛЬЗОВАТЕЛЯМИ ====================
|
||
|
||
function toggleUserSelection(checkbox, userId) {
|
||
if (checkbox.checked) {
|
||
if (!selectedUsers.includes(userId)) selectedUsers.push(userId);
|
||
} else {
|
||
selectedUsers = selectedUsers.filter(id => id !== userId);
|
||
}
|
||
}
|
||
|
||
function toggleEditUserSelection(checkbox, userId) {
|
||
if (checkbox.checked) {
|
||
if (!editSelectedUsers.includes(userId)) editSelectedUsers.push(userId);
|
||
} else {
|
||
editSelectedUsers = editSelectedUsers.filter(id => id !== userId);
|
||
}
|
||
}
|
||
|
||
function toggleCopyUserSelection(checkbox, userId) {
|
||
if (checkbox.checked) {
|
||
if (!copySelectedUsers.includes(userId)) copySelectedUsers.push(userId);
|
||
} else {
|
||
copySelectedUsers = copySelectedUsers.filter(id => id !== userId);
|
||
}
|
||
}
|
||
|
||
// ==================== ФУНКЦИИ ДЛЯ РАБОТЫ СО СПИСКАМИ ПОЛЬЗОВАТЕЛЕЙ ====================
|
||
|
||
async function loadUserLists() {
|
||
if (!currentUser) return;
|
||
isUserListsLoading = true;
|
||
renderUserListsPanel();
|
||
|
||
try {
|
||
const response = await fetch('/api/user/lists');
|
||
if (response.ok) {
|
||
const lists = await response.json();
|
||
// Сервер уже отдаёт user_ids как массив, просто копируем в userIds
|
||
userLists = lists.map(list => ({
|
||
...list,
|
||
userIds: list.user_ids || [] // предполагаем, что это массив
|
||
}));
|
||
} else {
|
||
console.error('Ошибка загрузки списков:', response.status);
|
||
userLists = [];
|
||
}
|
||
} catch (error) {
|
||
console.error('Сетевая ошибка при загрузке списков:', error);
|
||
userLists = [];
|
||
} finally {
|
||
isUserListsLoading = false;
|
||
renderUserListsPanel();
|
||
}
|
||
}
|
||
|
||
async function saveUserList(listData) {
|
||
try {
|
||
const response = await fetch('/api/user/lists', {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify(listData)
|
||
});
|
||
if (response.ok) {
|
||
const newList = await response.json();
|
||
const transformed = {
|
||
...newList,
|
||
userIds: newList.user_ids || []
|
||
};
|
||
userLists.push(transformed);
|
||
renderUserListsPanel();
|
||
return transformed;
|
||
} else {
|
||
const err = await response.json();
|
||
alert('Ошибка создания списка: ' + (err.error || 'Неизвестная ошибка'));
|
||
return null;
|
||
}
|
||
} catch (error) {
|
||
console.error('Сетевая ошибка:', error);
|
||
alert('Не удалось сохранить список. Проверьте соединение.');
|
||
return null;
|
||
}
|
||
}
|
||
|
||
async function updateUserList(listId, listData) {
|
||
try {
|
||
const response = await fetch(`/api/user/lists/${listId}`, {
|
||
method: 'PUT',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify(listData)
|
||
});
|
||
if (response.ok) {
|
||
const updatedList = await response.json();
|
||
const transformed = {
|
||
...updatedList,
|
||
userIds: updatedList.user_ids || []
|
||
};
|
||
const index = userLists.findIndex(l => l.id === listId);
|
||
if (index !== -1) userLists[index] = transformed;
|
||
renderUserListsPanel();
|
||
return transformed;
|
||
} else {
|
||
const err = await response.json();
|
||
alert('Ошибка обновления списка: ' + (err.error || 'Неизвестная ошибка'));
|
||
return null;
|
||
}
|
||
} catch (error) {
|
||
console.error('Сетевая ошибка:', error);
|
||
alert('Не удалось обновить список.');
|
||
return null;
|
||
}
|
||
}
|
||
|
||
async function deleteUserList(listId) {
|
||
if (!confirm('Вы уверены, что хотите удалить этот список?')) return;
|
||
try {
|
||
const response = await fetch(`/api/user/lists/${listId}`, {
|
||
method: 'DELETE'
|
||
});
|
||
if (response.ok) {
|
||
userLists = userLists.filter(l => l.id !== listId);
|
||
renderUserListsPanel();
|
||
} else {
|
||
const err = await response.json();
|
||
alert('Ошибка удаления списка: ' + (err.error || 'Неизвестная ошибка'));
|
||
}
|
||
} catch (error) {
|
||
console.error('Сетевая ошибка:', error);
|
||
alert('Не удалось удалить список.');
|
||
}
|
||
}
|
||
|
||
function applyUserList(list) {
|
||
if (!list || !list.userIds || list.userIds.length === 0) return;
|
||
|
||
// Очищаем текущий выбор
|
||
selectedUsers = [];
|
||
|
||
// Добавляем всех пользователей из списка
|
||
list.userIds.forEach(userId => {
|
||
if (!selectedUsers.includes(userId)) {
|
||
selectedUsers.push(userId);
|
||
}
|
||
});
|
||
|
||
// Обновляем состояние чекбоксов в левой колонке
|
||
const checkboxes = document.querySelectorAll('#users-checklist .left-column input[type="checkbox"]');
|
||
checkboxes.forEach(cb => {
|
||
const userId = parseInt(cb.value);
|
||
cb.checked = list.userIds.includes(userId);
|
||
});
|
||
}
|
||
|
||
function renderUserListsPanel() {
|
||
const panel = document.getElementById('user-lists-panel');
|
||
if (!panel) return;
|
||
|
||
if (isUserListsLoading) {
|
||
panel.innerHTML = '<div class="loading-spinner">⏳ Загрузка списков...</div>';
|
||
return;
|
||
}
|
||
|
||
let html = `
|
||
<div class="user-lists-header">
|
||
<h4>Мои списки</h4>
|
||
<button class="btn-create-list" onclick="openCreateListModal()">➕ Создать</button>
|
||
</div>
|
||
<div class="user-lists-container">
|
||
`;
|
||
|
||
if (userLists.length === 0) {
|
||
html += '<p class="no-lists">У вас пока нет списков</p>';
|
||
} else {
|
||
userLists.forEach(list => {
|
||
const memberCount = list.userIds ? list.userIds.length : 0;
|
||
// Экранируем название для безопасного использования в onclick
|
||
const listJson = JSON.stringify(list).replace(/"/g, '"');
|
||
html += `
|
||
<div class="user-list-item" data-list-id="${list.id}">
|
||
<div class="list-info" onclick="applyUserList(${listJson})">
|
||
<span class="list-name">${escapeHtml(list.name)}</span>
|
||
<span class="list-count">(${memberCount})</span>
|
||
</div>
|
||
<div class="list-actions">
|
||
<button class="list-edit-btn" onclick="event.stopPropagation(); openEditListModal(${list.id})" title="Редактировать">✏️</button>
|
||
<button class="list-delete-btn" onclick="event.stopPropagation(); deleteUserList(${list.id})" title="Удалить">🗑️</button>
|
||
</div>
|
||
</div>
|
||
`;
|
||
});
|
||
}
|
||
|
||
html += '</div>';
|
||
panel.innerHTML = html;
|
||
}
|
||
|
||
function openCreateListModal() {
|
||
currentEditingListId = null;
|
||
showListModal(null);
|
||
}
|
||
|
||
function openEditListModal(listId) {
|
||
const list = userLists.find(l => l.id === listId);
|
||
if (list) {
|
||
currentEditingListId = listId;
|
||
showListModal(list);
|
||
}
|
||
}
|
||
|
||
function showListModal(list) {
|
||
// Удаляем предыдущее модальное окно, если есть
|
||
const existingModal = document.getElementById('list-modal');
|
||
if (existingModal) existingModal.remove();
|
||
|
||
const modal = document.createElement('div');
|
||
modal.id = 'list-modal';
|
||
modal.className = 'modal';
|
||
modal.style.display = 'block';
|
||
|
||
const title = list ? 'Редактировать список' : 'Создать список';
|
||
const listName = list ? list.name : '';
|
||
const selectedUserIds = list ? list.userIds || [] : [];
|
||
|
||
// Генерируем чекбоксы всех пользователей (allUsers)
|
||
const usersCheckboxes = allUsers
|
||
.filter(user => user.id !== currentUser?.id)
|
||
.map(user => {
|
||
const checked = selectedUserIds.includes(user.id) ? 'checked' : '';
|
||
return `
|
||
<div class="checkbox-item">
|
||
<label>
|
||
<input type="checkbox" class="list-user-checkbox" value="${user.id}" ${checked}>
|
||
${escapeHtml(user.name)} (${escapeHtml(user.login)})
|
||
</label>
|
||
</div>
|
||
`;
|
||
}).join('');
|
||
|
||
modal.innerHTML = `
|
||
<div class="modal-content" style="max-width: 600px;">
|
||
<div class="modal-header">
|
||
<h3>${title}</h3>
|
||
<span class="close" onclick="closeListModal()">×</span>
|
||
</div>
|
||
<div class="modal-body">
|
||
<div class="form-group">
|
||
<label for="list-name">Название списка (до 35 символов):</label>
|
||
<input type="text" id="list-name" maxlength="35" value="${escapeHtml(listName)}" placeholder="Введите название">
|
||
</div>
|
||
<div class="form-group">
|
||
<label>Выберите пользователей:</label>
|
||
<div class="user-search-box" style="margin-bottom: 10px;">
|
||
<input type="text" id="list-user-search" placeholder="Поиск пользователей..." oninput="filterListUsers()" style="width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px;">
|
||
</div>
|
||
<div class="users-checklist-scroll" id="list-users-container" style="max-height: 300px; overflow-y: auto; border: 1px solid #ddd; padding: 10px;">
|
||
${usersCheckboxes}
|
||
</div>
|
||
</div>
|
||
<div class="modal-footer">
|
||
<button type="button" class="btn-cancel" onclick="closeListModal()">Отмена</button>
|
||
<button type="button" class="btn-primary" onclick="saveListFromModal()">Сохранить</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
`;
|
||
|
||
document.body.appendChild(modal);
|
||
}
|
||
function filterListUsers() {
|
||
const searchInput = document.getElementById('list-user-search');
|
||
if (!searchInput) return;
|
||
const searchTerm = searchInput.value.toLowerCase();
|
||
const container = document.getElementById('list-users-container');
|
||
if (!container) return;
|
||
const items = container.querySelectorAll('.checkbox-item');
|
||
items.forEach(item => {
|
||
const label = item.querySelector('label')?.innerText.toLowerCase() || '';
|
||
if (label.includes(searchTerm) || searchTerm === '') {
|
||
item.style.display = '';
|
||
} else {
|
||
item.style.display = 'none';
|
||
}
|
||
});
|
||
}
|
||
function closeListModal() {
|
||
const modal = document.getElementById('list-modal');
|
||
if (modal) {
|
||
modal.style.display = 'none';
|
||
setTimeout(() => modal.remove(), 300);
|
||
}
|
||
currentEditingListId = null;
|
||
}
|
||
|
||
async function saveListFromModal() {
|
||
const nameInput = document.getElementById('list-name');
|
||
const name = nameInput.value.trim();
|
||
if (!name) {
|
||
alert('Введите название списка');
|
||
return;
|
||
}
|
||
if (name.length > 35) {
|
||
alert('Название не должно превышать 35 символов');
|
||
return;
|
||
}
|
||
|
||
// Собираем выбранные ID пользователей
|
||
const checkboxes = document.querySelectorAll('#list-modal .list-user-checkbox:checked');
|
||
const userIds = Array.from(checkboxes).map(cb => parseInt(cb.value));
|
||
|
||
const listData = { name, userIds };
|
||
|
||
if (currentEditingListId) {
|
||
await updateUserList(currentEditingListId, listData);
|
||
} else {
|
||
await saveUserList(listData);
|
||
}
|
||
|
||
closeListModal();
|
||
}
|
||
|
||
// ==================== ФУНКЦИИ ДЛЯ ОЗНАКОМЛЕНИЯ (выбор исполнителей) ====================
|
||
|
||
function renderAcquaintanceUsersChecklist(filtered = users) {
|
||
const container = document.getElementById('acquaintance-users-checklist');
|
||
if (!container) return;
|
||
container.innerHTML = filtered
|
||
.filter(user => user.id !== currentUser?.id)
|
||
.map(user => `
|
||
<div class="checkbox-item">
|
||
<label>
|
||
<input type="checkbox" name="assignedUsers" value="${user.id}"
|
||
onchange="toggleAcquaintanceUserSelection(this, ${user.id})"
|
||
${acquaintanceSelectedUsers.includes(user.id) ? 'checked' : ''}>
|
||
${escapeHtml(user.name)} (${user.login})
|
||
</label>
|
||
</div>
|
||
`).join('');
|
||
}
|
||
|
||
function toggleAcquaintanceUserSelection(checkbox, userId) {
|
||
if (checkbox.checked) {
|
||
if (!acquaintanceSelectedUsers.includes(userId)) acquaintanceSelectedUsers.push(userId);
|
||
} else {
|
||
acquaintanceSelectedUsers = acquaintanceSelectedUsers.filter(id => id !== userId);
|
||
}
|
||
}
|
||
|
||
async function filterAcquaintanceUsers() {
|
||
const search = document.getElementById('acquaintance-user-search')?.value.toLowerCase() || '';
|
||
let filtered = users.filter(user =>
|
||
user.name.toLowerCase().includes(search) ||
|
||
user.login.toLowerCase().includes(search) ||
|
||
(user.email && user.email.toLowerCase().includes(search))
|
||
);
|
||
renderAcquaintanceUsersChecklist(filtered);
|
||
}
|
||
|
||
// ==================== ФУНКЦИИ ДЛЯ ВЫБОРА АВТОРА В ЗАДАЧЕ ОЗНАКОМЛЕНИЯ ====================
|
||
|
||
function renderAcquaintanceAuthorsChecklist(filtered = users) {
|
||
const container = document.getElementById('acquaintance-authors-checklist');
|
||
if (!container) return;
|
||
container.innerHTML = filtered
|
||
.filter(user => user.id !== currentUser?.id) // можно разрешить выбирать себя, если нужно
|
||
.map(user => `
|
||
<div class="checkbox-item">
|
||
<label>
|
||
<input type="radio" name="acquaintance-author" value="${user.id}"
|
||
onchange="toggleAcquaintanceAuthorSelection(this, ${user.id})"
|
||
${acquaintanceSelectedAuthor === user.id ? 'checked' : ''}>
|
||
${escapeHtml(user.name)} (${user.login})
|
||
</label>
|
||
</div>
|
||
`).join('');
|
||
}
|
||
|
||
function toggleAcquaintanceAuthorSelection(radio, userId) {
|
||
acquaintanceSelectedAuthor = radio.checked ? userId : null;
|
||
}
|
||
|
||
async function filterAcquaintanceAuthors() {
|
||
const search = document.getElementById('acquaintance-author-search')?.value.toLowerCase() || '';
|
||
let filtered = users.filter(user =>
|
||
user.name.toLowerCase().includes(search) ||
|
||
user.login.toLowerCase().includes(search) ||
|
||
(user.email && user.email.toLowerCase().includes(search))
|
||
);
|
||
renderAcquaintanceAuthorsChecklist(filtered);
|
||
}
|
||
|
||
// ==================== ВСПОМОГАТЕЛЬНЫЕ ФУНКЦИИ ====================
|
||
|
||
function escapeHtml(text) {
|
||
if (!text) return '';
|
||
return String(text)
|
||
.replace(/&/g, '&')
|
||
.replace(/</g, '<')
|
||
.replace(/>/g, '>')
|
||
.replace(/"/g, '"')
|
||
.replace(/'/g, ''');
|
||
}
|
||
|
||
function getUserTypeLabel(user, taskType) {
|
||
const labels = {
|
||
'document': '(Секретарь)',
|
||
'it': '(ИТ специалист)',
|
||
'ahch': '(АХЧ)',
|
||
'psychologist': '(Психолог)',
|
||
'speech_therapist': '(Логопед)',
|
||
'Social_educator': '(Социальный педагог)',
|
||
'hr': '(Диспетчер)',
|
||
'certificate': '(Администрация)',
|
||
'e_journal': '(Админ ЭЖ)'
|
||
};
|
||
return labels[taskType] || '';
|
||
}
|
||
|
||
// Экспорт функций в глобальную область (для вызова из HTML)
|
||
window.loadUsers = loadUsers;
|
||
window.filterUsers = filterUsers;
|
||
window.filterEditUsers = filterEditUsers;
|
||
window.filterCopyUsers = filterCopyUsers;
|
||
window.toggleUserSelection = toggleUserSelection;
|
||
window.toggleEditUserSelection = toggleEditUserSelection;
|
||
window.toggleCopyUserSelection = toggleCopyUserSelection;
|
||
window.openCreateListModal = openCreateListModal;
|
||
window.openEditListModal = openEditListModal;
|
||
window.deleteUserList = deleteUserList;
|
||
window.applyUserList = applyUserList;
|
||
window.closeListModal = closeListModal;
|
||
window.saveListFromModal = saveListFromModal;
|
||
window.filterListUsers = filterListUsers;
|
||
|
||
// Экспорт функций для ознакомления (исполнители)
|
||
window.renderAcquaintanceUsersChecklist = renderAcquaintanceUsersChecklist;
|
||
window.toggleAcquaintanceUserSelection = toggleAcquaintanceUserSelection;
|
||
window.filterAcquaintanceUsers = filterAcquaintanceUsers;
|
||
window.acquaintanceSelectedUsers = acquaintanceSelectedUsers;
|
||
|
||
// Экспорт функций для выбора автора
|
||
window.renderAcquaintanceAuthorsChecklist = renderAcquaintanceAuthorsChecklist;
|
||
window.toggleAcquaintanceAuthorSelection = toggleAcquaintanceAuthorSelection;
|
||
window.filterAcquaintanceAuthors = filterAcquaintanceAuthors;
|
||
window.acquaintanceSelectedAuthor = acquaintanceSelectedAuthor;
|
||
|
||
// Также экспортируем переменные, которые могут понадобиться в других скриптах
|
||
window.selectedUsers = selectedUsers;
|
||
window.editSelectedUsers = editSelectedUsers;
|
||
window.copySelectedUsers = copySelectedUsers; |