diff --git a/public/style.css b/public/style.css
index 929ca42..27533c5 100644
--- a/public/style.css
+++ b/public/style.css
@@ -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; }
}
\ No newline at end of file
diff --git a/public/ui.js b/public/ui.js
index 5bb7cb9..c1631d8 100644
--- a/public/ui.js
+++ b/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 => {
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 = '
⏳ Загрузка доступных пользователей...
';
+ }
+
+ // Загружаем пользователей для выбранного типа
+ 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 = 'Ошибка загрузки пользователей
';
+ }
+ }
+}
// функция для перезагрузки пользователей для типа "document"
async function reloadUsersForDocumentType() {
try {
diff --git a/public/users.js b/public/users.js
index c3db072..e2a0438 100644
--- a/public/users.js
+++ b/public/users.js
@@ -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 = '⏳ Загрузка пользователей...
';
+ return;
+ }
+
+ if (!filteredUsers || filteredUsers.length === 0) {
+ container.innerHTML = 'Нет доступных пользователей
';
+ return;
+ }
+
container.innerHTML = filteredUsers
- .filter(user => user.id !== currentUser.id)
+ .filter(user => user.id !== currentUser?.id)
.map(user => `
`).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