This commit is contained in:
2026-03-19 14:36:35 +07:00
parent 6d7d86befd
commit 96635dbcf2
28 changed files with 4332 additions and 1683 deletions

212
public/js/auth.js Normal file
View File

@@ -0,0 +1,212 @@
// Authentication Handler
class Auth {
constructor() {
this.user = null;
this.isAuthenticated = false;
this.checkAuth();
}
async checkAuth() {
try {
const response = await api.getCurrentUser();
if (response.success) {
this.user = response.user;
this.isAuthenticated = true;
return true;
}
} catch (error) {
this.logout();
}
return false;
}
async login(email, password) {
try {
const response = await api.login(email, password);
if (response.success) {
this.user = response.user;
this.isAuthenticated = true;
return { success: true, user: this.user };
}
return { success: false, error: response.message };
} catch (error) {
return { success: false, error: error.message };
}
}
async register(name, email, password) {
try {
const response = await api.register(name, email, password);
if (response.success) {
return { success: true, message: 'Регистрация успешна. Теперь вы можете войти.' };
}
return { success: false, error: response.message };
} catch (error) {
return { success: false, error: error.message };
}
}
async logout() {
try {
await api.logout();
} catch (error) {
console.error('Logout error:', error);
}
this.user = null;
this.isAuthenticated = false;
window.location.hash = '#login';
}
hasRole(role) {
return this.user && this.user.role === role;
}
hasPermission(permission) {
if (!this.user) return false;
return this.user.permissions && this.user.permissions.includes(permission);
}
getUser() {
return this.user;
}
isAdmin() {
return this.user && (this.user.role === 'superadmin' || this.user.role === 'admin');
}
isSuperAdmin() {
return this.user && this.user.role === 'superadmin';
}
}
// Global auth instance
const auth = new Auth();
// Login/Register form handler
document.addEventListener('DOMContentLoaded', () => {
// Tab switching
const authTabBtns = document.querySelectorAll('.auth-tab-btn');
const authForms = document.querySelectorAll('.auth-form');
authTabBtns.forEach(btn => {
btn.addEventListener('click', () => {
const tab = btn.dataset.tab;
// Update active tab
authTabBtns.forEach(b => b.classList.remove('active'));
btn.classList.add('active');
// Show/hide forms
authForms.forEach(form => form.classList.add('hidden'));
if (tab === 'login') {
document.getElementById('loginForm').classList.remove('hidden');
} else {
document.getElementById('registerForm').classList.remove('hidden');
}
});
});
// Login form
const loginForm = document.getElementById('loginForm');
if (loginForm) {
loginForm.addEventListener('submit', async (e) => {
e.preventDefault();
const email = document.getElementById('loginEmail').value;
const password = document.getElementById('loginPassword').value;
const loginText = document.querySelector('.login-text');
const loginLoader = document.querySelector('.login-loader');
loginText.style.display = 'none';
loginLoader.classList.remove('hidden');
const result = await auth.login(email, password);
if (result.success) {
// Redirect to dashboard
setTimeout(() => {
window.location.hash = '#dashboard';
}, 500);
} else {
showAuthError(result.error);
loginText.style.display = 'block';
loginLoader.classList.add('hidden');
}
});
}
// Register form
const registerForm = document.getElementById('registerForm');
if (registerForm) {
registerForm.addEventListener('submit', async (e) => {
e.preventDefault();
const name = document.getElementById('registerName').value;
const email = document.getElementById('registerEmail').value;
const password = document.getElementById('registerPassword').value;
const confirmPassword = document.getElementById('registerPasswordConfirm').value;
if (password !== confirmPassword) {
showAuthError('Пароли не совпадают');
return;
}
const registerText = document.querySelector('.register-text');
const registerLoader = document.querySelector('.register-loader');
registerText.style.display = 'none';
registerLoader.classList.remove('hidden');
const result = await auth.register(name, email, password);
if (result.success) {
showAuthError(result.message, 'success');
// Clear form and switch to login
registerForm.reset();
document.querySelector('[data-tab="login"]').click();
} else {
showAuthError(result.error);
}
registerText.style.display = 'block';
registerLoader.classList.add('hidden');
});
}
});
function showAuthError(message, type = 'error') {
const errorDiv = document.getElementById('authError');
const errorText = document.getElementById('authErrorText');
if (type === 'success') {
errorDiv.classList.remove('bg-red-50', 'border-red-200', 'text-red-600', 'dark:bg-red-900/20', 'dark:border-red-800', 'dark:text-red-400');
errorDiv.classList.add('bg-green-50', 'border-green-200', 'text-green-600', 'dark:bg-green-900/20', 'dark:border-green-800', 'dark:text-green-400');
const icon = errorDiv.querySelector('i');
if (icon) {
icon.setAttribute('data-lucide', 'check-circle');
}
} else {
errorDiv.classList.remove('bg-green-50', 'border-green-200', 'text-green-600', 'dark:bg-green-900/20', 'dark:border-green-800', 'dark:text-green-400');
errorDiv.classList.add('bg-red-50', 'border-red-200', 'text-red-600', 'dark:bg-red-900/20', 'dark:border-red-800', 'dark:text-red-400');
const icon = errorDiv.querySelector('i');
if (icon) {
icon.setAttribute('data-lucide', 'alert-circle');
}
}
errorText.textContent = message;
errorDiv.classList.remove('hidden');
// Auto-hide success messages
if (type === 'success') {
setTimeout(() => {
errorDiv.classList.add('hidden');
}, 3000);
}
// Update Lucide icons
if (typeof lucide !== 'undefined') {
lucide.createIcons();
}
}