Files
toolbox/api/static/js/user.js
T

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 };