Rechtsseiten crawlbar: /datenschutz /agb /impressum als eigenständige HTML-Seiten (einzige Quelle, indexierbar) statt 302→Hash; SPA-Module holen Inhalt per fetch+inject (kein Text-Duplikat); Kontaktformular in geteilte contact-form.js; Sitemap+SW ergänzt, v1278
This commit is contained in:
parent
43b6292d08
commit
40d117874b
13 changed files with 1047 additions and 782 deletions
58
backend/static/js/contact-form.js
Normal file
58
backend/static/js/contact-form.js
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
/* ============================================================
|
||||
BAN YARO — Kontaktformular (geteilt)
|
||||
Einzige Quelle für die Formular-Logik. Genutzt von:
|
||||
- der eigenständigen Seite /impressum (Auto-Init auf document)
|
||||
- der SPA-Impressum-Seite (window.initContactForm(container))
|
||||
============================================================ */
|
||||
|
||||
window.initContactForm = function (root) {
|
||||
const form = root.querySelector('#contact-form');
|
||||
if (!form || form._bound) return; // einmal binden
|
||||
form._bound = true;
|
||||
|
||||
const statusEl = root.querySelector('#cf-status');
|
||||
const submitBtn = root.querySelector('#cf-submit');
|
||||
|
||||
form.addEventListener('submit', async e => {
|
||||
e.preventDefault();
|
||||
const name = root.querySelector('#cf-name').value.trim();
|
||||
const email = root.querySelector('#cf-email').value.trim();
|
||||
const subject = root.querySelector('#cf-subject').value.trim();
|
||||
const message = root.querySelector('#cf-message').value.trim();
|
||||
|
||||
submitBtn.disabled = true;
|
||||
submitBtn.textContent = 'Wird gesendet…';
|
||||
statusEl.style.display = 'none';
|
||||
|
||||
try {
|
||||
const res = await fetch('/api/contact', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ name, email, subject, message }),
|
||||
});
|
||||
if (!res.ok) {
|
||||
const err = await res.json().catch(() => ({}));
|
||||
throw new Error(err.detail || 'Fehler beim Senden.');
|
||||
}
|
||||
statusEl.style.display = 'block';
|
||||
statusEl.style.background = 'var(--c-success-bg, #f0fdf4)';
|
||||
statusEl.style.color = 'var(--c-success, #16a34a)';
|
||||
statusEl.textContent = '✓ Nachricht gesendet — wir melden uns bald!';
|
||||
form.reset();
|
||||
} catch (err) {
|
||||
statusEl.style.display = 'block';
|
||||
statusEl.style.background = '#fef2f2';
|
||||
statusEl.style.color = '#dc2626';
|
||||
statusEl.textContent = err.message || 'Fehler beim Senden. Bitte versuche es später erneut.';
|
||||
submitBtn.disabled = false;
|
||||
submitBtn.textContent = 'Nachricht senden';
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// Auto-Init für die eigenständige /impressum-Seite (kein Inline-Handler wegen CSP)
|
||||
(function () {
|
||||
function go() { try { window.initContactForm(document); } catch (e) {} }
|
||||
if (document.readyState !== 'loading') go();
|
||||
else document.addEventListener('DOMContentLoaded', go);
|
||||
})();
|
||||
Loading…
Add table
Add a link
Reference in a new issue