Files
dogfather-universe/assets/js/forms.js
T
DogFatherGitandClaude Opus 5 755d0c7a0e Modi-Bewerbung: vierte Kategorie neben Creator/Scout/Kooperation
- bewerben.html: neuer Reiter "Modi" mit eigenem Formular (Name, E-Mail,
  Discord/TikTok-Handle, Community-Erfahrung, Verfuegbarkeit, Motivation)
- Reiter wechselt jetzt nicht nur das Hintergrundbild, sondern auch die
  Farbwelt: Creator/Scout/Kooperation bleiben Spicy-Media-rot (Agentur),
  Modi wird babyblau (Team Dogi) -- passend zur bestehenden Regel
  "Rot ist NUR die Agentur"
- Direkt-Link bewerben.html#modi oeffnet die Bewerbung gleich auf dem
  richtigen Reiter; team-modis.html hat jetzt einen "Als Modi bewerben"
  Knopf, der genau dahin fuehrt
- Worker: "modi" als vierter erlaubter Bewerbungstyp, laeuft ueber
  dieselbe Sichtbarkeits-Berechtigung wie Scout/Kooperation (der feste
  Rechte-Katalog aus dem Anforderungsdokument kennt nur zwei Bewerbungs-
  Sichtbereiche -- es wurde bewusst KEIN neuer Permission-Key erfunden)

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-07-31 22:16:57 +02:00

166 lines
6.5 KiB
JavaScript

/* =====================================================================
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 = "[email protected]"; // 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:
<a href="${mailto}" class="btn btn-outline" style="margin-top:.6rem;">E-Mail jetzt senden</a>`;
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 <body> — 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");
});