|
@@ -1,5 +1,7 @@
|
|
|
(function () {
|
|
(function () {
|
|
|
- const boot = window.APP_BOOT || { steps: [], csrf: '' };
|
|
|
|
|
|
|
+ const EMAIL_STORAGE_KEY = 'ff_member_form_email_v1';
|
|
|
|
|
+ const boot = window.APP_BOOT || { steps: [], csrf: '', contactEmail: '' };
|
|
|
|
|
+
|
|
|
const state = {
|
|
const state = {
|
|
|
email: '',
|
|
email: '',
|
|
|
currentStep: 1,
|
|
currentStep: 1,
|
|
@@ -8,9 +10,9 @@
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
const startForm = document.getElementById('startForm');
|
|
const startForm = document.getElementById('startForm');
|
|
|
|
|
+ const startEmailInput = document.getElementById('startEmail');
|
|
|
|
|
+ const resetDataBtn = document.getElementById('resetDataBtn');
|
|
|
const wizardSection = document.getElementById('wizardSection');
|
|
const wizardSection = document.getElementById('wizardSection');
|
|
|
- const blockedSection = document.getElementById('blockedSection');
|
|
|
|
|
- const statusSection = document.getElementById('statusSection');
|
|
|
|
|
const statusMessage = document.getElementById('statusMessage');
|
|
const statusMessage = document.getElementById('statusMessage');
|
|
|
const applicationForm = document.getElementById('applicationForm');
|
|
const applicationForm = document.getElementById('applicationForm');
|
|
|
const applicationEmail = document.getElementById('applicationEmail');
|
|
const applicationEmail = document.getElementById('applicationEmail');
|
|
@@ -23,8 +25,82 @@
|
|
|
const stepElements = Array.from(document.querySelectorAll('.step'));
|
|
const stepElements = Array.from(document.querySelectorAll('.step'));
|
|
|
|
|
|
|
|
function showMessage(text) {
|
|
function showMessage(text) {
|
|
|
- statusSection.classList.remove('hidden');
|
|
|
|
|
- statusMessage.textContent = 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() {
|
|
function clearErrors() {
|
|
@@ -43,6 +119,17 @@
|
|
|
});
|
|
});
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ function clearWizardData() {
|
|
|
|
|
+ applicationForm.reset();
|
|
|
|
|
+ clearErrors();
|
|
|
|
|
+ renderUploadInfo({});
|
|
|
|
|
+ state.currentStep = 1;
|
|
|
|
|
+ submitBtn.disabled = false;
|
|
|
|
|
+ nextBtn.disabled = false;
|
|
|
|
|
+ prevBtn.disabled = false;
|
|
|
|
|
+ updateProgress();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
function updateProgress() {
|
|
function updateProgress() {
|
|
|
progress.textContent = 'Schritt ' + state.currentStep + ' von ' + state.totalSteps;
|
|
progress.textContent = 'Schritt ' + state.currentStep + ' von ' + state.totalSteps;
|
|
|
stepElements.forEach((el) => {
|
|
stepElements.forEach((el) => {
|
|
@@ -138,10 +225,17 @@
|
|
|
fd.append('csrf', boot.csrf);
|
|
fd.append('csrf', boot.csrf);
|
|
|
fd.append('email', email);
|
|
fd.append('email', email);
|
|
|
fd.append('website', '');
|
|
fd.append('website', '');
|
|
|
-
|
|
|
|
|
return postForm('/api/load-draft.php', fd);
|
|
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) {
|
|
async function saveDraft(includeFiles, showSavedText) {
|
|
|
const payload = collectPayload(includeFiles);
|
|
const payload = collectPayload(includeFiles);
|
|
|
const response = await postForm('/api/save-draft.php', payload);
|
|
const response = await postForm('/api/save-draft.php', payload);
|
|
@@ -150,7 +244,7 @@
|
|
|
showErrors(response.upload_errors);
|
|
showErrors(response.upload_errors);
|
|
|
showMessage('Einige Dateien konnten nicht gespeichert werden.');
|
|
showMessage('Einige Dateien konnten nicht gespeichert werden.');
|
|
|
} else if (showSavedText) {
|
|
} else if (showSavedText) {
|
|
|
- showMessage('Entwurf gespeichert: ' + (response.updated_at || ''));
|
|
|
|
|
|
|
+ showMessage('Entwurf gespeichert: ' + (response.updated_at || ''));
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
if (response.uploads) {
|
|
if (response.uploads) {
|
|
@@ -177,56 +271,71 @@
|
|
|
return response;
|
|
return response;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- startForm.addEventListener('submit', async (event) => {
|
|
|
|
|
- event.preventDefault();
|
|
|
|
|
-
|
|
|
|
|
- const emailInput = document.getElementById('startEmail');
|
|
|
|
|
- const email = (emailInput.value || '').trim().toLowerCase();
|
|
|
|
|
- if (!email) {
|
|
|
|
|
- showMessage('Bitte E-Mail eingeben.');
|
|
|
|
|
|
|
+ async function startProcess(rawEmail) {
|
|
|
|
|
+ const email = normalizeEmail(rawEmail);
|
|
|
|
|
+ if (!isValidEmail(email)) {
|
|
|
|
|
+ showMessage('Bitte eine gültige E-Mail-Adresse eingeben.');
|
|
|
|
|
+ startEmailInput.focus();
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
try {
|
|
|
const result = await loadDraft(email);
|
|
const result = await loadDraft(email);
|
|
|
- state.email = email;
|
|
|
|
|
- applicationEmail.value = email;
|
|
|
|
|
|
|
+ lockEmail(email);
|
|
|
|
|
|
|
|
if (result.already_submitted) {
|
|
if (result.already_submitted) {
|
|
|
wizardSection.classList.add('hidden');
|
|
wizardSection.classList.add('hidden');
|
|
|
- blockedSection.classList.remove('hidden');
|
|
|
|
|
- showMessage(result.message || 'Antrag bereits abgeschlossen.');
|
|
|
|
|
|
|
+ showMessage(
|
|
|
|
|
+ (result.message || 'Antrag bereits abgeschlossen.') +
|
|
|
|
|
+ (boot.contactEmail ? ' Kontakt: ' + boot.contactEmail : '')
|
|
|
|
|
+ );
|
|
|
|
|
+ stopAutosave();
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- blockedSection.classList.add('hidden');
|
|
|
|
|
wizardSection.classList.remove('hidden');
|
|
wizardSection.classList.remove('hidden');
|
|
|
-
|
|
|
|
|
fillFormData(result.data || {});
|
|
fillFormData(result.data || {});
|
|
|
renderUploadInfo(result.uploads || {});
|
|
renderUploadInfo(result.uploads || {});
|
|
|
|
|
|
|
|
state.currentStep = Math.min(Math.max(Number(result.step || 1), 1), state.totalSteps);
|
|
state.currentStep = Math.min(Math.max(Number(result.step || 1), 1), state.totalSteps);
|
|
|
updateProgress();
|
|
updateProgress();
|
|
|
|
|
+ startAutosave();
|
|
|
|
|
|
|
|
showMessage('Formular geladen. Entwurf wird automatisch gespeichert.');
|
|
showMessage('Formular geladen. Entwurf wird automatisch gespeichert.');
|
|
|
-
|
|
|
|
|
- if (state.autosaveId) {
|
|
|
|
|
- clearInterval(state.autosaveId);
|
|
|
|
|
- }
|
|
|
|
|
- state.autosaveId = setInterval(async () => {
|
|
|
|
|
- if (!state.email || wizardSection.classList.contains('hidden')) {
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
- try {
|
|
|
|
|
- await saveDraft(false, false);
|
|
|
|
|
- } catch (_err) {
|
|
|
|
|
- // Autsave errors are visible on next manual action.
|
|
|
|
|
- }
|
|
|
|
|
- }, 15000);
|
|
|
|
|
} catch (err) {
|
|
} catch (err) {
|
|
|
const msg = (err.payload && err.payload.message) || err.message || 'Laden fehlgeschlagen.';
|
|
const msg = (err.payload && err.payload.message) || err.message || 'Laden fehlgeschlagen.';
|
|
|
showMessage(msg);
|
|
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 () => {
|
|
prevBtn.addEventListener('click', async () => {
|
|
@@ -277,11 +386,21 @@
|
|
|
const msg = payload.message || err.message || 'Absenden fehlgeschlagen.';
|
|
const msg = payload.message || err.message || 'Absenden fehlgeschlagen.';
|
|
|
showMessage(msg);
|
|
showMessage(msg);
|
|
|
if (payload.already_submitted) {
|
|
if (payload.already_submitted) {
|
|
|
- blockedSection.classList.remove('hidden');
|
|
|
|
|
wizardSection.classList.add('hidden');
|
|
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();
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
updateProgress();
|
|
updateProgress();
|
|
|
})();
|
|
})();
|