168 lines
6.3 KiB
HTML
168 lines
6.3 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">
|
|
</head>
|
|
<body>
|
|
<header></header>
|
|
<main>
|
|
<h1>Панель управления</h1>
|
|
<p>Сервис: <span id="serviceName"></span></p>
|
|
<button id="syncBtn">Запустить синхронизацию заявок</button>
|
|
<p id="syncStatus"></p>
|
|
|
|
<hr style="margin: 2rem 0;">
|
|
|
|
<h2>Управление администраторами</h2>
|
|
<table id="adminsTable">
|
|
<thead>
|
|
<tr><th>ID</th><th>Логин</th><th>Действия</th></tr>
|
|
</thead>
|
|
<tbody></tbody>
|
|
</table>
|
|
|
|
<h3>Добавить нового администратора</h3>
|
|
<form id="addAdminForm">
|
|
<label>Логин</label>
|
|
<input type="text" id="newLogin" required>
|
|
<label>Пароль</label>
|
|
<input type="password" id="newPassword" required>
|
|
<button type="submit">Добавить</button>
|
|
</form>
|
|
|
|
<!-- Модальное окно для смены пароля -->
|
|
<div id="editPasswordModal" class="modal" style="display:none;">
|
|
<div class="modal-content">
|
|
<h2>Смена пароля</h2>
|
|
<form id="editPasswordForm">
|
|
<input type="hidden" id="editAdminId">
|
|
<label>Новый пароль</label>
|
|
<input type="password" id="editPassword" required>
|
|
<button type="submit">Сохранить</button>
|
|
<button type="button" id="closePasswordModal">Отмена</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
</main>
|
|
<script src="nav.js"></script>
|
|
<script src="seo.js"></script>
|
|
<script>
|
|
// Проверка авторизации
|
|
fetch('/api/me').then(r => r.json()).then(data => {
|
|
if (!data.isAdmin) window.location.href = '/login.html';
|
|
});
|
|
|
|
// --- Синхронизация ---
|
|
document.getElementById('syncBtn').addEventListener('click', async () => {
|
|
const status = document.getElementById('syncStatus');
|
|
status.textContent = 'Синхронизация...';
|
|
const res = await fetch('/api/admin/sync', { method: 'POST' });
|
|
if (res.ok) status.textContent = 'Синхронизация завершена';
|
|
else status.textContent = 'Ошибка синхронизации';
|
|
});
|
|
|
|
// --- Управление администраторами ---
|
|
const adminsTbody = document.querySelector('#adminsTable tbody');
|
|
const addForm = document.getElementById('addAdminForm');
|
|
const modal = document.getElementById('editPasswordModal');
|
|
const closeModalBtn = document.getElementById('closePasswordModal');
|
|
|
|
async function loadAdmins() {
|
|
const res = await fetch('/api/admins');
|
|
const admins = await res.json();
|
|
adminsTbody.innerHTML = '';
|
|
for (const admin of admins) {
|
|
const tr = document.createElement('tr');
|
|
tr.innerHTML = `
|
|
<td>${admin.id}</td>
|
|
<td>${escapeHtml(admin.login)}</td>
|
|
<td>
|
|
<button class="changePasswordBtn" data-id="${admin.id}" data-login="${escapeHtml(admin.login)}">Сменить пароль</button>
|
|
<button class="deleteAdminBtn" data-id="${admin.id}">Удалить</button>
|
|
</td>
|
|
`;
|
|
adminsTbody.appendChild(tr);
|
|
}
|
|
// Привязываем обработчики к динамическим кнопкам
|
|
document.querySelectorAll('.changePasswordBtn').forEach(btn => {
|
|
btn.addEventListener('click', () => {
|
|
document.getElementById('editAdminId').value = btn.dataset.id;
|
|
document.getElementById('editPassword').value = '';
|
|
modal.style.display = 'flex';
|
|
});
|
|
});
|
|
document.querySelectorAll('.deleteAdminBtn').forEach(btn => {
|
|
btn.addEventListener('click', async () => {
|
|
const id = btn.dataset.id;
|
|
if (confirm('Удалить администратора? Вы не сможете удалить самого себя и последнего админа.')) {
|
|
const res = await fetch(`/api/admins/${id}`, { method: 'DELETE' });
|
|
if (res.ok) {
|
|
loadAdmins();
|
|
} else {
|
|
const err = await res.json();
|
|
alert('Ошибка: ' + (err.error || 'неизвестная'));
|
|
}
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
addForm.addEventListener('submit', async (e) => {
|
|
e.preventDefault();
|
|
const login = document.getElementById('newLogin').value.trim();
|
|
const password = document.getElementById('newPassword').value;
|
|
const res = await fetch('/api/admins', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ login, password })
|
|
});
|
|
if (res.ok) {
|
|
alert('Администратор добавлен');
|
|
addForm.reset();
|
|
loadAdmins();
|
|
} else {
|
|
const err = await res.json();
|
|
alert('Ошибка: ' + (err.error || 'неизвестная'));
|
|
}
|
|
});
|
|
|
|
document.getElementById('editPasswordForm').addEventListener('submit', async (e) => {
|
|
e.preventDefault();
|
|
const id = document.getElementById('editAdminId').value;
|
|
const password = document.getElementById('editPassword').value;
|
|
const res = await fetch(`/api/admins/${id}`, {
|
|
method: 'PUT',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ password })
|
|
});
|
|
if (res.ok) {
|
|
alert('Пароль изменён');
|
|
modal.style.display = 'none';
|
|
loadAdmins();
|
|
} else {
|
|
const err = await res.json();
|
|
alert('Ошибка: ' + (err.error || 'неизвестная'));
|
|
}
|
|
});
|
|
|
|
closeModalBtn.addEventListener('click', () => {
|
|
modal.style.display = 'none';
|
|
});
|
|
|
|
function escapeHtml(str) {
|
|
if (!str) return '';
|
|
return str.replace(/[&<>]/g, function(m) {
|
|
if (m === '&') return '&';
|
|
if (m === '<') return '<';
|
|
if (m === '>') return '>';
|
|
return m;
|
|
});
|
|
}
|
|
|
|
loadAdmins();
|
|
</script>
|
|
</body>
|
|
</html> |