Kaynağa Gözat

improving upload formatting, adding description to upload buttons, small changes

Medowar 1 ay önce
ebeveyn
işleme
5f0f4e7d5b
4 değiştirilmiş dosya ile 44 ekleme ve 12 silme
  1. 4 0
      assets/css/base.css
  2. 26 3
      assets/js/form.js
  3. 5 5
      config/form_schema.php
  4. 9 4
      index.php

+ 4 - 0
assets/css/base.css

@@ -306,6 +306,10 @@ button:disabled {
   gap: 0.75rem;
 }
 
+.upload-actions {
+  grid-template-columns: repeat(2, minmax(0, 1fr));
+}
+
 .error,
 .error-text {
   color: var(--brand-danger);

+ 26 - 3
assets/js/form.js

@@ -32,6 +32,7 @@
   const compactStatusBox = document.getElementById('compactStatusBox');
   const statusEmailValue = document.getElementById('statusEmailValue');
   const draftStatusValue = document.getElementById('draftStatusValue');
+  const startFeedbackMessage = document.getElementById('startFeedbackMessage');
   const feedbackMessage = document.getElementById('feedbackMessage');
 
   const wizardSection = document.getElementById('wizardSection');
@@ -76,9 +77,31 @@
     return (baseUrl ? baseUrl + '/' : '/') + normalizedPath;
   }
 
