Files
PPve/admin-dashboard.html
T
Tomas Dvorak 98bbee18ff s
2025-05-28 13:41:57 +02:00

2027 lines
68 KiB
HTML

<!DOCTYPE html>
<html lang="cs">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Admin Dashboard - PP Kunovice</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
:root {
--primary-color: #4a6cf7;
--primary-hover: #3a56d4;
--secondary-color: #6c757d;
--success-color: #28a745;
--danger-color: #dc3545;
--warning-color: #ffc107;
--info-color: #17a2b8;
--light-color: #f8f9fa;
--dark-color: #212529;
--border-color: #e9ecef;
--border-radius: 8px;
--box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05), 0 1px 3px rgba(0, 0, 0, 0.1);
--transition: all 0.3s ease;
}
* {
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Roboto, -apple-system, BlinkMacSystemFont, sans-serif;
margin: 0;
padding: 0;
background-color: #f8f9fa;
color: #333;
line-height: 1.6;
}
.header {
background-color: #fff;
color: var(--dark-color);
padding: 1rem 2rem;
display: flex;
justify-content: space-between;
align-items: center;
box-shadow: var(--box-shadow);
position: sticky;
top: 0;
z-index: 1000;
}
.header h1 {
margin: 0;
font-size: 1.5rem;
font-weight: 600;
color: var(--primary-color);
}
.logout-btn {
background-color: transparent;
border: 1px solid var(--primary-color);
color: var(--primary-color);
padding: 0.5rem 1.2rem;
border-radius: var(--border-radius);
cursor: pointer;
font-weight: 500;
transition: var(--transition);
}
.logout-btn:hover {
background-color: var(--primary-color);
color: white;
}
.container {
max-width: 1200px;
margin: 2rem auto;
padding: 0 1.5rem;
}
.dashboard-cards {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 1.5rem;
margin-top: 2rem;
}
.card {
background-color: white;
border-radius: var(--border-radius);
padding: 1.75rem;
box-shadow: var(--box-shadow);
transition: var(--transition);
border: 1px solid var(--border-color);
}
.card:hover {
transform: translateY(-5px);
box-shadow: 0 8px 15px rgba(0, 0, 0, 0.1);
}
.card h3 {
margin-top: 0;
color: var(--dark-color);
font-weight: 600;
font-size: 1.25rem;
margin-bottom: 1rem;
padding-bottom: 0.75rem;
border-bottom: 1px solid var(--border-color);
}
.form-group {
margin-bottom: 1.5rem;
}
.form-group label {
display: block;
margin-bottom: 0.5rem;
font-weight: 500;
color: var(--dark-color);
font-size: 0.95rem;
}
.form-group input[type="text"],
.form-group input[type="url"],
.form-group input[type="number"],
.form-group textarea,
.form-group select {
width: 100%;
padding: 0.75rem 1rem;
border: 1px solid var(--border-color);
border-radius: var(--border-radius);
font-size: 1rem;
transition: var(--transition);
box-shadow: inset 0 1px 2px rgba(0,0,0,0.05);
}
.form-group input[type="text"]:focus,
.form-group input[type="url"]:focus,
.form-group input[type="number"]:focus,
.form-group textarea:focus,
.form-group select:focus {
border-color: var(--primary-color);
outline: none;
box-shadow: 0 0 0 3px rgba(74, 108, 247, 0.15);
}
.form-group textarea {
min-height: 120px;
resize: vertical;
line-height: 1.5;
}
.color-preview {
display: inline-block;
width: 30px;
height: 30px;
border: 1px solid var(--border-color);
border-radius: 50%;
vertical-align: middle;
margin-left: 10px;
box-shadow: inset 0 1px 2px rgba(0,0,0,0.1);
}
.color-picker {
margin-left: 10px;
vertical-align: middle;
height: 30px;
padding: 0;
border: 1px solid var(--border-color);
border-radius: 4px;
cursor: pointer;
}
.style-presets {
display: flex;
flex-wrap: wrap;
gap: 12px;
margin: 15px 0;
}
.style-preset {
padding: 8px 16px;
border: 1px solid var(--border-color);
border-radius: var(--border-radius);
cursor: pointer;
font-size: 0.9rem;
transition: var(--transition);
background-color: white;
font-weight: 500;
}
.style-preset:hover {
background-color: var(--light-color);
border-color: var(--secondary-color);
transform: translateY(-2px);
}
.banner-preview {
margin: 2rem 0;
padding: 0;
border: 2px dashed var(--border-color);
border-radius: var(--border-radius);
background-color: #fff;
display: none;
position: relative;
overflow: hidden;
min-height: 180px;
transition: var(--transition);
box-shadow: var(--box-shadow);
width: 100%;
max-width: 100%;
}
.banner-preview:hover {
border-color: var(--primary-color);
box-shadow: 0 8px 16px rgba(0,0,0,0.1);
}
.banner-preview::before {
content: 'Náhled banneru';
position: absolute;
top: 10px;
right: 10px;
background-color: rgba(255,255,255,0.8);
padding: 4px 10px;
border-radius: 4px;
font-size: 0.8rem;
color: var(--secondary-color);
z-index: 3;
font-weight: 500;
}
.banner-preview img {
max-width: 100%;
max-height: 300px;
object-fit: contain;
display: block;
margin: 0 auto;
}
.banner-preview.with-image {
min-height: 220px;
}
.banner-preview-content {
position: relative;
z-index: 2;
text-align: center;
padding: 20px;
margin: 0;
width: 100%;
box-sizing: border-box;
display: block;
}
.banner-preview-bg {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-size: cover;
background-position: center;
background-repeat: no-repeat;
opacity: 0.7;
z-index: 1;
transition: var(--transition);
}
.banner-preview-text {
position: relative;
z-index: 2;
margin: 0;
padding: 0;
line-height: 1.5;
word-wrap: break-word;
}
.color-picker-container {
display: flex;
align-items: center;
gap: 10px;
}
.color-picker {
vertical-align: middle;
margin-right: 8px;
cursor: pointer;
width: 40px;
height: 40px;
border: none;
border-radius: 8px;
overflow: hidden;
padding: 0;
background: none;
}
.image-upload-container {
margin: 15px 0;
padding: 15px;
border: 1px dashed #ddd;
border-radius: 4px;
text-align: center;
}
.image-preview {
max-width: 200px;
max-height: 150px;
margin: 10px auto;
display: block;
}
.card p {
color: #666;
margin-bottom: 0;
}
/* Form Actions */
.form-actions {
margin-top: 2.5rem;
padding-top: 1.5rem;
border-top: 1px solid var(--border-color);
display: flex;
justify-content: flex-end;
gap: 1rem;
}
.btn {
display: inline-flex;
align-items: center;
justify-content: center;
padding: 0.75rem 1.5rem;
border: 1px solid transparent;
border-radius: var(--border-radius);
font-size: 1rem;
font-weight: 500;
cursor: pointer;
transition: var(--transition);
box-shadow: 0 2px 4px rgba(0,0,0,0.05);
position: relative;
overflow: hidden;
}
.btn:hover {
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
}
.btn:active {
transform: translateY(0);
}
.btn i {
margin-right: 0.5rem;
font-size: 0.9em;
}
.btn-primary {
background-color: var(--primary-color);
color: white;
border-color: var(--primary-color);
}
.btn-primary:hover {
background-color: var(--primary-hover);
border-color: var(--primary-hover);
}
.btn-secondary {
background-color: var(--secondary-color);
color: white;
border-color: var(--secondary-color);
}
.btn-secondary:hover {
background-color: #5a6268;
border-color: #545b62;
}
.btn-danger {
background-color: var(--danger-color);
color: white;
border-color: var(--danger-color);
}
.btn-danger:hover {
background-color: #c82333;
border-color: #bd2130;
}
.btn:disabled {
opacity: 0.65;
cursor: not-allowed;
transform: none;
}
/* Notifications */
.notification {
position: fixed;
top: 20px;
right: 20px;
padding: 16px 24px;
border-radius: var(--border-radius);
color: white;
display: flex;
align-items: center;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
z-index: 1000;
animation: fadeIn 0.3s, fadeOut 0.3s 2.7s forwards;
min-width: 300px;
max-width: 450px;
}
.notification i {
margin-right: 12px;
font-size: 1.2rem;
}
.notification.info {
background-color: var(--info-color);
border-left: 4px solid #117a8b;
}
.notification.success {
background-color: var(--success-color);
border-left: 4px solid #1e7e34;
}
.notification.warning {
background-color: var(--warning-color);
color: #212529;
border-left: 4px solid #d39e00;
}
.notification.error {
background-color: var(--danger-color);
border-left: 4px solid #bd2130;
}
.notification.fade-out {
animation: fadeOut 0.3s forwards;
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(-20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes fadeOut {
to {
opacity: 0;
transform: translateY(-20px);
}
}
/* Additional responsive styles */
@media (max-width: 768px) {
.container {
padding: 0 1rem;
}
.card {
padding: 1.25rem;
}
.notification {
left: 20px;
right: 20px;
max-width: calc(100% - 40px);
}
.form-actions {
flex-direction: column;
}
.form-actions .btn {
width: 100%;
margin-bottom: 0.5rem;
}
}
.drag-drop-area {
border: 2px dashed var(--border-color);
border-radius: var(--border-radius);
padding: 30px 20px;
text-align: center;
background-color: #f8f9fa;
cursor: pointer;
transition: var(--transition);
margin-bottom: 15px;
position: relative;
}
.drag-drop-area:hover, .drag-drop-area.dragover {
border-color: var(--primary-color);
background-color: rgba(74, 108, 247, 0.05);
}
.drag-drop-message {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
color: var(--secondary-color);
}
.drag-drop-message i {
font-size: 2.5rem;
margin-bottom: 15px;
color: var(--primary-color);
}
.drag-drop-message p {
margin: 0;
font-size: 1rem;
}
.image-actions {
display: flex;
gap: 10px;
}
.image-preview {
max-width: 100%;
max-height: 200px;
border-radius: 4px;
margin-top: 10px;
display: none;
}
.banner-preview.with-image {
min-height: 220px;
}
.image-position-options {
display: flex;
gap: 8px;
margin-bottom: 10px;
}
.image-position-btn {
padding: 6px 12px;
background-color: #f8f9fa;
border: 1px solid var(--border-color);
border-radius: var(--border-radius);
font-size: 0.9rem;
cursor: pointer;
transition: var(--transition);
}
.image-position-btn:hover {
background-color: #e9ecef;
}
.image-position-btn.active {
background-color: var(--primary-color);
color: white;
border-color: var(--primary-color);
}
.draggable-image {
cursor: move;
position: relative;
z-index: 10;
transition: none;
user-select: none;
}
.draggable-image.dragging {
opacity: 0.8;
box-shadow: 0 0 10px rgba(0,0,0,0.2);
}
</style>
</head>
<body>
<div class="header">
<h1>Admin Dashboard</h1>
<button class="logout-btn" id="logoutBtn">Odhlásit se</button>
</div>
<div class="container">
<h2>Vítejte v administraci</h2>
<div class="card" style="margin: 2rem auto; max-width: 1000px;">
<h3>Správa banneru</h3>
<div class="card mb-4">
<div class="card-header">
<h5 class="mb-0">Nastavení banneru</h5>
</div>
<div class="card-body">
<form id="bannerForm">
<!-- Templates Section -->
<div class="mb-4">
<label class="form-label fw-bold">Vyberte šablonu</label>
<div class="template-grid mb-4">
<!-- Templates will be inserted here by JavaScript -->
</div>
</div>
</div>
<style>
.template-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 25px;
margin-top: 20px;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 15px;
margin-bottom: 20px;
}
.template-item {
border: 1px solid #ddd;
border-radius: 5px;
padding: 10px;
cursor: pointer;
transition: all 0.3s ease;
background: white;
}
.template-item:hover {
transform: translateY(-3px);
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
border-color: #007bff;
}
.template-item.active {
border: 2px solid #007bff;
background-color: #f0f8ff;
}
.template-preview {
width: 100%;
height: 120px;
margin-bottom: 8px;
overflow: hidden;
border: 1px solid #eee;
}
.template-item span {
display: block;
text-align: center;
font-size: 13px;
color: #333;
}
</style>
</div>
<div id="imagePreviewContainer" style="display: none; margin-top: 15px; text-align: center;">
<img id="imagePreview" style="max-width: 100%; max-height: 200px; border-radius: 4px;" />
</div>
<div class="form-actions">
<button type="submit" id="saveBannerBtn" class="btn btn-primary">
<i class="fas fa-save"></i> Uložit banner
</button>
</div>
</form>
<div class="dashboard-cards">
<div class="card">
<h3>Uživatelé</h3>
<p>Správa uživatelských účtů</p>
</div>
<div class="card">
<h3>Nastavení</h3>
<p>Konfigurace systému</p>
</div>
<div class="card">
<h3>Statistiky</h3>
<p>Přehled aktivit</p>
</div>
</div>
</div>
<div class="card">
<h3>Uživatelé</h3>
<p>Správa uživatelských účtů</p>
</div>
<div class="card">
<h3>Nastavení</h3>
<p>Konfigurace systému</p>
</div>
<div class="card">
<h3>Statistiky</h3>
<p>Přehled aktivit</p>
</div>
</div>
</div>
<script>
// Get token and check authentication
const token = localStorage.getItem('token');
if (!token) {
window.location.href = '/login.html';
}
// Show notification to user
function showNotification(message, type = 'info') {
const notification = document.createElement('div');
notification.className = `notification ${type}`;
// Set icon based on notification type
let icon = 'info-circle';
if (type === 'success') icon = 'check-circle';
else if (type === 'error') icon = 'exclamation-circle';
else if (type === 'warning') icon = 'exclamation-triangle';
notification.innerHTML = `
<i class="fas fa-${icon}"></i>
<span>${message}</span>
`;
document.body.appendChild(notification);
// Auto-remove notification after delay
const delay = type === 'error' ? 5000 : 3000;
setTimeout(() => {
notification.classList.add('fade-out');
setTimeout(() => notification.remove(), 300);
}, delay);
}
// Override fetch to include token (but NOT for FormData requests)
const originalFetch = window.fetch;
window.fetch = async function(resource, init = {}) {
// Add token to headers if it's an API request
if (typeof resource === 'string' && resource.startsWith('/api/')) {
const headers = new Headers(init.headers || {});
if (token) {
headers.set('Authorization', `Bearer ${token}`);
}
// Only set content type if not FormData (FormData sets its own)
if (!headers.has('Content-Type') && init.body && !(init.body instanceof FormData)) {
headers.set('Content-Type', 'application/json');
}
init.headers = headers;
}
return originalFetch(resource, init);
};
// Image handling - Drag and Drop functionality
let dragDropArea, uploadImageBtn, bannerImage;
// Prevent default behavior for drag events
function preventDefaults(e) {
e.preventDefault();
e.stopPropagation();
}
// Visual feedback when dragging over the area
function highlight() {
if (dragDropArea) dragDropArea.classList.add('dragover');
}
function unhighlight() {
if (dragDropArea) dragDropArea.classList.remove('dragover');
}
// Initialize drag and drop functionality
function initDragAndDrop() {
dragDropArea = document.getElementById('dragDropArea');
uploadImageBtn = document.getElementById('uploadImageBtn');
bannerImage = document.getElementById('bannerImage');
if (!dragDropArea || !uploadImageBtn || !bannerImage) return;
// Click on drag area to select file
dragDropArea.addEventListener('click', function() {
bannerImage.click();
});
// Click on upload button to select file
uploadImageBtn.addEventListener('click', function() {
bannerImage.click();
});
// Prevent default behavior for drag events
['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
dragDropArea.addEventListener(eventName, preventDefaults, false);
});
// Visual feedback when dragging over the area
['dragenter', 'dragover'].forEach(eventName => {
dragDropArea.addEventListener(eventName, highlight, false);
});
['dragleave', 'drop'].forEach(eventName => {
dragDropArea.addEventListener(eventName, unhighlight, false);
});
// Handle dropped files
dragDropArea.addEventListener('drop', handleDrop, false);
}
// Initialize when DOM is loaded
document.addEventListener('DOMContentLoaded', initDragAndDrop);
// Handle dropped files
function handleDrop(e) {
const dt = e.dataTransfer;
const files = dt.files;
if (files.length) {
bannerImage.files = files;
const event = new Event('change');
bannerImage.dispatchEvent(event);
}
}
// Handle image upload
function handleImageUpload(event) {
const fileInput = event.target;
const file = fileInput.files[0];
if (!file) return;
// Check file type
const validImageTypes = ['image/jpeg', 'image/png', 'image/gif', 'image/svg+xml'];
if (!validImageTypes.includes(file.type)) {
showNotification('Vyberte prosím soubor obrázku (JPG, PNG, GIF, SVG)', 'warning');
fileInput.value = ''; // Reset file input
return;
}
// Check file size (max 5MB)
const maxSize = 5 * 1024 * 1024; // 5MB
if (file.size > maxSize) {
showNotification('Maximální velikost souboru je 5MB', 'warning');
fileInput.value = ''; // Reset file input
return;
}
// Show loading state
const previewContainer = document.getElementById('imagePreviewContainer');
const dragDropMessage = document.querySelector('.drag-drop-message');
if (previewContainer) {
previewContainer.style.display = 'block';
previewContainer.innerHTML = '<div class="text-center py-4"><div class="spinner-border text-primary" role="status"><span class="visually-hidden">Načítání...</span></div></div>';
}
// Hide the drag & drop message
if (dragDropMessage) {
dragDropMessage.style.display = 'none';
}
// Process the image
const reader = new FileReader();
reader.onload = function(e) {
// Create a new image to get dimensions
const img = new Image();
img.onload = function() {
// Update the current image
currentImage = e.target.result;
// Update the preview
const preview = document.getElementById('imagePreview');
if (preview) {
preview.src = e.target.result;
preview.alt = 'Náhled obrázku';
preview.style.display = 'block';
}
// Show the remove button
const removeBtn = document.getElementById('removeImageBtn');
if (removeBtn) {
removeBtn.style.display = 'inline-block';
}
// Update hidden input
const removeImageInput = document.getElementById('removeImage');
if (removeImageInput) {
removeImageInput.value = 'false';
}
// Set default dimensions based on the image aspect ratio
const aspectRatio = img.width / img.height;
let defaultWidth = 300;
let defaultHeight = Math.round(defaultWidth / aspectRatio);
// Ensure height is within reasonable bounds
if (defaultHeight > 500) {
defaultHeight = 500;
defaultWidth = Math.round(defaultHeight * aspectRatio);
}
// Update dimension inputs
const widthInput = document.getElementById('bannerImageWidth');
const heightInput = document.getElementById('bannerImageHeight');
if (widthInput && heightInput) {
widthInput.value = defaultWidth;
heightInput.value = defaultHeight;
}
// Update the banner preview with the new image
updateBannerPreview();
// Show templates if not already shown
const bannerTemplates = document.getElementById('bannerTemplates');
if (bannerTemplates) {
bannerTemplates.style.display = 'block';
}
// Show size controls
const imageSizeControls = document.getElementById('imageSizeControls');
if (imageSizeControls) {
imageSizeControls.style.display = 'flex';
}
};
img.src = e.target.result;
};
reader.onerror = function() {
showNotification('Při načítání obrázku došlo k chybě. Zkuste to prosím znovu.', 'error');
fileInput.value = ''; // Reset file input
// Reset preview
if (previewContainer) {
previewContainer.innerHTML = '';
previewContainer.style.display = 'none';
}
// Show drag & drop message again
if (dragDropMessage) {
dragDropMessage.style.display = 'flex';
}
};
reader.readAsDataURL(file);
}
// Logout functionality
document.getElementById('logoutBtn').addEventListener('click', function() {
localStorage.removeItem('token');
window.location.href = '/';
});
// DOM Elements
const bannerText = document.getElementById('bannerText');
const bannerVisible = document.getElementById('bannerVisible');
const bannerBgColor = document.getElementById('bannerBgColor');
const bannerTextColor = document.getElementById('bannerTextColor');
const bannerTextAlign = document.getElementById('bannerTextAlign');
const bannerFontSize = document.getElementById('bannerFontSize');
const bannerPadding = document.getElementById('bannerPadding');
const bannerMargin = document.getElementById('bannerMargin');
const bannerBorderRadius = document.getElementById('bannerBorderRadius');
const bannerPreview = document.getElementById('bannerPreview');
const bannerPreviewContent = bannerPreview.querySelector('.banner-preview-content');
const bannerPreviewText = bannerPreview.querySelector('.banner-preview-text');
const bannerPreviewBg = bannerPreview.querySelector('.banner-preview-bg');
const bgColorPreview = document.getElementById('bgColorPreview');
const textColorPreview = document.getElementById('textColorPreview');
const saveBannerBtn = document.getElementById('saveBannerBtn');
const stylePresets = document.querySelectorAll('.style-preset');
let currentImage = null;
// Preset styles
const presets = {
info: {
backgroundColor: '#cce5ff',
textColor: '#004085',
textAlign: 'left'
},
warning: {
backgroundColor: '#fff3cd',
textColor: '#856404',
textAlign: 'center'
},
success: {
backgroundColor: '#d4edda',
textColor: '#155724',
textAlign: 'center'
},
error: {
backgroundColor: '#f8d7da',
textColor: '#721c24',
textAlign: 'center'
}
};
// Variables for image positioning
let currentImagePosition = 'center'; // default position
let currentImageX = '0';
let currentImageY = '0';
// Load current banner
async function loadBanner() {
try {
const response = await fetch('/api/banner');
if (!response.ok) {
throw new Error('Nepodařilo se načíst banner');
}
const data = await response.json();
console.log('Loaded banner data:', data);
if (data) {
// Update form fields
document.getElementById('bannerText').value = data.text || '';
document.getElementById('bannerBgColor').value = data.style?.backgroundColor || '#f8d7da';
document.getElementById('bannerTextColor').value = data.style?.color || '#721c24';
document.getElementById('bannerTextAlign').value = data.style?.textAlign || 'center';
document.getElementById('bannerFontSize').value = data.style?.fontSize || '18';
document.getElementById('bannerPadding').value = data.style?.padding || '20';
document.getElementById('bannerMargin').value = data.style?.margin || '20';
document.getElementById('bannerBorderRadius').value = data.style?.borderRadius || '8';
// Initialize image position variables once
currentImagePosition = data.style?.imagePosition || 'center';
currentImageX = data.style?.imageX || '0';
currentImageY = data.style?.imageY || '0';
// Apply the saved template if it exists
if (data.style?.template && templateConfigs[data.style.template]) {
// Apply the template
applyTemplate(data.style.template);
// Update the active template in the UI
const templateItems = document.querySelectorAll('.template-item');
templateItems.forEach(item => {
if (item.dataset.template === data.style.template) {
item.classList.add('active');
} else {
item.classList.remove('active');
}
});
} else {
// Fallback to default template if none is set
applyTemplate('modern-minimal');
}
// Handle image
const imagePreview = document.getElementById('imagePreview');
const imagePreviewContainer = document.getElementById('imagePreviewContainer');
const removeBtn = document.getElementById('removeImageBtn');
const removeImageInput = document.getElementById('removeImage');
if (data.image) {
currentImage = data.image;
if (imagePreview) {
imagePreview.src = data.image;
imagePreview.alt = 'Nahraný obrázek banneru';
}
if (imagePreviewContainer) {
imagePreviewContainer.style.display = 'block';
}
if (removeBtn) {
removeBtn.style.display = 'inline-block';
}
if (removeImageInput) {
removeImageInput.value = 'false';
}
} else {
// No image in the saved banner
currentImage = null;
if (imagePreview) {
imagePreview.removeAttribute('src');
}
if (imagePreviewContainer) {
imagePreviewContainer.style.display = 'none';
}
if (removeBtn) {
removeBtn.style.display = 'none';
}
if (removeImageInput) {
removeImageInput.value = 'true';
}
}
// Update previews
updateColorPreviews();
updateBannerPreview();
}
} catch (error) {
console.error('Chyba při načítání banneru:', error);
showNotification('Chyba při načítání banneru', 'error');
}
}
// Add submission flag at the top of the script
let isSubmitting = false;
async function saveBanner(event) {
event.preventDefault();
// Prevent multiple submissions
if (isSubmitting) {
console.log('Form submission already in progress');
return;
}
isSubmitting = true;
const form = document.getElementById('bannerForm');
const formData = new FormData(form);
const submitButton = form.querySelector('button[type="submit"]');
const originalButtonText = submitButton ? submitButton.innerHTML : '';
try {
// Show loading state
if (submitButton) {
submitButton.disabled = true;
submitButton.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Ukládám...';
}
// Add text and link to form data
formData.append('text', document.getElementById('bannerText').value || '');
formData.append('link', document.getElementById('bannerLink').value || '');
formData.append('isVisible', document.getElementById('bannerVisible').checked);
// Get the current template or use default
const template = currentTemplate ? templateConfigs[currentTemplate] : templateConfigs['modern-minimal'];
// Add template data
if (template) {
formData.append('template', currentTemplate || 'modern-minimal');
// Add template styles
formData.append('style[backgroundColor]', template.bgColor || '#f8f9fa');
formData.append('style[textColor]', template.textColor || '#212529');
formData.append('style[textAlign]', template.textAlign || 'left');
formData.append('style[fontSize]', template.fontSize ? `${template.fontSize}px` : '16px');
formData.append('style[padding]', template.padding ? `${template.padding}px` : '20px');
formData.append('style[margin]', template.margin ? `${template.margin}px` : '20px');
formData.append('style[borderRadius]', template.borderRadius ? `${template.borderRadius}px` : '8px');
}
// Add image dimensions if available
const imageWidth = document.getElementById('bannerImageWidth');
const imageHeight = document.getElementById('bannerImageHeight');
if (imageWidth && imageHeight) {
formData.append('imageWidth', imageWidth.value || '300');
formData.append('imageHeight', imageHeight.value || '200');
}
// Add current image if available
if (currentImage) {
// If currentImage is a data URL, we need to convert it to a file
if (currentImage.startsWith('data:image')) {
const blob = await fetch(currentImage).then(r => r.blob());
formData.append('image', blob, 'banner-image.jpg');
} else {
// It's already a file, just append it
const fileInput = document.getElementById('bannerImage');
if (fileInput.files.length > 0) {
formData.append('image', fileInput.files[0]);
}
}
}
// Log form data for debugging (without the actual file data)
console.log('Odesílám data:');
for (let [key, value] of formData.entries()) {
console.log(key, value instanceof File ? `[File ${value.name}]` : value);
}
// Prepare headers
const headers = {};
// Add Authorization header if token exists
const token = localStorage.getItem('token');
if (token) {
headers['Authorization'] = `Bearer ${token}`;
}
// Send request with FormData (browser will set correct Content-Type with boundary)
const response = await fetch('/api/banner/update', {
method: 'POST',
headers: headers,
body: formData
});
if (!response.ok) {
const errorText = await response.text().catch(() => 'Neznámá chyba serveru');
console.error('Server error:', errorText);
let errorMessage = 'Chyba při ukládání banneru';
try {
const errorData = JSON.parse(errorText);
errorMessage = errorData.message || errorMessage;
} catch (e) {
errorMessage = errorText || errorMessage;
}
throw new Error(errorMessage);
}
const result = await response.json().catch(() => ({}));
// Show success message
showNotification('Banner byl úspěšně uložen', 'success');
// Update the preview with the new banner data
if (result.imageUrl) {
currentImage = result.imageUrl;
const imagePreview = document.getElementById('imagePreview');
const imagePreviewContainer = document.getElementById('imagePreviewContainer');
const removeBtn = document.getElementById('removeImageBtn');
if (imagePreview) imagePreview.src = currentImage;
if (imagePreviewContainer) imagePreviewContainer.style.display = 'block';
if (removeBtn) removeBtn.style.display = 'inline-block';
// Update the hidden input if the image was changed
const removeImageInput = document.getElementById('removeImage');
if (removeImageInput) removeImageInput.value = 'false';
// Show templates and size controls since we have an image
const bannerTemplates = document.getElementById('bannerTemplates');
const imageSizeControls = document.getElementById('imageSizeControls');
if (bannerTemplates) bannerTemplates.style.display = 'block';
if (imageSizeControls) imageSizeControls.style.display = 'flex';
}
// Update the preview
updateBannerPreview();
} catch (error) {
console.error('Chyba při ukládání banneru:', error);
showNotification(error.message || 'Nepodařilo se uložit banner', 'error');
} finally {
// Reset button state
if (submitButton) {
submitButton.disabled = false;
submitButton.innerHTML = originalButtonText;
}
isSubmitting = false;
}
}
// Update color previews
function updateColorPreviews() {
const bgColorPreview = document.getElementById('bgColorPreview');
const textColorPreview = document.getElementById('textColorPreview');
const bgColorPicker = document.getElementById('bannerBgColorPicker');
const textColorPicker = document.getElementById('bannerTextColorPicker');
const bgColorInput = document.getElementById('bannerBgColor');
const textColorInput = document.getElementById('bannerTextColor');
if (bgColorPreview && bgColorInput) {
bgColorPreview.style.backgroundColor = bgColorInput.value;
}
if (textColorPreview && textColorInput) {
textColorPreview.style.backgroundColor = textColorInput.value;
}
if (bgColorPicker && bgColorInput) {
bgColorPicker.value = bgColorInput.value;
}
if (textColorPicker && textColorInput) {
textColorPicker.value = textColorInput.value;
}
}
// Remove image
function removeImage() {
const fileInput = document.getElementById('bannerImage');
const preview = document.getElementById('imagePreview');
const previewContainer = document.getElementById('imagePreviewContainer');
const removeBtn = document.getElementById('removeImageBtn');
const dragDropMessage = document.querySelector('.drag-drop-message');
const removeImageInput = document.getElementById('removeImage');
const bannerTemplates = document.getElementById('bannerTemplates');
const imageSizeControls = document.getElementById('imageSizeControls');
// Reset file input
if (fileInput) {
fileInput.value = '';
}
// Clear preview
if (preview) {
preview.src = '';
preview.style.display = 'none';
}
// Hide preview container and show upload message
if (previewContainer) {
previewContainer.style.display = 'none';
previewContainer.innerHTML = '';
}
// Hide remove button
if (removeBtn) {
removeBtn.style.display = 'none';
}
// Show drag & drop message
if (dragDropMessage) {
dragDropMessage.style.display = 'flex';
}
// Hide templates when no image is present
if (bannerTemplates) {
bannerTemplates.style.display = 'none';
}
// Hide size controls
if (imageSizeControls) {
imageSizeControls.style.display = 'none';
}
// Update hidden input to indicate image removal
if (removeImageInput) {
removeImageInput.value = 'true';
}
// Clear current image
currentImage = null;
// Update banner preview
updateBannerPreview();
// Show message
showNotification('Obrázek byl odstraněn', 'info');
// Trigger change event on the file input
const bannerImage = document.getElementById('bannerImage');
if (bannerImage) {
const event = new Event('change');
bannerImage.dispatchEvent(event);
}
}
// Update banner preview
function updateBannerPreview() {
const bannerPreview = document.getElementById('bannerPreview');
const bannerPreviewContent = document.getElementById('bannerPreviewContent');
const bannerText = document.getElementById('bannerText')?.value || '';
const bannerVisible = document.getElementById('bannerVisible')?.checked !== false;
const bannerTemplates = document.getElementById('bannerTemplates');
const imagePreview = document.getElementById('imagePreview');
const imagePreviewContainer = document.getElementById('imagePreviewContainer');
const imageSizeControls = document.getElementById('imageSizeControls');
// Get the current template config or use default
const template = currentTemplate ? templateConfigs[currentTemplate] : templateConfigs['modern-minimal'];
const hasImage = currentImage || (bannerImage && bannerImage.files.length > 0);
// Show/hide templates and size controls based on image presence
if (bannerTemplates) {
bannerTemplates.style.display = hasImage ? 'block' : 'none';
}
if (imageSizeControls) {
imageSizeControls.style.display = hasImage ? 'flex' : 'none';
}
// Create banner content based on template
let bannerContent = '';
if (hasImage && currentImage) {
// Get image dimensions from inputs or use defaults
const imageWidth = parseInt(document.getElementById('bannerImageWidth')?.value || '300');
const imageHeight = parseInt(document.getElementById('bannerImageHeight')?.value || '200');
// Create image element with template styles and dynamic dimensions
const imgStyle = `
${template.imageStyle || ''}
width: ${imageWidth}px;
height: ${imageHeight}px;
object-fit: cover;
border-radius: ${template.borderRadius || '0'}px;
`;
const imgElement = `
<img
src="${currentImage}"
style="${imgStyle}"
alt="Nahraný obrázek"
class="banner-image"
>`;
// Create text content with template styles
const textContent = bannerText || 'Náhled banneru';
const textElement = `
<div class="banner-text" style="${template.textStyle || ''}">
${textContent}
</div>`;
// Create container with template styles
bannerContent = `
<div class="banner-content" style="${template.containerStyle || ''}">
${imgElement}
${textElement}
</div>`;
// Show the image preview in the container
if (imagePreview && imagePreviewContainer) {
imagePreview.src = currentImage;
imagePreview.style.width = '100%';
imagePreview.style.height = 'auto';
imagePreview.style.maxHeight = '200px';
imagePreviewContainer.style.display = 'block';
}
// Add the with-image class to the banner preview for proper spacing
bannerPreview.classList.add('with-image');
} else {
// No image, just show text
bannerContent = `
<div class="banner-content" style="
${template.containerStyle || ''}
display: flex;
align-items: center;
justify-content: center;
padding: 30px;
min-height: 200px;
">
<div class="banner-text" style="
text-align: center;
color: #666;
font-style: italic;
${template.textStyle || ''}
">
${bannerText || 'Náhled banneru (pro zobrazení šablon nahrajte obrázek)'}
</div>
</div>`;
// Hide image preview container if no image
if (imagePreviewContainer) {
imagePreviewContainer.style.display = 'none';
}
bannerPreview.classList.remove('with-image');
}
// Apply template styles to the banner preview
if (template) {
Object.assign(bannerPreview.style, {
backgroundColor: template.bgColor || '#f8f9fa',
color: template.textColor || '#212529',
textAlign: template.textAlign || 'left',
fontSize: `${template.fontSize || 16}px`,
padding: `${template.padding || 20}px`,
margin: `${template.margin || 20}px 0`,
borderRadius: `${template.borderRadius || 8}px`,
boxShadow: '0 2px 4px rgba(0,0,0,0.1)',
display: 'block',
overflow: 'hidden',
width: '100%',
maxWidth: '1200px',
transition: 'all 0.3s ease',
position: 'relative'
});
}
// Update the banner content with the generated HTML
bannerPreviewContent.innerHTML = bannerContent;
// Add event listeners for width/height changes
const imageWidthInput = document.getElementById('bannerImageWidth');
const imageHeightInput = document.getElementById('bannerImageHeight');
if (imageWidthInput) {
imageWidthInput.value = template.imageWidth || 300;
imageWidthInput.addEventListener('input', updateBannerPreview);
}
if (imageHeightInput) {
imageHeightInput.value = template.imageHeight || 200;
imageHeightInput.addEventListener('input', updateBannerPreview);
}
// Make sure the preview is visible
bannerPreview.style.visibility = 'visible';
}
// Apply preset
function applyPreset(preset) {
const style = presets[preset];
if (!style) return;
bannerBgColor.value = style.backgroundColor;
bannerTextColor.value = style.textColor;
bannerTextAlign.value = style.textAlign;
// Update color pickers to match input fields
bannerBgColorPicker.value = style.backgroundColor;
bannerTextColorPicker.value = style.textColor;
updateColorPreviews();
updateBannerPreview();
}
// Event Listeners
// Debounced update for text inputs
const debouncedUpdatePreview = debounce(() => {
updateColorPreviews();
updateBannerPreview();
}, 300);
bannerBgColor.addEventListener('input', () => {
// Update color preview immediately
const bgColorPreview = document.getElementById('bgColorPreview');
if (bgColorPreview) {
bgColorPreview.style.backgroundColor = bannerBgColor.value;
}
// Debounce the full preview update
debouncedUpdatePreview();
});
bannerTextColor.addEventListener('input', () => {
// Update color preview immediately
const textColorPreview = document.getElementById('textColorPreview');
if (textColorPreview) {
textColorPreview.style.backgroundColor = bannerTextColor.value;
}
// Debounce the full preview update
debouncedUpdatePreview();
});
// Connect color pickers to input fields
const bannerBgColorPicker = document.getElementById('bannerBgColorPicker');
const bannerTextColorPicker = document.getElementById('bannerTextColorPicker');
// Debounce function to improve performance
function debounce(func, wait) {
let timeout;
return function(...args) {
const context = this;
clearTimeout(timeout);
timeout = setTimeout(() => func.apply(context, args), wait);
};
}
// These event listeners will be set up after the DOM is fully loaded
// Setup draggable image functionality
function setupDraggableImage() {
const draggableImage = document.querySelector('.draggable-image');
if (!draggableImage) return;
// Remove any existing event listeners to prevent duplicates
const newDraggable = draggableImage.cloneNode(true);
draggableImage.parentNode.replaceChild(newDraggable, draggableImage);
let isDragging = false;
let startX, startY;
let originalX = parseInt(currentImageX) || 0;
let originalY = parseInt(currentImageY) || 0;
// Mouse events for desktop
newDraggable.addEventListener('mousedown', startDrag);
document.addEventListener('mousemove', drag);
document.addEventListener('mouseup', endDrag);
// Touch events for mobile
newDraggable.addEventListener('touchstart', startDragTouch);
document.addEventListener('touchmove', dragTouch);
document.addEventListener('touchend', endDrag);
function startDrag(e) {
e.preventDefault();
isDragging = true;
startX = e.clientX;
startY = e.clientY;
newDraggable.classList.add('dragging');
console.log('Started dragging at', startX, startY);
}
function startDragTouch(e) {
if (e.touches.length === 1) {
isDragging = true;
startX = e.touches[0].clientX;
startY = e.touches[0].clientY;
newDraggable.classList.add('dragging');
}
}
function drag(e) {
if (!isDragging) return;
const deltaX = e.clientX - startX;
const deltaY = e.clientY - startY;
const newX = originalX + deltaX;
const newY = originalY + deltaY;
newDraggable.style.left = `${newX}px`;
newDraggable.style.top = `${newY}px`;
// Update current position values
currentImageX = newX.toString();
currentImageY = newY.toString();
console.log('Dragging to', newX, newY);
}
function dragTouch(e) {
if (!isDragging || e.touches.length !== 1) return;
const deltaX = e.touches[0].clientX - startX;
const deltaY = e.touches[0].clientY - startY;
const newX = originalX + deltaX;
const newY = originalY + deltaY;
newDraggable.style.left = `${newX}px`;
newDraggable.style.top = `${newY}px`;
// Update current position values
currentImageX = newX.toString();
currentImageY = newY.toString();
}
function endDrag() {
if (!isDragging) return;
isDragging = false;
originalX = parseInt(currentImageX);
originalY = parseInt(currentImageY);
newDraggable.classList.remove('dragging');
console.log('Finished dragging at', originalX, originalY);
// Update preview with new position
updateBannerPreview();
}
}
// Template configurations - 10 comprehensive presets
const templateConfigs = {
'modern-minimal': {
name: 'Moderní minimalistický',
containerStyle: 'display: flex; align-items: center; justify-content: space-between; padding: 40px;',
imageStyle: 'width: 40%; border-radius: 8px; box-shadow: 0 4px 12px rgba(0,0,0,0.1);',
textStyle: 'width: 55%; padding: 20px;',
bgColor: '#ffffff',
textColor: '#333333',
textAlign: 'left',
fontSize: '16',
padding: '30',
margin: '20',
borderRadius: '8'
},
'dark-elegant': {
name: 'Tmavý elegantní',
containerStyle: 'display: flex; align-items: center; justify-content: space-between; padding: 30px; background: linear-gradient(135deg, #2c3e50 0%, #1a252f 100%);',
imageStyle: 'width: 35%; border-radius: 4px; border: 2px solid rgba(255,255,255,0.1);',
textStyle: 'width: 60%; padding: 20px; color: #ffffff;',
bgColor: '#2c3e50',
textColor: '#ffffff',
textAlign: 'right',
fontSize: '18',
padding: '30',
margin: '15',
borderRadius: '4'
},
'vibrant-cta': {
name: 'Výrazný CTA',
containerStyle: 'text-align: center; padding: 40px 20px; background: linear-gradient(45deg, #ff6b6b, #ff8e53);',
imageStyle: 'max-width: 200px; margin: 0 auto 20px; border-radius: 50%; border: 4px solid white;',
textStyle: 'color: white; padding: 20px;',
bgColor: '#ff6b6b',
textColor: '#ffffff',
textAlign: 'center',
fontSize: '20',
padding: '40',
margin: '10',
borderRadius: '0'
},
'corporate-clean': {
name: 'Čistý korporátní',
containerStyle: 'display: grid; grid-template-columns: 1fr 1.5fr; gap: 30px; padding: 30px; background: #f8f9fa;',
imageStyle: 'width: 100%; height: 100%; object-fit: cover; border-radius: 4px;',
textStyle: 'padding: 15px 0;',
bgColor: '#f8f9fa',
textColor: '#495057',
textAlign: 'left',
fontSize: '16',
padding: '30',
margin: '20',
borderRadius: '4'
},
'nature-organic': {
name: 'Přírodní organický',
containerStyle: 'position: relative; min-height: 300px; background: linear-gradient(rgba(255,255,255,0.9), rgba(255,255,255,0.9));',
imageStyle: 'position: absolute; width: 100%; height: 100%; object-fit: cover; z-index: -1; opacity: 0.9;',
textStyle: 'position: relative; z-index: 1; padding: 40px; max-width: 60%;',
bgColor: '#f1f8e9',
textColor: '#2e7d32',
textAlign: 'left',
fontSize: '18',
padding: '40',
margin: '10',
borderRadius: '0'
},
'bold-contrast': {
name: 'Výrazný kontrast',
containerStyle: 'display: flex; min-height: 250px;',
imageStyle: 'width: 50%; object-fit: cover;',
textStyle: 'width: 50%; padding: 30px; display: flex; flex-direction: column; justify-content: center;',
bgColor: '#212529',
textColor: '#f8f9fa',
textAlign: 'left',
fontSize: '18',
padding: '30',
margin: '0',
borderRadius: '0'
},
'minimal-card': {
name: 'Minimalistická karta',
containerStyle: 'max-width: 800px; margin: 0 auto; padding: 30px; box-shadow: 0 5px 15px rgba(0,0,0,0.1); border-radius: 12px;',
imageStyle: 'width: 100%; border-radius: 8px; margin-bottom: 20px;',
textStyle: 'padding: 10px 0;',
bgColor: '#ffffff',
textColor: '#333333',
textAlign: 'center',
fontSize: '16',
padding: '30',
margin: '20',
borderRadius: '12'
},
'split-screen': {
name: 'Rozdělená obrazovka',
containerStyle: 'display: grid; grid-template-columns: 1fr 1fr; min-height: 400px;',
imageStyle: 'width: 100%; height: 100%; object-fit: cover;',
textStyle: 'padding: 40px; display: flex; flex-direction: column; justify-content: center;',
bgColor: '#ffffff',
textColor: '#333333',
textAlign: 'left',
fontSize: '16',
padding: '40',
margin: '0',
borderRadius: '0'
},
'hero-banner': {
name: 'Hero banner',
containerStyle: 'position: relative; min-height: 500px; display: flex; align-items: center; justify-content: center; text-align: center; color: white;',
imageStyle: 'position: absolute; width: 100%; height: 100%; object-fit: cover; z-index: -1;',
textStyle: 'max-width: 800px; padding: 20px; z-index: 1; text-shadow: 0 2px 4px rgba(0,0,0,0.3);',
bgColor: '#1a237e',
textColor: '#ffffff',
textAlign: 'center',
fontSize: '24',
padding: '20',
margin: '0',
borderRadius: '0'
},
'floating-card': {
name: 'Plovoucí karta',
containerStyle: 'background: white; border-radius: 16px; overflow: hidden; box-shadow: 0 10px 30px rgba(0,0,0,0.1);',
imageStyle: 'width: 100%; height: 200px; object-fit: cover;',
textStyle: 'padding: 30px;',
bgColor: '#ffffff',
textColor: '#333333',
textAlign: 'center',
fontSize: '16',
padding: '30',
margin: '20',
borderRadius: '16'
}
};
// Setup template selection and generate template previews
document.addEventListener('DOMContentLoaded', () => {
const templateGrid = document.querySelector('.template-grid');
// Get references to form elements
const bannerBgColorPicker = document.getElementById('bannerBgColorPicker');
const bannerBgColor = document.getElementById('bannerBgColor');
const bannerTextColorPicker = document.getElementById('bannerTextColorPicker');
const bannerTextColor = document.getElementById('bannerTextColor');
const bannerText = document.getElementById('bannerText');
const bannerTextAlign = document.getElementById('bannerTextAlign');
const bannerFontSize = document.getElementById('bannerFontSize');
const bannerPadding = document.getElementById('bannerPadding');
const bannerMargin = document.getElementById('bannerMargin');
const bannerBorderRadius = document.getElementById('bannerBorderRadius');
const bannerVisible = document.getElementById('bannerVisible');
const stylePresets = document.querySelectorAll('.style-preset');
const saveBannerBtn = document.getElementById('saveBannerBtn');
// Set up color picker event listeners if elements exist
if (bannerBgColorPicker && bannerBgColor) {
// Update only the color preview during dragging (lightweight)
bannerBgColorPicker.addEventListener('input', () => {
bannerBgColor.value = bannerBgColorPicker.value;
const bgColorPreview = document.getElementById('bgColorPreview');
if (bgColorPreview) {
bgColorPreview.style.backgroundColor = bannerBgColorPicker.value;
}
});
// Update the full preview only when the user has finished selecting a color
bannerBgColorPicker.addEventListener('change', () => {
updateColorPreviews();
updateBannerPreview();
});
}
if (bannerTextColorPicker && bannerTextColor) {
// Same for text color picker
bannerTextColorPicker.addEventListener('input', () => {
bannerTextColor.value = bannerTextColorPicker.value;
const textColorPreview = document.getElementById('textColorPreview');
if (textColorPreview) {
textColorPreview.style.backgroundColor = bannerTextColorPicker.value;
}
});
bannerTextColorPicker.addEventListener('change', () => {
updateColorPreviews();
updateBannerPreview();
});
}
// For other form controls, use debounced updates on input
const formControls = [
bannerText, bannerTextAlign, bannerFontSize,
bannerPadding, bannerMargin, bannerBorderRadius, bannerVisible
].filter(Boolean); // Filter out any null/undefined elements
formControls.forEach(el => {
if (el) {
el.addEventListener('change', updateBannerPreview);
el.addEventListener('input', debouncedUpdatePreview);
}
});
// Style presets
if (stylePresets.length > 0) {
stylePresets.forEach(preset => {
preset.addEventListener('click', () => applyPreset(preset.dataset.preset));
});
}
// Save button
if (saveBannerBtn) {
saveBannerBtn.addEventListener('click', saveBanner);
}
// Generate template previews
if (templateGrid) {
templateGrid.innerHTML = ''; // Clear any existing templates
// Create a preview for each template
Object.entries(templateConfigs).forEach(([id, template]) => {
const templateItem = document.createElement('div');
templateItem.className = 'template-item';
templateItem.dataset.template = id;
// Create preview container with the template's container style
const preview = document.createElement('div');
preview.className = 'template-preview';
preview.style.cssText = template.containerStyle;
// Create image placeholder
const imgPlaceholder = document.createElement('div');
imgPlaceholder.className = 'template-img-placeholder';
imgPlaceholder.style.cssText = template.imageStyle;
// Create text placeholder
const textPlaceholder = document.createElement('div');
textPlaceholder.className = 'template-text-placeholder';
textPlaceholder.style.cssText = template.textStyle;
// Add placeholders to preview
preview.appendChild(imgPlaceholder);
preview.appendChild(textPlaceholder);
// Create template name
const name = document.createElement('div');
name.className = 'template-name';
name.textContent = template.name;
// Create badge for template type
const badge = document.createElement('div');
badge.className = 'template-badge';
badge.textContent = 'Doporučeno';
// Add elements to template item
templateItem.appendChild(badge);
templateItem.appendChild(preview);
templateItem.appendChild(name);
// Add to grid
templateGrid.appendChild(templateItem);
});
}
// Setup event listeners
function setupEventListeners() {
// Banner form submission
const bannerForm = document.getElementById('bannerForm');
if (bannerForm) {
bannerForm.addEventListener('submit', saveBanner);
}
// Image upload
const bannerImage = document.getElementById('bannerImage');
if (bannerImage) {
bannerImage.addEventListener('change', handleImageUpload);
}
// Remove image button
const removeImageBtn = document.getElementById('removeImageBtn');
if (removeImageBtn) {
removeImageBtn.addEventListener('click', removeImage);
}
// Template selection
const templateItems = document.querySelectorAll('.template-item');
templateItems.forEach(item => {
item.addEventListener('click', function() {
const templateId = this.dataset.template;
if (templateId) {
applyTemplate(templateId);
}
});
});
// Image size controls
const sizeInputs = document.querySelectorAll('#bannerImageWidth, #bannerImageHeight');
sizeInputs.forEach(input => {
input.addEventListener('input', updateBannerPreview);
});
// Preview updates for text content
const previewInputs = [
document.getElementById('bannerText'),
document.getElementById('bannerLink'),
document.getElementById('bannerVisible')
];
previewInputs.forEach(input => {
if (input) {
input.addEventListener('input', updateBannerPreview);
input.addEventListener('change', updateBannerPreview);
}
});
// Checkbox for banner visibility
const bannerVisible = document.getElementById('bannerVisible');
if (bannerVisible) {
bannerVisible.addEventListener('change', updateBannerPreview);
}
// Initialize with default template if no template is selected
if (!currentTemplate) {
currentTemplate = 'modern-minimal';
const defaultTemplate = document.querySelector(`[data-template="${currentTemplate}"]`);
if (defaultTemplate) {
defaultTemplate.classList.add('active');
}
}
// Initial preview update
updateBannerPreview();
// Show/hide templates and size controls based on image presence
const bannerTemplates = document.getElementById('bannerTemplates');
const imageSizeControls = document.getElementById('imageSizeControls');
const hasImage = currentImage || (bannerImage && bannerImage.files.length > 0);
if (bannerTemplates) {
bannerTemplates.style.display = hasImage ? 'block' : 'none';
}
if (imageSizeControls) {
imageSizeControls.style.display = hasImage ? 'flex' : 'none';
}
}
setupEventListeners();
// Show templates when image is uploaded
const bannerImage = document.getElementById('bannerImage');
if (bannerImage) {
bannerImage.addEventListener('change', () => {
const templates = document.getElementById('bannerTemplates');
if (templates) {
templates.style.display = 'block';
}
});
}
loadBanner();
});
// Apply selected template and update form fields
function applyTemplate(templateId) {
const template = templateConfigs[templateId];
if (!template) return;
// Store the selected template
currentTemplate = templateId;
// Update the banner preview with the new template
updateBannerPreview();
// Update active state of template items
const templateItems = document.querySelectorAll('.template-item');
templateItems.forEach(item => {
if (item.dataset.template === templateId) {
item.classList.add('active');
} else {
item.classList.remove('active');
}
});
// Show success message
showNotification(`Šablona "${template.name}" byla použita`, 'success');
// Scroll to preview to show the changes
const bannerPreview = document.getElementById('bannerPreview');
if (bannerPreview) {
bannerPreview.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
}
}
</script>
</body>
</html>