|
|
@@ -284,6 +284,7 @@
|
|
|
submitSpinner.classList.add('hidden');
|
|
|
}
|
|
|
submitBtn.classList.remove('is-loading');
|
|
|
+ applyFieldVisibility();
|
|
|
refreshRequiredMarkers();
|
|
|
updateProgress();
|
|
|
Array.from(document.querySelectorAll('[data-upload-key]')).forEach((control) => {
|
|
|
@@ -326,6 +327,32 @@
|
|
|
return 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"]');
|
|
|
@@ -359,6 +386,32 @@
|
|
|
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();
|
|
|
|
|
|
@@ -373,9 +426,10 @@
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
+ const visibleNow = isFieldVisible(field, formData);
|
|
|
const requiredNow = isFieldRequired(field, formData);
|
|
|
const completed = isFieldCompleted(field, formData);
|
|
|
- const hideMarker = !requiredNow || completed;
|
|
|
+ const hideMarker = !visibleNow || !requiredNow || completed;
|
|
|
|
|
|
markers.forEach((marker) => {
|
|
|
marker.classList.toggle('hidden', hideMarker);
|
|
|
@@ -388,8 +442,14 @@
|
|
|
if (!el.name || !el.name.startsWith('form_data[')) {
|
|
|
return;
|
|
|
}
|
|
|
- el.addEventListener('blur', refreshRequiredMarkers);
|
|
|
- el.addEventListener('change', refreshRequiredMarkers);
|
|
|
+ el.addEventListener('blur', () => {
|
|
|
+ applyFieldVisibility();
|
|
|
+ refreshRequiredMarkers();
|
|
|
+ });
|
|
|
+ el.addEventListener('change', () => {
|
|
|
+ applyFieldVisibility();
|
|
|
+ refreshRequiredMarkers();
|
|
|
+ });
|
|
|
});
|
|
|
}
|
|
|
|
|
|
@@ -398,6 +458,10 @@
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
+ if (!isFieldVisible(field, formData)) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
if (field.required === true) {
|
|
|
return true;
|
|
|
}
|
|
|
@@ -406,13 +470,7 @@
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
- const depField = String(field.required_if.field || '').trim();
|
|
|
- const depValue = String(field.required_if.equals || '');
|
|
|
- if (!depField) {
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- return String(formData[depField] || '') === depValue;
|
|
|
+ return evaluateFieldRule(field.required_if, formData);
|
|
|
}
|
|
|
|
|
|
function isFieldMissing(field, formData) {
|
|
|
@@ -523,7 +581,8 @@
|
|
|
fragment.appendChild(introCard);
|
|
|
|
|
|
schemaSteps.forEach((step, stepIndex) => {
|
|
|
- const fields = Array.isArray(step.fields) ? step.fields.filter((field) => field && typeof field === 'object') : [];
|
|
|
+ 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;
|
|
|
}
|
|
|
@@ -649,6 +708,7 @@
|
|
|
field.value = data[key] || '';
|
|
|
}
|
|
|
});
|
|
|
+ applyFieldVisibility();
|
|
|
refreshRequiredMarkers();
|
|
|
}
|
|
|
|
|
|
@@ -672,6 +732,7 @@
|
|
|
target.appendChild(div);
|
|
|
});
|
|
|
});
|
|
|
+ applyFieldVisibility();
|
|
|
refreshRequiredMarkers();
|
|
|
}
|
|
|
|
|
|
@@ -785,6 +846,9 @@
|
|
|
if (!el.name) {
|
|
|
return;
|
|
|
}
|
|
|
+ if (el.disabled) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
if (!el.name.startsWith('form_data[')) {
|
|
|
return;
|
|
|
}
|
|
|
@@ -798,6 +862,9 @@
|
|
|
|
|
|
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]);
|
|
|
}
|
|
|
@@ -1120,6 +1187,7 @@
|
|
|
|
|
|
initUploadControls();
|
|
|
initRequiredMarkerTracking();
|
|
|
+ applyFieldVisibility();
|
|
|
refreshRequiredMarkers();
|
|
|
updateStartEmailRequiredMarker();
|
|
|
updateProgress();
|