diff --git a/admin-dashboard.html b/admin-dashboard.html index 244051a..86f5b8d 100644 --- a/admin-dashboard.html +++ b/admin-dashboard.html @@ -55,6 +55,56 @@ margin-top: 0; color: #333; } + .form-group { + margin-bottom: 1rem; + } + .form-group label { + display: block; + margin-bottom: 0.5rem; + font-weight: bold; + } + .form-group input[type="text"], + .form-group textarea, + .form-group select { + width: 100%; + padding: 0.5rem; + border: 1px solid #ddd; + border-radius: 4px; + font-size: 1rem; + } + .form-group textarea { + min-height: 100px; + resize: vertical; + } + .color-preview { + display: inline-block; + width: 24px; + height: 24px; + border: 1px solid #ddd; + vertical-align: middle; + margin-left: 10px; + } + .style-presets { + display: flex; + gap: 10px; + margin: 10px 0; + } + .style-preset { + padding: 5px 10px; + border: 1px solid #ddd; + border-radius: 4px; + cursor: pointer; + font-size: 0.9rem; + } + .style-preset:hover { + background-color: #f0f0f0; + } + .banner-preview { + margin-top: 20px; + padding: 15px; + border: 1px dashed #ccc; + border-radius: 4px; + } .card p { color: #666; margin-bottom: 0; @@ -70,6 +120,73 @@

Vítejte v administraci

+
+

Správa banneru

+
+ + +
+ +

Styl banneru

+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +

Předvolby stylů

+
+
Informační
+
Upozornění
+
Úspěch
+
Chyba
+
+ + + + +
+

Uživatelé

