Auto-Commit: 2026-06-21 14:51:15

This commit is contained in:
2026-06-21 14:51:15 +02:00
parent ca9a6b2882
commit 160821c7f5
8 changed files with 1015 additions and 225 deletions
+169 -19
View File
@@ -14,6 +14,54 @@ document.addEventListener('DOMContentLoaded', () => {
});
}
// --- 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');
@@ -91,6 +139,9 @@ document.addEventListener('DOMContentLoaded', () => {
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 = '';
@@ -115,11 +166,17 @@ document.addEventListener('DOMContentLoaded', () => {
categoryLabel = 'Lob';
}
// Show delete button if user is admin
const deleteBtnHtml = isAdmin
? `<button class="fb-admin-delete-btn" data-id="${item.id}" title="Eintrag löschen"><i class="fa-solid fa-trash"></i> Löschen</button>`
: '';
card.innerHTML = `
<div class="fb-card-content">
<div class="fb-meta">
<span class="fb-tag ${item.type}">${badgeIcon} ${categoryLabel}</span>
<span class="fb-author">von ${escapeHtml(item.author)}</span>
${deleteBtnHtml}
</div>
<h4>${escapeHtml(item.title)}</h4>
<p>${escapeHtml(item.text)}</p>
@@ -136,6 +193,22 @@ document.addEventListener('DOMContentLoaded', () => {
});
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) => {
@@ -224,25 +297,10 @@ document.addEventListener('DOMContentLoaded', () => {
const modals = document.querySelectorAll('.modal-overlay');
const closeButtons = document.querySelectorAll('.modal-close');
modalTriggers.forEach(trigger => {
trigger.addEventListener('click', (e) => {
e.preventDefault();
const targetId = trigger.getAttribute('href').substring(1);
const targetModal = document.getElementById(targetId);
if (targetModal) {
targetModal.classList.add('open');
document.body.style.overflow = 'hidden'; // Stop scrolling background
}
});
});
// --- Modal close helpers for admin modals ---
const closeModal = (modal) => {
modal.classList.remove('open');
// Only restore scroll if no other modals are open
const anyOpen = Array.from(modals).some(m => m.classList.contains('open'));
if (!anyOpen) {
document.body.style.overflow = '';
}
document.body.style.overflow = '';
};
closeButtons.forEach(btn => {
@@ -253,7 +311,6 @@ document.addEventListener('DOMContentLoaded', () => {
});
modals.forEach(modal => {
// Close modal when clicking outside of the content block
modal.addEventListener('click', (e) => {
if (e.target === modal) {
closeModal(modal);
@@ -261,7 +318,6 @@ document.addEventListener('DOMContentLoaded', () => {
});
});
// Close modal on Escape key press
window.addEventListener('keydown', (e) => {
if (e.key === 'Escape') {
modals.forEach(modal => {
@@ -270,6 +326,100 @@ document.addEventListener('DOMContentLoaded', () => {
}
});
// --- 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();
});