(function () { const EMAIL_STORAGE_KEY = 'ff_member_form_email_v1'; const boot = window.APP_BOOT || { steps: [], csrf: '', contactEmail: '' }; const baseUrl = String(boot.baseUrl || '').replace(/\/+$/, ''); const schemaSteps = Array.isArray(boot.steps) ? boot.steps : []; const state = { email: '', currentStep: 1, totalSteps: schemaSteps.length, summaryStep: schemaSteps.length + 1, autosaveId: null, uploads: {}, isSubmitting: false, summaryMissingCount: 0, }; const disclaimerSection = document.getElementById('disclaimerSection'); const disclaimerReadCheckbox = document.getElementById('disclaimerReadCheckbox'); const disclaimerReadError = document.getElementById('disclaimerReadError'); const acceptDisclaimerBtn = document.getElementById('acceptDisclaimerBtn'); const startSection = document.getElementById('startSection'); const startForm = document.getElementById('startForm'); const startIntroText = document.getElementById('startIntroText'); const startEmailField = document.getElementById('startEmailField'); const startActions = document.getElementById('startActions'); const startEmailInput = document.getElementById('startEmail'); const startEmailError = document.getElementById('startEmailError'); const startSubmitBtn = document.getElementById('startSubmitBtn'); const resetDataBtn = document.getElementById('resetDataBtn'); const compactStatusBox = document.getElementById('compactStatusBox'); const statusEmailValue = document.getElementById('statusEmailValue'); const draftStatusValue = document.getElementById('draftStatusValue'); const startFeedbackMessage = document.getElementById('startFeedbackMessage'); const feedbackMessage = document.getElementById('feedbackMessage'); const wizardSection = document.getElementById('wizardSection'); const applicationForm = document.getElementById('applicationForm'); const applicationEmail = document.getElementById('applicationEmail'); const progress = document.getElementById('progress'); const prevBtn = document.getElementById('prevBtn'); const nextBtn = document.getElementById('nextBtn'); const submitBtn = document.getElementById('submitBtn'); const submitSpinner = document.getElementById('submitSpinner'); const submitLabel = submitBtn ? submitBtn.querySelector('[data-submit-label]') : null; const summarySection = document.getElementById('summarySection'); const summaryContent = document.getElementById('summaryContent'); const summaryMissingNotice = document.getElementById('summaryMissingNotice'); const stepElements = Array.from(document.querySelectorAll('.step')); const startEmailRequiredMark = document.querySelector('#startEmailField .required-mark-field-start'); const fieldContainersByKey = new Map(); const fieldsByKey = new Map(); document.querySelectorAll('.field[data-field]').forEach((container) => { const key = String(container.getAttribute('data-field') || '').trim(); if (key !== '') { fieldContainersByKey.set(key, container); } }); schemaSteps.forEach((step) => { const fields = Array.isArray(step.fields) ? step.fields : []; fields.forEach((field) => { if (!field || typeof field !== 'object') { return; } const key = String(field.key || '').trim(); if (key !== '') { fieldsByKey.set(key, field); } }); }); function appUrl(path) { const normalizedPath = String(path || '').replace(/^\/+/, ''); return (baseUrl ? baseUrl + '/' : '/') + normalizedPath; } function setFeedbackText(target, text, isError) { if (!target) { return; } target.textContent = text || ''; target.classList.toggle('error-text', Boolean(isError)); } function setFeedback(text, isError, scope) { const targetScope = scope || (wizardSection && !wizardSection.classList.contains('hidden') ? 'wizard' : 'start'); if (targetScope === 'wizard') { setFeedbackText(feedbackMessage, text, isError); setFeedbackText(startFeedbackMessage, '', false); return; } if (targetScope === 'start') { setFeedbackText(startFeedbackMessage, text, isError); setFeedbackText(feedbackMessage, '', false); return; } setFeedbackText(feedbackMessage, text, isError); setFeedbackText(startFeedbackMessage, text, isError); } function setDraftStatus(text, isError) { draftStatusValue.textContent = text || ''; draftStatusValue.classList.toggle('error-text', Boolean(isError)); } function setStartEmailError(text) { if (!startEmailError) { return; } startEmailError.textContent = text || ''; } function setDisclaimerError(text) { if (!disclaimerReadError) { return; } disclaimerReadError.textContent = text || ''; } function updateStartEmailRequiredMarker() { if (!startEmailRequiredMark) { return; } const email = normalizeEmail(startEmailInput.value || ''); startEmailRequiredMark.classList.toggle('hidden', isValidEmail(email)); } function formatTimestamp(isoDate) { if (!isoDate) { return ''; } const date = new Date(isoDate); if (Number.isNaN(date.getTime())) { return String(isoDate); } return date.toLocaleString('de-DE'); } function normalizeEmail(email) { return (email || '').trim().toLowerCase(); } function isValidEmail(email) { return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email); } function rememberEmail(email) { try { localStorage.setItem(EMAIL_STORAGE_KEY, email); } catch (_err) { // ignore localStorage errors } } function getRememberedEmail() { try { return localStorage.getItem(EMAIL_STORAGE_KEY) || ''; } catch (_err) { return ''; } } function forgetRememberedEmail() { try { localStorage.removeItem(EMAIL_STORAGE_KEY); } catch (_err) { // ignore localStorage errors } } function updateDisclaimerAcceptanceState() { if (!acceptDisclaimerBtn || !disclaimerReadCheckbox) { return; } const accepted = disclaimerReadCheckbox.checked; acceptDisclaimerBtn.disabled = !accepted; if (accepted) { setDisclaimerError(''); } } function enterCompactStatus(email) { statusEmailValue.textContent = email; startSection.classList.add('compact-mode'); compactStatusBox.classList.remove('hidden'); startIntroText.classList.add('hidden'); startEmailField.classList.add('hidden'); startActions.classList.add('hidden'); } function leaveCompactStatus() { startSection.classList.remove('compact-mode'); compactStatusBox.classList.add('hidden'); startIntroText.classList.remove('hidden'); startEmailField.classList.remove('hidden'); startActions.classList.remove('hidden'); statusEmailValue.textContent = '-'; setDraftStatus('Noch nicht gespeichert', false); } function lockEmail(email) { state.email = email; applicationEmail.value = email; startEmailInput.value = email; startEmailInput.readOnly = true; startEmailInput.setAttribute('aria-readonly', 'true'); updateStartEmailRequiredMarker(); rememberEmail(email); enterCompactStatus(email); } function unlockEmail(clearInput) { state.email = ''; applicationEmail.value = ''; startEmailInput.readOnly = false; startEmailInput.removeAttribute('aria-readonly'); if (clearInput) { startEmailInput.value = ''; setStartEmailError(''); } updateStartEmailRequiredMarker(); forgetRememberedEmail(); leaveCompactStatus(); } function stopAutosave() { if (state.autosaveId) { clearInterval(state.autosaveId); state.autosaveId = null; } } function startAutosave() { stopAutosave(); state.autosaveId = setInterval(async () => { if (!state.email || wizardSection.classList.contains('hidden')) { return; } try { await saveDraft(false); } catch (_err) { // visible on next manual action } }, 15000); } function clearErrors() { document.querySelectorAll('[data-error-for]').forEach((el) => { el.textContent = ''; }); } function showErrors(errors) { clearErrors(); Object.keys(errors || {}).forEach((key) => { const el = document.querySelector('[data-error-for="' + key + '"]'); if (el) { el.textContent = errors[key]; } }); } function clearWizardData() { applicationForm.reset(); clearErrors(); renderUploadInfo({}); state.currentStep = 1; state.summaryMissingCount = 0; state.isSubmitting = false; if (submitLabel) { submitLabel.textContent = 'Verbindlich absenden'; } if (submitSpinner) { submitSpinner.classList.add('hidden'); } submitBtn.classList.remove('is-loading'); applyFieldVisibility(); refreshRequiredMarkers(); updateProgress(); Array.from(document.querySelectorAll('[data-upload-key]')).forEach((control) => { const fieldKey = control.getAttribute('data-upload-key'); if (fieldKey) { updateUploadSelectionText(fieldKey); } }); } function parseFieldKey(name) { const match = /^form_data\[(.+)\]$/.exec(String(name || '')); return match ? match[1] : ''; } function isCheckboxTrue(value) { return ['1', 'on', 'true', true, 1].includes(value); } function parseBirthdateParts(value) { const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(String(value || '').trim()); if (!match) { return null; } const year = Number(match[1]); const month = Number(match[2]); const day = Number(match[3]); const candidate = new Date(Date.UTC(year, month - 1, day)); if ( Number.isNaN(candidate.getTime()) || candidate.getUTCFullYear() !== year || candidate.getUTCMonth() !== month - 1 || candidate.getUTCDate() !== day ) { return null; } return { year, month, day }; } function deriveAdultFlagFromBirthdate(birthdateValue) { const parts = parseBirthdateParts(birthdateValue); if (!parts) { return ''; } const now = new Date(); const yearNow = now.getFullYear(); const monthNow = now.getMonth() + 1; const dayNow = now.getDate(); const isFutureBirthdate = parts.year > yearNow || (parts.year === yearNow && (parts.month > monthNow || (parts.month === monthNow && parts.day > dayNow))); if (isFutureBirthdate) { return ''; } let age = yearNow - parts.year; const hadBirthdayThisYear = monthNow > parts.month || (monthNow === parts.month && dayNow >= parts.day); if (!hadBirthdayThisYear) { age -= 1; } return age >= 18 ? '1' : '0'; } function addComputedAgeFlags(data) { const adultFlag = deriveAdultFlagFromBirthdate(data.geburtsdatum || ''); data.is_adult = adultFlag; data.is_minor = adultFlag === '' ? '' : adultFlag === '1' ? '0' : '1'; return data; } function collectCurrentFormData() { const data = {}; Array.from(applicationForm.elements).forEach((el) => { if (!el.name) { return; } const key = parseFieldKey(el.name); if (!key) { return; } if (el.type === 'checkbox') { data[key] = el.checked ? '1' : '0'; } else { data[key] = el.value || ''; } }); return addComputedAgeFlags(data); } function evaluateFieldRule(rule, formData) { if (!rule || typeof rule !== 'object') { return false; } const depField = String(rule.field || '').trim(); const depValue = String(rule.equals || ''); if (depField === '') { return false; } return String(formData[depField] || '') === depValue; } function isFieldVisible(field, formData) { if (!field || typeof field !== 'object') { return true; } if (!field.visible_if || typeof field.visible_if !== 'object') { return true; } return evaluateFieldRule(field.visible_if, formData); } function hasFileValue(fieldKey) { const fileInput = applicationForm.querySelector('[name="' + fieldKey + '"]'); const cameraInput = applicationForm.querySelector('[name="' + fieldKey + '__camera"]'); const hasLocalSelection = (fileInput && fileInput.files && fileInput.files.length > 0) || (cameraInput && cameraInput.files && cameraInput.files.length > 0); if (hasLocalSelection) { return true; } const storedUploads = state.uploads[fieldKey]; return Array.isArray(storedUploads) && storedUploads.length > 0; } function isFieldCompleted(field, formData) { const key = String(field.key || '').trim(); const type = String(field.type || 'text'); if (key === '') { return false; } if (type === 'file') { return hasFileValue(key); } if (type === 'checkbox') { return isCheckboxTrue(formData[key]); } return String(formData[key] || '').trim() !== ''; } function applyFieldVisibility() { const formData = collectCurrentFormData(); fieldsByKey.forEach((field, key) => { const container = fieldContainersByKey.get(key); if (!container) { return; } const visible = isFieldVisible(field, formData); container.classList.toggle('field-hidden-by-rule', !visible); const controlElements = container.querySelectorAll('input, select, textarea'); controlElements.forEach((el) => { el.disabled = !visible; }); if (!visible) { const errorEl = container.querySelector('[data-error-for="' + key + '"]'); if (errorEl) { errorEl.textContent = ''; } } }); } function refreshRequiredMarkers() { const formData = collectCurrentFormData(); fieldsByKey.forEach((field, key) => { const container = fieldContainersByKey.get(key); if (!container) { return; } const markers = container.querySelectorAll('.required-mark-field'); if (!markers.length) { return; } const visibleNow = isFieldVisible(field, formData); const requiredNow = isFieldRequired(field, formData); const completed = isFieldCompleted(field, formData); const hideMarker = !visibleNow || !requiredNow || completed; markers.forEach((marker) => { marker.classList.toggle('hidden', hideMarker); }); }); } function initRequiredMarkerTracking() { Array.from(applicationForm.elements).forEach((el) => { if (!el.name || !el.name.startsWith('form_data[')) { return; } el.addEventListener('blur', () => { applyFieldVisibility(); refreshRequiredMarkers(); }); el.addEventListener('change', () => { applyFieldVisibility(); refreshRequiredMarkers(); }); }); } function isFieldRequired(field, formData) { if (!field || typeof field !== 'object') { return false; } if (!isFieldVisible(field, formData)) { return false; } if (field.required === true) { return true; } if (!field.required_if || typeof field.required_if !== 'object') { return false; } return evaluateFieldRule(field.required_if, formData); } function isFieldMissing(field, formData) { if (!isFieldRequired(field, formData)) { return false; } const key = String(field.key || '').trim(); if (!key) { return false; } const type = String(field.type || 'text'); if (type === 'file') { const uploadItems = state.uploads[key]; return !Array.isArray(uploadItems) || uploadItems.length === 0; } if (type === 'checkbox') { return !isCheckboxTrue(formData[key]); } return String(formData[key] || '').trim() === ''; } function resolveSelectLabel(field, value) { if (!Array.isArray(field.options)) { return value; } const matched = field.options.find((option) => { return option && String(option.value || '') === String(value); }); if (!matched) { return value; } return String(matched.label || value); } function fieldDisplayValue(field, formData) { const key = String(field.key || '').trim(); const type = String(field.type || 'text'); if (!key) { return ''; } if (type === 'file') { const uploadItems = state.uploads[key]; if (!Array.isArray(uploadItems) || uploadItems.length === 0) { return ''; } return uploadItems .map((item) => { if (!item || typeof item !== 'object') { return ''; } return String(item.original_filename || item.stored_filename || '').trim(); }) .filter(Boolean) .join(', '); } if (type === 'checkbox') { return isCheckboxTrue(formData[key]) ? 'Ja' : 'Nein'; } const rawValue = String(formData[key] || '').trim(); if (rawValue === '') { return ''; } if (type === 'select') { return resolveSelectLabel(field, rawValue); } return rawValue; } function renderSummary() { if (!summarySection || !summaryContent || !summaryMissingNotice) { return; } const formData = collectCurrentFormData(); const fragment = document.createDocumentFragment(); let missingCount = 0; const introCard = document.createElement('div'); introCard.className = 'summary-step-card'; const introTitle = document.createElement('h4'); introTitle.textContent = 'Startdaten'; introCard.appendChild(introTitle); const emailRow = document.createElement('div'); emailRow.className = 'summary-item'; const emailLabel = document.createElement('div'); emailLabel.className = 'summary-item-label'; emailLabel.textContent = 'E-Mail'; const emailValue = document.createElement('div'); emailValue.className = 'summary-item-value'; emailValue.textContent = state.email || 'Nicht gesetzt'; emailRow.appendChild(emailLabel); emailRow.appendChild(emailValue); introCard.appendChild(emailRow); fragment.appendChild(introCard); schemaSteps.forEach((step, stepIndex) => { const allFields = Array.isArray(step.fields) ? step.fields.filter((field) => field && typeof field === 'object') : []; const fields = allFields.filter((field) => isFieldVisible(field, formData)); if (fields.length === 0) { return; } const card = document.createElement('div'); card.className = 'summary-step-card'; const title = document.createElement('h4'); title.textContent = 'Schritt ' + String(stepIndex + 1) + ': ' + String(step.title || ''); card.appendChild(title); fields.forEach((field) => { const required = isFieldRequired(field, formData); const missing = isFieldMissing(field, formData); if (missing) { missingCount += 1; } const row = document.createElement('div'); row.className = 'summary-item'; if (required) { row.classList.add('summary-item-required'); } if (missing) { row.classList.add('summary-item-missing'); } const labelEl = document.createElement('div'); labelEl.className = 'summary-item-label'; labelEl.textContent = String(field.label || field.key || 'Feld'); if (required) { const requiredBadge = document.createElement('span'); requiredBadge.className = 'summary-badge summary-badge-required'; requiredBadge.textContent = 'Pflichtfeld'; labelEl.appendChild(requiredBadge); } if (missing) { const missingBadge = document.createElement('span'); missingBadge.className = 'summary-badge summary-badge-missing'; missingBadge.textContent = '! Pflichtfeld fehlt'; labelEl.appendChild(missingBadge); } const valueEl = document.createElement('div'); valueEl.className = 'summary-item-value'; const value = fieldDisplayValue(field, formData); if (value !== '') { valueEl.textContent = value; } else { valueEl.textContent = String(field.type || '') === 'file' ? 'Keine Datei hochgeladen' : 'Nicht ausgefüllt'; valueEl.classList.add('summary-item-value-empty'); if (missing) { valueEl.classList.add('summary-item-value-missing'); } } row.appendChild(labelEl); row.appendChild(valueEl); card.appendChild(row); }); fragment.appendChild(card); }); summaryContent.innerHTML = ''; summaryContent.appendChild(fragment); state.summaryMissingCount = missingCount; if (missingCount > 0) { summaryMissingNotice.classList.remove('hidden'); summaryMissingNotice.classList.add('summary-missing-warning'); summaryMissingNotice.textContent = '! Es fehlen noch ' + String(missingCount) + ' Pflichtfelder. Bitte korrigieren Sie die rot markierten Einträge.'; } else { summaryMissingNotice.classList.remove('hidden'); summaryMissingNotice.classList.remove('summary-missing-warning'); summaryMissingNotice.textContent = 'Alle Pflichtfelder sind ausgefüllt.'; } } function updateProgress() { const isSummary = state.currentStep === state.summaryStep; if (isSummary) { progress.textContent = 'Zusammenfassung (Schritt ' + String(state.summaryStep) + ' von ' + String(state.summaryStep) + ')'; } else { progress.textContent = 'Schritt ' + String(state.currentStep) + ' von ' + String(state.totalSteps); } stepElements.forEach((el) => { const step = Number(el.getAttribute('data-step')); el.classList.toggle('hidden', step !== state.currentStep); }); if (summarySection) { summarySection.classList.toggle('hidden', !isSummary); if (isSummary) { renderSummary(); } } prevBtn.disabled = state.isSubmitting || state.currentStep === 1; nextBtn.textContent = state.currentStep === state.totalSteps ? 'Zur Zusammenfassung' : 'Weiter'; nextBtn.classList.toggle('hidden', state.currentStep >= state.summaryStep); nextBtn.disabled = state.isSubmitting; submitBtn.classList.toggle('hidden', !isSummary); submitBtn.disabled = state.isSubmitting; } function fillFormData(data) { Object.keys(data || {}).forEach((key) => { const field = applicationForm.querySelector('[name="form_data[' + key + ']"]'); if (!field) { return; } if (field.type === 'checkbox') { field.checked = ['1', 'on', 'true', true].includes(data[key]); } else { field.value = data[key] || ''; } }); applyFieldVisibility(); refreshRequiredMarkers(); } function renderUploadInfo(uploads) { state.uploads = uploads && typeof uploads === 'object' ? uploads : {}; document.querySelectorAll('[data-upload-list]').forEach((el) => { el.innerHTML = ''; }); Object.keys(state.uploads).forEach((field) => { const target = document.querySelector('[data-upload-list="' + field + '"]'); if (!target || !Array.isArray(state.uploads[field])) { return; } state.uploads[field].forEach((item) => { const div = document.createElement('div'); div.className = 'upload-item'; div.textContent = item.original_filename + ' (' + item.uploaded_at + ')'; target.appendChild(div); }); }); applyFieldVisibility(); refreshRequiredMarkers(); } function updateUploadSelectionText(fieldKey) { const target = document.querySelector('[data-upload-selected="' + fieldKey + '"]'); if (!target) { return; } const fileInput = applicationForm.querySelector('[name="' + fieldKey + '"]'); const cameraInput = applicationForm.querySelector('[name="' + fieldKey + '__camera"]'); let label = 'Keine Datei gewählt'; if (fileInput && fileInput.files && fileInput.files[0]) { label = 'Ausgewählt: ' + fileInput.files[0].name; } if (cameraInput && cameraInput.files && cameraInput.files[0]) { label = 'Ausgewählt: ' + cameraInput.files[0].name + ' (Foto)'; } target.textContent = label; refreshRequiredMarkers(); } async function triggerInstantUpload() { if (!state.email || wizardSection.classList.contains('hidden')) { return; } setDraftStatus('Datei wird hochgeladen ...', false); try { await saveDraft(true); setFeedback('', false); refreshRequiredMarkers(); if (state.currentStep === state.summaryStep) { renderSummary(); } } catch (err) { const msg = (err.payload && err.payload.message) || err.message || 'Upload fehlgeschlagen.'; setDraftStatus('Upload fehlgeschlagen', true); setFeedback(msg, true); } } function initUploadControls() { const controls = Array.from(document.querySelectorAll('[data-upload-key]')); controls.forEach((control) => { const fieldKey = control.getAttribute('data-upload-key') || ''; if (!fieldKey) { return; } const fileInput = applicationForm.querySelector('[name="' + fieldKey + '"]'); const cameraInput = applicationForm.querySelector('[name="' + fieldKey + '__camera"]'); if (fileInput) { fileInput.addEventListener('change', async () => { if (fileInput.files && fileInput.files[0] && cameraInput) { cameraInput.value = ''; } updateUploadSelectionText(fieldKey); if (fileInput.files && fileInput.files[0]) { await triggerInstantUpload(); } }); } if (cameraInput) { cameraInput.addEventListener('change', async () => { if (cameraInput.files && cameraInput.files[0] && fileInput) { fileInput.value = ''; } updateUploadSelectionText(fieldKey); if (cameraInput.files && cameraInput.files[0]) { await triggerInstantUpload(); } }); } updateUploadSelectionText(fieldKey); }); } async function postForm(url, formData) { const response = await fetch(url, { method: 'POST', body: formData, credentials: 'same-origin', headers: { 'X-Requested-With': 'XMLHttpRequest' }, }); const payload = await response.json(); if (!response.ok || payload.ok === false) { const err = new Error(payload.message || 'Anfrage fehlgeschlagen'); err.payload = payload; throw err; } return payload; } function collectPayload(includeFiles) { const fd = new FormData(); fd.append('csrf', boot.csrf); fd.append('email', state.email); fd.append('step', String(Math.min(state.currentStep, state.totalSteps))); fd.append('website', ''); Array.from(applicationForm.elements).forEach((el) => { if (!el.name) { return; } if (el.disabled) { return; } if (!el.name.startsWith('form_data[')) { return; } if (el.type === 'checkbox') { fd.append(el.name, el.checked ? '1' : '0'); } else { fd.append(el.name, el.value || ''); } }); if (includeFiles) { Array.from(applicationForm.querySelectorAll('input[type="file"]')).forEach((input) => { if (input.disabled) { return; } if (input.files && input.files[0]) { fd.append(input.name, input.files[0]); } }); } return fd; } async function loadDraft(email) { const fd = new FormData(); fd.append('csrf', boot.csrf); fd.append('email', email); fd.append('website', ''); return postForm(appUrl('api/load-draft.php'), fd); } async function resetSavedData(email) { const fd = new FormData(); fd.append('csrf', boot.csrf); fd.append('email', email); fd.append('website', ''); return postForm(appUrl('api/reset.php'), fd); } async function saveDraft(includeFiles) { const payload = collectPayload(includeFiles); const response = await postForm(appUrl('api/save-draft.php'), payload); if (response.upload_errors && Object.keys(response.upload_errors).length > 0) { showErrors(response.upload_errors); setDraftStatus('Uploadfehler', true); setFeedback('Einige Dateien konnten nicht gespeichert werden.', true); } else { const ts = formatTimestamp(response.updated_at); setDraftStatus('Gespeichert: ' + (ts || 'gerade eben'), false); } if (response.uploads) { renderUploadInfo(response.uploads); } if (includeFiles) { Array.from(applicationForm.querySelectorAll('input[type="file"]')).forEach((input) => { input.value = ''; }); Array.from(document.querySelectorAll('[data-upload-key]')).forEach((control) => { const fieldKey = control.getAttribute('data-upload-key'); if (fieldKey) { updateUploadSelectionText(fieldKey); } }); } return response; } function setSubmitting(isSubmitting) { state.isSubmitting = isSubmitting; submitBtn.classList.toggle('is-loading', isSubmitting); if (submitSpinner) { submitSpinner.classList.toggle('hidden', !isSubmitting); } if (submitLabel) { submitLabel.textContent = isSubmitting ? 'Wird gesendet ...' : 'Verbindlich absenden'; } updateProgress(); } async function submitApplication() { setSubmitting(true); setFeedback('Absenden gestartet ...', false); try { const payload = collectPayload(true); const response = await postForm(appUrl('api/submit.php'), payload); clearErrors(); setDraftStatus('Abgeschlossen', false); setFeedback('Antrag erfolgreich abgeschlossen. Vielen Dank.', false); setSubmitting(false); submitBtn.disabled = true; nextBtn.disabled = true; prevBtn.disabled = true; if (submitLabel) { submitLabel.textContent = 'Abgesendet'; } return response; } catch (err) { setSubmitting(false); throw err; } } function validateStartEmail(showError) { const email = normalizeEmail(startEmailInput.value || ''); const valid = isValidEmail(email); startEmailInput.value = email; if (valid) { setStartEmailError(''); startEmailInput.setCustomValidity(''); updateStartEmailRequiredMarker(); return true; } const message = 'Bitte eine gültige E-Mail-Adresse eingeben.'; startEmailInput.setCustomValidity(message); if (showError) { setStartEmailError(message); setFeedback(message, true); } updateStartEmailRequiredMarker(); return false; } async function startProcess(rawEmail) { const email = normalizeEmail(rawEmail); if (!isValidEmail(email)) { const message = 'Bitte eine gültige E-Mail-Adresse eingeben.'; setStartEmailError(message); setFeedback(message, true); startEmailInput.focus(); return; } startEmailInput.value = email; setStartEmailError(''); startEmailInput.setCustomValidity(''); updateStartEmailRequiredMarker(); if (startSubmitBtn) { startSubmitBtn.disabled = true; } try { const result = await loadDraft(email); lockEmail(email); if (result.already_submitted) { wizardSection.classList.add('hidden'); setDraftStatus('Antrag bereits abgeschlossen', false); setFeedback(boot.contactEmail ? 'Kontakt: ' + boot.contactEmail : '', false); stopAutosave(); return; } wizardSection.classList.remove('hidden'); fillFormData(result.data || {}); renderUploadInfo(result.uploads || {}); state.currentStep = Math.min(Math.max(Number(result.step || 1), 1), state.totalSteps); updateProgress(); startAutosave(); const loadedAt = formatTimestamp(result.updated_at); if (loadedAt) { setDraftStatus('Entwurf geladen: ' + loadedAt, false); } else { setDraftStatus('Neuer Entwurf gestartet', false); } setFeedback('', false); } catch (err) { const msg = (err.payload && err.payload.message) || err.message || 'Laden fehlgeschlagen.'; setFeedback(msg, true); } finally { if (startSubmitBtn) { startSubmitBtn.disabled = false; } } } startForm.addEventListener('submit', async (event) => { event.preventDefault(); if (!validateStartEmail(true)) { startEmailInput.focus(); return; } await startProcess(startEmailInput.value || ''); }); startEmailInput.addEventListener('input', () => { if (startEmailInput.readOnly) { return; } if (startEmailInput.value.trim() === '') { setStartEmailError(''); startEmailInput.setCustomValidity(''); updateStartEmailRequiredMarker(); return; } validateStartEmail(false); }); startEmailInput.addEventListener('blur', () => { if (startEmailInput.readOnly) { return; } if (startEmailInput.value.trim() === '') { updateStartEmailRequiredMarker(); return; } validateStartEmail(true); }); resetDataBtn.addEventListener('click', async () => { if (!state.email) { setFeedback('Keine aktive E-Mail vorhanden.', true); return; } const confirmed = window.confirm('Alle gespeicherten Daten zu dieser E-Mail endgültig löschen und neu starten?'); if (!confirmed) { return; } try { await resetSavedData(state.email); stopAutosave(); wizardSection.classList.add('hidden'); clearWizardData(); unlockEmail(true); setFeedback('Alle gespeicherten Daten wurden gelöscht. Sie können neu starten.', false); startEmailInput.focus(); } catch (err) { const msg = (err.payload && err.payload.message) || err.message || 'Löschen fehlgeschlagen.'; setFeedback(msg, true); } }); prevBtn.addEventListener('click', async () => { if (state.currentStep <= 1 || state.isSubmitting) { return; } try { if (state.currentStep <= state.totalSteps) { await saveDraft(false); } state.currentStep -= 1; updateProgress(); } catch (err) { const msg = (err.payload && err.payload.message) || err.message; setFeedback(msg, true); } }); nextBtn.addEventListener('click', async () => { if (state.currentStep >= state.summaryStep || state.isSubmitting) { return; } try { await saveDraft(false); state.currentStep += 1; updateProgress(); } catch (err) { const msg = (err.payload && err.payload.message) || err.message; setFeedback(msg, true); } }); submitBtn.addEventListener('click', async () => { if (state.isSubmitting) { return; } renderSummary(); if (state.summaryMissingCount > 0) { setFeedback('Bitte zuerst alle rot markierten Pflichtfelder ausfüllen.', true); return; } try { await submitApplication(); } catch (err) { const payload = err.payload || {}; if (payload.errors) { showErrors(payload.errors); } const msg = payload.message || err.message || 'Absenden fehlgeschlagen.'; setFeedback(msg, true); if (payload.already_submitted) { wizardSection.classList.add('hidden'); setDraftStatus('Antrag bereits abgeschlossen', false); } } }); function initializeAfterDisclaimer() { disclaimerSection.classList.add('hidden'); startSection.classList.remove('hidden'); const rememberedEmail = normalizeEmail(getRememberedEmail()); if (rememberedEmail !== '' && isValidEmail(rememberedEmail)) { startEmailInput.value = rememberedEmail; setFeedback('', false); startProcess(rememberedEmail); } } if (disclaimerReadCheckbox) { disclaimerReadCheckbox.addEventListener('change', updateDisclaimerAcceptanceState); } if (acceptDisclaimerBtn) { acceptDisclaimerBtn.addEventListener('click', () => { if (!disclaimerReadCheckbox || !disclaimerReadCheckbox.checked) { setDisclaimerError('Bitte lesen und bestätigen Sie den Hinweis.'); return; } setDisclaimerError(''); initializeAfterDisclaimer(); }); } updateDisclaimerAcceptanceState(); disclaimerSection.classList.remove('hidden'); startSection.classList.add('hidden'); initUploadControls(); initRequiredMarkerTracking(); applyFieldVisibility(); refreshRequiredMarkers(); updateStartEmailRequiredMarker(); updateProgress(); })();