From 160821c7f5776ccd94057a9fc27e044eea294496 Mon Sep 17 00:00:00 2001 From: orfelorfel23 Date: Sun, 21 Jun 2026 14:51:15 +0200 Subject: [PATCH] Auto-Commit: 2026-06-21 14:51:15 --- app.js | 188 ++++++++++++++++++++++++++++++---- datenschutz.html | 105 +++++++++++++++++++ faq.html | 142 +++++++++++++++++++++++++ features.html | 127 +++++++++++++++++++++++ feedback.html | 139 +++++++++++++++++++++++++ impressum.html | 106 +++++++++++++++++++ index.html | 262 ++++++++++++----------------------------------- style.css | 171 +++++++++++++++++++++++++++++-- 8 files changed, 1015 insertions(+), 225 deletions(-) create mode 100644 datenschutz.html create mode 100644 faq.html create mode 100644 features.html create mode 100644 feedback.html create mode 100644 impressum.html diff --git a/app.js b/app.js index 9c3f985..8d47a85 100644 --- a/app.js +++ b/app.js @@ -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 + ? `` + : ''; + card.innerHTML = `
${badgeIcon} ${categoryLabel} von ${escapeHtml(item.author)} + ${deleteBtnHtml}

${escapeHtml(item.title)}

${escapeHtml(item.text)}

@@ -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(); }); diff --git a/datenschutz.html b/datenschutz.html new file mode 100644 index 0000000..1e00ef4 --- /dev/null +++ b/datenschutz.html @@ -0,0 +1,105 @@ + + + + + + Schnappix - Datenschutzerklärung + + + + + + + + + +
+ +
+ + +
+
+
+

Datenschutzerklärung

+ + +
+
+
+ + + + + + + + + + + + diff --git a/faq.html b/faq.html new file mode 100644 index 0000000..8a204d3 --- /dev/null +++ b/faq.html @@ -0,0 +1,142 @@ + + + + + + Schnappix - Häufig gestellte Fragen (FAQ) + + + + + + + + + + +
+ +
+ + +
+
+

Häufig gestellte Fragen

+

Alles Wissenswerte zur Einrichtung, Hardware-Kompatibilität und Anpassung der Photobooth App.

+
+
+ + +
+
+
+
+ +
+

Damit der Kiosk-Modus (LockTask) ohne Bestätigungsdialoge funktioniert, muss die App als Device Owner festgelegt werden. Führe dazu folgenden ADB-Befehl aus:

+ adb shell dpm set-device-owner de.orfel.schnappix/de.orfel.schnappix.KioskDeviceAdminReceiver +

Danach kannst du den Kiosk-Modus einfach im passwortgeschützten Admin-Bereich ein- und ausschalten.

+
+
+ +
+ +
+

Erstelle ein transparentes PNG im Format 1500 x 1000 px (3:2 Querformat, max. 500 KB) und lege es im Ordner assets/frames/ als frame_name.png ab. Trage das Template anschließend in src/data/frames.ts ein und führe einen neuen Build aus.

+
+
+ +
+ +
+

Es werden alle Drucker unterstützt, die das IPP (Internet Printing Protocol) im lokalen Netzwerk unterstützen. Beliebte Fotodrucker wie der Canon CP1300 lassen sich direkt über ihre lokale IP-Adresse ansteuern.

+
+
+ +
+ +
+

Schnappix nutzt die moderne Android MediaStore API und speichert Collagen sowie Originale direkt in der Galerie deines Tablets unter den Ordnern:

+
    +
  • Schnappix - <EventName> - Collagen
  • +
  • Schnappix - <EventName> - Originale
  • +
+
+
+
+
+
+ + + + + + + + + + + + diff --git a/features.html b/features.html new file mode 100644 index 0000000..eac124f --- /dev/null +++ b/features.html @@ -0,0 +1,127 @@ + + + + + + Schnappix - Features & Funktionen + + + + + + + + + + +
+ +
+ + +
+
+

Unsere Features

+

Entdecke die geballte Funktionsvielfalt, die Schnappix zur optimalen Fotobox-App macht.

+
+
+ + +
+
+
+
+
+

Echter Kiosk-Modus

+

Dank nativer Kotlin-Module sperrt die App das Tablet bombensicher für Gäste. Kein Verlassen der App ohne Admin-Code möglich.

+
+
+
+

Direkter Netzwerkdruck

+

Integrierter IPP-Drucker-Support (z.B. Canon Selphy). Fotos werden direkt aus dem Vorschaubildschirm gedruckt.

+
+
+
+

Sticker & Emojis

+

Interaktiver Editor wie bei Social Media. Emojis, Datumsanzeige und Eventname frei verschieben, drehen und skalieren.

+
+
+
+

Serienbild & Collagen

+

Nimm 1-5 Fotos hintereinander auf. Die App generiert automatisch eine wunderschöne Collage zum Drucken und Speichern.

+
+
+
+

Individuelle Rahmen

+

Blende maßgeschneiderte, transparente PNG-Rahmen über die Aufnahmen. Perfekt für Geburtstage, Hochzeiten oder Firmenfeiern.

+
+
+
+

USB-Kamera Support

+

Nutze entweder die Frontkamera deines Tablets oder schließe eine externe USB-Kamera an für überragende Bildqualität.

+
+
+
+
+ + + + + + + + + + + + diff --git a/feedback.html b/feedback.html new file mode 100644 index 0000000..5813723 --- /dev/null +++ b/feedback.html @@ -0,0 +1,139 @@ + + + + + + Schnappix - Feedback Hub + + + + + + + + + + +
+ +
+ + +
+
+

Feedback & Ideen

+

Deine Meinung hilft uns, Schnappix noch besser zu machen. Hinterlasse Feedback oder stimme für Ideen ab!

+
+
+ + + + + + + + + + + + + + + diff --git a/impressum.html b/impressum.html new file mode 100644 index 0000000..38af868 --- /dev/null +++ b/impressum.html @@ -0,0 +1,106 @@ + + + + + + Schnappix - Impressum + + + + + + + + + +
+ +
+ + +
+
+
+

Impressum

+ +

Angaben gemäß § 5 TMG

+

Jannik [Nachname]
+ [Straße und Hausnummer]
+ [PLZ und Ort]

+ +

Kontakt

+

E-Mail: info@schnappix.de
+ Telefon: [Telefonnummer - optional]

+ +

Verantwortlich für den Inhalt nach § 55 Abs. 2 RStV

+

Jannik [Nachname]
+ [Adresse wie oben]

+ +

Streitschlichtung

+

Die Europäische Kommission stellt eine Plattform zur Online-Streitbeilegung (OS) bereit: https://ec.europa.eu/consumers/odr.
+ Unsere E-Mail-Adresse finden Sie oben im Impressum. Wir sind nicht bereit oder verpflichtet, an Streitbeilegungsverfahren vor einer Verbraucherschlichtungsstelle teilzunehmen.

+
+
+
+ + + + + + + + + + + + diff --git a/index.html b/index.html index 1184d85..ab91045 100644 --- a/index.html +++ b/index.html @@ -6,11 +6,9 @@ Schnappix - Die ultimative Photobooth App - - @@ -19,13 +17,14 @@
-
+
-
+
+
Schnappix
-
+

Zeit für ein Erinnerungsfoto!

+ +
@@ -71,208 +99,48 @@
- -
-
-

Geniale Features für dein Event

-
-
-
-

Echter Kiosk-Modus

-

Dank nativer Kotlin-Module sperrt die App das Tablet bombensicher für Gäste. Kein Verlassen der App ohne Admin-Code möglich.

-
-
-
-

Direkter Netzwerkdruck

-

Integrierter IPP-Drucker-Support (z.B. Canon Selphy). Fotos werden direkt aus dem Vorschaubildschirm gedruckt.

-
-
-
-

Sticker & Emojis

-

Interaktiver Editor wie bei Social Media. Emojis, Datumsanzeige und Eventname frei verschieben, drehen und skalieren.

-
-
-
-

Serienbild & Collagen

-

Nimm 1-5 Fotos hintereinander auf. Die App generiert automatisch eine wunderschöne Collage zum Drucken und Speichern.

-
-
-
-

Individuelle Rahmen

-

Blende maßgeschneiderte, transparente PNG-Rahmen über die Aufnahmen. Perfekt für Geburtstage, Hochzeiten oder Firmenfeiern.

-
-
-
-

USB-Kamera Support

-

Nutze entweder die Frontkamera deines Tablets oder schließe eine externe USB-Kamera an für überragende Bildqualität.

-
-
-
-
- - -
-
-

Häufig gestellte Fragen

- -
-
- -
-

Damit der Kiosk-Modus (LockTask) ohne Bestätigungsdialoge funktioniert, muss die App als Device Owner festgelegt werden. Führe dazu folgenden ADB-Befehl aus:

- adb shell dpm set-device-owner de.orfel.schnappix/de.orfel.schnappix.KioskDeviceAdminReceiver -

Danach kannst du den Kiosk-Modus einfach im passwortgeschützten Admin-Bereich ein- und ausschalten.

-
-
- -
- -
-

Erstelle ein transparentes PNG im Format 1500 x 1000 px (3:2 Querformat, max. 500 KB) und lege es im Ordner assets/frames/ als frame_name.png ab. Trage das Template anschließend in src/data/frames.ts ein und führe einen neuen Build aus.

-
-
- -
- -
-

Es werden alle Drucker unterstützt, die das IPP (Internet Printing Protocol) im lokalen Netzwerk unterstützen. Beliebte Fotodrucker wie der Canon CP1300 lassen sich direkt über ihre lokale IP-Adresse ansteuern.

-
-
- -
- -
-

Schnappix nutzt die moderne Android MediaStore API und speichert Collagen sowie Originale direkt in der Galerie deines Tablets unter den Ordnern:

-
    -
  • Schnappix - <EventName> - Collagen
  • -
  • Schnappix - <EventName> - Originale
  • -
-
-
-
-
-
- - - - - -