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();
});