(function () { const EMAIL_STORAGE_KEY = 'ff_member_form_email_v1'; const boot = window.APP_BOOT || { steps: [], csrf: '', contactEmail: '' }; const state = { email: '', currentStep: 1, totalSteps: boot.steps.length, autosaveId: null, }; const startForm = document.getElementById('startForm'); const startEmailInput = document.getElementById('startEmail'); const resetDataBtn = document.getElementById('resetDataBtn'); const wizardSection = document.getElementById('wizardSection'); const statusMessage = document.getElementById('statusMessage'); 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 uploadNowBtn = document.getElementById('uploadNowBtn'); const stepElements = Array.from(document.querySelectorAll('.step')); function showMessage(text) { statusMessage.textContent = text || ''; } 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 lockEmail(email) { state.email = email; applicationEmail.value = email; startEmailInput.value = email; startEmailInput.readOnly = true; startEmailInput.setAttribute('aria-readonly', 'true'); resetDataBtn.classList.remove('hidden'); rememberEmail(email); } function unlockEmail(clearInput) { state.email = ''; applicationEmail.value = ''; startEmailInput.readOnly = false; startEmailInput.removeAttribute('aria-readonly'); resetDataBtn.classList.add('hidden'); if (clearInput) { startEmailInput.value = ''; } forgetRememberedEmail(); } 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, 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; submitBtn.disabled = false; nextBtn.disabled = false; prevBtn.disabled = false; updateProgress(); } function updateProgress() { progress.textContent = 'Schritt ' + state.currentStep + ' von ' + state.totalSteps; stepElements.forEach((el) => { const step = Number(el.getAttribute('data-step')); el.classList.toggle('hidden', step !== state.currentStep); }); prevBtn.disabled = state.currentStep === 1; nextBtn.classList.toggle('hidden', state.currentStep === state.totalSteps); submitBtn.classList.toggle('hidden', state.currentStep !== state.totalSteps); } 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] || ''; } }); } function renderUploadInfo(uploads) { document.querySelectorAll('[data-upload-list]').forEach((el) => { el.innerHTML = ''; }); Object.keys(uploads || {}).forEach((field) => { const target = document.querySelector('[data-upload-list="' + field + '"]'); if (!target || !Array.isArray(uploads[field])) return; uploads[field].forEach((item) => { const div = document.createElement('div'); div.className = 'upload-item'; div.textContent = item.original_filename + ' (' + item.uploaded_at + ')'; target.appendChild(div); }); }); } 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; } 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', () => { if (fileInput.files && fileInput.files[0] && cameraInput) { cameraInput.value = ''; } updateUploadSelectionText(fieldKey); }); } if (cameraInput) { cameraInput.addEventListener('change', () => { if (cameraInput.files && cameraInput.files[0] && fileInput) { fileInput.value = ''; } updateUploadSelectionText(fieldKey); }); } 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(state.currentStep)); fd.append('website', ''); Array.from(applicationForm.elements).forEach((el) => { if (!el.name) 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.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('/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('/api/reset.php', fd); } async function saveDraft(includeFiles, showSavedText) { const payload = collectPayload(includeFiles); const response = await postForm('/api/save-draft.php', payload); if (response.upload_errors && Object.keys(response.upload_errors).length > 0) { showErrors(response.upload_errors); showMessage('Einige Dateien konnten nicht gespeichert werden.'); } else if (showSavedText) { showMessage('Entwurf gespeichert: ' + (response.updated_at || '')); } 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; } async function submitApplication() { const payload = collectPayload(true); const response = await postForm('/api/submit.php', payload); clearErrors(); showMessage('Antrag erfolgreich abgeschlossen. Vielen Dank.'); submitBtn.disabled = true; nextBtn.disabled = true; prevBtn.disabled = true; return response; } async function startProcess(rawEmail) { const email = normalizeEmail(rawEmail); if (!isValidEmail(email)) { showMessage('Bitte eine gültige E-Mail-Adresse eingeben.'); startEmailInput.focus(); return; } try { const result = await loadDraft(email); lockEmail(email); if (result.already_submitted) { wizardSection.classList.add('hidden'); showMessage( (result.message || 'Antrag bereits abgeschlossen.') + (boot.contactEmail ? ' Kontakt: ' + boot.contactEmail : '') ); 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(); showMessage('Formular geladen. Entwurf wird automatisch gespeichert.'); } catch (err) { const msg = (err.payload && err.payload.message) || err.message || 'Laden fehlgeschlagen.'; showMessage(msg); } } startForm.addEventListener('submit', async (event) => { event.preventDefault(); await startProcess(startEmailInput.value || ''); }); resetDataBtn.addEventListener('click', async () => { if (!state.email) { showMessage('Keine aktive E-Mail vorhanden.'); 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); showMessage('Alle gespeicherten Daten wurden gelöscht. Sie können neu starten.'); startEmailInput.focus(); } catch (err) { const msg = (err.payload && err.payload.message) || err.message || 'Löschen fehlgeschlagen.'; showMessage(msg); } }); prevBtn.addEventListener('click', async () => { if (state.currentStep <= 1) return; try { await saveDraft(false, true); state.currentStep -= 1; updateProgress(); } catch (err) { const msg = (err.payload && err.payload.message) || err.message; showMessage(msg); } }); nextBtn.addEventListener('click', async () => { if (state.currentStep >= state.totalSteps) return; try { await saveDraft(false, true); state.currentStep += 1; updateProgress(); } catch (err) { const msg = (err.payload && err.payload.message) || err.message; showMessage(msg); } }); if (uploadNowBtn) { uploadNowBtn.addEventListener('click', async () => { try { await saveDraft(true, true); } catch (err) { const msg = (err.payload && err.payload.message) || err.message; showMessage(msg); } }); } submitBtn.addEventListener('click', async () => { try { await submitApplication(); } catch (err) { const payload = err.payload || {}; if (payload.errors) { showErrors(payload.errors); } const msg = payload.message || err.message || 'Absenden fehlgeschlagen.'; showMessage(msg); if (payload.already_submitted) { wizardSection.classList.add('hidden'); } } }); const rememberedEmail = normalizeEmail(getRememberedEmail()); if (rememberedEmail !== '') { if (isValidEmail(rememberedEmail)) { startEmailInput.value = rememberedEmail; showMessage('Gespeicherte E-Mail erkannt. Formular wird geladen ...'); startProcess(rememberedEmail); } else { forgetRememberedEmail(); } } initUploadControls(); updateProgress(); })();