индикатор загрузки в чеклист пользователей

This commit is contained in:
2026-02-13 21:27:07 +05:00
parent 40c454a786
commit db2ae5a654
3 changed files with 181 additions and 36 deletions

View File

@@ -4378,4 +4378,46 @@ button.btn-primary {
.profile-stats {
grid-template-columns: 1fr;
}
}
/* Добавить в CSS */
.loading-spinner {
padding: 20px;
text-align: center;
color: #666;
font-style: italic;
}
.no-users {
padding: 20px;
text-align: center;
color: #999;
background: #f5f5f5;
border-radius: 5px;
}
.user-checkbox:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.checkbox-item {
transition: opacity 0.3s;
}
.checkbox-item.loading {
opacity: 0.7;
pointer-events: none;
}
.loading-badge {
color: #f39c12;
margin-left: 5px;
font-size: 0.8em;
animation: pulse 1.5s infinite;
}
@keyframes pulse {
0% { opacity: 0.6; }
50% { opacity: 1; }
100% { opacity: 0.6; }
}

View File

@@ -856,7 +856,7 @@ async function loadTaskFiles(taskId) {
}
// Функция для выбора типа задачи
async function selectTaskType(type) {
async function selectTaskType_old(type) {
// Убираем активный класс со всех кнопок
document.querySelectorAll('.task-type-btn').forEach(btn => {
btn.classList.remove('active');
@@ -895,6 +895,60 @@ async function selectTaskType(type) {
await loadUsers();
}
}
// ui.js - обновить функцию selectTaskType
async function selectTaskType(type) {
// Убираем активный класс со всех кнопок
document.querySelectorAll('.task-type-btn').forEach(btn => {
btn.classList.remove('active');
});
// Добавляем активный класс выбранной кнопке
const selectedBtn = document.querySelector(`.task-type-btn[data-type="${type}"]`);
if (selectedBtn) {
selectedBtn.classList.add('active');
}
// Устанавливаем значение в скрытое поле
document.getElementById('task-type').value = type;
// Обновляем форму
updateTaskFormBasedOnType(type);
suggestDefaultTitle(type);
// Очищаем выбранных пользователей при смене типа
selectedUsers = [];
// Показываем загрузку
const checklist = document.getElementById('users-checklist');
if (checklist) {
checklist.innerHTML = '<div class="loading-spinner">⏳ Загрузка доступных пользователей...</div>';
}
// Загружаем пользователей для выбранного типа
try {
if (type === 'document') {
await reloadUsersForDocumentType();
} else if (['it', 'ahch', 'psychologist', 'speech_therapist', 'hr', 'certificate', 'e_journal'].includes(type)) {
const groupNames = {
'it': 'ИТ специалист',
'ahch': 'АХЧ',
'psychologist': 'психолог',
'speech_therapist': 'логопед',
'hr': 'Диспетчер',
'certificate': 'Администрация',
'e_journal': 'Админ ЭЖ'
};
await reloadUsersForType(groupNames[type]);
} else {
await loadUsers();
}
} catch (error) {
console.error('Ошибка загрузки пользователей:', error);
if (checklist) {
checklist.innerHTML = '<div class="error">Ошибка загрузки пользователей</div>';
}
}
}
// функция для перезагрузки пользователей для типа "document"
async function reloadUsersForDocumentType() {
try {

View File

@@ -5,6 +5,8 @@ let filteredUsers = [];
let selectedUsers = [];
let editSelectedUsers = [];
let copySelectedUsers = [];
// добавить переменную для отслеживания загрузки
let isUsersLoading = false;
// Добавьте переменную для хранения групп пользователей
let userGroupsCache = {};
@@ -150,41 +152,65 @@ function populateFilterDropdowns() {
// Обновите функцию filterUsers с учетом типа задачи
async function filterUsers() {
const search = document.getElementById('user-search').value.toLowerCase();
const taskType = document.getElementById('task-type').value || 'regular';
const search = document.getElementById('user-search')?.value.toLowerCase() || '';
const taskType = document.getElementById('task-type')?.value || 'regular';
// Для задач типа "document" нужна особая фильтрация
if (taskType === 'document') {
// Сначала фильтруем по поисковому запросу
let tempFiltered = users.filter(user =>
user.name.toLowerCase().includes(search) ||
user.login.toLowerCase().includes(search) ||
user.email.toLowerCase().includes(search)
);
// Затем проверяем группы для каждого пользователя
filteredUsers = [];
for (const user of tempFiltered) {
const groups = await getUserGroups(user.id);
const hasSecretaryGroup = groups.some(group =>
group.name === 'Секретарь' ||
(typeof group === 'string' && group.includes('Секретарь'))
isUsersLoading = true;
renderUsersChecklist(); // Показываем загрузку
try {
if (taskType === 'document' || taskType === 'it' || taskType === 'ahch' ||
taskType === 'psychologist' || taskType === 'speech_therapist' ||
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)
);
if (hasSecretaryGroup) {
filteredUsers.push(user);
// Затем проверяем группы
filteredUsers = [];
const groupNames = {
'document': 'Секретарь',
'it': 'ИТ специалист',
'ahch': 'АХЧ',
'psychologist': 'психолог',
'speech_therapist': 'логопед',
'hr': 'Диспетчер',
'certificate': 'Администрация',
'e_journal': 'Админ ЭЖ'
};
const targetGroup = groupNames[taskType];
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 = users.filter(user =>
user.name.toLowerCase().includes(search) ||
user.login.toLowerCase().includes(search) ||
user.email.toLowerCase().includes(search)
);
}
} else {
// Обычная фильтрация для других типов задач
filteredUsers = users.filter(user =>
user.name.toLowerCase().includes(search) ||
user.login.toLowerCase().includes(search) ||
user.email.toLowerCase().includes(search)
);
} catch (error) {
console.error('Ошибка фильтрации пользователей:', error);
filteredUsers = [];
} finally {
isUsersLoading = false;
renderUsersChecklist();
}
renderUsersChecklist();
}
async function filterEditUsers() {
@@ -266,22 +292,45 @@ async function filterCopyUsers() {
function renderUsersChecklist() {
const container = document.getElementById('users-checklist');
if (isUsersLoading) {
container.innerHTML = '<div class="loading-spinner">⏳ Загрузка пользователей...</div>';
return;
}
if (!filteredUsers || filteredUsers.length === 0) {
container.innerHTML = '<div class="no-users">Нет доступных пользователей</div>';
return;
}
container.innerHTML = filteredUsers
.filter(user => user.id !== currentUser.id)
.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})">
onchange="toggleUserSelection(this, ${user.id})"
${selectedUsers.includes(user.id) ? 'checked' : ''}>
${user.name}
${document.getElementById('task-type').value === 'document' ? '(Директор)' : ''}
${document.getElementById('task-type').value === 'it' ? '(Админ)' : ''}
${document.getElementById('task-type').value === 'certificate' ? '(Администрация)' : ''}
${getUserTypeLabel(user, document.getElementById('task-type')?.value)}
</label>
</div>
`).join('');
}
// Вспомогательная функция для отображения типа пользователя
function getUserTypeLabel(user, taskType) {
const labels = {
'document': '(Секретарь)',
'it': '(ИТ специалист)',
'ahch': '(АХЧ)',
'psychologist': '(Психолог)',
'speech_therapist': '(Логопед)',
'hr': '(Диспетчер)',
'certificate': '(Администрация)',
'e_journal': '(Админ ЭЖ)'
};
return labels[taskType] || '';
}
function renderEditUsersChecklist(filtered = users) {
const container = document.getElementById('edit-users-checklist');
container.innerHTML = filtered