mirror of
https://github.com/Dvorinka/PPve.git
synced 2026-06-05 04:52:58 +00:00
f
This commit is contained in:
+133
-108
@@ -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 => {
|
||||||
|
|||||||
Reference in New Issue
Block a user