diff --git a/database.js b/database.js index c778680..9de8bdc 100644 --- a/database.js +++ b/database.js @@ -71,6 +71,9 @@ async function initializeDatabase() { await initializeSQLite(); } + // Синхронизируем группы пользователей + await syncUserGroups(); + return db; } @@ -92,6 +95,77 @@ function initializeSQLite() { }); } +// Функция для синхронизации групп пользователей из старой структуры в новую +async function syncUserGroups() { + console.log('🔄 Синхронизация групп пользователей...'); + + try { + // Получаем всех пользователей + const users = await new Promise((resolve, reject) => { + db.all("SELECT id, groups FROM users WHERE groups IS NOT NULL AND groups != ''", [], (err, rows) => { + if (err) reject(err); + else resolve(rows || []); + }); + }); + + let syncedCount = 0; + + for (const user of users) { + try { + let groups = []; + try { + groups = JSON.parse(user.groups); + } catch (e) { + console.log(`⚠️ Не удалось распарсить группы для пользователя ${user.id}: ${user.groups}`); + continue; + } + + // Для каждой группы + for (const groupName of groups) { + if (!groupName || typeof groupName !== 'string') continue; + + // Находим ID группы + const group = await new Promise((resolve, reject) => { + db.get("SELECT id FROM user_groups WHERE name = ?", [groupName.trim()], (err, row) => { + if (err) reject(err); + else resolve(row); + }); + }); + + if (group) { + // Добавляем пользователя в группу + await new Promise((resolve, reject) => { + db.run( + `INSERT INTO user_group_memberships (user_id, group_id) + VALUES (?, ?) + ON CONFLICT (user_id, group_id) DO NOTHING`, + [user.id, group.id], + function(err) { + if (err) reject(err); + else resolve(); + } + ); + }); + console.log(`✅ Пользователь ${user.id} добавлен в группу "${groupName}"`); + } else { + console.log(`⚠️ Группа "${groupName}" не найдена для пользователя ${user.id}`); + } + } + + syncedCount++; + + } catch (error) { + console.error(`❌ Ошибка синхронизации пользователя ${user.id}:`, error.message); + } + } + + console.log(`✅ Синхронизировано ${syncedCount} пользователей`); + + } catch (error) { + console.error('❌ Ошибка синхронизации групп:', error); + } +} + function createSQLiteTables() { // Таблица для истории уведомлений db.run(`CREATE TABLE IF NOT EXISTS notification_history ( @@ -395,6 +469,71 @@ function createSQLiteTables() { setTimeout(() => { checkAndUpdateTableStructure(); }, 2000); + + // Добавляем группы по умолчанию + setTimeout(() => { + addDefaultGroups(); + }, 1000); +} + +// Добавляем группы по умолчанию +function addDefaultGroups() { + const defaultGroups = [ + { + name: 'Администрация', + description: 'Пользователи с правами администратора системы', + color: '#e74c3c', + can_approve_documents: true + }, + { + name: 'Секретарь', + description: 'Группа для согласования документов', + color: '#3498db', + can_approve_documents: true + }, + { + name: 'help', + description: 'Группа для получения заявок поддержки', + color: '#27ae60', + can_approve_documents: false + }, + { + name: 'doc', + description: 'Группа для работы с документами', + color: '#9b59b6', + can_approve_documents: false + }, + { + name: 'ahch', + description: 'Группа для AHCH задач', + color: '#e67e22', + can_approve_documents: false + } + ]; + + defaultGroups.forEach(group => { + db.get("SELECT id FROM user_groups WHERE name = ?", [group.name], (err, existing) => { + if (err) { + console.error(`❌ Ошибка проверки группы ${group.name}:`, err.message); + return; + } + + if (!existing) { + db.run( + `INSERT INTO user_groups (name, description, color, can_approve_documents) + VALUES (?, ?, ?, ?)`, + [group.name, group.description, group.color, group.can_approve_documents ? 1 : 0], + (insertErr) => { + if (insertErr) { + console.error(`❌ Ошибка создания группы ${group.name}:`, insertErr.message); + } else { + console.log(`✅ Группа "${group.name}" создана по умолчанию`); + } + } + ); + } + }); + }); } function createSQLiteIndexes() { @@ -1096,31 +1235,76 @@ async function createPostgresTables() { // Создаем индексы await createPostgresIndexes(client); + // Добавляем группы по умолчанию для PostgreSQL + await addDefaultGroupsPostgreSQL(client); + client.release(); console.log('✅ Таблицы PostgreSQL проверены/созданы'); // Проверяем структуру PostgreSQL таблиц await checkPostgresTableStructure(); - // Создаем группу "Секретарь" по умолчанию - try { - const checkResult = await client.query("SELECT id FROM user_groups WHERE name = 'Секретарь'"); - if (checkResult.rows.length === 0) { - await client.query(` - INSERT INTO user_groups (name, description, color, can_approve_documents) - VALUES ('Секретарь', 'Группа для согласования документов', '#e74c3c', true) - `); - console.log('✅ Группа "Секретарь" создана по умолчанию'); - } - } catch (error) { - console.warn('⚠️ Не удалось создать группу "Секретарь":', error.message); - } - } catch (error) { console.error('❌ Ошибка создания таблиц PostgreSQL:', error.message); } } +// Добавляем группы по умолчанию для PostgreSQL +async function addDefaultGroupsPostgreSQL(client) { + const defaultGroups = [ + { + name: 'Администрация', + description: 'Пользователи с правами администратора системы', + color: '#e74c3c', + can_approve_documents: true + }, + { + name: 'Секретарь', + description: 'Группа для согласования документов', + color: '#3498db', + can_approve_documents: true + }, + { + name: 'help', + description: 'Группа для получения заявок поддержки', + color: '#27ae60', + can_approve_documents: false + }, + { + name: 'doc', + description: 'Группа для работы с документами', + color: '#9b59b6', + can_approve_documents: false + }, + { + name: 'ahch', + description: 'Группа для AHCH задач', + color: '#e67e22', + can_approve_documents: false + } + ]; + + for (const group of defaultGroups) { + try { + const checkResult = await client.query( + "SELECT id FROM user_groups WHERE name = $1", + [group.name] + ); + + if (checkResult.rows.length === 0) { + await client.query( + `INSERT INTO user_groups (name, description, color, can_approve_documents) + VALUES ($1, $2, $3, $4)`, + [group.name, group.description, group.color, group.can_approve_documents] + ); + console.log(`✅ Группа "${group.name}" создана по умолчанию в PostgreSQL`); + } + } catch (error) { + console.error(`❌ Ошибка создания группы ${group.name}:`, error.message); + } + } +} + async function createPostgresIndexes(client) { console.log('🔧 Создаем индексы для PostgreSQL...'); @@ -1620,6 +1804,7 @@ module.exports = { USE_POSTGRES, getDatabaseType: () => USE_POSTGRES ? 'PostgreSQL' : 'SQLite', checkAndUpdateTableStructure, // Экспортируем для ручного запуска + syncUserGroups, // Экспортируем функцию синхронизации // Функции для работы с группами getUserGroups, getGroupMembers, diff --git a/public/admin-doc.html b/public/admin-doc.html index ad9c369..d19cada 100644 --- a/public/admin-doc.html +++ b/public/admin-doc.html @@ -11,10 +11,13 @@ :root { --admin-color: #e74c3c; --secretary-color: #3498db; + --help-color: #27ae60; + --doc-color: #9b59b6; + --ahch-color: #e67e22; } .container { - max-width: 1200px; + max-width: 1400px; margin: 0 auto; padding: 20px; } @@ -43,6 +46,7 @@ .tabs { display: flex; + flex-wrap: wrap; gap: 5px; margin-bottom: 20px; background: white; @@ -52,16 +56,17 @@ } .tab { - padding: 12px 24px; + padding: 12px 20px; border: none; background: none; cursor: pointer; - font-size: 16px; + font-size: 14px; border-radius: 6px; transition: all 0.3s; display: flex; align-items: center; gap: 8px; + white-space: nowrap; } .tab:hover { @@ -69,10 +74,16 @@ } .tab.active { - background: #3498db; color: white; } + .tab.secretary.active { background: var(--secretary-color); } + .tab.administration.active { background: var(--admin-color); } + .tab.help.active { background: var(--help-color); } + .tab.doc.active { background: var(--doc-color); } + .tab.ahch.active { background: var(--ahch-color); } + .tab.all-users.active { background: #2c3e50; } + .content-section { display: none; background: white; @@ -101,13 +112,11 @@ border-radius: 50%; } - .group-color.admin { - background: var(--admin-color); - } - - .group-color.secretary { - background: var(--secretary-color); - } + .group-color.admin { background: var(--admin-color); } + .group-color.secretary { background: var(--secretary-color); } + .group-color.help { background: var(--help-color); } + .group-color.doc { background: var(--doc-color); } + .group-color.ahch { background: var(--ahch-color); } .users-container { display: grid; @@ -122,6 +131,7 @@ border-radius: 8px; padding: 15px; transition: all 0.3s; + position: relative; } .user-card:hover { @@ -175,33 +185,32 @@ .group-badge { padding: 4px 8px; border-radius: 4px; - font-size: 12px; + font-size: 11px; color: white; display: flex; align-items: center; gap: 4px; } - .group-badge.admin { - background: var(--admin-color); - } - - .group-badge.secretary { - background: var(--secretary-color); - } + .group-badge.admin { background: var(--admin-color); } + .group-badge.secretary { background: var(--secretary-color); } + .group-badge.help { background: var(--help-color); } + .group-badge.doc { background: var(--doc-color); } + .group-badge.ahch { background: var(--ahch-color); } .user-actions { display: flex; - gap: 10px; + flex-wrap: wrap; + gap: 8px; margin-top: 10px; } .btn { - padding: 8px 16px; + padding: 6px 12px; border: none; border-radius: 4px; cursor: pointer; - font-size: 14px; + font-size: 12px; transition: all 0.3s; display: flex; align-items: center; @@ -235,6 +244,15 @@ background: #229954; } + .btn-help { background: var(--help-color); color: white; } + .btn-help:hover { background: #219653; } + + .btn-doc { background: var(--doc-color); color: white; } + .btn-doc:hover { background: #8e44ad; } + + .btn-ahch { background: var(--ahch-color); color: white; } + .btn-ahch:hover { background: #d35400; } + .loading { text-align: center; padding: 40px; @@ -260,13 +278,13 @@ } .stats { - display: flex; - gap: 20px; + display: grid; + grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); + gap: 15px; margin-bottom: 20px; } .stat-card { - flex: 1; background: white; padding: 15px; border-radius: 8px; @@ -277,7 +295,6 @@ .stat-number { font-size: 24px; font-weight: bold; - color: #3498db; margin: 10px 0; } @@ -292,17 +309,40 @@ color: #666; } + .all-groups-badges { + display: flex; + flex-wrap: wrap; + gap: 5px; + margin-top: 5px; + } + @media (max-width: 768px) { .users-container { grid-template-columns: 1fr; } - .stats { + .user-actions { flex-direction: column; } - .user-actions { - flex-direction: column; + .tab { + padding: 10px 15px; + font-size: 13px; + } + + .stats { + grid-template-columns: repeat(2, 1fr); + } + } + + @media (max-width: 480px) { + .stats { + grid-template-columns: 1fr; + } + + .tab { + padding: 8px 12px; + font-size: 12px; } } @@ -322,13 +362,22 @@
- - - + + +
@@ -401,6 +450,108 @@ + +
+
+
+
+

