418 lines
17 KiB
HTML
418 lines
17 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="de">
|
|
<head>
|
|
<link rel="icon" type="image/png" href="favicon.png">
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Schnappix - Hintergrund Upload</title>
|
|
<meta name="description" content="Lade deine eigenen, maßgeschneiderten Rahmen für Schnappix hoch.">
|
|
<link rel="stylesheet" href="style.css">
|
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600&family=Outfit:wght@500;700;800&display=swap" rel="stylesheet">
|
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
|
<style>
|
|
.upload-section {
|
|
padding: 140px 0 100px;
|
|
min-height: 100vh;
|
|
display: flex;
|
|
align-items: center;
|
|
}
|
|
|
|
.upload-card {
|
|
max-width: 600px;
|
|
margin: 0 auto;
|
|
padding: 40px;
|
|
text-align: center;
|
|
}
|
|
|
|
.upload-area {
|
|
border: 2px dashed var(--border-color);
|
|
border-radius: 16px;
|
|
padding: 40px 20px;
|
|
margin: 30px 0;
|
|
background: rgba(5, 5, 16, 0.4);
|
|
cursor: pointer;
|
|
transition: var(--transition-smooth);
|
|
position: relative;
|
|
}
|
|
|
|
.upload-area:hover, .upload-area.dragover {
|
|
border-color: var(--primary-color);
|
|
background: rgba(255, 43, 214, 0.05);
|
|
box-shadow: 0 0 20px rgba(255, 43, 214, 0.1);
|
|
}
|
|
|
|
.upload-icon {
|
|
font-size: 48px;
|
|
color: var(--accent-color);
|
|
margin-bottom: 16px;
|
|
transition: var(--transition-smooth);
|
|
}
|
|
|
|
.upload-area:hover .upload-icon {
|
|
transform: translateY(-5px);
|
|
color: var(--primary-color);
|
|
}
|
|
|
|
.upload-text {
|
|
font-size: 16px;
|
|
color: #fff;
|
|
font-weight: 500;
|
|
margin-bottom: 8px;
|
|
}
|
|
|
|
.upload-hint {
|
|
font-size: 13px;
|
|
color: var(--text-muted);
|
|
}
|
|
|
|
#file-input {
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
opacity: 0;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.validation-messages {
|
|
margin-top: 20px;
|
|
text-align: left;
|
|
display: none;
|
|
background: rgba(5, 5, 16, 0.8);
|
|
padding: 15px;
|
|
border-radius: 8px;
|
|
border-left: 3px solid var(--accent-color);
|
|
}
|
|
|
|
.validation-messages p {
|
|
margin-bottom: 8px;
|
|
font-size: 14px;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
}
|
|
|
|
.validation-messages p:last-child {
|
|
margin-bottom: 0;
|
|
}
|
|
|
|
.valid-msg { color: #4ade80; }
|
|
.invalid-msg { color: #ff4466; }
|
|
|
|
.preview-container {
|
|
display: none;
|
|
margin-top: 20px;
|
|
border-radius: 12px;
|
|
overflow: hidden;
|
|
border: 1px solid var(--border-color);
|
|
background: #000;
|
|
position: relative;
|
|
}
|
|
|
|
.preview-container img {
|
|
width: 100%;
|
|
height: auto;
|
|
display: block;
|
|
}
|
|
|
|
.preview-overlay-checkerboard {
|
|
background-image: linear-gradient(45deg, #222 25%, transparent 25%),
|
|
linear-gradient(-45deg, #222 25%, transparent 25%),
|
|
linear-gradient(45deg, transparent 75%, #222 75%),
|
|
linear-gradient(-45deg, transparent 75%, #222 75%);
|
|
background-size: 20px 20px;
|
|
background-position: 0 0, 0 10px, 10px -10px, -10px 0px;
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
bottom: 0;
|
|
z-index: -1;
|
|
}
|
|
|
|
#submit-btn {
|
|
margin-top: 25px;
|
|
display: none;
|
|
}
|
|
|
|
.email-info {
|
|
font-size: 12px;
|
|
color: var(--text-muted);
|
|
margin-top: 15px;
|
|
display: none;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
|
|
<!-- Header Navigation -->
|
|
<header class="main-header">
|
|
<div class="container nav-container">
|
|
<div class="logo">
|
|
<a href="index.html" style="text-decoration: none;"><span class="logo-glow">Schnappix</span></a>
|
|
</div>
|
|
<nav class="nav-links">
|
|
<a href="features.html">Features</a>
|
|
<a href="upload.html" class="active-nav">Upload</a>
|
|
<a href="faq.html">FAQ</a>
|
|
<a href="feedback.html">Feedback Hub</a>
|
|
<a href="tester.html">App Testing</a>
|
|
<a href="#" id="admin-login-trigger"><i class="fa-solid fa-user-gear"></i> Admin</a>
|
|
</nav>
|
|
<button class="menu-toggle" aria-label="Menu öffnen">
|
|
<i class="fa-solid fa-bars"></i>
|
|
</button>
|
|
</div>
|
|
</header>
|
|
|
|
<!-- Upload Section -->
|
|
<section class="upload-section">
|
|
<div class="container">
|
|
<div class="upload-card glass">
|
|
<h1 style="font-family: var(--font-titles); font-size: 32px; margin-bottom: 10px;">Hintergrund <span class="text-gradient">Upload</span></h1>
|
|
<p style="color: var(--text-muted); font-size: 15px;">Lade dein eigenes PNG hoch. Nach erfolgreicher Prüfung wird dein Rahmen in die App eingebaut und ist dort für alle Nutzer sichtbar.</p>
|
|
|
|
<!-- FormSubmit.co is used to send the email directly with attachment without a backend -->
|
|
<form id="bg-upload-form">
|
|
|
|
<div class="upload-area" id="drop-zone">
|
|
<i class="fa-solid fa-cloud-arrow-up upload-icon"></i>
|
|
<div class="upload-text">Datei hier ablegen oder klicken</div>
|
|
<div class="upload-hint">Erforderlich: PNG Format • Exakt 1500x1000 Pixel</div>
|
|
<input type="file" id="file-input" name="background_image" accept="image/png" required>
|
|
</div>
|
|
|
|
<div class="form-group" style="text-align: left; display: none;" id="frame-info">
|
|
<label for="frame-name">Name des Rahmens (nur Kleinbuchstaben & Zahlen)</label>
|
|
<input type="text" id="frame-name" name="frame_name" pattern="[a-z0-9_]+" placeholder="z.B. summer1" required>
|
|
<div class="upload-hint" style="margin-top: 5px;">Wird gespeichert als: <strong style="color: var(--primary-color);">frame_<span id="frame-preview-name">...</span>.png</strong></div>
|
|
</div>
|
|
|
|
<div class="form-group" style="text-align: left; display: none;" id="customer-info">
|
|
<label for="customer-email">Deine E-Mail Adresse (für Rückfragen)</label>
|
|
<input type="email" id="customer-email" name="email" placeholder="mail@example.com" required>
|
|
</div>
|
|
|
|
<div class="validation-messages" id="validation-box">
|
|
<p id="val-format"><i class="fa-solid fa-circle-notch fa-spin"></i> Überprüfe Dateiformat (.png)...</p>
|
|
<p id="val-resolution"><i class="fa-solid fa-circle-notch fa-spin"></i> Überprüfe Auflösung (1500x1000)...</p>
|
|
</div>
|
|
|
|
<div class="preview-container" id="preview-box">
|
|
<div class="preview-overlay-checkerboard"></div>
|
|
<img id="image-preview" src="" alt="Vorschau">
|
|
</div>
|
|
|
|
<button type="submit" class="btn btn-primary btn-block" id="submit-btn">
|
|
<i class="fa-solid fa-paper-plane" style="margin-right: 8px;"></i> Hintergrund absenden
|
|
</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Footer -->
|
|
<footer class="main-footer">
|
|
<div class="container footer-container">
|
|
<p>© 2026 Schnappix. Alle Rechte vorbehalten.</p>
|
|
<div class="footer-links">
|
|
<a href="impressum.html">Impressum</a>
|
|
<a href="datenschutz.html">Datenschutzerklärung</a>
|
|
</div>
|
|
</div>
|
|
</footer>
|
|
|
|
<!-- Admin Modals (Required for Header Nav) -->
|
|
<div id="admin-login-modal" class="modal-overlay">
|
|
<div class="modal-content glass">
|
|
<button class="modal-close" aria-label="Schließen">×</button>
|
|
<h2>Admin Login</h2>
|
|
<form id="admin-login-form">
|
|
<div class="form-group">
|
|
<label for="admin-password">Admin Passwort</label>
|
|
<input type="password" id="admin-password" placeholder="Passwort eingeben" required>
|
|
</div>
|
|
<div id="admin-login-error" style="color: #ff4466; font-size: 14px; margin-bottom: 15px; display: none;">Falsches Passwort!</div>
|
|
<button type="submit" class="btn btn-primary btn-block">Anmelden</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<script src="app.js"></script>
|
|
<script>
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
const dropZone = document.getElementById('drop-zone');
|
|
const fileInput = document.getElementById('file-input');
|
|
const validationBox = document.getElementById('validation-box');
|
|
const valFormat = document.getElementById('val-format');
|
|
const valResolution = document.getElementById('val-resolution');
|
|
const previewBox = document.getElementById('preview-box');
|
|
const imagePreview = document.getElementById('image-preview');
|
|
const submitBtn = document.getElementById('submit-btn');
|
|
const frameInfo = document.getElementById('frame-info');
|
|
const frameNameInput = document.getElementById('frame-name');
|
|
const framePreviewName = document.getElementById('frame-preview-name');
|
|
const customerInfo = document.getElementById('customer-info');
|
|
|
|
frameNameInput.addEventListener('input', (e) => {
|
|
const val = e.target.value.trim().toLowerCase().replace(/[^a-z0-9_]/g, '');
|
|
e.target.value = val;
|
|
framePreviewName.textContent = val || '...';
|
|
});
|
|
|
|
// Drag and Drop Effects
|
|
['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
|
|
dropZone.addEventListener(eventName, preventDefaults, false);
|
|
});
|
|
|
|
function preventDefaults(e) {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
}
|
|
|
|
['dragenter', 'dragover'].forEach(eventName => {
|
|
dropZone.addEventListener(eventName, () => dropZone.classList.add('dragover'), false);
|
|
});
|
|
|
|
['dragleave', 'drop'].forEach(eventName => {
|
|
dropZone.addEventListener(eventName, () => dropZone.classList.remove('dragover'), false);
|
|
});
|
|
|
|
dropZone.addEventListener('drop', (e) => {
|
|
const dt = e.dataTransfer;
|
|
const files = dt.files;
|
|
if(files.length > 0) {
|
|
fileInput.files = files;
|
|
handleFile(files[0]);
|
|
}
|
|
});
|
|
|
|
fileInput.addEventListener('change', function() {
|
|
if(this.files && this.files.length > 0) {
|
|
handleFile(this.files[0]);
|
|
}
|
|
});
|
|
|
|
function setValidationMsg(el, isValid, validText, invalidText) {
|
|
if (isValid) {
|
|
el.innerHTML = `<i class="fa-solid fa-check valid-msg"></i> ${validText}`;
|
|
el.className = 'valid-msg';
|
|
} else {
|
|
el.innerHTML = `<i class="fa-solid fa-xmark invalid-msg"></i> ${invalidText}`;
|
|
el.className = 'invalid-msg';
|
|
}
|
|
}
|
|
|
|
function handleFile(file) {
|
|
// Reset UI
|
|
validationBox.style.display = 'block';
|
|
previewBox.style.display = 'none';
|
|
submitBtn.style.display = 'none';
|
|
frameInfo.style.display = 'none';
|
|
customerInfo.style.display = 'none';
|
|
|
|
valFormat.innerHTML = `<i class="fa-solid fa-circle-notch fa-spin"></i> Überprüfe Dateiformat...`;
|
|
valFormat.className = '';
|
|
valResolution.innerHTML = `<i class="fa-solid fa-circle-notch fa-spin"></i> Überprüfe Auflösung...`;
|
|
valResolution.className = '';
|
|
|
|
// 1. Check Format
|
|
const isPng = file.type === 'image/png';
|
|
|
|
if (!isPng) {
|
|
setValidationMsg(valFormat, false, '', 'Fehler: Die Datei muss im .png Format sein.');
|
|
setValidationMsg(valResolution, false, '', 'Abbruch: Falsches Format.');
|
|
return;
|
|
} else {
|
|
setValidationMsg(valFormat, true, 'Format ist korrekt (.png)', '');
|
|
}
|
|
|
|
// 2. Check Resolution
|
|
const reader = new FileReader();
|
|
reader.onload = function(e) {
|
|
const img = new Image();
|
|
img.onload = function() {
|
|
const width = this.width;
|
|
const height = this.height;
|
|
|
|
if (width === 1500 && height === 1000) {
|
|
setValidationMsg(valResolution, true, `Auflösung ist korrekt (${width}x${height}px)`, '');
|
|
|
|
// Show Preview and Submit Button
|
|
imagePreview.src = e.target.result;
|
|
previewBox.style.display = 'block';
|
|
frameInfo.style.display = 'block';
|
|
customerInfo.style.display = 'block';
|
|
submitBtn.style.display = 'block';
|
|
} else {
|
|
setValidationMsg(valResolution, false, '', `Fehler: Falsche Auflösung (${width}x${height}px). Erwartet: 1500x1000px.`);
|
|
}
|
|
};
|
|
img.onerror = function() {
|
|
setValidationMsg(valResolution, false, '', 'Fehler: Bilddatei konnte nicht gelesen werden.');
|
|
};
|
|
img.src = e.target.result;
|
|
};
|
|
reader.readAsDataURL(file);
|
|
}
|
|
|
|
const form = document.getElementById('bg-upload-form');
|
|
form.addEventListener('submit', async (e) => {
|
|
e.preventDefault();
|
|
|
|
const formData = new FormData(form);
|
|
const originalBtnText = submitBtn.innerHTML;
|
|
submitBtn.innerHTML = '<i class="fa-solid fa-circle-notch fa-spin"></i> Lade hoch...';
|
|
submitBtn.disabled = true;
|
|
|
|
try {
|
|
const response = await fetch('upload_backend.php', {
|
|
method: 'POST',
|
|
body: formData
|
|
});
|
|
|
|
const responseText = await response.text();
|
|
let result;
|
|
try {
|
|
result = JSON.parse(responseText);
|
|
} catch (e) {
|
|
console.error('Server antwortete nicht mit gültigem JSON:', responseText);
|
|
alert('Ein Server-Fehler ist aufgetreten (siehe Konsole). Das Bild wurde eventuell trotzdem hochgeladen.');
|
|
form.reset();
|
|
framePreviewName.textContent = '...';
|
|
previewBox.style.display = 'none';
|
|
submitBtn.style.display = 'none';
|
|
frameInfo.style.display = 'none';
|
|
customerInfo.style.display = 'none';
|
|
validationBox.style.display = 'none';
|
|
return;
|
|
}
|
|
|
|
if (response.ok) {
|
|
alert('Erfolgreich hochgeladen!');
|
|
form.reset();
|
|
framePreviewName.textContent = '...';
|
|
previewBox.style.display = 'none';
|
|
submitBtn.style.display = 'none';
|
|
frameInfo.style.display = 'none';
|
|
customerInfo.style.display = 'none';
|
|
validationBox.style.display = 'none';
|
|
} else {
|
|
alert('Fehler: ' + (result.error || 'Unbekannter Fehler'));
|
|
}
|
|
} catch (error) {
|
|
console.error('Upload Error:', error);
|
|
alert('Upload fehlgeschlagen. Bitte prüfe deine Verbindung.');
|
|
} finally {
|
|
submitBtn.innerHTML = originalBtnText;
|
|
submitBtn.disabled = false;
|
|
}
|
|
});
|
|
});
|
|
</script>
|
|
</body>
|
|
</html>
|