This commit is contained in:
Tomas Dvorak
2025-05-28 22:17:14 +02:00
parent 0cff3b9b44
commit 955906178c
+133 -108
View File
@@ -583,10 +583,21 @@
<div class="card" style="margin: 2rem auto; max-width: 1000px;"> <div class="card" style="margin: 2rem auto; max-width: 1000px;">
<h3>Správa banneru</h3> <h3>Správa banneru</h3>
<div class="card mb-4"> <div class="form-group">
<div class="card-header"> <div class="form-check form-switch mb-3">
<h5 class="mb-0">Nastavení banneru</h5> <input class="form-check-input" type="checkbox" id="bannerVisibility" checked>
<label class="form-check-label" for="bannerVisibility">Viditelnost banneru</label>
</div> </div>
<label for="bannerText">Text banneru:</label>
<textarea id="bannerText" class="form-control" rows="3" placeholder="Zadejte text banneru..."></textarea>
<div class="mt-3">
<label for="bannerLink">Odkaz (volitelný):</label>
<input type="url" id="bannerLink" class="form-control" placeholder="https://example.com">
</div>
</div>
<div class="card-body"> <div class="card-body">
<form id="bannerForm"> <form id="bannerForm">
<!-- Templates Section --> <!-- Templates Section -->
@@ -596,66 +607,52 @@
<!-- Templates will be inserted here by JavaScript --> <!-- Templates will be inserted here by JavaScript -->
</div> </div>
</div> </div>
<div class="form-group">
<label>Obrázek banneru:</label>
<div id="dropArea" class="drop-zone">
<input type="file" id="fileElem" accept="image/*" class="d-none">
<img id="bannerImagePreview" src="" alt="Náhled obrázku" class="img-preview d-none">
<div class="drop-zone-content">
<i class="fas fa-cloud-upload-alt fa-3x mb-2"></i>
<p>Přetáhněte obrázek sem nebo klikněte pro nahrání</p>
<button type="button" class="btn btn-outline-primary btn-sm" id="uploadBtn">Vybrat soubor</button>
</div>
</div>
<div class="mt-2">
<button type="button" class="btn btn-outline-danger btn-sm" id="removeImageBtn" style="display: none;">
<i class="fas fa-trash"></i> Odstranit obrázek
</button>
</div> </div>
<style> <!-- Image position controls -->
.template-grid { <div class="mt-3">
display: grid; <label>Pozice obrázku:</label>
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); <div class="btn-group w-100" role="group">
gap: 25px; <button type="button" class="btn btn-outline-secondary image-pos-btn" data-pos="left">
margin-top: 20px; <i class="fas fa-align-left"></i> Vlevo
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); </button>
gap: 15px; <button type="button" class="btn btn-outline-secondary image-pos-btn" data-pos="center">
margin-bottom: 20px; <i class="fas fa-align-center"></i> Uprostřed
} </button>
<button type="button" class="btn btn-outline-secondary image-pos-btn" data-pos="right">
.template-item { <i class="fas fa-align-right"></i> Vpravo
border: 1px solid #ddd; </button>
border-radius: 5px; <button type="button" class="btn btn-outline-primary image-pos-btn" data-pos="custom" id="customPosBtn">
padding: 10px; <i class="fas fa-hand-pointer"></i> Vlastní
cursor: pointer; </button>
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>
<small class="text-muted">Pro vlastní pozici přetáhněte obrázek myší</small>
<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>
</div>
</form>
<div class="form-actions"> <div class="form-actions">
<button type="submit" id="saveBannerBtn" class="btn btn-primary"> <button type="submit" id="saveBannerBtn" class="btn btn-primary">
<i class="fas fa-save"></i> Uložit banner <i class="fas fa-save"></i> Uložit banner
</button> </button>
</div> </div>
</form> </div>
<div class="dashboard-cards"> <div class="dashboard-cards">
<div class="card"> <div class="card">
@@ -742,7 +739,7 @@ window.fetch = async function(resource, init = {}) {
}; };
// Image handling - Drag and Drop functionality // Image handling - Drag and Drop functionality
let dragDropArea, uploadImageBtn, bannerImage; let dragDropArea, uploadImageBtn, bannerImageElement;
// Prevent default behavior for drag events // Prevent default behavior for drag events
function preventDefaults(e) { function preventDefaults(e) {
@@ -1011,36 +1008,23 @@ const presets = {
let currentImagePosition = 'center'; // default position let currentImagePosition = 'center'; // default position
let currentImageX = '0'; let currentImageX = '0';
let currentImageY = '0'; let currentImageY = '0';
// Load current banner
async function loadBanner() { async function loadBanner() {
try { try {
const response = await fetch('/api/banner'); const response = await fetch('/api/banner');
if (!response.ok) { if (!response.ok) throw new Error('Nepodařilo se načíst banner');
throw new Error('Nepodařilo se načíst banner');
}
const data = await response.json(); const data = await response.json();
console.log('Loaded banner data:', data); console.log('Loaded banner data:', data);
if (data) { if (data) {
// Update form fields // Update form fields
const bannerText = document.getElementById('bannerText'); const bannerText = document.getElementById('bannerText');
const bannerBgColor = document.getElementById('bannerBgColor'); const bannerLink = document.getElementById('bannerLink');
const bannerTextColor = document.getElementById('bannerTextColor'); const bannerVisible = document.getElementById('bannerVisible');
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');
if (bannerText) bannerText.value = data.text || ''; if (bannerText) bannerText.value = data.text || '';
if (bannerBgColor) bannerBgColor.value = data.style?.backgroundColor || '#f8d7da'; if (bannerLink) bannerLink.value = data.link || '';
if (bannerTextColor) bannerTextColor.value = data.style?.color || '#721c24'; if (bannerVisible) bannerVisible.checked = data.style?.isVisible !== false;
if (bannerTextAlign) bannerTextAlign.value = data.style?.textAlign || 'center';
if (bannerFontSize) bannerFontSize.value = data.style?.fontSize || '18';
if (bannerPadding) bannerPadding.value = data.style?.padding || '20';
if (bannerMargin) bannerMargin.value = data.style?.margin || '20';
if (bannerBorderRadius) bannerBorderRadius.value = data.style?.borderRadius || '8';
// Initialize image position variables once // Initialize image position variables once
currentImagePosition = data.style?.imagePosition || 'center'; currentImagePosition = data.style?.imagePosition || 'center';
@@ -1074,22 +1058,54 @@ async function loadBanner() {
if (data.image) { if (data.image) {
currentImage = data.image; currentImage = data.image;
const bannerImgElement = document.getElementById('bannerImagePreview');
if (bannerImgElement) {
const bannerLinkValue = document.getElementById('bannerLink')?.value || '';
if (bannerLinkValue) {
bannerImgElement.style.cursor = 'pointer';
bannerImgElement.onclick = (e) => {
e.preventDefault();
window.open(bannerLinkValue, '_blank');
};
} else {
bannerImgElement.style.cursor = 'default';
bannerImgElement.onclick = null;
}
}
if (imagePreview) { if (imagePreview) {
imagePreview.src = data.image; imagePreview.src = data.image;
imagePreview.alt = 'Nahraný obrázek banneru'; imagePreview.classList.remove('d-none');
// Update position if exists
if (data.style?.imagePosition) {
currentImagePosition = data.style.imagePosition;
imagePreview.dataset.position = currentImagePosition;
// Update active state of position buttons
document.querySelectorAll('.image-pos-btn').forEach(btn => {
btn.classList.remove('active');
if (btn.dataset.pos === currentImagePosition) {
btn.classList.add('active');
}
});
} }
if (imagePreviewContainer) { // Update coordinates if they exist
imagePreviewContainer.style.display = 'block'; if (data.style?.imageX !== undefined && data.style?.imageY !== undefined) {
} currentImageX = data.style.imageX;
currentImageY = data.style.imageY;
imagePreview.style.left = `${currentImageX}px`;
imagePreview.style.top = `${currentImageY}px`;
if (removeBtn) { // Activate custom position
removeBtn.style.display = 'inline-block'; document.getElementById('customPosBtn').classList.add('active');
}
} }
if (removeImageInput) { if (removeImageInput) {
removeImageInput.value = 'false'; removeImageInput.value = 'true';
} }
} else { } else {
// No image in the saved banner // No image in the saved banner
@@ -1520,6 +1536,25 @@ function updateBannerPreview() {
if (bannerPreview) { if (bannerPreview) {
bannerPreview.style.visibility = 'visible'; bannerPreview.style.visibility = 'visible';
} }
// Update image position and visibility
const bannerImgElement = document.getElementById('bannerImagePreview');
const hasImageElement = bannerImgElement && !bannerImgElement.classList.contains('d-none') && bannerImgElement.src;
if (hasImageElement) {
const bannerLinkValue = document.getElementById('bannerLink')?.value || '';
if (bannerLinkValue) {
bannerImgElement.style.cursor = 'pointer';
bannerImgElement.onclick = (e) => {
e.preventDefault();
window.open(bannerLinkValue, '_blank');
};
} else {
bannerImgElement.style.cursor = 'default';
bannerImgElement.onclick = null;
}
}
} }
// Apply preset // Apply preset
@@ -1823,62 +1858,52 @@ document.addEventListener('DOMContentLoaded', () => {
const bannerTextColorPicker = document.getElementById('bannerTextColorPicker'); const bannerTextColorPicker = document.getElementById('bannerTextColorPicker');
const bannerTextColor = document.getElementById('bannerTextColor'); const bannerTextColor = document.getElementById('bannerTextColor');
const bannerText = document.getElementById('bannerText'); const bannerText = document.getElementById('bannerText');
const bannerLink = document.getElementById('bannerLink');
const bannerTextAlign = document.getElementById('bannerTextAlign'); const bannerTextAlign = document.getElementById('bannerTextAlign');
const bannerFontSize = document.getElementById('bannerFontSize'); const bannerFontSize = document.getElementById('bannerFontSize');
const bannerPadding = document.getElementById('bannerPadding'); const bannerPadding = document.getElementById('bannerPadding');
const bannerMargin = document.getElementById('bannerMargin'); const bannerMargin = document.getElementById('bannerMargin');
const bannerBorderRadius = document.getElementById('bannerBorderRadius'); const bannerBorderRadius = document.getElementById('bannerBorderRadius');
const bannerVisible = document.getElementById('bannerVisible'); const bannerVisibility = document.getElementById('bannerVisibility');
const stylePresets = document.querySelectorAll('.style-preset'); const stylePresets = document.querySelectorAll('.style-preset');
const saveBannerBtn = document.getElementById('saveBannerBtn'); const saveBannerBtn = document.getElementById('saveBannerBtn');
// Set up color picker event listeners if elements exist // Set up color picker event listeners if elements exist
if (bannerBgColorPicker && bannerBgColor) { if (bannerBgColorPicker && bannerBgColor) {
// Update only the color preview during dragging (lightweight)
bannerBgColorPicker.addEventListener('input', () => { bannerBgColorPicker.addEventListener('input', () => {
bannerBgColor.value = bannerBgColorPicker.value; 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(); updateColorPreviews();
updateBannerPreview();
}); });
} }
if (bannerTextColorPicker && bannerTextColor) { if (bannerTextColorPicker && bannerTextColor) {
// Same for text color picker
bannerTextColorPicker.addEventListener('input', () => { bannerTextColorPicker.addEventListener('input', () => {
bannerTextColor.value = bannerTextColorPicker.value; bannerTextColor.value = bannerTextColorPicker.value;
const textColorPreview = document.getElementById('textColorPreview');
if (textColorPreview) {
textColorPreview.style.backgroundColor = bannerTextColorPicker.value;
}
});
bannerTextColorPicker.addEventListener('change', () => {
updateColorPreviews(); updateColorPreviews();
updateBannerPreview();
}); });
} }
// For other form controls, use debounced updates on input // For other form controls, use debounced updates on input
const formControls = [ const formControls = [
bannerText, bannerTextAlign, bannerFontSize, bannerText, bannerLink, bannerTextAlign, bannerFontSize,
bannerPadding, bannerMargin, bannerBorderRadius, bannerVisible bannerPadding, bannerMargin, bannerBorderRadius, bannerVisibility
].filter(Boolean); // Filter out any null/undefined elements ];
formControls.forEach(el => { formControls.forEach(control => {
if (el) { if (control) {
el.addEventListener('change', updateBannerPreview); control.addEventListener('input', debounce(() => {
el.addEventListener('input', debouncedUpdatePreview); updateBannerPreview();
}, 300));
} }
}); });
// Toggle visibility
if (bannerVisibility) {
bannerVisibility.addEventListener('change', () => {
updateBannerPreview();
});
}
// Style presets // Style presets
if (stylePresets.length > 0) { if (stylePresets.length > 0) {
stylePresets.forEach(preset => { stylePresets.forEach(preset => {