203 lines
6.0 KiB
HTML
203 lines
6.0 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="ru" data-title="Вход в панель" data-description="Авторизация администратора гостиницы">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<link rel="stylesheet" href="style.css">
|
||
<title>Вход в систему</title>
|
||
<style>
|
||
.login-page {
|
||
min-height: 100vh;
|
||
display: flex;
|
||
flex-direction: column;
|
||
}
|
||
|
||
.login-main {
|
||
flex: 1;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
padding: 2rem 1rem;
|
||
}
|
||
|
||
.login-card {
|
||
background: var(--bg-card);
|
||
border: 1px solid var(--border);
|
||
border-radius: var(--radius-xl);
|
||
padding: clamp(1.5rem, 5vw, 3rem);
|
||
width: 100%;
|
||
max-width: 420px;
|
||
box-shadow: var(--shadow-lg);
|
||
animation: fadeInUp 0.5s ease-out;
|
||
}
|
||
|
||
@keyframes fadeInUp {
|
||
from { opacity: 0; transform: translateY(20px); }
|
||
to { opacity: 1; transform: translateY(0); }
|
||
}
|
||
|
||
.login-icon {
|
||
width: 56px;
|
||
height: 56px;
|
||
background: var(--primary-light);
|
||
border-radius: 16px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
margin: 0 auto 1.5rem;
|
||
}
|
||
|
||
.login-icon svg {
|
||
width: 28px;
|
||
height: 28px;
|
||
color: var(--primary);
|
||
}
|
||
|
||
.login-title {
|
||
font-size: clamp(1.375rem, 4vw, 1.75rem);
|
||
font-weight: 700;
|
||
text-align: center;
|
||
margin-bottom: 0.375rem;
|
||
color: var(--text-primary);
|
||
letter-spacing: -0.025em;
|
||
}
|
||
|
||
.login-subtitle {
|
||
color: var(--text-muted);
|
||
text-align: center;
|
||
font-size: 0.875rem;
|
||
margin-bottom: 2rem;
|
||
}
|
||
|
||
.login-btn {
|
||
width: 100%;
|
||
padding: 0.75rem;
|
||
font-size: 0.9375rem;
|
||
font-weight: 600;
|
||
margin-top: 0.5rem;
|
||
justify-content: center;
|
||
}
|
||
|
||
.error-message {
|
||
background: var(--danger-light);
|
||
border: 1px solid #fca5a5;
|
||
color: var(--danger);
|
||
padding: 0.75rem 1rem;
|
||
border-radius: var(--radius-sm);
|
||
font-size: 0.875rem;
|
||
margin-top: 1rem;
|
||
display: none;
|
||
animation: shake 0.4s ease-in-out;
|
||
}
|
||
|
||
@keyframes shake {
|
||
0%, 100% { transform: translateX(0); }
|
||
25% { transform: translateX(-6px); }
|
||
75% { transform: translateX(6px); }
|
||
}
|
||
|
||
.form-group {
|
||
margin-bottom: 1rem;
|
||
}
|
||
|
||
.form-group label {
|
||
display: block;
|
||
margin-bottom: 0.375rem;
|
||
color: var(--text-secondary);
|
||
font-size: 0.875rem;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.form-group input {
|
||
width: 100%;
|
||
padding: 0.75rem 0.875rem;
|
||
font-size: 0.9375rem;
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="login-page">
|
||
<header></header>
|
||
|
||
<main class="login-main">
|
||
<div class="login-card">
|
||
<div class="login-icon">
|
||
<svg fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M8.25 21v-4.875c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125V21m0 0h4.5V3.545L12.75 2.25 5.25 3.545V21"/></svg>
|
||
</div>
|
||
<h1 class="login-title">Отель 777</h1>
|
||
<p class="login-subtitle">Панель управления</p>
|
||
|
||
<form id="loginForm">
|
||
<div class="form-group">
|
||
<label for="login">Логин</label>
|
||
<input type="text" id="login" name="login" required autocomplete="username" placeholder="Введите логин">
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label for="password">Пароль</label>
|
||
<input type="password" id="password" name="password" required autocomplete="current-password" placeholder="Введите пароль">
|
||
</div>
|
||
|
||
<button type="submit" class="login-btn" id="loginBtn">
|
||
<span id="loginBtnText">Войти</span>
|
||
<span id="loginBtnSpinner" class="loading-spinner" style="display:none;"></span>
|
||
</button>
|
||
|
||
<p id="error" class="error-message"></p>
|
||
</form>
|
||
</div>
|
||
</main>
|
||
</div>
|
||
|
||
<script src="nav.js"></script>
|
||
<script>
|
||
document.getElementById('loginForm').addEventListener('submit', async (e) => {
|
||
e.preventDefault();
|
||
const loginBtn = document.getElementById('loginBtn');
|
||
const loginBtnText = document.getElementById('loginBtnText');
|
||
const loginBtnSpinner = document.getElementById('loginBtnSpinner');
|
||
const errorEl = document.getElementById('error');
|
||
|
||
loginBtn.disabled = true;
|
||
loginBtnText.style.display = 'none';
|
||
loginBtnSpinner.style.display = 'inline-block';
|
||
errorEl.style.display = 'none';
|
||
|
||
try {
|
||
const csrfRes = await fetch('/api/csrf-token');
|
||
const csrfData = await csrfRes.json();
|
||
|
||
const res = await fetch('/api/login', {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
'X-CSRF-Token': csrfData.csrfToken
|
||
},
|
||
body: JSON.stringify({
|
||
login: document.getElementById('login').value,
|
||
password: document.getElementById('password').value
|
||
})
|
||
});
|
||
|
||
if (res.ok) {
|
||
const data = await res.json();
|
||
window.localStorage.setItem('csrfToken', data.csrfToken);
|
||
window.location.href = '/index.html';
|
||
} else {
|
||
const err = await res.json();
|
||
errorEl.textContent = err.error || 'Неверный логин или пароль';
|
||
errorEl.style.display = 'block';
|
||
}
|
||
} catch (err) {
|
||
errorEl.textContent = 'Ошибка соединения с сервером';
|
||
errorEl.style.display = 'block';
|
||
} finally {
|
||
loginBtn.disabled = false;
|
||
loginBtnText.style.display = 'inline';
|
||
loginBtnSpinner.style.display = 'none';
|
||
}
|
||
});
|
||
</script>
|
||
</body>
|
||
</html>
|