/* ===================================================================== forms.js — Validierung + Versand für die drei Bewerbungsformulare (Creator / Scout / Kooperation) auf bewerben.html Versand-Logik: 1) Wenn API_BASE_URL gesetzt ist (Cloudflare-Worker-Postfach deployed, siehe cloudflare-worker/README.md): Bewerbung wird per POST an `${API_BASE_URL}/submit` gesendet und landet im code-geschützten Postfach (postfach.html). 2) Solange API_BASE_URL leer ist ODER der Versand fehlschlägt (z.B. offline, Worker noch nicht deployed): Fallback auf einen vorausgefüllten "mailto:"-Link, wie bisher. ===================================================================== */ // TODO: nach dem Deploy des Cloudflare Workers hier die Worker-URL ohne // abschließenden Slash eintragen, z.B. // "https://dogfather-universe-postfach.DEIN-ACCOUNT.workers.dev" const API_BASE_URL = "https://dogfather-universe-postfach.dogfather1608.workers.dev"; const CONTACT_EMAIL = "kontakt@dogfather-universe.example"; // TODO: echte Kontakt-E-Mail eintragen const FORM_TYPES = { "form-creator": "creator", "form-scout": "scout", "form-modi": "modi", "form-kooperation": "kooperation", }; function validateField(field) { const wrapper = field.closest(".field"); if (!wrapper) return true; let valid = field.checkValidity(); if (valid && field.type === "email" && field.value) { valid = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(field.value); } wrapper.classList.toggle("has-error", !valid); return valid; } function buildMailto(subjectLabel, data) { const lines = [...data.entries()].map(([k, v]) => `${k}: ${v}`).join("\n"); return `mailto:${CONTACT_EMAIL}?subject=${encodeURIComponent( "Bewerbung: " + subjectLabel )}&body=${encodeURIComponent(lines)}`; } async function submitToWorker(type, data) { if (!API_BASE_URL) return false; const payload = { type, data: Object.fromEntries(data.entries()) }; try { const res = await fetch(`${API_BASE_URL}/submit`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(payload), }); if (!res.ok) return false; const json = await res.json(); return !!json.ok; } catch { return false; // z.B. offline oder Worker nicht erreichbar -> Fallback greift } } function setupForm(formId, subjectLabel) { const form = document.getElementById(formId); if (!form) return; const status = form.querySelector(".form-status"); const type = FORM_TYPES[formId]; form.addEventListener("submit", async (e) => { e.preventDefault(); const fields = [...form.querySelectorAll("input[required], select[required], textarea[required], input[type=email]")]; const allValid = fields.map(validateField).every(Boolean); if (!allValid) { if (status) { status.textContent = "Bitte prüfe die markierten Felder — es fehlt noch etwas."; status.className = "form-status show error"; } return; } const data = new FormData(form); const submitBtn = form.querySelector('button[type="submit"]'); if (submitBtn) submitBtn.disabled = true; const delivered = await submitToWorker(type, data); if (status) { if (delivered) { status.innerHTML = `Danke, das sieht super aus! 🩵 Deine Bewerbung ist sicher im Postfach von Team Dogi angekommen.`; status.className = "form-status show success"; } else { const mailto = buildMailto(subjectLabel, data); status.innerHTML = `Danke, das sieht super aus! 🩵 Solange noch kein automatischer Versand eingerichtet ist: E-Mail jetzt senden`; status.className = "form-status show success"; } } if (submitBtn) submitBtn.disabled = false; form.reset(); }); form.querySelectorAll("input, select, textarea").forEach((field) => { field.addEventListener("blur", () => validateField(field)); }); } /* Der Kopfbereich wechselt Hintergrundbild, Welt-Farbe (Theme) und Eyebrow- Text mit dem gewählten Reiter: Creator/Scout/Kooperation sind Spicy-Media- Agentur (rot), Modi ist Dogfathers persönliches Team (babyblau) — genau wie überall sonst auf der Seite (Rot = NUR Agentur). So passt Bild UND Farbwelt immer zur gerade gewählten Bewerbung. */ const TAB_HINTERGRUENDE = { "tab-creator": { bg: "bg-bewerben-creator", theme: "spicymedia", eyebrow: "Manager Dogfather" }, "tab-scout": { bg: "bg-bewerben", theme: "spicymedia", eyebrow: "Manager Dogfather" }, "tab-modi": { bg: "bg-dogfather", theme: "dogfather", eyebrow: "🩵 Team Dogi" }, "tab-kooperation": { bg: "bg-bewerben", theme: "spicymedia", eyebrow: "Manager Dogfather" }, }; function setzeTabHintergrund(tabId) { const einstellung = TAB_HINTERGRUENDE[tabId]; if (!einstellung) return; // Die Bildvariablen hängen am — das Motiv trägt die ganze Seite. document.body.style.setProperty("--page-bg", `url('/assets/img/${einstellung.bg}.jpg')`); document.body.style.setProperty("--page-bg-mobile", `url('/assets/img/${einstellung.bg}-mobile.jpg')`); document.documentElement.setAttribute("data-theme", einstellung.theme); const eyebrow = document.getElementById("bewerben-eyebrow"); if (eyebrow) eyebrow.textContent = einstellung.eyebrow; } function setupTabs() { const tabs = document.querySelectorAll(".tab-btn"); const panels = document.querySelectorAll(".tab-panel"); tabs.forEach((tab) => { tab.addEventListener("click", () => { tabs.forEach((t) => t.classList.remove("active")); panels.forEach((p) => p.classList.remove("active")); tab.classList.add("active"); document.getElementById(tab.dataset.tab).classList.add("active"); setzeTabHintergrund(tab.dataset.tab); }); }); // Direkt-Link auf einen Reiter, z.B. bewerben.html#modi von der Modi- // Team-Seite aus — öffnet die Bewerbung gleich auf dem richtigen Tab. const gewuenscht = location.hash.replace("#", ""); const linkBtn = gewuenscht && document.querySelector(`.tab-btn[data-tab="tab-${gewuenscht}"]`); if (linkBtn) linkBtn.click(); // Beim Laden passend zum aktiven Reiter setzen (bereits aktiv ODER gerade per Hash geöffnet) const aktiv = document.querySelector(".tab-btn.active"); if (aktiv) setzeTabHintergrund(aktiv.dataset.tab); } document.addEventListener("DOMContentLoaded", () => { setupTabs(); setupForm("form-creator", "Creator"); setupForm("form-scout", "Scout"); setupForm("form-modi", "Modi"); setupForm("form-kooperation", "Kooperation"); });