This commit is contained in:
2026-02-13 14:02:21 +05:00
parent 0bf6dd74ca
commit 59f5ac5741
9 changed files with 303 additions and 53 deletions

View File

@@ -28,7 +28,8 @@ module.exports = function(app, db) {
} }
const isAdmin = user.role === 'admin'; const isAdmin = user.role === 'admin';
callback(null, isAdmin); const isTasks = user.role === 'tasks'; // Пользователи с ролью tasks тоже имеют доступ
callback(null, isAdmin || isTasks);
}); });
}); });
} }

View File

@@ -83,6 +83,12 @@ function showMainInterface() {
document.getElementById('current-user').textContent = userInfo; document.getElementById('current-user').textContent = userInfo;
// 👇 СОЗДАЕМ НАВИГАЦИЮ ПОСЛЕ УСТАНОВКИ ПОЛЬЗОВАТЕЛЯ 👇
if (typeof createNavigation === 'function') {
console.log('🔄 Создание навигации для пользователя:', currentUser.role);
createNavigation();
}
document.getElementById('tasks-controls').style.display = 'block'; document.getElementById('tasks-controls').style.display = 'block';
const showDeletedLabel = document.querySelector('.show-deleted-label'); const showDeletedLabel = document.querySelector('.show-deleted-label');

View File

@@ -203,7 +203,7 @@
<section id="profile-section" class="section"> <section id="profile-section" class="section">
<h2><i class="fas fa-user-circle"></i> Личный кабинет</h2> <h2><i class="fas fa-user-circle"></i> Личный кабинет</h2>
<div id="user-profile-info"></div>
<div class="notification-settings"> <div class="notification-settings">
<h3><i class="fas fa-bell"></i> Настройки уведомлений</h3> <h3><i class="fas fa-bell"></i> Настройки уведомлений</h3>
<form id="notification-settings-form"> <form id="notification-settings-form">
@@ -373,7 +373,6 @@
</div> </div>
</div> </div>
<script src="navbar.js"></script>
<script src="auth.js"></script> <script src="auth.js"></script>
<script src="users.js"></script> <script src="users.js"></script>
<script src="tasks.js"></script> <script src="tasks.js"></script>
@@ -385,6 +384,7 @@
<script src="openTaskChat.js"></script> <script src="openTaskChat.js"></script>
<script src="main.js"></script> <script src="main.js"></script>
<script src="tasks_files.js"></script> <script src="tasks_files.js"></script>
<script src="navbar.js"></script>
</body> </body>
</html> </html>

View File

@@ -2,7 +2,15 @@
function createNavigation() { function createNavigation() {
const navbar = document.getElementById('navbar-container'); const navbar = document.getElementById('navbar-container');
if (!navbar) return; if (!navbar) return;
// 👇 ДОБАВЛЯЕМ ПОДРОБНЫЕ ЛОГИ 👇
if (currentUser) {
console.log('ID:', currentUser.id);
console.log('ФИО:', currentUser.name);
console.log('Логин:', currentUser.login);
console.log('Роль:', currentUser.role);
} else {
console.log('currentUser отсутствует (не авторизован)');
}
// Базовые кнопки для всех авторизованных пользователей // Базовые кнопки для всех авторизованных пользователей
const navButtons = [ const navButtons = [
{ {
@@ -42,8 +50,8 @@ function createNavigation() {
} }
]; ];
// Кнопка админ-панели только для admin // 👇 Кнопка админ-панели ТОЛЬКО для admin 👇
//if (currentUser.role === 'admin') { if (currentUser && currentUser.role === 'admin') {
navButtons.push({ navButtons.push({
onclick: "window.location.href = '/admin'", onclick: "window.location.href = '/admin'",
className: "nav-btn admin", className: "nav-btn admin",
@@ -51,7 +59,7 @@ function createNavigation() {
text: "Админ-панель", text: "Админ-панель",
id: "admin-btn" id: "admin-btn"
}); });
//} }
// Кнопка выхода // Кнопка выхода
navButtons.push({ navButtons.push({
@@ -76,7 +84,7 @@ function createNavigation() {
// Инициализация при загрузке страницы // Инициализация при загрузке страницы
document.addEventListener('DOMContentLoaded', function() { document.addEventListener('DOMContentLoaded', function() {
createNavigation(); // createNavigation();
// Если нужно обновлять навигацию при изменениях // Если нужно обновлять навигацию при изменениях
// window.addEventListener('userRoleChanged', createNavigation); // window.addEventListener('userRoleChanged', createNavigation);

View File

@@ -15,56 +15,77 @@ async function loadUserProfile() {
if (data.user) { if (data.user) {
const userInfo = document.getElementById('user-profile-info'); const userInfo = document.getElementById('user-profile-info');
userInfo.innerHTML = ` userInfo.innerHTML = `
<div class="profile-card"> <div class="profile-modern">
<div class="profile-field"> <!-- Шапка профиля с аватаром -->
<i class="fas fa-user"></i> <div class="profile-header">
<div> <div class="profile-avatar">
<strong>Имя:</strong> <i class="fas fa-user-circle"></i>
<p>${data.user.name}</p> </div>
<div class="profile-title">
<h3>${data.user.name}</h3>
<span class="profile-badge ${data.user.role}">
${data.user.role === 'admin' ? 'Администратор' : 'Сотрудник'}
</span>
</div> </div>
</div> </div>
<div class="profile-field"> <!-- Сетка с информацией -->
<i class="fas fa-at"></i> <div class="profile-grid">
<div> <div class="profile-info-card">
<strong>Логин:</strong> <div class="info-icon">
<p>${data.user.login}</p> <i class="fas fa-at"></i>
</div> </div>
</div> <div class="info-content">
<div class="profile-field"> <span class="info-label">Логин</span>
<i class="fas fa-envelope"></i> <span class="info-value">${data.user.login}</span>
<div>
<strong>Email:</strong>
<p>${data.user.email || 'Не указан'}</p>
</div>
</div>
<div class="profile-field">
<i class="fas fa-user-tag"></i>
<div>
<strong>Роль:</strong>
<p>${data.user.role === 'admin' ? 'Администратор' : 'Учитель'}</p>
</div>
</div>
<div class="profile-field">
<i class="fas fa-key"></i>
<div>
<strong>Тип авторизации:</strong>
<p>${data.user.auth_type === 'ldap' ? 'LDAP' : 'Локальная'}</p>
</div>
</div>
${data.user.groups && data.user.groups.length > 0 ? `
<div class="profile-field">
<i class="fas fa-users"></i>
<div>
<strong>Группы:</strong>
<p>${Array.isArray(data.user.groups) ? data.user.groups.join(', ') : data.user.groups}</p>
</div> </div>
</div> </div>
` : ''} <div class="profile-info-card">
<div class="info-icon">
<i class="fas fa-shield-alt"></i>
</div>
<div class="info-content">
<span class="info-label">Тип авторизации</span>
<span class="info-value">
${data.user.auth_type === 'ldap' ?
'<span class="badge-ldap"><i class="fas fa-building"></i> LDAP</span>' :
'<span class="badge-local"><i class="fas fa-database"></i> Локальная</span>'}
</span>
</div>
</div>
${data.user.groups && data.user.groups.length > 0 ? `
<div class="profile-info-card groups-card">
<div class="info-icon">
<i class="fas fa-users"></i>
</div>
<div class="info-content">
<span class="info-label">Группы доступа</span>
<div class="groups-list">
${Array.isArray(data.user.groups) ?
data.user.groups.map(group =>
`<span class="group-tag">${group}</span>`
).join('') :
`<span class="group-tag">${data.user.groups}</span>`
}
</div>
</div>
</div>
` : ''}
</div>
</div> </div>
`; `;
} }
} catch (error) { } catch (error) {
console.error('Ошибка загрузки профиля:', error); console.error('Ошибка загрузки профиля:', error);
const userInfo = document.getElementById('user-profile-info');
if (userInfo) {
userInfo.innerHTML = `
<div class="profile-error">
<i class="fas fa-exclamation-circle"></i>
<p>Ошибка загрузки профиля</p>
</div>
`;
}
} }
} }

View File

@@ -4175,4 +4175,207 @@ button.btn-primary {
background-color: #f1c40f; background-color: #f1c40f;
cursor: not-allowed; cursor: not-allowed;
opacity: 0.7; opacity: 0.7;
}
/* Современный профиль */
.profile-modern {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border-radius: 20px;
padding: 30px;
color: white;
margin-bottom: 30px;
box-shadow: 0 10px 40px rgba(0,0,0,0.1);
}
.profile-header {
display: flex;
align-items: center;
gap: 20px;
margin-bottom: 30px;
}
.profile-avatar {
width: 80px;
height: 80px;
background: rgba(255,255,255,0.2);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
border: 3px solid rgba(255,255,255,0.5);
}
.profile-avatar i {
font-size: 50px;
color: white;
}
.profile-title {
flex: 1;
}
.profile-title h3 {
margin: 0 0 5px 0;
font-size: 24px;
font-weight: 600;
}
.profile-badge {
display: inline-block;
padding: 5px 15px;
background: rgba(255,255,255,0.2);
border-radius: 20px;
font-size: 14px;
font-weight: 500;
backdrop-filter: blur(5px);
}
.profile-badge.admin {
background: linear-gradient(45deg, #f093fb 0%, #f5576c 100%);
}
.profile-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 15px;
margin-bottom: 30px;
}
.profile-info-card {
background: rgba(255,255,255,0.1);
border-radius: 15px;
padding: 15px;
display: flex;
align-items: center;
gap: 15px;
backdrop-filter: blur(10px);
transition: transform 0.3s, background 0.3s;
}
.profile-info-card:hover {
background: rgba(255,255,255,0.2);
transform: translateY(-2px);
}
.info-icon {
width: 45px;
height: 45px;
background: rgba(255,255,255,0.2);
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
}
.info-icon i {
font-size: 20px;
color: white;
}
.info-content {
flex: 1;
display: flex;
flex-direction: column;
}
.info-label {
font-size: 12px;
opacity: 0.8;
margin-bottom: 2px;
}
.info-value {
font-size: 16px;
font-weight: 600;
}
.groups-card {
grid-column: 1 / -1;
}
.groups-list {
display: flex;
flex-wrap: wrap;
gap: 8px;
margin-top: 5px;
}
.group-tag {
background: rgba(255,255,255,0.2);
padding: 4px 12px;
border-radius: 15px;
font-size: 13px;
font-weight: 500;
backdrop-filter: blur(5px);
}
.badge-ldap, .badge-local {
display: inline-flex;
align-items: center;
gap: 5px;
padding: 4px 12px;
border-radius: 15px;
font-size: 13px;
background: rgba(255,255,255,0.2);
}
.profile-stats {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 15px;
margin-top: 20px;
padding-top: 20px;
border-top: 1px solid rgba(255,255,255,0.2);
}
.stat-item {
text-align: center;
}
.stat-value {
font-size: 28px;
font-weight: 700;
margin-bottom: 5px;
}
.stat-label {
font-size: 13px;
opacity: 0.8;
}
.profile-error {
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
border-radius: 20px;
padding: 30px;
color: white;
text-align: center;
}
.profile-error i {
font-size: 40px;
margin-bottom: 10px;
}
.profile-error p {
margin: 0;
font-size: 16px;
}
/* Адаптивность */
@media (max-width: 768px) {
.profile-modern {
padding: 20px;
}
.profile-header {
flex-direction: column;
text-align: center;
}
.profile-grid {
grid-template-columns: 1fr;
}
.profile-stats {
grid-template-columns: 1fr;
}
} }

View File

@@ -467,6 +467,8 @@ function canUserEditTask(task) {
// Администратор может всё // Администратор может всё
if (currentUser.role === 'admin') return true; if (currentUser.role === 'admin') return true;
// Пользователи с ролью 'tasks' могут редактировать любые задачи
if (currentUser.role === 'tasks') return true;
// Создатель может редактировать свою задачу // Создатель может редактировать свою задачу
if (parseInt(task.created_by) === currentUser.id) { if (parseInt(task.created_by) === currentUser.id) {

View File

@@ -87,8 +87,8 @@ function renderTasks() {
<button class="add-file-btn" onclick="openAddFileModal(${task.id})" title="Добавить файл">📎</button> <button class="add-file-btn" onclick="openAddFileModal(${task.id})" title="Добавить файл">📎</button>
${currentUser && currentUser.login === 'minicrm' ? `<button class="edit-btn" onclick="openEditModal(${task.id})" title="Редактировать">✏️</button>` : ''} ${currentUser && currentUser.login === 'minicrm' ? `<button class="edit-btn" onclick="openEditModal(${task.id})" title="Редактировать">✏️</button>` : ''}
${currentUser && currentUser.login === 'kalugin.o' ? `<button class="manage-assignees-btn" onclick="openManageAssigneesModal(${task.id})" title="Управление исполнителями">👥</button>` : ''} ${currentUser && currentUser.login === 'kalugin.o' ? `<button class="manage-assignees-btn" onclick="openManageAssigneesModal(${task.id})" title="Управление исполнителями">👥</button>` : ''}
${currentUser && currentUser.login === 'kalugin.o' ? `<button class="manage-assignees-btn" onclick="assignAdd_openModal(${task.id})" title="Управление исполнителями">🧑‍💼➕Добавить</button>` : ''} ${currentUser && currentUser.role === 'tasks' && canEdit || currentUser.role === 'admin' ? `<button class="manage-assignees-btn" onclick="assignAdd_openModal(${task.id})" title="Управление исполнителями">🧑‍💼➕Добавить</button>` : ''}
${currentUser && currentUser.login === 'kalugin.o' ? `<button class="manage-assignees-btn" onclick="assignRemove_openModal(${task.id})" title="Управление исполнителями">🧑‍💼❌Удалить</button>` : ''} ${currentUser && currentUser.role === 'tasks' && canEdit || currentUser.role === 'admin' ? `<button class="manage-assignees-btn" onclick="assignRemove_openModal(${task.id})" title="Управление исполнителями">🧑‍💼❌Удалить</button>` : ''}
<button class="copy-btn" onclick="openCopyModal(${task.id})" title="Создать копию">📋</button> <button class="copy-btn" onclick="openCopyModal(${task.id})" title="Создать копию">📋</button>
${currentUser && currentUser.login === 'minicrm' ? `<button class="rework-btn" onclick="openReworkModal(${task.id})" title="Вернуть на доработку">🔄</button>` : ''} ${currentUser && currentUser.login === 'minicrm' ? `<button class="rework-btn" onclick="openReworkModal(${task.id})" title="Вернуть на доработку">🔄</button>` : ''}
${currentUser && currentUser.login === 'minicrm' ? `<button class="close-btn" onclick="closeTask(${task.id})" title="Закрыть задачу">🔒</button>` : ''} ${currentUser && currentUser.login === 'minicrm' ? `<button class="close-btn" onclick="closeTask(${task.id})" title="Закрыть задачу">🔒</button>` : ''}
@@ -1320,8 +1320,15 @@ async function assignAdd_openModal(taskId) {
// Получаем доступных для добавления исполнителей // Получаем доступных для добавления исполнителей
try { try {
const response = await fetch(`/api/tasks/${taskId}/available-assignees`); const response = await fetch(`/api/tasks/${taskId}/available-assignees`);
const availableUsers = await response.json(); //const availableUsers = await response.json();
let availableUsers = await response.json();
// 👇 ИСКЛЮЧАЕМ АВТОРА ЗАДАЧИ ИЗ СПИСКА 👇
const taskCreatorId = task.created_by;
availableUsers = availableUsers.filter(user =>
parseInt(user.id) !== parseInt(taskCreatorId)
);
// Создаем модальное окно // Создаем модальное окно
const modalHtml = ` const modalHtml = `
<div class="modal" id="manage-assignees-modal"> <div class="modal" id="manage-assignees-modal">

View File

@@ -32,7 +32,9 @@ function setupTaskEndpoints(app, db, upload) {
// Администратор может всё // Администратор может всё
if (user.role === 'admin') return true; if (user.role === 'admin') return true;
// Пользователи с ролью 'tasks' могут редактировать любые задачи
if (user.role === 'tasks') return true;
// Создатель может редактировать свою задачу только если она не назначена другим // Создатель может редактировать свою задачу только если она не назначена другим
if (parseInt(task.created_by) === user.id) { if (parseInt(task.created_by) === user.id) {
if (task.assignments && task.assignments.length > 0) { if (task.assignments && task.assignments.length > 0) {