369 lines
16 KiB
JavaScript
369 lines
16 KiB
JavaScript
import { apiRequest } from './api.js';
|
|
import { getCookie, deleteCookie, setCookie } from './cookies.js';
|
|
|
|
class ClientManager {
|
|
constructor() {
|
|
this.userData = null;
|
|
this.accessData = null;
|
|
this.init();
|
|
}
|
|
|
|
async init() {
|
|
try {
|
|
// Получаем данные пользователя из cookie
|
|
this.userData = await getCookie('toolbox_user');
|
|
this.accessData = await getCookie('toolbox_access');
|
|
|
|
if (!this.userData || !this.accessData) {
|
|
console.warn('User data or access data not found in cookie');
|
|
this.initLogout();
|
|
}
|
|
|
|
// Вставляем данные пользователя в DOM
|
|
this.renderUserInfo();
|
|
|
|
// Инициализируем обработчики
|
|
this.initLogoutHandler();
|
|
this.initHoverEffects();
|
|
this.addAvatarErrorHandler();
|
|
|
|
} catch (error) {
|
|
console.error('Error initializing client manager:', error);
|
|
}
|
|
}
|
|
|
|
renderUserInfo() {
|
|
if (!this.userData) {
|
|
this.initLogout();
|
|
return;
|
|
}
|
|
|
|
// Находим элементы для обновления
|
|
const avatar = document.querySelector('.client-avatar');
|
|
const nameElement = document.querySelector('.client-name');
|
|
const roleElement = document.querySelector('.client-role');
|
|
|
|
// Обновляем аватар
|
|
if (avatar) {
|
|
avatar.src = this.userData.photo || 'static/images/users/default.png';
|
|
avatar.alt = this.userData.username || 'Пользователь';
|
|
}
|
|
|
|
// Обновляем имя
|
|
if (nameElement) {
|
|
nameElement.textContent = this.userData.username || 'Неизвестный пользователь';
|
|
}
|
|
|
|
// Обновляем роль
|
|
if (roleElement) {
|
|
const role = this.accessData.title || 'Неизвестная роль';
|
|
roleElement.textContent = role;
|
|
}
|
|
|
|
}
|
|
|
|
initLogoutHandler() {
|
|
const logoutBtn = document.getElementById('clientLogoutBtn');
|
|
if (logoutBtn) {
|
|
logoutBtn.addEventListener('click', (e) => {
|
|
e.preventDefault();
|
|
this.handleLogout(logoutBtn);
|
|
});
|
|
}
|
|
}
|
|
|
|
initLogout() {
|
|
const logoutBtn = document.getElementById('clientLogoutBtn');
|
|
if (logoutBtn) {
|
|
this.handleLogout(logoutBtn);
|
|
}
|
|
}
|
|
|
|
handleLogout(button) {
|
|
// Анимация нажатия
|
|
button.style.transform = 'scale(0.95)';
|
|
|
|
// Добавляем иконку загрузки
|
|
button.innerHTML = `
|
|
<span class="spinner-border spinner-border-sm me-2" role="status" aria-hidden="true"></span>
|
|
Выход...
|
|
`;
|
|
button.disabled = true;
|
|
|
|
// Очищаем cookie пользователя
|
|
this.clearUserCookie();
|
|
|
|
// Очищаем локальное хранилище
|
|
localStorage.clear();
|
|
|
|
// Переход на страницу выхода
|
|
setTimeout(() => {
|
|
window.location.href = '/user/login';
|
|
}, 800);
|
|
}
|
|
|
|
clearUserCookie() {
|
|
try {
|
|
const cookieNames = ['toolbox_user', 'toolbox_access'];
|
|
|
|
cookieNames.forEach(cookieName => {
|
|
deleteCookie(cookieName);
|
|
});
|
|
} catch (error) {
|
|
console.warn('Error clearing cookies:', error);
|
|
}
|
|
}
|
|
|
|
initHoverEffects() {
|
|
const clientCard = document.querySelector('.client-card');
|
|
if (clientCard) {
|
|
clientCard.addEventListener('mouseenter', () => {
|
|
clientCard.style.transform = 'translateY(-4px) scale(1.01)';
|
|
});
|
|
|
|
clientCard.addEventListener('mouseleave', () => {
|
|
clientCard.style.transform = 'translateY(0) scale(1)';
|
|
});
|
|
}
|
|
}
|
|
|
|
addAvatarErrorHandler() {
|
|
const avatar = document.querySelector('.client-avatar');
|
|
if (avatar) {
|
|
avatar.onerror = () => {
|
|
avatar.src = 'static/images/users/default.png';
|
|
};
|
|
}
|
|
}
|
|
|
|
userProfile() {
|
|
const avatarBtn = document.getElementById('userProfile');
|
|
|
|
async function openEditUserModal(user = null) {
|
|
const isNew = !user;
|
|
const modalId = `profile-edit-user-modal`;
|
|
|
|
// Удаляем старую модалку, если есть
|
|
const existingModal = document.getElementById(modalId);
|
|
if (existingModal) {
|
|
existingModal.remove();
|
|
}
|
|
|
|
const modalHTML = `
|
|
<div class="modal fade" id="${modalId}" tabindex="-1" aria-hidden="true">
|
|
<div class="modal-dialog">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title">${isNew ? 'Добавить пользователя' : 'Редактировать пользователя'}</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<form id="${modalId}-form">
|
|
<div class="text-center mb-3">
|
|
<img src="${user?.photo || 'static/images/users/default.png'}"
|
|
alt="Фото пользователя"
|
|
class="rounded-circle border object-fit-cover"
|
|
width="100" height="100"
|
|
id="${modalId}-photo-preview"
|
|
onerror="this.src='static/images/users/default.png'">
|
|
<div class="mt-2">
|
|
<input type="file"
|
|
class="form-control form-control-sm"
|
|
id="${modalId}-photo-input"
|
|
accept="image/*"
|
|
style="display: none;">
|
|
<div class="btn-group btn-group-sm" role="group">
|
|
<button type="button" class="btn btn-outline-primary" id="${modalId}-change-photo-btn">
|
|
<i class="bi bi-upload"></i> Изменить
|
|
</button>
|
|
${user?.photo && !user.photo.includes('default.png') ? `
|
|
<button type="button" class="btn btn-outline-danger" id="${modalId}-remove-photo-btn">
|
|
<i class="bi bi-trash"></i> Удалить
|
|
</button>
|
|
` : ''}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label for="${modalId}-login" class="form-label">Логин *</label>
|
|
<input type="text" class="form-control" id="${modalId}-login"
|
|
value="${user?.login || ''}" required>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="${modalId}-username" class="form-label">Имя пользователя *</label>
|
|
<input type="text" class="form-control" id="${modalId}-username"
|
|
value="${user?.username || ''}" required>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label for="${modalId}-password" class="form-label">${isNew ? 'Пароль *' : 'Новый пароль'}</label>
|
|
<input type="password" class="form-control" id="${modalId}-password"
|
|
${isNew ? 'required' : ''}
|
|
placeholder="${isNew ? '' : 'Оставьте пустым, если не нужно менять'}">
|
|
${!isNew ? '<div class="form-text">Оставьте пустым, если не нужно менять пароль</div>' : ''}
|
|
</div>
|
|
|
|
</form>
|
|
<div id="${modalId}-error-message" class="alert alert-danger mb-3" role="alert" hidden></div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Отмена</button>
|
|
<button type="button" class="btn btn-primary" id="${modalId}-save-btn">Сохранить</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
`;
|
|
|
|
document.body.insertAdjacentHTML('beforeend', modalHTML);
|
|
const modal = new bootstrap.Modal(document.getElementById(modalId));
|
|
let photoFile = null;
|
|
let removePhoto = false;
|
|
|
|
// Обработчики для фото
|
|
const changePhotoBtn = document.getElementById(`${modalId}-change-photo-btn`);
|
|
const photoInput = document.getElementById(`${modalId}-photo-input`);
|
|
const photoPreview = document.getElementById(`${modalId}-photo-preview`);
|
|
|
|
changePhotoBtn.addEventListener('click', () => {
|
|
photoInput.click();
|
|
});
|
|
|
|
photoInput.addEventListener('change', (e) => {
|
|
const file = e.target.files[0];
|
|
if (file) {
|
|
if (file.type.startsWith('image/')) {
|
|
photoFile = file;
|
|
const reader = new FileReader();
|
|
reader.onload = (e) => {
|
|
photoPreview.src = e.target.result;
|
|
};
|
|
reader.readAsDataURL(file);
|
|
removePhoto = false;
|
|
} else {
|
|
showConfirmationModal('Ошибка', 'Пожалуйста, выберите файл изображения', () => { });
|
|
photoInput.value = '';
|
|
}
|
|
}
|
|
});
|
|
|
|
if (document.getElementById(`${modalId}-remove-photo-btn`)) {
|
|
document.getElementById(`${modalId}-remove-photo-btn`).addEventListener('click', () => {
|
|
photoPreview.src = 'static/images/users/default.png';
|
|
photoFile = null;
|
|
removePhoto = true;
|
|
});
|
|
}
|
|
|
|
// Обработчик сохранения
|
|
document.getElementById(`${modalId}-save-btn`).addEventListener('click', async () => {
|
|
const form = document.getElementById(`${modalId}-form`);
|
|
if (!form.checkValidity()) {
|
|
form.reportValidity();
|
|
return;
|
|
}
|
|
|
|
const formUserData = {
|
|
login: document.getElementById(`${modalId}-login`).value.trim(),
|
|
username: document.getElementById(`${modalId}-username`).value.trim(),
|
|
};
|
|
|
|
// Добавляем пароль, если он указан
|
|
const password = document.getElementById(`${modalId}-password`).value;
|
|
if (password || isNew) {
|
|
formUserData.password = password || '';
|
|
}
|
|
|
|
// Определяем действие
|
|
let action = isNew ? 'create' : 'update';
|
|
|
|
// Обработка фото
|
|
if (photoFile) {
|
|
if (photoFile.size > 5 * 1024 * 1024) {
|
|
showInfo('Фото больше 5 МБ, оно не подходит', 'warning');
|
|
return;
|
|
}
|
|
formUserData.photo = await new Promise((resolve, reject) => {
|
|
const reader = new FileReader();
|
|
|
|
reader.onload = () => resolve(reader.result);
|
|
reader.onerror = () => reject(new Error('Ошибка чтения файла'));
|
|
|
|
reader.readAsDataURL(photoFile);
|
|
});
|
|
|
|
} else if (removePhoto) {
|
|
formUserData.photo = '';
|
|
}
|
|
|
|
let changedUserData = {};
|
|
if (isNew) {
|
|
changedUserData = formUserData;
|
|
} else {
|
|
changedUserData = Object.keys(formUserData).reduce((acc, key) => {
|
|
if (formUserData[key] !== user[key]) {
|
|
acc[key] = formUserData[key];
|
|
}
|
|
return acc;
|
|
}, {});
|
|
}
|
|
|
|
if (Object.keys(changedUserData).length === 0) {
|
|
showInfo('Нет изменений', 'info');
|
|
return;
|
|
}
|
|
|
|
// Обновляем данные
|
|
if (!isNew) {
|
|
changedUserData.id = user.id;
|
|
}
|
|
|
|
const result = await apiRequest('/user/', { action, userData: changedUserData, userId: user.id });
|
|
|
|
if (result && result.status === 'ok') {
|
|
const newUserData = await apiRequest('/user/', { userId: user.id }, 'get');
|
|
await setCookie('toolbox_user', JSON.stringify(newUserData));
|
|
const checkData = await getCookie('toolbox_user');
|
|
if (window.clientManager) {
|
|
window.clientManager.userData = newUserData;
|
|
window.clientManager.renderUserInfo();
|
|
}
|
|
if (modal) { modal.hide(); }
|
|
} else {
|
|
const errorMessageDiv = document.getElementById(`${modalId}-error-message`);
|
|
errorMessageDiv.textContent = result.message;
|
|
errorMessageDiv.hidden = false;
|
|
}
|
|
});
|
|
|
|
modal.show();
|
|
|
|
// Удаляем модалку после скрытия
|
|
modal._element.addEventListener('hidden.bs.modal', () => {
|
|
modal._element.remove();
|
|
});
|
|
}
|
|
|
|
avatarBtn.addEventListener('click', async () => {
|
|
await openEditUserModal(this.userData);
|
|
});
|
|
}
|
|
}
|
|
|
|
// Инициализация при загрузке документа
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
// Инициализируем менеджер пользователя
|
|
window.clientManager = new ClientManager();
|
|
|
|
// Добавляем анимацию появления
|
|
const elements = document.querySelectorAll('.animate-fade-up');
|
|
elements.forEach((el, index) => {
|
|
el.style.animationDelay = `${index * 0.1}s`;
|
|
});
|
|
|
|
window.clientManager.userProfile();
|
|
});
|
|
|
|
// Экспорт для использования в других модулях
|
|
export { ClientManager }; |