список пользователя
This commit is contained in:
797
public/users.js
797
public/users.js
@@ -1,45 +1,63 @@
|
||||
// users.js - Управление пользователями
|
||||
// users.js - Управление пользователями и пользовательскими списками
|
||||
|
||||
let users = [];
|
||||
let allUsers = [];
|
||||
let usersLoadingPromise = null;
|
||||
let filteredUsers = [];
|
||||
let selectedUsers = [];
|
||||
let editSelectedUsers = [];
|
||||
let copySelectedUsers = [];
|
||||
// добавить переменную для отслеживания загрузки
|
||||
let isUsersLoading = false;
|
||||
|
||||
// Добавьте переменную для хранения групп пользователей
|
||||
// Переменные для пользовательских списков
|
||||
let userLists = [];
|
||||
let isUserListsLoading = false;
|
||||
let currentEditingListId = null;
|
||||
|
||||
// Кэш групп пользователей
|
||||
let userGroupsCache = {};
|
||||
|
||||
let isUsersLoading = false;
|
||||
|
||||
// ==================== ЗАГРУЗКА ПОЛЬЗОВАТЕЛЕЙ ====================
|
||||
|
||||
async function loadUsers() {
|
||||
try {
|
||||
const response = await fetch('/api/users');
|
||||
const allUsersData = await response.json();
|
||||
//users = await response.json();
|
||||
// Сохраняем всех пользователей
|
||||
allUsers = allUsersData;
|
||||
// Фильтруем пользователей в зависимости от прав текущего пользователя
|
||||
users = filterAssignableUsers(allUsersData);
|
||||
filteredUsers = [...users];
|
||||
renderUsersChecklist();
|
||||
renderEditUsersChecklist();
|
||||
renderCopyUsersChecklist();
|
||||
populateFilterDropdowns();
|
||||
} catch (error) {
|
||||
console.error('Ошибка загрузки пользователей:', error);
|
||||
}
|
||||
// Если загрузка уже идёт, возвращаем существующий промис
|
||||
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];
|
||||
@@ -49,47 +67,16 @@ async function getUserGroups(userId) {
|
||||
}
|
||||
}
|
||||
|
||||
// Обновите функцию filterAssignableUsers
|
||||
// ==================== ФИЛЬТРАЦИЯ ПОЛЬЗОВАТЕЛЕЙ ПО ПРАВАМ ====================
|
||||
|
||||
function filterAssignableUsers(allUsers, taskType = 'regular') {
|
||||
if (!currentUser) return [];
|
||||
|
||||
// Для задач типа "document" - только пользователи из группы "Секретарь"
|
||||
if (taskType === 'document') {
|
||||
return allUsers.filter(async (user) => {
|
||||
if (user.id === currentUser.id) return false;
|
||||
|
||||
// Получаем группы пользователя
|
||||
const groups = await getUserGroups(user.id);
|
||||
|
||||
// Проверяем, есть ли группа "Секретарь"
|
||||
const hasSecretaryGroup = groups.some(group =>
|
||||
group.name === 'Секретарь' ||
|
||||
(typeof group === 'string' && group.includes('Секретарь'))
|
||||
);
|
||||
|
||||
return hasSecretaryGroup;
|
||||
});
|
||||
}
|
||||
// Для задач типа "it" - только пользователи из группы "ИТ специалист"
|
||||
if (taskType === 'it') {
|
||||
return allUsers.filter(async (user) => {
|
||||
if (user.id === currentUser.id) return false;
|
||||
|
||||
// Получаем группы пользователя
|
||||
const groups = await getUserGroups(user.id);
|
||||
|
||||
// Проверяем, есть ли группа "Секретарь"
|
||||
const hasSecretaryGroup = groups.some(group =>
|
||||
group.name === 'ИТ специалист' ||
|
||||
(typeof group === 'string' && group.includes('ИТ специалист'))
|
||||
);
|
||||
|
||||
return hasSecretaryGroup;
|
||||
});
|
||||
}
|
||||
|
||||
// Для других типов задач - обычная фильтрация
|
||||
// Администратор видит всех пользователей
|
||||
|
||||
// Для задач типа "document" – только секретари (асинхронно не получится здесь, но мы фильтруем позже)
|
||||
// В текущей реализации эта функция вызывается синхронно, поэтому для специальных типов фильтрация будет в filterUsers
|
||||
// Здесь оставляем базовую фильтрацию по ролям
|
||||
|
||||
// Администратор видит всех, кроме себя
|
||||
if (currentUser.role === 'admin') {
|
||||
return allUsers.filter(user => user.id !== currentUser.id);
|
||||
}
|
||||
@@ -97,81 +84,82 @@ function filterAssignableUsers(allUsers, taskType = 'regular') {
|
||||
return allUsers.filter(user => user.id !== currentUser.id);
|
||||
}
|
||||
if (currentUser.role === 'ithelp') {
|
||||
return allUsers.filter(user =>
|
||||
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 =>
|
||||
return allUsers.filter(user =>
|
||||
(user.role === 'teacher' || user.role === 'tasks' || user.role === 'help' || user.role === 'request' || user.role === 'ithelp') &&
|
||||
user.id !== currentUser.id
|
||||
);
|
||||
}
|
||||
// tasks видит учителей и других tasks
|
||||
if (currentUser.role === 'help') {
|
||||
return allUsers.filter(user =>
|
||||
return allUsers.filter(user =>
|
||||
(user.role === 'teacher' || user.role === 'tasks' || user.role === 'help' || user.role === 'request' || user.role === 'ithelp') &&
|
||||
user.id !== currentUser.id
|
||||
);
|
||||
}
|
||||
// tasks видит учителей и других tasks
|
||||
if (currentUser.role === 'tasks') {
|
||||
return allUsers.filter(user =>
|
||||
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') &&
|
||||
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');
|
||||
|
||||
creatorFilter.innerHTML = '<option value="">Все заказчики</option>';
|
||||
assigneeFilter.innerHTML = '<option value="">Все исполнители</option>';
|
||||
|
||||
|
||||
if (creatorFilter) {
|
||||
creatorFilter.innerHTML = '<option value="">Все заказчики</option>';
|
||||
}
|
||||
if (assigneeFilter) {
|
||||
assigneeFilter.innerHTML = '<option value="">Все исполнители</option>';
|
||||
}
|
||||
|
||||
users.forEach(user => {
|
||||
const creatorOption = document.createElement('option');
|
||||
creatorOption.value = user.id;
|
||||
creatorOption.textContent = `${user.name} (${user.login})`;
|
||||
creatorFilter.appendChild(creatorOption.cloneNode(true));
|
||||
|
||||
const assigneeOption = creatorOption.cloneNode(true);
|
||||
assigneeFilter.appendChild(assigneeOption);
|
||||
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));
|
||||
});
|
||||
}
|
||||
|
||||
// Обновите функцию filterUsers с учетом типа задачи
|
||||
// ==================== ФИЛЬТРАЦИЯ ПРИ ПОИСКЕ (С УЧЁТОМ ТИПА ЗАДАЧИ) ====================
|
||||
|
||||
async function filterUsers() {
|
||||
const search = document.getElementById('user-search')?.value.toLowerCase() || '';
|
||||
const taskType = document.getElementById('task-type')?.value || 'regular';
|
||||
|
||||
|
||||
isUsersLoading = true;
|
||||
renderUsersChecklist(); // Показываем загрузку
|
||||
|
||||
|
||||
try {
|
||||
if (taskType === 'document' || taskType === 'it' || taskType === 'ahch' ||
|
||||
taskType === 'psychologist' || taskType === 'speech_therapist' || taskType === 'Social_educator' ||
|
||||
taskType === 'hr' || taskType === 'certificate' || taskType === 'e_journal') {
|
||||
|
||||
// Фильтруем по поиску
|
||||
let tempFiltered = users.filter(user =>
|
||||
user.name.toLowerCase().includes(search) ||
|
||||
user.login.toLowerCase().includes(search) ||
|
||||
user.email.toLowerCase().includes(search)
|
||||
);
|
||||
|
||||
// Затем проверяем группы
|
||||
filteredUsers = [];
|
||||
// Сначала фильтруем по поиску
|
||||
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': 'ИТ специалист',
|
||||
@@ -183,27 +171,21 @@ async function filterUsers() {
|
||||
'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 ||
|
||||
const hasTargetGroup = groups.some(group =>
|
||||
group.name === targetGroup ||
|
||||
(typeof group === 'string' && group.includes(targetGroup))
|
||||
);
|
||||
|
||||
if (hasTargetGroup) {
|
||||
filteredUsers.push(user);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Обычная фильтрация
|
||||
filteredUsers = users.filter(user =>
|
||||
user.name.toLowerCase().includes(search) ||
|
||||
user.login.toLowerCase().includes(search) ||
|
||||
user.email.toLowerCase().includes(search)
|
||||
);
|
||||
filteredUsers = tempFiltered;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Ошибка фильтрации пользователей:', error);
|
||||
@@ -215,110 +197,483 @@ async function filterUsers() {
|
||||
}
|
||||
|
||||
async function filterEditUsers() {
|
||||
const search = document.getElementById('edit-user-search').value.toLowerCase();
|
||||
const task = tasks.find(t => t.id === document.getElementById('edit-task-id').value);
|
||||
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 = [];
|
||||
|
||||
|
||||
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') {
|
||||
// Для задач типа "document" - только секретари
|
||||
let tempFiltered = users.filter(user =>
|
||||
user.name.toLowerCase().includes(search) ||
|
||||
user.login.toLowerCase().includes(search) ||
|
||||
user.email.toLowerCase().includes(search)
|
||||
);
|
||||
|
||||
filtered = [];
|
||||
for (const user of tempFiltered) {
|
||||
const filteredByGroup = [];
|
||||
for (const user of filtered) {
|
||||
const groups = await getUserGroups(user.id);
|
||||
const hasSecretaryGroup = groups.some(group =>
|
||||
group.name === 'Секретарь' ||
|
||||
const hasSecretaryGroup = groups.some(group =>
|
||||
group.name === 'Секретарь' ||
|
||||
(typeof group === 'string' && group.includes('Секретарь'))
|
||||
);
|
||||
|
||||
if (hasSecretaryGroup) {
|
||||
filtered.push(user);
|
||||
}
|
||||
if (hasSecretaryGroup) filteredByGroup.push(user);
|
||||
}
|
||||
} else {
|
||||
filtered = users.filter(user =>
|
||||
user.name.toLowerCase().includes(search) ||
|
||||
user.login.toLowerCase().includes(search) ||
|
||||
user.email.toLowerCase().includes(search)
|
||||
);
|
||||
filtered = filteredByGroup;
|
||||
}
|
||||
|
||||
|
||||
renderEditUsersChecklist(filtered);
|
||||
}
|
||||
|
||||
async function filterCopyUsers() {
|
||||
const search = document.getElementById('copy-user-search').value.toLowerCase();
|
||||
const taskId = document.getElementById('copy-task-id').value;
|
||||
const task = tasks.find(t => t.id === taskId);
|
||||
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 = [];
|
||||
|
||||
|
||||
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') {
|
||||
// Для задач типа "document" - только секретари
|
||||
let tempFiltered = users.filter(user =>
|
||||
user.name.toLowerCase().includes(search) ||
|
||||
user.login.toLowerCase().includes(search) ||
|
||||
user.email.toLowerCase().includes(search)
|
||||
);
|
||||
|
||||
filtered = [];
|
||||
for (const user of tempFiltered) {
|
||||
const filteredByGroup = [];
|
||||
for (const user of filtered) {
|
||||
const groups = await getUserGroups(user.id);
|
||||
const hasSecretaryGroup = groups.some(group =>
|
||||
group.name === 'Секретарь' ||
|
||||
const hasSecretaryGroup = groups.some(group =>
|
||||
group.name === 'Секретарь' ||
|
||||
(typeof group === 'string' && group.includes('Секретарь'))
|
||||
);
|
||||
|
||||
if (hasSecretaryGroup) {
|
||||
filtered.push(user);
|
||||
}
|
||||
if (hasSecretaryGroup) filteredByGroup.push(user);
|
||||
}
|
||||
} else {
|
||||
filtered = users.filter(user =>
|
||||
user.name.toLowerCase().includes(search) ||
|
||||
user.login.toLowerCase().includes(search) ||
|
||||
user.email.toLowerCase().includes(search)
|
||||
);
|
||||
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) {
|
||||
container.innerHTML = '<div class="loading-spinner">⏳ Загрузка пользователей...</div>';
|
||||
return;
|
||||
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 (!filteredUsers || filteredUsers.length === 0) {
|
||||
container.innerHTML = '<div class="no-users">Нет доступных пользователей</div>';
|
||||
return;
|
||||
|
||||
// Правая колонка – панель списков пользователя
|
||||
if (!document.getElementById('user-lists-panel')) {
|
||||
const panel = document.createElement('div');
|
||||
panel.id = 'user-lists-panel';
|
||||
rightCol.appendChild(panel);
|
||||
}
|
||||
|
||||
container.innerHTML = filteredUsers
|
||||
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="toggleUserSelection(this, ${user.id})"
|
||||
${selectedUsers.includes(user.id) ? 'checked' : ''}>
|
||||
${user.name}
|
||||
${getUserTypeLabel(user, document.getElementById('task-type')?.value)}
|
||||
</label>
|
||||
</div>
|
||||
`).join('');
|
||||
<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 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': '(Секретарь)',
|
||||
@@ -333,58 +688,24 @@ function getUserTypeLabel(user, taskType) {
|
||||
};
|
||||
return labels[taskType] || '';
|
||||
}
|
||||
function renderEditUsersChecklist(filtered = users) {
|
||||
const container = document.getElementById('edit-users-checklist');
|
||||
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})">
|
||||
${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');
|
||||
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})">
|
||||
${user.name} (${user.email})
|
||||
${user.auth_type === 'ldap' ? '<small style="color: #666;"> - LDAP</small>' : ''}
|
||||
</label>
|
||||
</div>
|
||||
`).join('');
|
||||
}
|
||||
// Экспорт функций в глобальную область (для вызова из 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;
|
||||
|
||||
function toggleUserSelection(checkbox, userId) {
|
||||
if (checkbox.checked) {
|
||||
selectedUsers.push(userId);
|
||||
} else {
|
||||
selectedUsers = selectedUsers.filter(id => id !== userId);
|
||||
}
|
||||
}
|
||||
|
||||
function toggleEditUserSelection(checkbox, userId) {
|
||||
if (checkbox.checked) {
|
||||
editSelectedUsers.push(userId);
|
||||
} else {
|
||||
editSelectedUsers = editSelectedUsers.filter(id => id !== userId);
|
||||
}
|
||||
}
|
||||
|
||||
function toggleCopyUserSelection(checkbox, userId) {
|
||||
if (checkbox.checked) {
|
||||
copySelectedUsers.push(userId);
|
||||
} else {
|
||||
copySelectedUsers = copySelectedUsers.filter(id => id !== userId);
|
||||
}
|
||||
}
|
||||
// Также экспортируем переменные, которые могут понадобиться в других скриптах
|
||||
window.selectedUsers = selectedUsers;
|
||||
window.editSelectedUsers = editSelectedUsers;
|
||||
window.copySelectedUsers = copySelectedUsers;
|
||||
Reference in New Issue
Block a user