document.addEventListener('DOMContentLoaded', () => { // --- Mobile Menu Toggle --- const menuToggle = document.querySelector('.menu-toggle'); const navLinks = document.querySelector('.nav-links'); if (menuToggle && navLinks) { menuToggle.addEventListener('click', () => { navLinks.classList.toggle('mobile-open'); const icon = menuToggle.querySelector('i'); if (icon) { icon.classList.toggle('fa-bars'); icon.classList.toggle('fa-xmark'); } }); } // --- Tablet Mockup Interactive Demo --- const captureBtn = document.getElementById('demo-capture-btn'); const screenWelcome = document.getElementById('demo-screen-welcome'); const screenCountdown = document.getElementById('demo-screen-countdown'); const screenPreview = document.getElementById('demo-screen-preview'); const timerDisplay = document.getElementById('demo-timer'); const flashOverlay = document.getElementById('demo-flash'); const btnPrint = document.getElementById('demo-btn-print'); const btnReset = document.getElementById('demo-btn-reset'); if (captureBtn && screenWelcome && screenCountdown && screenPreview && timerDisplay && flashOverlay) { captureBtn.addEventListener('click', () => { // Step 1: Switch screen to countdown screenWelcome.style.display = 'none'; screenCountdown.style.display = 'flex'; // Step 2: 3-second countdown countdown let timeLeft = 3; timerDisplay.textContent = timeLeft; const timerInterval = setInterval(() => { timeLeft--; if (timeLeft > 0) { timerDisplay.textContent = timeLeft; } else { clearInterval(timerInterval); // Step 3: Trigger Camera Flash overlay flashOverlay.classList.add('flash-active'); setTimeout(() => { flashOverlay.classList.remove('flash-active'); // Step 4: Transition to captured photo preview screen screenCountdown.style.display = 'none'; screenPreview.style.display = 'flex'; }, 400); } }, 800); }); } if (btnReset && screenWelcome && screenPreview) { btnReset.addEventListener('click', () => { screenPreview.style.display = 'none'; screenWelcome.style.display = 'flex'; }); } // --- Accordion FAQ --- const accordionHeaders = document.querySelectorAll('.accordion-header'); accordionHeaders.forEach(header => { header.addEventListener('click', () => { const item = header.parentElement; const isActive = item.classList.contains('active'); // Close all items document.querySelectorAll('.accordion-item').forEach(i => { i.classList.remove('active'); const content = i.querySelector('.accordion-content'); if (content) content.style.maxHeight = null; }); // Toggle active item if (!isActive) { item.classList.add('active'); const content = item.querySelector('.accordion-content'); if (content) { content.style.maxHeight = content.scrollHeight + "px"; } } }); }); // --- Feedback Hub Logic --- const feedbackForm = document.getElementById('feedback-form'); const feedbackList = document.getElementById('feedback-list'); const filterButtons = document.querySelectorAll('.filter-btn'); // Default Seed Data const defaultFeedback = [ { id: 'fb-1', author: 'Markus T.', type: 'feature', title: 'QR-Code Download für Gäste', text: 'Es wäre super cool, wenn nach der Aufnahme ein QR-Code angezeigt wird, über den sich die Gäste das Foto direkt auf das Handy laden können.', upvotes: 42, voted: false }, { id: 'fb-2', author: 'Sabrina', type: 'love', title: 'Sticker-Editor ist genial!', text: 'Unsere Hochzeitsgäste haben stundenlang Emojis und Eventtexte verschoben. Die Bedienung ist wirklich super intuitiv und einfach.', upvotes: 28, voted: false }, { id: 'fb-3', author: 'EventTech GmbH', type: 'bug', title: 'Drucker-Timeout bei schlechtem WLAN', text: 'Manchmal meldet die App einen Fehler, wenn der Drucker im lokalen Netzwerk kurz die Verbindung verliert. Ein automatischer Retry wäre hilfreich.', upvotes: 15, voted: false } ]; // Load from LocalStorage or seed defaults let feedbackData = JSON.parse(localStorage.getItem('schnappix_feedback')); if (!feedbackData) { feedbackData = defaultFeedback; localStorage.setItem('schnappix_feedback', JSON.stringify(feedbackData)); } // Upvote storage track (local to session/device) let upvotedIds = JSON.parse(localStorage.getItem('schnappix_upvoted_ids')) || []; const saveToLocalStorage = () => { localStorage.setItem('schnappix_feedback', JSON.stringify(feedbackData)); localStorage.setItem('schnappix_upvoted_ids', JSON.stringify(upvotedIds)); }; // Admin State let isAdmin = sessionStorage.getItem('schnappix_is_admin') === 'true'; const renderFeedback = (filter = 'all') => { if (!feedbackList) return; feedbackList.innerHTML = ''; // Sort by upvotes descending const sortedData = [...feedbackData].sort((a, b) => b.upvotes - a.upvotes); sortedData.forEach(item => { if (filter !== 'all' && item.type !== filter) return; const isVoted = upvotedIds.includes(item.id); const card = document.createElement('div'); card.className = 'feedback-item glass'; let badgeIcon = '✨'; let categoryLabel = 'Feature'; if (item.type === 'bug') { badgeIcon = '🐛'; categoryLabel = 'Bug'; } else if (item.type === 'love') { badgeIcon = '💖'; categoryLabel = 'Lob'; } // Show delete button if user is admin const deleteBtnHtml = isAdmin ? `` : ''; card.innerHTML = `
${badgeIcon} ${categoryLabel} von ${escapeHtml(item.author)} ${deleteBtnHtml}

${escapeHtml(item.title)}

${escapeHtml(item.text)}

`; feedbackList.appendChild(card); }); setupUpvoteButtons(); setupAdminDeleteButtons(); }; const setupAdminDeleteButtons = () => { if (!isAdmin) return; const deleteButtons = document.querySelectorAll('.fb-admin-delete-btn'); deleteButtons.forEach(btn => { btn.addEventListener('click', () => { const id = btn.getAttribute('data-id'); if (confirm('Möchten Sie diesen Eintrag wirklich permanent löschen?')) { feedbackData = feedbackData.filter(item => item.id !== id); saveToLocalStorage(); renderFeedback(getActiveFilter()); } }); }); }; const escapeHtml = (str) => { return str.replace(/&/g, "&") .replace(//g, ">") .replace(/"/g, """) .replace(/'/g, "'"); }; const setupUpvoteButtons = () => { const buttons = document.querySelectorAll('.upvote-btn'); buttons.forEach(btn => { btn.addEventListener('click', () => { const id = btn.getAttribute('data-id'); const index = feedbackData.findIndex(item => item.id === id); if (index !== -1) { if (upvotedIds.includes(id)) { // Undo upvote feedbackData[index].upvotes--; upvotedIds = upvotedIds.filter(vId => vId !== id); } else { // Upvote feedbackData[index].upvotes++; upvotedIds.push(id); } saveToLocalStorage(); renderFeedback(getActiveFilter()); } }); }); }; const getActiveFilter = () => { const activeBtn = document.querySelector('.filter-btn.active'); return activeBtn ? activeBtn.getAttribute('data-filter') : 'all'; }; // Filter switching filterButtons.forEach(btn => { btn.addEventListener('click', () => { filterButtons.forEach(b => b.classList.remove('active')); btn.classList.add('active'); renderFeedback(btn.getAttribute('data-filter')); }); }); // Form Submission if (feedbackForm) { feedbackForm.addEventListener('submit', (e) => { e.preventDefault(); const authorInput = document.getElementById('fb-author'); const typeSelect = document.getElementById('fb-type'); const titleInput = document.getElementById('fb-title'); const textInput = document.getElementById('fb-text'); const newFb = { id: 'fb-' + Date.now(), author: authorInput.value, type: typeSelect.value, title: titleInput.value, text: textInput.value, upvotes: 1, voted: true }; feedbackData.push(newFb); upvotedIds.push(newFb.id); // Auto-upvote own post saveToLocalStorage(); // Reset form feedbackForm.reset(); // Re-render renderFeedback(getActiveFilter()); // Smooth scroll to list feedbackList.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); }); } // --- Modal Management for Impressum & Privacy --- const modalTriggers = document.querySelectorAll('.legal-trigger'); const modals = document.querySelectorAll('.modal-overlay'); const closeButtons = document.querySelectorAll('.modal-close'); // --- Modal close helpers for admin modals --- const closeModal = (modal) => { modal.classList.remove('open'); document.body.style.overflow = ''; }; closeButtons.forEach(btn => { btn.addEventListener('click', () => { const modal = btn.closest('.modal-overlay'); if (modal) closeModal(modal); }); }); modals.forEach(modal => { modal.addEventListener('click', (e) => { if (e.target === modal) { closeModal(modal); } }); }); window.addEventListener('keydown', (e) => { if (e.key === 'Escape') { modals.forEach(modal => { if (modal.classList.contains('open')) closeModal(modal); }); } }); // --- Admin Panel Modals & Operations --- const adminTrigger = document.getElementById('admin-login-trigger'); const adminLoginModal = document.getElementById('admin-login-modal'); const adminPanelModal = document.getElementById('admin-panel-modal'); const adminLoginForm = document.getElementById('admin-login-form'); const adminPasswordInput = document.getElementById('admin-password'); const adminLoginError = document.getElementById('admin-login-error'); const adminLogoutBtn = document.getElementById('admin-logout-btn'); const adminExportBtn = document.getElementById('admin-export-btn'); const exportContainer = document.getElementById('export-container'); const exportTextarea = document.getElementById('export-textarea'); if (adminTrigger) { adminTrigger.addEventListener('click', (e) => { e.preventDefault(); if (isAdmin) { adminPanelModal.classList.add('open'); } else { adminLoginModal.classList.add('open'); adminPasswordInput.focus(); } document.body.style.overflow = 'hidden'; }); } if (adminLoginForm) { adminLoginForm.addEventListener('submit', (e) => { e.preventDefault(); const password = adminPasswordInput.value; // Admin password matching the default app settings: "1234" if (password === '1234') { isAdmin = true; sessionStorage.setItem('schnappix_is_admin', 'true'); adminPasswordInput.value = ''; adminLoginError.style.display = 'none'; // Switch Modals adminLoginModal.classList.remove('open'); adminPanelModal.classList.add('open'); // Re-render to show trash cans renderFeedback(getActiveFilter()); } else { adminLoginError.style.display = 'block'; adminPasswordInput.value = ''; adminPasswordInput.focus(); } }); } if (adminLogoutBtn) { adminLogoutBtn.addEventListener('click', () => { isAdmin = false; sessionStorage.removeItem('schnappix_is_admin'); adminPanelModal.classList.remove('open'); document.body.style.overflow = ''; // Hide export code if visible if (exportContainer) exportContainer.style.display = 'none'; // Re-render to hide trash cans renderFeedback(getActiveFilter()); }); } if (adminExportBtn && exportContainer && exportTextarea) { adminExportBtn.addEventListener('click', () => { // Strip out own custom elements or internal helper flags if necessary const cleanFeedback = feedbackData.map(item => ({ id: item.id, author: item.author, type: item.type, title: item.title, text: item.text, upvotes: item.upvotes, voted: false })); exportTextarea.value = JSON.stringify(cleanFeedback, null, 4); exportContainer.style.display = 'block'; exportTextarea.select(); }); } // Modal Close overrides to reset error states const allCloseButtons = document.querySelectorAll('.modal-close'); allCloseButtons.forEach(btn => { btn.addEventListener('click', () => { if (adminLoginError) adminLoginError.style.display = 'none'; if (exportContainer) exportContainer.style.display = 'none'; }); }); // Initial render renderFeedback(); });