+++
This commit is contained in:
@@ -28,7 +28,8 @@ module.exports = function(app, db) {
|
||||
}
|
||||
|
||||
const isAdmin = user.role === 'admin';
|
||||
callback(null, isAdmin);
|
||||
const isTasks = user.role === 'tasks'; // Пользователи с ролью tasks тоже имеют доступ
|
||||
callback(null, isAdmin || isTasks);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -83,6 +83,12 @@ function showMainInterface() {
|
||||
|
||||
document.getElementById('current-user').textContent = userInfo;
|
||||
|
||||
// 👇 СОЗДАЕМ НАВИГАЦИЮ ПОСЛЕ УСТАНОВКИ ПОЛЬЗОВАТЕЛЯ 👇
|
||||
if (typeof createNavigation === 'function') {
|
||||
console.log('🔄 Создание навигации для пользователя:', currentUser.role);
|
||||
createNavigation();
|
||||
}
|
||||
|
||||
document.getElementById('tasks-controls').style.display = 'block';
|
||||
|
||||
const showDeletedLabel = document.querySelector('.show-deleted-label');
|
||||
|
||||
@@ -203,7 +203,7 @@
|
||||
|
||||
<section id="profile-section" class="section">
|
||||
<h2><i class="fas fa-user-circle"></i> Личный кабинет</h2>
|
||||
|
||||
<div id="user-profile-info"></div>
|
||||
<div class="notification-settings">
|
||||
<h3><i class="fas fa-bell"></i> Настройки уведомлений</h3>
|
||||
<form id="notification-settings-form">
|
||||
@@ -373,7 +373,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="navbar.js"></script>
|
||||
<script src="auth.js"></script>
|
||||
<script src="users.js"></script>
|
||||
<script src="tasks.js"></script>
|
||||
@@ -385,6 +384,7 @@
|
||||
<script src="openTaskChat.js"></script>
|
||||
<script src="main.js"></script>
|
||||
<script src="tasks_files.js"></script>
|
||||
<script src="navbar.js"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -2,7 +2,15 @@
|
||||
function createNavigation() {
|
||||
const navbar = document.getElementById('navbar-container');
|
||||
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 = [
|
||||
{
|
||||
@@ -42,8 +50,8 @@ function createNavigation() {
|
||||
}
|
||||
];
|
||||
|
||||
// Кнопка админ-панели только для admin
|
||||
//if (currentUser.role === 'admin') {
|
||||
// 👇 Кнопка админ-панели ТОЛЬКО для admin 👇
|
||||
if (currentUser && currentUser.role === 'admin') {
|
||||
navButtons.push({
|
||||
onclick: "window.location.href = '/admin'",
|
||||
className: "nav-btn admin",
|
||||
@@ -51,7 +59,7 @@ function createNavigation() {
|
||||
text: "Админ-панель",
|
||||
id: "admin-btn"
|
||||
});
|
||||
//}
|
||||
}
|
||||
|
||||
// Кнопка выхода
|
||||
navButtons.push({
|
||||
@@ -76,7 +84,7 @@ function createNavigation() {
|
||||
|
||||
// Инициализация при загрузке страницы
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
createNavigation();
|
||||
// createNavigation();
|
||||
|
||||
// Если нужно обновлять навигацию при изменениях
|
||||
// window.addEventListener('userRoleChanged', createNavigation);
|
||||
|
||||
@@ -15,56 +15,77 @@ async function loadUserProfile() {
|
||||
if (data.user) {
|
||||
const userInfo = document.getElementById('user-profile-info');
|
||||
userInfo.innerHTML = `
|
||||
<div class="profile-card">
|
||||
<div class="profile-field">
|
||||
<i class="fas fa-user"></i>
|
||||
<div>
|
||||
<strong>Имя:</strong>
|
||||
<p>${data.user.name}</p>
|
||||
<div class="profile-modern">
|
||||
<!-- Шапка профиля с аватаром -->
|
||||
<div class="profile-header">
|
||||
<div class="profile-avatar">
|
||||
<i class="fas fa-user-circle"></i>
|
||||
</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 class="profile-field">
|
||||
<i class="fas fa-at"></i>
|
||||
<div>
|
||||
<strong>Логин:</strong>
|
||||
<p>${data.user.login}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="profile-field">
|
||||
<i class="fas fa-envelope"></i>
|
||||
<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 class="profile-grid">
|
||||
<div class="profile-info-card">
|
||||
<div class="info-icon">
|
||||
<i class="fas fa-at"></i>
|
||||
</div>
|
||||
<div class="info-content">
|
||||
<span class="info-label">Логин</span>
|
||||
<span class="info-value">${data.user.login}</span>
|
||||
</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>
|
||||
`;
|
||||
}
|
||||
} catch (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>
|
||||
`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
203
public/style.css
203
public/style.css
@@ -4175,4 +4175,207 @@ button.btn-primary {
|
||||
background-color: #f1c40f;
|
||||
cursor: not-allowed;
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -467,6 +467,8 @@ function canUserEditTask(task) {
|
||||
|
||||
// Администратор может всё
|
||||
if (currentUser.role === 'admin') return true;
|
||||
// Пользователи с ролью 'tasks' могут редактировать любые задачи
|
||||
if (currentUser.role === 'tasks') return true;
|
||||
|
||||
// Создатель может редактировать свою задачу
|
||||
if (parseInt(task.created_by) === currentUser.id) {
|
||||
|
||||
13
public/ui.js
13
public/ui.js
@@ -87,8 +87,8 @@ function renderTasks() {
|
||||
<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 === '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.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="assignAdd_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>
|
||||
${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>` : ''}
|
||||
@@ -1320,8 +1320,15 @@ async function assignAdd_openModal(taskId) {
|
||||
// Получаем доступных для добавления исполнителей
|
||||
try {
|
||||
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 = `
|
||||
<div class="modal" id="manage-assignees-modal">
|
||||
|
||||
@@ -32,7 +32,9 @@ function setupTaskEndpoints(app, db, upload) {
|
||||
|
||||
// Администратор может всё
|
||||
if (user.role === 'admin') return true;
|
||||
|
||||
// Пользователи с ролью 'tasks' могут редактировать любые задачи
|
||||
if (user.role === 'tasks') return true;
|
||||
|
||||
// Создатель может редактировать свою задачу только если она не назначена другим
|
||||
if (parseInt(task.created_by) === user.id) {
|
||||
if (task.assignments && task.assignments.length > 0) {
|
||||
|
||||
Reference in New Issue
Block a user