This commit is contained in:
Tomas Dvorak
2025-06-18 09:45:29 +02:00
parent 4146410c4b
commit 8958d3f4eb
8 changed files with 469 additions and 79 deletions
+225 -6
View File
@@ -1004,11 +1004,12 @@
<div class="header">
<div class="header-content">
<h1>Admin Dashboard</h1>
<nav class="admin-nav">
<a href="#aplikace" class="nav-link active">Aplikace</a>
<a href="#banner" class="nav-link">Správa banneru</a>
<a href="#rezervace" class="nav-link">Správa rezervací</a>
</nav>
<div class="admin-nav">
<a href="#aplikace" class="nav-link active" data-section="aplikace">Aplikace</a>
<a href="#banner" class="nav-link" data-section="banner">Správa banneru</a>
<a href="#rezervace" class="nav-link" data-section="rezervace">Správa rezervací</a>
<a href="#nastaveni" class="nav-link" data-section="nastaveni">Nastavení</a>
</div>
</div>
<button class="logout-btn" id="logoutBtn">Odhlásit se</button>
</div>
@@ -1062,7 +1063,62 @@
.logout-btn:hover {
background-color: #c0392b;
}
</style>
</div>
<!-- Credentials Management Section -->
<div class="card" style="margin: 2rem auto; max-width: 800px; display: none;" id="credentials">
<h3>Nastavení přihlašování</h3>
<div id="credentialsAlert" class="hidden p-4 mb-4 rounded"></div>
<form id="credentialsForm" class="space-y-4">
<div>
<label for="currentUsername" class="block text-sm font-medium text-gray-700">Aktuální uživatelské jméno</label>
<input type="text" id="currentUsername" name="currentUsername" required
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500">
</div>
<div>
<label for="currentPassword" class="block text-sm font-medium text-gray-700">Aktuální heslo</label>
<input type="password" id="currentPassword" name="currentPassword" required
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500">
</div>
<div class="border-t border-gray-200 pt-4">
<h4 class="text-md font-medium text-gray-900 mb-3">Nové přihlašovací údaje</h4>
<div class="mb-4">
<label for="newUsername" class="block text-sm font-medium text-gray-700">Nové uživatelské jméno</label>
<input type="text" id="newUsername" name="newUsername" required
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500">
</div>
<div class="mb-4">
<label for="newPassword" class="block text-sm font-medium text-gray-700">Nové heslo</label>
<input type="password" id="newPassword" name="newPassword" required minlength="8"
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500">
<p class="mt-1 text-sm text-gray-500">Heslo musí mít alespoň 8 znaků</p>
</div>
<div class="mb-4">
<label for="confirmPassword" class="block text-sm font-medium text-gray-700">Potvrďte nové heslo</label>
<input type="password" id="confirmPassword" name="confirmPassword" required minlength="8"
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500">
</div>
</div>
<div class="flex justify-end space-x-3 pt-4">
<button type="button" onclick="document.getElementById('credentials').style.display = 'none'"
class="px-4 py-2 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500">
Zrušit
</button>
<button type="submit"
class="px-4 py-2 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500">
Uložit změny
</button>
</div>
</form>
</div>
<div class="container">
<h2>Vítejte v administraci</h2>
@@ -3046,6 +3102,163 @@ async function loadBanner() {
// Add submission flag at the top of the script
let isSubmitting = false;
// Setup credentials form
function setupCredentialsForm() {
const credentialsForm = document.getElementById('credentialsForm');
if (!credentialsForm) return;
credentialsForm.addEventListener('submit', async (e) => {
e.preventDefault();
// Prevent multiple submissions
if (isSubmitting) return;
isSubmitting = true;
const formData = new FormData(credentialsForm);
const currentUsername = formData.get('currentUsername');
const currentPassword = formData.get('currentPassword');
const newUsername = formData.get('newUsername');
const newPassword = formData.get('newPassword');
const confirmPassword = formData.get('confirmPassword');
// Reset previous errors
document.querySelectorAll('.form-control').forEach(el => el.classList.remove('border-red-500'));
const alertEl = document.getElementById('credentialsAlert');
alertEl.classList.add('hidden');
// Client-side validation
let isValid = true;
if (!currentUsername || !currentPassword) {
showError('Vyplňte prosím aktuální přihlašovací údaje.');
isValid = false;
}
if (newPassword && newPassword.length < 8) {
document.getElementById('newPassword').classList.add('border-red-500');
showError('Nové heslo musí mít alespoň 8 znaků.');
isValid = false;
}
if (newPassword !== confirmPassword) {
document.getElementById('confirmPassword').classList.add('border-red-500');
showError('Nová hesla se neshodují.');
isValid = false;
}
if (!isValid) {
isSubmitting = false;
return;
}
// Prepare request data
const requestData = {
currentUsername,
currentPassword,
newUsername: newUsername || undefined,
newPassword: newPassword || undefined
};
try {
const response = await fetch('/api/update-credentials', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${localStorage.getItem('token')}`
},
body: JSON.stringify(requestData)
});
const data = await response.json();
if (!response.ok) {
throw new Error(data.error || 'Nepodařilo se aktualizovat přihlašovací údaje');
}
// Show success message
showSuccess('Přihlašovací údaje byly úspěšně aktualizovány. Budete odhlášeni za 3 sekundy...');
// Logout after a delay
setTimeout(() => {
localStorage.removeItem('token');
window.location.href = '/login.html';
}, 3000);
} catch (error) {
console.error('Chyba při aktualizaci přihlašovacích údajů:', error);
showError(error.message || 'Nastala chyba při aktualizaci přihlašovacích údajů');
} finally {
isSubmitting = false;
}
});
function showError(message) {
const alertEl = document.getElementById('credentialsAlert');
alertEl.textContent = message;
alertEl.className = 'bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded relative mb-4';
alertEl.classList.remove('hidden');
alertEl.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
}
function showSuccess(message) {
const alertEl = document.getElementById('credentialsAlert');
alertEl.textContent = message;
alertEl.className = 'bg-green-100 border border-green-400 text-green-700 px-4 py-3 rounded relative mb-4';
alertEl.classList.remove('hidden');
alertEl.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
}
}
// Setup navigation
function setupNavigation() {
const navLinks = document.querySelectorAll('.nav-link');
const sections = document.querySelectorAll('.card[id]');
// Show section based on hash or default to first section
function showSection(sectionId) {
// Hide all sections
sections.forEach(section => {
section.style.display = 'none';
});
// Show selected section
const targetSection = document.getElementById(sectionId);
if (targetSection) {
targetSection.style.display = 'block';
} else if (sections.length > 0) {
// Default to first section if target not found
sections[0].style.display = 'block';
}
// Update active nav link
navLinks.forEach(link => {
link.classList.toggle('active', link.getAttribute('data-section') === sectionId);
});
// Update URL hash
window.location.hash = `#${sectionId}`;
}
// Handle nav link clicks
navLinks.forEach(link => {
link.addEventListener('click', (e) => {
e.preventDefault();
const sectionId = link.getAttribute('data-section');
showSection(sectionId);
});
});
// Handle initial load
const initialSection = window.location.hash ? window.location.hash.substring(1) : 'aplikace';
showSection(initialSection);
// Handle browser back/forward
window.addEventListener('popstate', () => {
const sectionId = window.location.hash ? window.location.hash.substring(1) : 'aplikace';
showSection(sectionId);
});
}
async function saveBanner(event) {
event.preventDefault();
@@ -4295,6 +4508,12 @@ document.addEventListener('DOMContentLoaded', function() {
// Initialize banner image upload functionality
const dragDropArea = document.getElementById('dragDropArea');
// Initialize credentials form
setupCredentialsForm();
// Navigation handling
setupNavigation();
const uploadImageBtn = document.getElementById('uploadImageBtn');
const bannerImageInput = document.getElementById('bannerImage');