index.php 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. <?php
  2. declare(strict_types=1);
  3. use App\App\Bootstrap;
  4. use App\Form\FormSchema;
  5. use App\Security\Csrf;
  6. require __DIR__ . '/src/autoload.php';
  7. Bootstrap::init();
  8. $schema = new FormSchema();
  9. $steps = $schema->getSteps();
  10. $csrf = Csrf::token();
  11. $app = Bootstrap::config('app');
  12. /** @param array<string, mixed> $field */
  13. function renderField(array $field): void
  14. {
  15. $key = htmlspecialchars((string) $field['key']);
  16. $label = htmlspecialchars((string) $field['label']);
  17. $type = (string) ($field['type'] ?? 'text');
  18. $required = ((bool) ($field['required'] ?? false)) ? 'required' : '';
  19. echo '<div class="field" data-field="' . $key . '">';
  20. if ($type === 'checkbox') {
  21. echo '<label class="checkbox-label"><input type="checkbox" name="form_data[' . $key . ']" value="1" ' . $required . '> ' . $label . '</label>';
  22. } else {
  23. echo '<label for="' . $key . '">' . $label . '</label>';
  24. if ($type === 'textarea') {
  25. echo '<textarea id="' . $key . '" name="form_data[' . $key . ']" ' . $required . '></textarea>';
  26. } elseif ($type === 'select') {
  27. echo '<select id="' . $key . '" name="form_data[' . $key . ']" ' . $required . '>';
  28. echo '<option value="">Bitte wählen</option>';
  29. foreach (($field['options'] ?? []) as $option) {
  30. if (!is_array($option)) {
  31. continue;
  32. }
  33. $value = htmlspecialchars((string) ($option['value'] ?? ''));
  34. $optLabel = htmlspecialchars((string) ($option['label'] ?? ''));
  35. echo '<option value="' . $value . '">' . $optLabel . '</option>';
  36. }
  37. echo '</select>';
  38. } elseif ($type === 'file') {
  39. $accept = htmlspecialchars((string) ($field['accept'] ?? ''));
  40. echo '<input id="' . $key . '" type="file" name="' . $key . '" accept="' . $accept . '">';
  41. echo '<small>Original-Dateiname wird übernommen und im System sicher gespeichert.</small>';
  42. echo '<div class="upload-list" data-upload-list="' . $key . '"></div>';
  43. } else {
  44. $inputType = htmlspecialchars($type);
  45. echo '<input id="' . $key . '" type="' . $inputType . '" name="form_data[' . $key . ']" ' . $required . '>';
  46. }
  47. }
  48. if (isset($field['required_if']) && is_array($field['required_if'])) {
  49. $depField = htmlspecialchars((string) ($field['required_if']['field'] ?? ''));
  50. $depValue = htmlspecialchars((string) ($field['required_if']['equals'] ?? ''));
  51. echo '<small class="hint">Pflicht, wenn ' . $depField . ' = ' . $depValue . '.</small>';
  52. }
  53. echo '<div class="error" data-error-for="' . $key . '"></div>';
  54. echo '</div>';
  55. }
  56. ?><!doctype html>
  57. <html lang="de">
  58. <head>
  59. <meta charset="utf-8">
  60. <meta name="viewport" content="width=device-width, initial-scale=1">
  61. <title><?= htmlspecialchars((string) $app['project_name']) ?></title>
  62. <link rel="stylesheet" href="/assets/css/tokens.css">
  63. <link rel="stylesheet" href="/assets/css/base.css">
  64. </head>
  65. <body>
  66. <main class="container">
  67. <h1>Digitaler Mitgliedsantrag Feuerwehrverein</h1>
  68. <section id="startSection" class="card">
  69. <h2>Start & Status</h2>
  70. <p>Bitte E-Mail eingeben. Bestehende Entwürfe werden automatisch geladen.</p>
  71. <form id="startForm" novalidate>
  72. <input type="hidden" name="csrf" value="<?= htmlspecialchars($csrf) ?>">
  73. <div class="hp-field" aria-hidden="true">
  74. <label for="website">Website</label>
  75. <input id="website" type="text" name="website" autocomplete="off" tabindex="-1">
  76. </div>
  77. <div class="field">
  78. <label for="startEmail">E-Mail</label>
  79. <input id="startEmail" type="email" name="email" required>
  80. </div>
  81. <div class="inline-actions">
  82. <button id="startSubmitBtn" type="submit">Formular laden</button>
  83. <button id="resetDataBtn" type="button" class="hidden">Gespeicherte Daten löschen und neu starten</button>
  84. </div>
  85. <p id="statusMessage" class="status-text" role="status" aria-live="polite"></p>
  86. </form>
  87. </section>
  88. <section id="wizardSection" class="card hidden">
  89. <h2>Mitgliedsantrag</h2>
  90. <div id="progress" class="progress"></div>
  91. <form id="applicationForm" enctype="multipart/form-data" novalidate>
  92. <input type="hidden" name="csrf" value="<?= htmlspecialchars($csrf) ?>">
  93. <input type="hidden" id="applicationEmail" name="email" value="">
  94. <input type="hidden" id="applicationWebsite" name="website" value="">
  95. <?php foreach ($steps as $index => $step): ?>
  96. <section class="step hidden" data-step="<?= $index + 1 ?>">
  97. <h3>Schritt <?= $index + 1 ?>: <?= htmlspecialchars((string) ($step['title'] ?? '')) ?></h3>
  98. <p><?= htmlspecialchars((string) ($step['description'] ?? '')) ?></p>
  99. <?php foreach (($step['fields'] ?? []) as $field): ?>
  100. <?php if (is_array($field)) { renderField($field); } ?>
  101. <?php endforeach; ?>
  102. <?php if ((int) ($index + 1) === 3): ?>
  103. <button type="button" id="uploadNowBtn">Dateien jetzt speichern</button>
  104. <?php endif; ?>
  105. </section>
  106. <?php endforeach; ?>
  107. <div class="wizard-actions">
  108. <button type="button" id="prevBtn">Zurück</button>
  109. <button type="button" id="nextBtn">Weiter</button>
  110. <button type="button" id="submitBtn" class="hidden">Verbindlich absenden</button>
  111. </div>
  112. </form>
  113. </section>
  114. </main>
  115. <script>
  116. window.APP_BOOT = {
  117. steps: <?= json_encode($steps, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?>,
  118. csrf: <?= json_encode($csrf, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?>,
  119. contactEmail: <?= json_encode((string) ($app['contact_email'] ?? ''), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?>
  120. };
  121. </script>
  122. <script src="/assets/js/form.js"></script>
  123. </body>
  124. </html>