|
|
@@ -1,5 +1,6 @@
|
|
|
(function () {
|
|
|
const EMAIL_STORAGE_KEY = 'ff_member_form_email_v1';
|
|
|
+ const DISCLAIMER_ACCEPTED_KEY = 'ff_member_form_disclaimer_accepted_v1';
|
|
|
const boot = window.APP_BOOT || { steps: [], csrf: '', contactEmail: '' };
|
|
|
|
|
|
const state = {
|
|
|
@@ -9,23 +10,50 @@
|
|
|
autosaveId: null,
|
|
|
};
|
|
|
|
|
|
+ const disclaimerSection = document.getElementById('disclaimerSection');
|
|
|
+ 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 resetDataBtn = document.getElementById('resetDataBtn');
|
|
|
+ const compactStatusBox = document.getElementById('compactStatusBox');
|
|
|
+ const statusEmailValue = document.getElementById('statusEmailValue');
|
|
|
+ const draftStatusValue = document.getElementById('draftStatusValue');
|
|
|
+ const feedbackMessage = document.getElementById('feedbackMessage');
|
|
|
+
|
|
|
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 setFeedback(text, isError) {
|
|
|
+ feedbackMessage.textContent = text || '';
|
|
|
+ feedbackMessage.classList.toggle('error-text', Boolean(isError));
|
|
|
+ }
|
|
|
+
|
|
|
+ function setDraftStatus(text, isError) {
|
|
|
+ draftStatusValue.textContent = text || '';
|
|
|
+ draftStatusValue.classList.toggle('error-text', Boolean(isError));
|
|
|
+ }
|
|
|
+
|
|
|
+ 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) {
|
|
|
@@ -60,14 +88,49 @@
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ function hasAcceptedDisclaimer() {
|
|
|
+ try {
|
|
|
+ return localStorage.getItem(DISCLAIMER_ACCEPTED_KEY) === '1';
|
|
|
+ } catch (_err) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ function setDisclaimerAccepted() {
|
|
|
+ try {
|
|
|
+ localStorage.setItem(DISCLAIMER_ACCEPTED_KEY, '1');
|
|
|
+ } catch (_err) {
|
|
|
+ // ignore localStorage errors
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ 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');
|
|
|
- resetDataBtn.classList.remove('hidden');
|
|
|
rememberEmail(email);
|
|
|
+ enterCompactStatus(email);
|
|
|
}
|
|
|
|
|
|
function unlockEmail(clearInput) {
|
|
|
@@ -75,11 +138,11 @@
|
|
|
applicationEmail.value = '';
|
|
|
startEmailInput.readOnly = false;
|
|
|
startEmailInput.removeAttribute('aria-readonly');
|
|
|
- resetDataBtn.classList.add('hidden');
|
|
|
if (clearInput) {
|
|
|
startEmailInput.value = '';
|
|
|
}
|
|
|
forgetRememberedEmail();
|
|
|
+ leaveCompactStatus();
|
|
|
}
|
|
|
|
|
|
function stopAutosave() {
|
|
|
@@ -96,7 +159,7 @@
|
|
|
return;
|
|
|
}
|
|
|
try {
|
|
|
- await saveDraft(false, false);
|
|
|
+ await saveDraft(false);
|
|
|
} catch (_err) {
|
|
|
// visible on next manual action
|
|
|
}
|
|
|
@@ -128,6 +191,12 @@
|
|
|
nextBtn.disabled = false;
|
|
|
prevBtn.disabled = false;
|
|
|
updateProgress();
|
|
|
+ Array.from(document.querySelectorAll('[data-upload-key]')).forEach((control) => {
|
|
|
+ const fieldKey = control.getAttribute('data-upload-key');
|
|
|
+ if (fieldKey) {
|
|
|
+ updateUploadSelectionText(fieldKey);
|
|
|
+ }
|
|
|
+ });
|
|
|
}
|
|
|
|
|
|
function updateProgress() {
|
|
|
@@ -193,6 +262,22 @@
|
|
|
target.textContent = label;
|
|
|
}
|
|
|
|
|
|
+ async function triggerInstantUpload() {
|
|
|
+ if (!state.email || wizardSection.classList.contains('hidden')) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ setDraftStatus('Datei wird hochgeladen ...', false);
|
|
|
+ try {
|
|
|
+ await saveDraft(true);
|
|
|
+ setFeedback('', false);
|
|
|
+ } 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]'));
|
|
|
|
|
|
@@ -206,20 +291,26 @@
|
|
|
const cameraInput = applicationForm.querySelector('[name="' + fieldKey + '__camera"]');
|
|
|
|
|
|
if (fileInput) {
|
|
|
- fileInput.addEventListener('change', () => {
|
|
|
+ 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', () => {
|
|
|
+ cameraInput.addEventListener('change', async () => {
|
|
|
if (cameraInput.files && cameraInput.files[0] && fileInput) {
|
|
|
fileInput.value = '';
|
|
|
}
|
|
|
updateUploadSelectionText(fieldKey);
|
|
|
+ if (cameraInput.files && cameraInput.files[0]) {
|
|
|
+ await triggerInstantUpload();
|
|
|
+ }
|
|
|
});
|
|
|
}
|
|
|
|
|
|
@@ -290,15 +381,17 @@
|
|
|
return postForm('/api/reset.php', fd);
|
|
|
}
|
|
|
|
|
|
- async function saveDraft(includeFiles, showSavedText) {
|
|
|
+ async function saveDraft(includeFiles) {
|
|
|
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 || ''));
|
|
|
+ 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) {
|
|
|
@@ -324,7 +417,8 @@
|
|
|
const payload = collectPayload(true);
|
|
|
const response = await postForm('/api/submit.php', payload);
|
|
|
clearErrors();
|
|
|
- showMessage('Antrag erfolgreich abgeschlossen. Vielen Dank.');
|
|
|
+ setDraftStatus('Abgeschlossen', false);
|
|
|
+ setFeedback('Antrag erfolgreich abgeschlossen. Vielen Dank.', false);
|
|
|
submitBtn.disabled = true;
|
|
|
nextBtn.disabled = true;
|
|
|
prevBtn.disabled = true;
|
|
|
@@ -334,7 +428,7 @@
|
|
|
async function startProcess(rawEmail) {
|
|
|
const email = normalizeEmail(rawEmail);
|
|
|
if (!isValidEmail(email)) {
|
|
|
- showMessage('Bitte eine gültige E-Mail-Adresse eingeben.');
|
|
|
+ setFeedback('Bitte eine gültige E-Mail-Adresse eingeben.', true);
|
|
|
startEmailInput.focus();
|
|
|
return;
|
|
|
}
|
|
|
@@ -345,10 +439,8 @@
|
|
|
|
|
|
if (result.already_submitted) {
|
|
|
wizardSection.classList.add('hidden');
|
|
|
- showMessage(
|
|
|
- (result.message || 'Antrag bereits abgeschlossen.') +
|
|
|
- (boot.contactEmail ? ' Kontakt: ' + boot.contactEmail : '')
|
|
|
- );
|
|
|
+ setDraftStatus('Antrag bereits abgeschlossen', false);
|
|
|
+ setFeedback(boot.contactEmail ? 'Kontakt: ' + boot.contactEmail : '', false);
|
|
|
stopAutosave();
|
|
|
return;
|
|
|
}
|
|
|
@@ -361,10 +453,16 @@
|
|
|
updateProgress();
|
|
|
startAutosave();
|
|
|
|
|
|
- showMessage('Formular geladen. Entwurf wird automatisch gespeichert.');
|
|
|
+ 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.';
|
|
|
- showMessage(msg);
|
|
|
+ setFeedback(msg, true);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -375,7 +473,7 @@
|
|
|
|
|
|
resetDataBtn.addEventListener('click', async () => {
|
|
|
if (!state.email) {
|
|
|
- showMessage('Keine aktive E-Mail vorhanden.');
|
|
|
+ setFeedback('Keine aktive E-Mail vorhanden.', true);
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
@@ -390,11 +488,11 @@
|
|
|
wizardSection.classList.add('hidden');
|
|
|
clearWizardData();
|
|
|
unlockEmail(true);
|
|
|
- showMessage('Alle gespeicherten Daten wurden gelöscht. Sie können neu starten.');
|
|
|
+ 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.';
|
|
|
- showMessage(msg);
|
|
|
+ setFeedback(msg, true);
|
|
|
}
|
|
|
});
|
|
|
|
|
|
@@ -402,12 +500,12 @@
|
|
|
if (state.currentStep <= 1) return;
|
|
|
|
|
|
try {
|
|
|
- await saveDraft(false, true);
|
|
|
+ await saveDraft(false);
|
|
|
state.currentStep -= 1;
|
|
|
updateProgress();
|
|
|
} catch (err) {
|
|
|
const msg = (err.payload && err.payload.message) || err.message;
|
|
|
- showMessage(msg);
|
|
|
+ setFeedback(msg, true);
|
|
|
}
|
|
|
});
|
|
|
|
|
|
@@ -415,26 +513,15 @@
|
|
|
if (state.currentStep >= state.totalSteps) return;
|
|
|
|
|
|
try {
|
|
|
- await saveDraft(false, true);
|
|
|
+ await saveDraft(false);
|
|
|
state.currentStep += 1;
|
|
|
updateProgress();
|
|
|
} catch (err) {
|
|
|
const msg = (err.payload && err.payload.message) || err.message;
|
|
|
- showMessage(msg);
|
|
|
+ setFeedback(msg, true);
|
|
|
}
|
|
|
});
|
|
|
|
|
|
- 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();
|
|
|
@@ -444,24 +531,44 @@
|
|
|
showErrors(payload.errors);
|
|
|
}
|
|
|
const msg = payload.message || err.message || 'Absenden fehlgeschlagen.';
|
|
|
- showMessage(msg);
|
|
|
+ setFeedback(msg, true);
|
|
|
if (payload.already_submitted) {
|
|
|
wizardSection.classList.add('hidden');
|
|
|
+ setDraftStatus('Antrag bereits abgeschlossen', false);
|
|
|
}
|
|
|
}
|
|
|
});
|
|
|
|
|
|
- const rememberedEmail = normalizeEmail(getRememberedEmail());
|
|
|
- if (rememberedEmail !== '') {
|
|
|
- if (isValidEmail(rememberedEmail)) {
|
|
|
- startEmailInput.value = rememberedEmail;
|
|
|
- showMessage('Gespeicherte E-Mail erkannt. Formular wird geladen ...');
|
|
|
- startProcess(rememberedEmail);
|
|
|
- } else {
|
|
|
- forgetRememberedEmail();
|
|
|
+ function initializeAfterDisclaimer() {
|
|
|
+ disclaimerSection.classList.add('hidden');
|
|
|
+ startSection.classList.remove('hidden');
|
|
|
+
|
|
|
+ const rememberedEmail = normalizeEmail(getRememberedEmail());
|
|
|
+ if (rememberedEmail !== '') {
|
|
|
+ if (isValidEmail(rememberedEmail)) {
|
|
|
+ startEmailInput.value = rememberedEmail;
|
|
|
+ setFeedback('', false);
|
|
|
+ startProcess(rememberedEmail);
|
|
|
+ } else {
|
|
|
+ forgetRememberedEmail();
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ if (acceptDisclaimerBtn) {
|
|
|
+ acceptDisclaimerBtn.addEventListener('click', () => {
|
|
|
+ setDisclaimerAccepted();
|
|
|
+ initializeAfterDisclaimer();
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ if (hasAcceptedDisclaimer()) {
|
|
|
+ initializeAfterDisclaimer();
|
|
|
+ } else {
|
|
|
+ disclaimerSection.classList.remove('hidden');
|
|
|
+ startSection.classList.add('hidden');
|
|
|
+ }
|
|
|
+
|
|
|
initUploadControls();
|
|
|
updateProgress();
|
|
|
})();
|