@@ -123,6 +240,189 @@ return response; }; + + // 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 bannerPreview = document.getElementById('bannerPreview'); + const bgColorPreview = document.getElementById('bgColorPreview'); + const textColorPreview = document.getElementById('textColorPreview'); + const saveBannerBtn = document.getElementById('saveBannerBtn'); + const stylePresets = document.querySelectorAll('.style-preset'); + + // 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' + } + }; + + // Load current banner + async function loadBanner() { + try { + const response = await fetch('/api/banner'); + if (!response.ok) throw new Error('Failed to load banner'); + + const banner = await response.json(); + + // Update form fields + bannerText.value = banner.text || ''; + bannerVisible.checked = banner.style.isVisible !== false; + bannerBgColor.value = banner.style.backgroundColor || '#f8d7da'; + bannerTextColor.value = banner.style.textColor || '#721c24'; + bannerTextAlign.value = banner.style.textAlign || 'center'; + bannerFontSize.value = parseInt(banner.style.fontSize || '16'); + bannerPadding.value = parseInt(banner.style.padding || '10'); + + updateColorPreviews(); + updateBannerPreview(); + + } catch (error) { + console.error('Error loading banner:', error); + alert('Nepodařilo se načíst banner'); + } + } + + // Save banner + async function saveBanner() { + try { + const bannerData = { + text: bannerText.value, + style: { + backgroundColor: bannerBgColor.value, + textColor: bannerTextColor.value, + textAlign: bannerTextAlign.value, + fontSize: `${bannerFontSize.value}px`, + padding: `${bannerPadding.value}px`, + isVisible: bannerVisible.checked + } + }; + + const response = await fetch('/api/banner/update', { + method: 'POST', + body: JSON.stringify(bannerData) + }); + + if (!response.ok) throw new Error('Failed to save banner'); + + alert('Banner byl úspěšně uložen'); + updateBannerPreview(); + + } catch (error) { + console.error('Error saving banner:', error); + alert('Nepodařilo se uložit banner'); + } + } + + // Update color previews + function updateColorPreviews() { + bgColorPreview.style.backgroundColor = bannerBgColor.value; + textColorPreview.style.backgroundColor = bannerTextColor.value; + } + + // Update banner preview + function updateBannerPreview() { + if (!bannerText.value.trim()) { + bannerPreview.style.display = 'none'; + return; + } + + + bannerPreview.style.display = 'block'; + bannerPreview.textContent = bannerText.value; + bannerPreview.style.backgroundColor = bannerBgColor.value; + bannerPreview.style.color = bannerTextColor.value; + bannerPreview.style.textAlign = bannerTextAlign.value; + bannerPreview.style.fontSize = `${bannerFontSize.value}px`; + bannerPreview.style.padding = `${bannerPadding.value}px`; + } + + // Apply preset + function applyPreset(preset) { + const style = presets[preset]; + if (!style) return; + + bannerBgColor.value = style.backgroundColor; + bannerTextColor.value = style.textColor; + bannerTextAlign.value = style.textAlign; + + updateColorPreviews(); + updateBannerPreview(); + } + + // Event Listeners + bannerBgColor.addEventListener('input', () => { + updateColorPreviews(); + updateBannerPreview(); + }); + + bannerTextColor.addEventListener('input', () => { + updateColorPreviews(); + updateBannerPreview(); + }); + + [bannerText, bannerTextAlign, bannerFontSize, bannerPadding, bannerVisible].forEach(el => { + el.addEventListener('change', updateBannerPreview); + el.addEventListener('input', updateBannerPreview); + }); + + stylePresets.forEach(preset => { + preset.addEventListener('click', () => applyPreset(preset.dataset.preset)); + }); + + saveBannerBtn.addEventListener('click', saveBanner); + + // Initialize + async function fetch(resource, init = {}) { + // Add auth token to headers if it exists + const token = localStorage.getItem('token'); + if (token) { + init.headers = { + ...init.headers, + 'Authorization': `Bearer ${token}`, + 'Content-Type': 'application/json' + }; + } + + const response = await window.fetch(resource, { + credentials: 'same-origin', + ...init + }); + + if (response.status === 401) { + // Unauthorized - redirect to login + window.location.href = '/admin'; + return; + } + + + return response; + } + + // Load banner when page loads + document.addEventListener('DOMContentLoaded', loadBanner);; diff --git a/banner.go b/banner.go new file mode 100644 index 0000000..9dbc6f3 --- /dev/null +++ b/banner.go @@ -0,0 +1,111 @@ +package main + +import ( + "encoding/json" + "log" + "net/http" + "os" + "sync" +) + +type BannerStyle struct { + BackgroundColor string `json:"backgroundColor"` + TextColor string `json:"textColor"` + FontSize string `json:"fontSize"` + TextAlign string `json:"textAlign"` + Padding string `json:"padding"` + IsVisible bool `json:"isVisible"` +} + +type BannerContent struct { + Text string `json:"text"` + Style BannerStyle `json:"style"` +} + +var ( + banner BannerContent + bannerLock sync.RWMutex + bannerFile = "banner.json" +) + +func init() { + // Initialize with default values + banner = BannerContent{ + Text: "Důležité oznámení: Tento banner lze upravit v administraci.", + Style: BannerStyle{ + BackgroundColor: "#f8d7da", + TextColor: "#721c24", + FontSize: "16px", + TextAlign: "center", + Padding: "10px", + IsVisible: true, + }, + } + loadBanner() +} + +func loadBanner() { + if _, err := os.Stat(bannerFile); os.IsNotExist(err) { + saveBanner() + return + } + + data, err := os.ReadFile(bannerFile) + if err != nil { + log.Printf("Error reading banner file: %v", err) + return + } + + bannerLock.Lock() + defer bannerLock.Unlock() + + if err := json.Unmarshal(data, &banner); err != nil { + log.Printf("Error parsing banner data: %v", err) + } +} + +func saveBanner() error { + bannerLock.RLock() + defer bannerLock.RUnlock() + + data, err := json.MarshalIndent(banner, "", " ") + if err != nil { + return err + } + + return os.WriteFile(bannerFile, data, 0644) +} + +func GetBannerHandler(w http.ResponseWriter, r *http.Request) { + bannerLock.RLock() + defer bannerLock.RUnlock() + + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(banner) +} + +func UpdateBannerHandler(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodPost { + http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) + return + } + + var newBanner BannerContent + if err := json.NewDecoder(r.Body).Decode(&newBanner); err != nil { + http.Error(w, "Invalid request body", http.StatusBadRequest) + return + } + + bannerLock.Lock() + banner = newBanner + err := saveBanner() + bannerLock.Unlock() + + if err != nil { + http.Error(w, "Error saving banner", http.StatusInternalServerError) + return + } + + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(map[string]string{"status": "success"}) +} diff --git a/index.html b/index.html index a397a7e..ac06bfb 100644 --- a/index.html +++ b/index.html @@ -16,6 +16,40 @@ } + + +