индикатор загрузки в чеклист пользователей
This commit is contained in:
@@ -4379,3 +4379,45 @@ button.btn-primary {
|
|||||||
grid-template-columns: 1fr;
|
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; }
|
||||||
|
}
|
||||||
56
public/ui.js
56
public/ui.js
@@ -856,7 +856,7 @@ async function loadTaskFiles(taskId) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Функция для выбора типа задачи
|
// Функция для выбора типа задачи
|
||||||
async function selectTaskType(type) {
|
async function selectTaskType_old(type) {
|
||||||
// Убираем активный класс со всех кнопок
|
// Убираем активный класс со всех кнопок
|
||||||
document.querySelectorAll('.task-type-btn').forEach(btn => {
|
document.querySelectorAll('.task-type-btn').forEach(btn => {
|
||||||
btn.classList.remove('active');
|
btn.classList.remove('active');
|
||||||
@@ -895,6 +895,60 @@ async function selectTaskType(type) {
|
|||||||
await loadUsers();
|
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"
|
// функция для перезагрузки пользователей для типа "document"
|
||||||
async function reloadUsersForDocumentType() {
|
async function reloadUsersForDocumentType() {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ let filteredUsers = [];
|
|||||||
let selectedUsers = [];
|
let selectedUsers = [];
|
||||||
let editSelectedUsers = [];
|
let editSelectedUsers = [];
|
||||||
let copySelectedUsers = [];
|
let copySelectedUsers = [];
|
||||||
|
// добавить переменную для отслеживания загрузки
|
||||||
|
let isUsersLoading = false;
|
||||||
|
|
||||||
// Добавьте переменную для хранения групп пользователей
|
// Добавьте переменную для хранения групп пользователей
|
||||||
let userGroupsCache = {};
|
let userGroupsCache = {};
|
||||||
@@ -150,42 +152,66 @@ function populateFilterDropdowns() {
|
|||||||
|
|
||||||
// Обновите функцию filterUsers с учетом типа задачи
|
// Обновите функцию filterUsers с учетом типа задачи
|
||||||
async function filterUsers() {
|
async function filterUsers() {
|
||||||
const search = document.getElementById('user-search').value.toLowerCase();
|
const search = document.getElementById('user-search')?.value.toLowerCase() || '';
|
||||||
const taskType = document.getElementById('task-type').value || 'regular';
|
const taskType = document.getElementById('task-type')?.value || 'regular';
|
||||||
|
|
||||||
// Для задач типа "document" нужна особая фильтрация
|
isUsersLoading = true;
|
||||||
if (taskType === 'document') {
|
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 =>
|
let tempFiltered = users.filter(user =>
|
||||||
user.name.toLowerCase().includes(search) ||
|
user.name.toLowerCase().includes(search) ||
|
||||||
user.login.toLowerCase().includes(search) ||
|
user.login.toLowerCase().includes(search) ||
|
||||||
user.email.toLowerCase().includes(search)
|
user.email.toLowerCase().includes(search)
|
||||||
);
|
);
|
||||||
|
|
||||||
// Затем проверяем группы для каждого пользователя
|
// Затем проверяем группы
|
||||||
filteredUsers = [];
|
filteredUsers = [];
|
||||||
|
const groupNames = {
|
||||||
|
'document': 'Секретарь',
|
||||||
|
'it': 'ИТ специалист',
|
||||||
|
'ahch': 'АХЧ',
|
||||||
|
'psychologist': 'психолог',
|
||||||
|
'speech_therapist': 'логопед',
|
||||||
|
'hr': 'Диспетчер',
|
||||||
|
'certificate': 'Администрация',
|
||||||
|
'e_journal': 'Админ ЭЖ'
|
||||||
|
};
|
||||||
|
|
||||||
|
const targetGroup = groupNames[taskType];
|
||||||
|
|
||||||
for (const user of tempFiltered) {
|
for (const user of tempFiltered) {
|
||||||
const groups = await getUserGroups(user.id);
|
const groups = await getUserGroups(user.id);
|
||||||
const hasSecretaryGroup = groups.some(group =>
|
const hasTargetGroup = groups.some(group =>
|
||||||
group.name === 'Секретарь' ||
|
group.name === targetGroup ||
|
||||||
(typeof group === 'string' && group.includes('Секретарь'))
|
(typeof group === 'string' && group.includes(targetGroup))
|
||||||
);
|
);
|
||||||
|
|
||||||
if (hasSecretaryGroup) {
|
if (hasTargetGroup) {
|
||||||
filteredUsers.push(user);
|
filteredUsers.push(user);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Обычная фильтрация для других типов задач
|
// Обычная фильтрация
|
||||||
filteredUsers = users.filter(user =>
|
filteredUsers = users.filter(user =>
|
||||||
user.name.toLowerCase().includes(search) ||
|
user.name.toLowerCase().includes(search) ||
|
||||||
user.login.toLowerCase().includes(search) ||
|
user.login.toLowerCase().includes(search) ||
|
||||||
user.email.toLowerCase().includes(search)
|
user.email.toLowerCase().includes(search)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Ошибка фильтрации пользователей:', error);
|
||||||
|
filteredUsers = [];
|
||||||
|
} finally {
|
||||||
|
isUsersLoading = false;
|
||||||
renderUsersChecklist();
|
renderUsersChecklist();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function filterEditUsers() {
|
async function filterEditUsers() {
|
||||||
const search = document.getElementById('edit-user-search').value.toLowerCase();
|
const search = document.getElementById('edit-user-search').value.toLowerCase();
|
||||||
@@ -266,22 +292,45 @@ async function filterCopyUsers() {
|
|||||||
|
|
||||||
function renderUsersChecklist() {
|
function renderUsersChecklist() {
|
||||||
const container = document.getElementById('users-checklist');
|
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
|
container.innerHTML = filteredUsers
|
||||||
.filter(user => user.id !== currentUser.id)
|
.filter(user => user.id !== currentUser?.id)
|
||||||
.map(user => `
|
.map(user => `
|
||||||
<div class="checkbox-item">
|
<div class="checkbox-item">
|
||||||
<label>
|
<label>
|
||||||
<input type="checkbox" name="assignedUsers" value="${user.id}"
|
<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}
|
${user.name}
|
||||||
${document.getElementById('task-type').value === 'document' ? '(Директор)' : ''}
|
${getUserTypeLabel(user, document.getElementById('task-type')?.value)}
|
||||||
${document.getElementById('task-type').value === 'it' ? '(Админ)' : ''}
|
|
||||||
${document.getElementById('task-type').value === 'certificate' ? '(Администрация)' : ''}
|
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
`).join('');
|
`).join('');
|
||||||
}
|
}
|
||||||
|
// Вспомогательная функция для отображения типа пользователя
|
||||||
|
function getUserTypeLabel(user, taskType) {
|
||||||
|
const labels = {
|
||||||
|
'document': '(Секретарь)',
|
||||||
|
'it': '(ИТ специалист)',
|
||||||
|
'ahch': '(АХЧ)',
|
||||||
|
'psychologist': '(Психолог)',
|
||||||
|
'speech_therapist': '(Логопед)',
|
||||||
|
'hr': '(Диспетчер)',
|
||||||
|
'certificate': '(Администрация)',
|
||||||
|
'e_journal': '(Админ ЭЖ)'
|
||||||
|
};
|
||||||
|
return labels[taskType] || '';
|
||||||
|
}
|
||||||
function renderEditUsersChecklist(filtered = users) {
|
function renderEditUsersChecklist(filtered = users) {
|
||||||
const container = document.getElementById('edit-users-checklist');
|
const container = document.getElementById('edit-users-checklist');
|
||||||
container.innerHTML = filtered
|
container.innerHTML = filtered
|
||||||
|
|||||||
Reference in New Issue
Block a user