Группа "Help"

+

Пользователи этой группы получают заявки поддержки

+
+
+ + + +
+
+
0
+
Всего пользователей
+
+
+
0
+
В группе "Help"
+
+
+ +
+
+ +

Загрузка пользователей...

+
+
+
+ + +
+
+
+
+

Группа "Doc"

+

Пользователи этой группы работают с документами

+
+
+ + + +
+
+
0
+
Всего пользователей
+
+
+
0
+
В группе "Doc"
+
+
+ +
+
+ +

Загрузка пользователей...

+
+
+
+ + +
+
+
+
+

Группа "AHCH"

+

Пользователи этой группы работают с AHCH задачами

+
+
+ + + +
+
+
0
+
Всего пользователей
+
+
+
0
+
В группе "AHCH"
+
+
+ +
+
+ +

Загрузка пользователей...

+
+
+
+
0
-
Администраторы
+
Администрация
0
Секретари
+
+
0
+
Help
+
+
+
0
+
Doc
+
+
+
0
+
AHCH
+
@@ -435,8 +598,13 @@ - - + + diff --git a/public/tasks-doc.js b/public/help-tasks.js similarity index 70% rename from public/tasks-doc.js rename to public/help-tasks.js index 7425b3a..7c19c2a 100644 --- a/public/tasks-doc.js +++ b/public/help-tasks.js @@ -1,4 +1,4 @@ -// tasks.js - Основные операции с согласованиями +// help-tasks.js - Основные операции с заявками let tasks = []; let expandedTasks = new Set(); let showingTasksWithoutDate = false; @@ -27,7 +27,7 @@ async function loadTasks() { const response = await fetch(url); tasks = await response.json(); - // Загружаем файлы для всех согласований + // Загружаем файлы для всех заявок await Promise.all(tasks.map(async (task) => { try { const filesResponse = await fetch(`/api/tasks/${task.id}/files`); @@ -37,7 +37,7 @@ async function loadTasks() { task.files = []; } } catch (error) { - console.error(`Ошибка загрузки файлов для согласования ${task.id}:`, error); + console.error(`Ошибка загрузки файлов для заявки ${task.id}:`, error); task.files = []; } })); @@ -45,7 +45,7 @@ async function loadTasks() { renderTasks(); } catch (error) { - console.error('Ошибка загрузки согласований:', error); + console.error('Ошибка загрузки заявок:', error); } } @@ -59,7 +59,7 @@ function showTasksWithoutDate() { async function loadTasksWithoutDate() { try { const response = await fetch('/api/tasks'); - if (!response.ok) throw new Error('Ошибка загрузки согласований'); + if (!response.ok) throw new Error('Ошибка загрузки заявок'); const allTasks = await response.json(); tasks = allTasks.filter(task => { @@ -69,7 +69,7 @@ async function loadTasksWithoutDate() { return hasTaskDueDate && hasAssignmentDueDates; }); - // Загружаем файлы для всех согласований + // Загружаем файлы для всех заявок await Promise.all(tasks.map(async (task) => { try { const filesResponse = await fetch(`/api/tasks/${task.id}/files`); @@ -79,14 +79,27 @@ async function loadTasksWithoutDate() { task.files = []; } } catch (error) { - console.error(`Ошибка загрузки файлов для согласования ${task.id}:`, error); + console.error(`Ошибка загрузки файлов для заявки ${task.id}:`, error); task.files = []; } })); renderTasks(); } catch (error) { - console.error('Ошибка загрузки согласований без срока:', error); + console.error('Ошибка загрузки заявок без срока:', error); + } +} + +async function getHelpUsers() { + try { + const response = await fetch('/api/users/group/help'); + if (response.ok) { + return await response.json(); + } + return []; + } catch (error) { + console.error('Ошибка получения пользователей группы help:', error); + return []; } } @@ -97,7 +110,7 @@ async function createTask(event) { alert('Требуется аутентификация'); return; } - + const formData = new FormData(); formData.append('title', document.getElementById('title').value); formData.append('description', document.getElementById('description').value); @@ -109,13 +122,18 @@ async function createTask(event) { } formData.append('dueDate', dueDate); - // Используем selectedUsers вместо прямого доступа к DOM - if (selectedUsers.length === 0) { - alert('Выберите хотя бы одного секретаря в качестве исполнителя'); + // Заявка автоматически назначается всем пользователям группы "help" + // Получаем пользователей группы help + const helpUsers = await getHelpUsers(); + + if (helpUsers.length === 0) { + alert('Нет пользователей в группе "help". Заявка не может быть создана.'); return; } - selectedUsers.forEach(userId => { - formData.append('assignedUsers', userId); + + // Добавляем всех пользователей группы help как исполнителей + helpUsers.forEach(user => { + formData.append('assignedUsers', user.id); }); const files = document.getElementById('files').files; @@ -130,22 +148,19 @@ async function createTask(event) { }); if (response.ok) { - alert('Согласование успешно создано!'); + alert('Заявка успешно создана и назначена всем пользователям группы "help"!'); document.getElementById('create-task-form').reset(); document.getElementById('file-list').innerHTML = ''; - document.getElementById('user-search').value = ''; - selectedUsers = []; - renderUsersChecklist(); loadTasks(); loadActivityLogs(); showSection('tasks'); } else { const error = await response.json(); - alert(error.error || 'Ошибка создания согласования'); + alert(error.error || 'Ошибка создания заявки'); } } catch (error) { console.error('Ошибка:', error); - alert('Ошибка создания согласования'); + alert('Ошибка создания заявки'); } } @@ -154,15 +169,15 @@ async function openEditModal(taskId) { const response = await fetch(`/api/tasks/${taskId}`); if (!response.ok) { if (response.status === 404) { - alert('Согласование не найдено или у вас нет прав доступа'); + alert('Заявка не найдена или у вас нет прав доступа'); } - throw new Error('Ошибка загрузки согласования'); + throw new Error('Ошибка загрузки заявки'); } const task = await response.json(); if (!canUserEditTask(task)) { - alert('У вас нет прав для редактирования этого согласования'); + alert('У вас нет прав для редактирования этой заявки'); return; } @@ -172,9 +187,8 @@ async function openEditModal(taskId) { document.getElementById('edit-due-date').value = task.due_date ? formatDateTimeForInput(task.due_date) : ''; - // Устанавливаем выбранных пользователей (только секретарей) - editSelectedUsers = task.assignments ? task.assignments.map(a => a.user_id) : []; - renderEditUsersChecklist(users); + // Показываем пользователей группы help, назначенных на заявку + showHelpGroupUsersInEditModal(task); // Показываем существующие файлы currentEditTaskFiles = task.files || []; @@ -183,17 +197,38 @@ async function openEditModal(taskId) { document.getElementById('edit-task-modal').style.display = 'block'; } catch (error) { console.error('Ошибка:', error); - alert('Ошибка загрузки согласования'); + alert('Ошибка загрузки заявки'); } } +function showHelpGroupUsersInEditModal(task) { + const container = document.getElementById('edit-help-group-users'); + const helpUsers = users.filter(user => user.groups && user.groups.includes('help')); + + if (helpUsers.length === 0) { + container.innerHTML = '

