Sfoglia il codice sorgente

adding option to show fields depending on previous selection type

Medowar 1 mese fa
parent
commit
ebfdec0ecc
4 ha cambiato i file con 108 aggiunte e 13 eliminazioni
  1. 4 0
      assets/css/base.css
  2. 79 11
      assets/js/form.js
  3. 1 1
      config/form_schema.php
  4. 24 1
      src/Form/Validator.php

+ 4 - 0
assets/css/base.css

@@ -354,6 +354,10 @@ small {
   display: none;
 }
 
+.field-hidden-by-rule {
+  display: none !important;
+}
+
 .upload-control {
   border: 1px dashed var(--brand-border);
   border-radius: 8px;

+ 79 - 11
assets/js/form.js

@@ -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();

+ 1 - 1
config/form_schema.php

@@ -41,7 +41,7 @@ return [
                 ['key' => 'portraitfoto', 'label' => 'Portraitfoto', 'type' => 'file', 'required' => true, 'accept' => '.jpg,.jpeg,.png,.webp', 'description' => 'Bitte ein aktuelles, gut erkennbares Foto hochladen.'],
                 ['key' => 'ausweisnachweis', 'label' => 'Ausweisnachweis', 'type' => 'file', 'required' => true, 'accept' => '.pdf,.jpg,.jpeg,.png', 'description' => 'Vorder- und Rueckseite gut lesbar hochladen.'],
                 ['key' => 'qualifikationsnachweise', 'label' => 'Qualifikationsnachweise', 'type' => 'file', 'required' => false, 'required_if' => ['field' => 'qualifikation_vorhanden', 'equals' => 'ja'], 'accept' => '.pdf,.jpg,.jpeg,.png', 'description' => 'Nur erforderlich, wenn Qualifikationen mit Ja beantwortet wurden.'],
-                ['key' => 'einverstaendniserklaerung', 'label' => 'Einverständniserklärung Erziehungsberechtigte', 'type' => 'file', 'required' => false, 'required_if' => ['field' => 'ist_minderjaehrig', 'equals' => 'ja'], 'accept' => '.pdf,.jpg,.jpeg,.png', 'description' => 'Bei Minderjaehrigen mit Unterschrift der Erziehungsberechtigten.'],
+                ['key' => 'einverstaendniserklaerung', 'label' => 'Einverständniserklärung Erziehungsberechtigte', 'type' => 'file', 'required' => false, 'visible_if' => ['field' => 'mitgliedsart', 'equals' => 'Jugend'], 'accept' => '.pdf,.jpg,.jpeg,.png', 'description' => 'Wird bei Mitgliedsart Jugend angezeigt.'],
                 ['key' => 'zusatzunterlagen', 'label' => 'Zusatzunterlagen (optional)', 'type' => 'file', 'required' => false, 'accept' => '.pdf,.jpg,.jpeg,.png,.webp', 'description' => 'Optional: weitere relevante Unterlagen.'],
             ],
         ],

+ 24 - 1
src/Form/Validator.php

@@ -25,6 +25,10 @@ final class Validator
 
         foreach ($this->fields as $key => $field) {
             $type = $field['type'] ?? 'text';
+            $visible = $this->isVisible($field, $data);
+            if (!$visible) {
+                continue;
+            }
             $required = $this->isRequired($field, $data);
 
             if ($type === 'file') {
@@ -70,6 +74,10 @@ final class Validator
     /** @param array<string, mixed> $field */
     private function isRequired(array $field, array $data): bool
     {
+        if (!$this->isVisible($field, $data)) {
+            return false;
+        }
+
         if (($field['required'] ?? false) === true) {
             return true;
         }
@@ -78,7 +86,22 @@ final class Validator
             return false;
         }
 
-        $rule = $field['required_if'];
+        return $this->ruleMatches($field['required_if'], $data);
+    }
+
+    /** @param array<string, mixed> $field */
+    private function isVisible(array $field, array $data): bool
+    {
+        if (!isset($field['visible_if']) || !is_array($field['visible_if'])) {
+            return true;
+        }
+
+        return $this->ruleMatches($field['visible_if'], $data);
+    }
+
+    /** @param array<string, mixed> $rule */
+    private function ruleMatches(array $rule, array $data): bool
+    {
         $sourceField = (string) ($rule['field'] ?? '');
         $equals = (string) ($rule['equals'] ?? '');