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'); let feedbackData = []; // Upvote storage track (local to session/device) let upvotedIds = JSON.parse(localStorage.getItem('schnappix_upvoted_ids')) || []; const saveUpvotesToLocal = () => { localStorage.setItem('schnappix_upvoted_ids', JSON.stringify(upvotedIds)); }; // Admin State let isAdmin = sessionStorage.getItem('schnappix_is_admin') === 'true'; const loadFeedback = async () => { if (!feedbackList) return; try { const response = await fetch('feedback_backend.php?action=get'); const result = await response.json(); if (result.success) { feedbackData = result.data; renderFeedback(getActiveFilter()); } } catch (error) { console.error('Error loading feedback:', error); } }; 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 admin actions if user is admin let adminActionsHtml = ''; if (isAdmin) { adminActionsHtml = `
`; } let answerHtml = ''; if (item.answer) { answerHtml = `
Offizielle Antwort

${escapeHtml(item.answer)}

`; } card.innerHTML = `
${badgeIcon} ${categoryLabel} von ${escapeHtml(item.author)}
${adminActionsHtml}

${escapeHtml(item.title)}

${escapeHtml(item.text)}

${answerHtml}
`; feedbackList.appendChild(card); }); setupUpvoteButtons(); setupAdminDeleteButtons(); setupAdminAnswerButtons(); }; const setupAdminDeleteButtons = () => { if (!isAdmin) return; const deleteButtons = document.querySelectorAll('.fb-admin-delete-btn'); deleteButtons.forEach(btn => { btn.addEventListener('click', async () => { const id = btn.getAttribute('data-id'); if (confirm('Möchten Sie diesen Eintrag wirklich permanent löschen?')) { try { const response = await fetch('feedback_backend.php', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ action: 'delete', id: id, secret: '1234' }) }); const result = await response.json(); if (result.success) { loadFeedback(); } else { alert('Fehler beim Löschen.'); } } catch (e) { console.error(e); } } }); }); }; const setupAdminAnswerButtons = () => { if (!isAdmin) return; const answerButtons = document.querySelectorAll('.fb-admin-answer-btn'); answerButtons.forEach(btn => { btn.addEventListener('click', () => { const id = btn.getAttribute('data-id'); const item = feedbackData.find(i => i.id === id); const currentAnswer = item ? (item.answer || '') : ''; const answer = prompt('Antwort fĂŒr das Feedback (leer lassen zum Löschen der Antwort):', currentAnswer); if (answer !== null) { submitAnswer(id, answer); } }); }); }; const submitAnswer = async (id, answer) => { try { const response = await fetch('feedback_backend.php', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ action: 'answer', id: id, answer: answer, secret: '1234' }) }); const result = await response.json(); if (result.success) { loadFeedback(); } else { alert('Fehler beim Speichern der Antwort.'); } } catch (e) { console.error(e); } }; const escapeHtml = (str) => { if (!str) return ''; return String(str).replace(/&/g, "&") .replace(//g, ">") .replace(/"/g, """) .replace(/'/g, "'"); }; const setupUpvoteButtons = () => { const buttons = document.querySelectorAll('.upvote-btn'); buttons.forEach(btn => { btn.addEventListener('click', async () => { const id = btn.getAttribute('data-id'); const isVoted = upvotedIds.includes(id); // Optimistic UI update const btnSpan = btn.querySelector('span'); let currentVotes = parseInt(btnSpan.textContent); if (isVoted) { btn.classList.remove('voted'); btnSpan.textContent = currentVotes - 1; upvotedIds = upvotedIds.filter(vId => vId !== id); } else { btn.classList.add('voted'); btnSpan.textContent = currentVotes + 1; upvotedIds.push(id); } saveUpvotesToLocal(); try { await fetch('feedback_backend.php', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ action: 'upvote', id: id, undo: isVoted }) }); } catch (e) { console.error('Error upvoting', e); } }); }); }; 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', async (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 submitBtn = feedbackForm.querySelector('button[type="submit"]'); const newFb = { action: 'add', author: authorInput.value, type: typeSelect.value, title: titleInput.value, text: textInput.value }; submitBtn.disabled = true; submitBtn.textContent = 'Wird gesendet...'; try { const response = await fetch('feedback_backend.php', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(newFb) }); const result = await response.json(); if (result.success) { // Auto-upvote own post upvotedIds.push(result.data.id); saveUpvotesToLocal(); feedbackForm.reset(); await loadFeedback(); // Reload the list feedbackList.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); } else { alert('Fehler beim Senden des Feedbacks.'); } } catch (error) { console.error(error); alert('Netzwerkfehler. Bitte spĂ€ter erneut versuchen.'); } finally { submitBtn.disabled = false; submitBtn.textContent = 'Absenden'; } }); } // --- 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 if (feedbackList) { loadFeedback(); } });