Нет пользователей в группе "help"

'; + return; + } + + // Получаем назначенных пользователей + const assignedUserIds = task.assignments ? task.assignments.map(a => a.user_id) : []; + + container.innerHTML = helpUsers.map(user => { + const isAssigned = assignedUserIds.includes(user.id.toString()); + return ` +
+ + ${user.name} (${user.email}) + ${isAssigned ? 'назначен' : ''} +
+ `; + }).join(''); +} + function closeEditModal() { document.getElementById('edit-task-modal').style.display = 'none'; document.getElementById('edit-file-list').innerHTML = ''; - document.getElementById('edit-user-search').value = ''; - editSelectedUsers = []; currentEditTaskFiles = []; - filterEditUsers(); } async function updateTask(event) { @@ -209,11 +244,12 @@ async function updateTask(event) { return; } - // Используем editSelectedUsers (только секретари) - const assignedUserIds = editSelectedUsers; + // Заявка автоматически назначается всем пользователям группы "help" + const helpUsers = users.filter(user => user.groups && user.groups.includes('help')); + const assignedUserIds = helpUsers.map(user => user.id); if (assignedUserIds.length === 0) { - alert('Выберите хотя бы одного секретаря в качестве исполнителя'); + alert('Нет пользователей в группе "help". Заявка не может быть обновлена.'); return; } @@ -235,17 +271,17 @@ async function updateTask(event) { }); if (response.ok) { - alert('Согласование успешно обновлено!'); + alert('Заявка успешно обновлена и назначена всем пользователям группы "help"!'); closeEditModal(); loadTasks(); loadActivityLogs(); } else { const error = await response.json(); - alert(error.error || 'Ошибка обновления согласования'); + alert(error.error || 'Ошибка обновления заявки'); } } catch (error) { console.error('Ошибка:', error); - alert('Ошибка обновления согласования'); + alert('Ошибка обновления заявки'); } } @@ -257,18 +293,11 @@ function openCopyModal(taskId) { defaultDate.setDate(defaultDate.getDate() + 7); document.getElementById('copy-due-date').value = defaultDate.toISOString().substring(0, 16); - // Сбрасываем выбранных пользователей (только секретари) - copySelectedUsers = []; - renderCopyUsersChecklist(users); - document.getElementById('copy-task-modal').style.display = 'block'; } function closeCopyModal() { document.getElementById('copy-task-modal').style.display = 'none'; - document.getElementById('copy-user-search').value = ''; - copySelectedUsers = []; - filterCopyUsers(); } async function copyTask(event) { @@ -278,15 +307,16 @@ async function copyTask(event) { const dueDate = document.getElementById('copy-due-date').value; if (!dueDate) { - alert('Дата и время выполнения обязательны для копии согласования'); + alert('Дата и время выполнения обязательны для копии заявки'); return; } - // Используем copySelectedUsers (только секретари) - const assignedUserIds = copySelectedUsers; + // Копия заявки автоматически назначается всем пользователям группы "help" + const helpUsers = users.filter(user => user.groups && user.groups.includes('help')); + const assignedUserIds = helpUsers.map(user => user.id); if (assignedUserIds.length === 0) { - alert('Выберите хотя бы одного секретаря в качестве исполнителя для копии согласования'); + alert('Нет пользователей в группе "help". Копия заявки не может быть создана.'); return; } @@ -303,22 +333,22 @@ async function copyTask(event) { }); if (response.ok) { - alert('Копия согласования успешно создана!'); + alert('Копия заявки успешно создана и назначена всем пользователям группы "help"!'); closeCopyModal(); loadTasks(); loadActivityLogs(); } else { const error = await response.json(); - alert(error.error || 'Ошибка создания копии согласования'); + alert(error.error || 'Ошибка создания копии заявки'); } } catch (error) { console.error('Ошибка:', error); - alert('Ошибка создания копии согласования'); + alert('Ошибка создания копии заявки'); } } async function closeTask(taskId) { - if (!confirm('Вы уверены, что хотите закрыть это согласование? Секретари больше не будут видеть его.')) { + if (!confirm('Вы уверены, что хотите закрыть эту заявку? Исполнители больше не будут видеть ее.')) { return; } @@ -328,16 +358,16 @@ async function closeTask(taskId) { }); if (response.ok) { - alert('Согласование закрыто!'); + alert('Заявка закрыта!'); loadTasks(); loadActivityLogs(); } else { const error = await response.json(); - alert(error.error || 'Ошибка закрытия согласования'); + alert(error.error || 'Ошибка закрытия заявки'); } } catch (error) { console.error('Ошибка:', error); - alert('Ошибка закрытия согласования'); + alert('Ошибка закрытия заявки'); } } @@ -348,21 +378,21 @@ async function reopenTask(taskId) { }); if (response.ok) { - alert('Согласование открыто!'); + alert('Заявка открыта!'); loadTasks(); loadActivityLogs(); } else { const error = await response.json(); - alert(error.error || 'Ошибка открытия согласования'); + alert(error.error || 'Ошибка открытия заявки'); } } catch (error) { console.error('Ошибка:', error); - alert('Ошибка открытия согласования'); + alert('Ошибка открытия заявки'); } } async function deleteTask(taskId) { - if (!confirm('Вы уверены, что хотите удалить это согласование?')) { + if (!confirm('Вы уверены, что хотите удалить эту заявку?')) { return; } @@ -372,16 +402,16 @@ async function deleteTask(taskId) { }); if (response.ok) { - alert('Согласование удалено!'); + alert('Заявка удалена!'); loadTasks(); loadActivityLogs(); } else { const error = await response.json(); - alert(error.error || 'Ошибка удаления согласования'); + alert(error.error || 'Ошибка удаления заявки'); } } catch (error) { console.error('Ошибка:', error); - alert('Ошибка удаления согласования'); + alert('Ошибка удаления заявки'); } } @@ -392,16 +422,16 @@ async function restoreTask(taskId) { }); if (response.ok) { - alert('Согласование восстановлено!'); + alert('Заявка восстановлена!'); loadTasks(); loadActivityLogs(); } else { const error = await response.json(); - alert(error.error || 'Ошибка восстановления согласования'); + alert(error.error || 'Ошибка восстановления заявки'); } } catch (error) { console.error('Ошибка:', error); - alert('Ошибка восстановления согласования'); + alert('Ошибка восстановления заявки'); } } @@ -447,7 +477,7 @@ async function updateAssignment(event) { }); if (response.ok) { - alert('Сроки секретаря обновлены!'); + alert('Сроки исполнителя обновлены!'); closeEditAssignmentModal(); loadTasks(); loadActivityLogs(); @@ -487,17 +517,17 @@ async function sendForRework(event) { }); if (response.ok) { - alert('Согласование возвращено на доработку!'); + alert('Заявка возвращена на доработку!'); closeReworkModal(); loadTasks(); loadActivityLogs(); } else { const error = await response.json(); - alert(error.error || 'Ошибка возврата согласования на доработку'); + alert(error.error || 'Ошибка возврата заявки на доработку'); } } catch (error) { console.error('Ошибка:', error); - alert('Ошибка возврата согласования на доработку'); + alert('Ошибка возврата заявки на доработку'); } } @@ -530,34 +560,38 @@ function canUserEditTask(task) { // Администратор может всё if (currentUser.role === 'admin') return true; - // Создатель может редактировать свое согласование + // Создатель может редактировать свою заявку if (parseInt(task.created_by) === currentUser.id) { - // Но если согласование уже назначено секретарям, + // Но если заявка уже назначена группе "help", // создатель может только просматривать if (task.assignments && task.assignments.length > 0) { - // Проверяем, назначено ли согласование секретарям (не только себе) - const assignedToSecretaries = task.assignments.some(assignment => - parseInt(assignment.user_id) !== currentUser.id - ); - - if (assignedToSecretaries) { - // Создатель может только просматривать и закрывать согласование - return false; - } + return false; } return true; } - // Секретарь может менять только свой статус + // Пользователи группы "help" могут менять только свой статус if (task.assignments) { - const isSecretary = task.assignments.some(assignment => + const isHelpUser = task.assignments.some(assignment => parseInt(assignment.user_id) === currentUser.id ); - if (isSecretary) { - // Секретарь может менять только статус - return false; + if (isHelpUser) { + return false; // Могут менять только статус } } return false; +} + +// Функция для отображения пользователей группы help при создании заявки +function showHelpGroupUsers() { + const container = document.getElementById('help-group-users'); + const helpUsers = users.filter(user => user.groups && user.groups.includes('help')); + + container.innerHTML = helpUsers.map(user => ` +
+ + ${user.name} (${user.email}) +
+ `).join(''); } \ No newline at end of file diff --git a/public/help-users.js b/public/help-users.js new file mode 100644 index 0000000..673b53d --- /dev/null +++ b/public/help-users.js @@ -0,0 +1,153 @@ +// help-users.js - Управление пользователями +let users = []; +let allUsers = []; +let filteredUsers = []; +let selectedUsers = []; +let editSelectedUsers = []; +let copySelectedUsers = []; + +async function loadUsers() { + try { + const response = await fetch('/api/users'); + users = await response.json(); + allUsers = users; + + // Получаем пользователей группы "help" + const helpUsers = users.filter(user => user.groups && user.groups.includes('help')); + filteredUsers = helpUsers; + + // Показываем пользователей группы help при создании заявки + showHelpGroupUsers(); + + populateFilterDropdowns(); + } catch (error) { + console.error('Ошибка загрузки пользователей:', error); + } +} + +function populateFilterDropdowns() { + const creatorFilter = document.getElementById('creator-filter'); + const assigneeFilter = document.getElementById('assignee-filter'); + + // Проверяем существование элементов (они есть только на странице help.html) + if (!creatorFilter || !assigneeFilter) { + console.log('Фильтры не найдены (возможно, не на странице help.html)'); + return; + } + + creatorFilter.innerHTML = ''; + assigneeFilter.innerHTML = ''; + + // Для заказчиков показываем всех пользователей + users.forEach(user => { + const creatorOption = document.createElement('option'); + creatorOption.value = user.id; + creatorOption.textContent = `${user.name} (${user.login})`; + creatorFilter.appendChild(creatorOption); + }); + + // Для исполнителей показываем только пользователей группы "help" + const helpUsers = users.filter(user => user.groups && user.groups.includes('help')); + helpUsers.forEach(user => { + const assigneeOption = document.createElement('option'); + assigneeOption.value = user.id; + assigneeOption.textContent = `${user.name} (${user.login}) - группа "help"`; + assigneeFilter.appendChild(assigneeOption); + }); +} + +// Функция для отображения пользователей группы help +function showHelpGroupUsers() { + const container = document.getElementById('help-group-users'); + + // Проверяем существование элемента (он есть только на странице help.html) + if (!container) { + console.log('Контейнер help-group-users не найден (возможно, не на странице help.html)'); + return; + } + + const helpUsers = users.filter(user => user.groups && user.groups.includes('help')); + + if (helpUsers.length === 0) { + container.innerHTML = '
Нет пользователей в группе "help"
'; + return; + } + + container.innerHTML = ` +
+ Заявка будет автоматически назначена ${helpUsers.length} пользователям группы "help": +
+
+ ${helpUsers.map(user => ` +
+ + ${user.name} + (${user.email}) +
+ `).join('')} +
+ `; +} + +// Старые функции фильтрации оставляем, но они теперь не используются для выбора исполнителей +function filterUsers() { + const searchInput = document.getElementById('user-search'); + if (!searchInput) return; // Элемент есть только на странице help.html + + const search = searchInput.value.toLowerCase(); + // Фильтруем пользователей группы "help" + filteredUsers = users.filter(user => + user.groups && user.groups.includes('help') && ( + user.name.toLowerCase().includes(search) || + user.login.toLowerCase().includes(search) || + user.email.toLowerCase().includes(search) + ) + ); + // Не рендерим чеклист, так как выбираем всех пользователей группы help +} + +function filterEditUsers() { + // Не используется, так как заявка автоматически назначается всем пользователям группы help +} + +function filterCopyUsers() { + // Не используется, так как заявка автоматически назначается всем пользователям группы help +} + +// Старые функции рендеринга оставляем для совместимости, но они не будут использоваться +function renderUsersChecklist() { + // Не рендерим чеклист, так как выбираем всех пользователей группы help +} + +function renderEditUsersChecklist(filtered = users) { + // Не рендерим чеклист, так как заявка автоматически назначается всем пользователям группы help +} + +function renderCopyUsersChecklist(filtered = users) { + // Не рендерим чеклист, так как заявка автоматически назначается всем пользователям группы help +} + +// Старые функции выбора пользователей оставляем для совместимости +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); + } +} \ No newline at end of file diff --git a/public/help.html b/public/help.html index 10d2140..3f62436 100644 --- a/public/help.html +++ b/public/help.html @@ -25,7 +25,7 @@
-

Управление согласованиями

+

Группа поддержки "help"

@@ -36,7 +36,7 @@
-

School CRM - поддержка

+

School CRM - система заявок

-

Все согласования

+

Все заявки

+
@@ -85,11 +87,12 @@
-
@@ -104,22 +107,22 @@
- +