-  function setFeedback(text, isError) {
-    feedbackMessage.textContent = text || '';
-    feedbackMessage.classList.toggle('error-text', Boolean(isError));
+  function setFeedbackText(target, text, isError) {
+    if (!target) {
+      return;
+    }
+    target.textContent = text || '';
+    target.classList.toggle('error-text', Boolean(isError));
+  }
+
+  function setFeedback(text, isError, scope) {
+    const targetScope = scope || (wizardSection && !wizardSection.classList.contains('hidden') ? 'wizard' : 'start');
+
+    if (targetScope === 'wizard') {
+      setFeedbackText(feedbackMessage, text, isError);
+      setFeedbackText(startFeedbackMessage, '', false);
+      return;
+    }
+
+    if (targetScope === 'start') {
+      setFeedbackText(startFeedbackMessage, text, isError);
+      setFeedbackText(feedbackMessage, '', false);
+      return;
+    }
+
+    setFeedbackText(feedbackMessage, text, isError);
+    setFeedbackText(startFeedbackMessage, text, isError);
   }
 
   function setDraftStatus(text, isError) {

+ 5 - 5
config/form_schema.php

@@ -38,11 +38,11 @@ return [
             'title' => 'Uploads',
             'description' => 'Bitte laden Sie die erforderlichen Unterlagen hoch.',
             'fields' => [
-                ['key' => 'portraitfoto', 'label' => 'Portraitfoto', 'type' => 'file', 'required' => true, 'accept' => '.jpg,.jpeg,.png,.webp'],
-                ['key' => 'ausweisnachweis', 'label' => 'Ausweisnachweis', 'type' => 'file', 'required' => true, 'accept' => '.pdf,.jpg,.jpeg,.png'],
-                ['key' => 'qualifikationsnachweise', 'label' => 'Qualifikationsnachweise', 'type' => 'file', 'required' => false, 'required_if' => ['field' => 'qualifikation_vorhanden', 'equals' => 'ja'], 'accept' => '.pdf,.jpg,.jpeg,.png'],
-                ['key' => 'einverstaendniserklaerung', 'label' => 'Einverständniserklärung Erziehungsberechtigte', 'type' => 'file', 'required' => false, 'required_if' => ['field' => 'ist_minderjaehrig', 'equals' => 'ja'], 'accept' => '.pdf,.jpg,.jpeg,.png'],
-                ['key' => 'zusatzunterlagen', 'label' => 'Zusatzunterlagen (optional)', 'type' => 'file', 'required' => false, 'accept' => '.pdf,.jpg,.jpeg,.png,.webp'],
+                ['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' => 'zusatzunterlagen', 'label' => 'Zusatzunterlagen (optional)', 'type' => 'file', 'required' => false, 'accept' => '.pdf,.jpg,.jpeg,.png,.webp', 'description' => 'Optional: weitere relevante Unterlagen.'],
             ],
         ],
         [

+ 9 - 4
index.php

@@ -49,7 +49,7 @@ function renderField(array $field, string $addressDisclaimerText): void
     if ($requiredAlways) {
         $requiredLabel = ' <span class="required-mark required-mark-field" aria-hidden="true">* Pflichtfeld</span>';
     } elseif ($requiredConditional) {
-        $requiredLabel = ' <span class="required-mark required-mark-field required-mark-conditional" aria-hidden="true">* Bedingt Pflicht</span>';
+        $requiredLabel = ' <span class="required-mark required-mark-field" aria-hidden="true">* Pflichtfeld</span>';
     }
     $fieldClass = 'field';
     if ($requiredAlways || $requiredConditional) {
@@ -82,6 +82,7 @@ function renderField(array $field, string $addressDisclaimerText): void
             echo '</select>';
         } elseif ($type === 'file') {
             $accept = htmlspecialchars((string) ($field['accept'] ?? ''));
+            $description = trim((string) ($field['description'] ?? ''));
             $fileInputId = $key . '_file';
             $cameraInputId = $key . '_camera';
             echo '<div class="upload-control" data-upload-key="' . $key . '">';
@@ -89,6 +90,9 @@ function renderField(array $field, string $addressDisclaimerText): void
             echo '<label class="upload-action-btn" for="' . $fileInputId . '">Datei auswählen</label>';
             echo '<label class="upload-action-btn upload-action-btn-camera" for="' . $cameraInputId . '">Foto aufnehmen</label>';
             echo '</div>';
+            if ($description !== '') {
+                echo '<small class="hint">' . nl2br(htmlspecialchars($description)) . '</small>';
+            }
             echo '<input id="' . $fileInputId . '" class="upload-native-input" type="file" name="' . $key . '" accept="' . $accept . '">';
             echo '<input id="' . $cameraInputId . '" class="upload-native-input" type="file" name="' . $key . '__camera" accept="image/*" capture="environment">';
             echo '<p class="upload-selected" data-upload-selected="' . $key . '">Keine Datei gewählt</p>';
@@ -110,7 +114,7 @@ function renderField(array $field, string $addressDisclaimerText): void
     if (isset($field['required_if']) && is_array($field['required_if'])) {
         $depField = htmlspecialchars((string) ($field['required_if']['field'] ?? ''));
         $depValue = htmlspecialchars((string) ($field['required_if']['equals'] ?? ''));
-        echo '<small class="hint">Bedingtes Pflichtfeld, wenn ' . $depField . ' = ' . $depValue . '.</small>';
+        echo '<small class="hint">Pflichtfeld, wenn ' . $depField . ' = ' . $depValue . '.</small>';
     }
 
     echo '<div class="error" data-error-for="' . $key . '"></div>';
@@ -188,11 +192,12 @@ function renderField(array $field, string $addressDisclaimerText): void
                     <span id="submitSpinner" class="btn-spinner hidden" aria-hidden="true"></span>
                 </button>
             </div>
+            <p id="feedbackMessage" class="status-text" role="status" aria-live="polite"></p>
         </form>
     </section>
 
     <section id="startSection" class="card hidden">
-        <h2>Start</h2>
+        <h2>Status</h2>
         <p id="startIntroText">Bitte E-Mail eingeben. Bestehende Entwürfe werden automatisch geladen.</p>
         <form id="startForm" novalidate>
             <input type="hidden" name="csrf" value="<?= htmlspecialchars($csrf) ?>">
@@ -213,7 +218,7 @@ function renderField(array $field, string $addressDisclaimerText): void
                 <p><strong>Speicherstatus:</strong> <span id="draftStatusValue">Noch nicht gespeichert</span></p>
                 <button id="resetDataBtn" type="button" class="btn btn-small">Gespeicherte Daten löschen und neu starten</button>
             </div>
-            <p id="feedbackMessage" class="status-text" role="status" aria-live="polite"></p>
+            <p id="startFeedbackMessage" class="status-text" role="status" aria-live="polite"></p>
         </form>
     </section>
 </main>