1// Tooltip-Texte definieren 2const tooltipTexte = { 3 modelle_info: { 4 titel: "Versicherungsmodelle im Ãberblick", 5 text: ` 6 <strong>Standard:</strong> Freie Arztwahl, kein Rabatt<br> 7 <strong>Hausarzt:</strong> Fester Hausarzt als erste Anlaufstelle, 15â20% Rabatt<br> 8 <strong>HMO:</strong> Behandlung im Gesundheitszentrum, bis 25% Rabatt<br> 9 <strong>Telmed:</strong> Telefonische Erstberatung, 15â20% Rabatt` 10}, 11standard: { 12 titel: "Standard-Modell", 13 text: "Beim Standard-Modell hast du die freie Arztwahl und kannst direkt zu jedem Arzt oder Spezialisten gehen. Du bezahlst die vollen Prämien ohne Rabatt, hast dafür aber maximale Flexibilität bei der Wahl deiner Behandlung." 14}, 15hausarzt: { 16 titel: "Hausarzt-Modell", 17 text: "Beim Hausarzt-Modell wählst du einen festen Hausarzt, der deine erste Anlaufstelle ist. Er überweist dich bei Bedarf an Spezialisten weiter. Dafür erhältst du 15â20% Rabatt auf deine Prämie. Notfälle sind ausgenommen." 18}, 19hmo: { 20 titel: "HMO-Modell", 21 text: "HMO steht für Health Maintenance Organization. Du lässt dich in einer HMO-Praxis oder einem Gesundheitszentrum behandeln. Die Ãrzte dort koordinieren deine gesamte Behandlung. Dafür erhältst du bis zu 25% Rabatt auf die Prämie." 22}, 23telmed: { 24 titel: "Telmed-Modell", 25 text: "Beim Telmed-Modell kontaktierst du vor jedem Arztbesuch zuerst eine medizinische Beratungshotline. Die Fachpersonen beraten dich und empfehlen das weitere Vorgehen. Für diese Einschränkung erhältst du 15â20% Prämienrabatt." 26}, 27franchise: { 28 titel: "Franchise", 29 text: "Die Franchise ist der jährliche Selbstbehalt, den du zuerst selber bezahlst, bevor deine Versicherung die restlichen Kosten übernimmt." 30}, 31unfalldeckung: { 32 titel: "Unfalldeckung", 33 text: "Die Unfalldeckung ist nur notwendig, wenn du nicht mindestens 8 Stunden pro Woche beim gleichen Arbeitgeber arbeitest. Angestellte sind bereits über ihren Arbeitgeber unfallversichert und können auf die Unfalldeckung in der Grundversicherung verzichten, was die Prämie reduziert." 34}, 35zusatzversicherung: { 36 titel: "Zusatzversicherung", 37 text: "Zusatzversicherungen ergänzen die Grundversicherung um wichtige Leistungen wie Zahnbehandlungen, Brillen, Alternativmedizin, freie Spitalwahl und weltweiten Schutz. Die Aufnahme erfolgt mit Gesundheitsprüfung â je früher, desto besser!" 38} 39 40}; 41 42jQuery(document).ready(function($) { 43 // ===== MODAL FUNKTIONALITÃT ===== 44 45 // Mobile Detection 46 const isMobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent); 47 48 // Tooltip für einzelne Modelle - Mobile-optimiert 49 const handleTooltipClick = function(e) { 50 e.preventDefault(); 51 e.stopPropagation(); 52 53 // Alle anderen Tooltips schlieÃen 54 $('.tooltip-popup').remove(); 55 56 const $this = $(this); 57 const modell = $this.data('modell'); 58 const info = tooltipTexte[modell]; 59 60 if (!info) return; 61 62 const offset = $this.offset(); 63 const $popup = $(` 64 <div class="tooltip-popup ${isMobile ? 'mobile-tooltip' : ''}"> 65 <h4>${info.titel}</h4> 66 <p>${info.text}</p> 67 ${isMobile ? '<button class="tooltip-close-btn">Schliessen</button>' : ''} 68 </div> 69 `); 70 71 // Mobile: Zentriert positionieren 72 if (isMobile) { 73 $popup.css({ 74 position: 'fixed', 75 top: '50%', 76 left: '50%', 77 transform: 'translate(-50%, -50%)', 78 maxWidth: '90%', 79 width: '300px', 80 zIndex: 10001 81 }); 82 83 // Overlay hinzufügen 84 $('body').append('<div class="tooltip-overlay"></div>'); 85 } else { 86 // Desktop: normale Positionierung 87 $popup.css({ 88 top: offset.top + 30 + 'px', 89 left: offset.left - 150 + 'px' 90 }); 91 } 92 93 $('body').append($popup); 94 95 // Desktop: Position anpassen wenn auÃerhalb des Viewports 96 if (!isMobile) { 97 setTimeout(() => { 98 const popupLeft = $popup.offset().left; 99 const popupRight = popupLeft + $popup.outerWidth(); 100 const windowWidth = $(window).width(); 101 102 if (popupRight > windowWidth - 20) { 103 $popup.css('left', offset.left - $popup.outerWidth() + 20 + 'px'); 104 } 105 if (popupLeft < 20) { 106 $popup.css('left', '20px'); 107 } 108 }, 10); 109 } 110 }; 111 112 // Touch-Events für Mobile, Click für Desktop 113 if (isMobile) { 114 $(document).on('touchend', '.icon_question_modell', handleTooltipClick); 115 } else { 116 $(document).on('click', '.icon_question_modell, .icon_franchise', handleTooltipClick); 117 118 } 119 120 // Tooltip schlieÃen - Mobile-optimiert 121 $(document).on('click touchend', '.tooltip-close-btn, .tooltip-overlay', function(e) { 122 e.preventDefault(); 123 $('.tooltip-popup, .tooltip-overlay').remove(); 124 }); 125 126 127 // Modal öffnen beim Klick auf "Offerte anfordern" 128 $(document).on('click', '.tarif-btn', function(e) { 129 e.preventDefault(); 130 131 // Speichere die Tarif-Daten 132 const versicherer = $(this).data('versicherer') || ''; 133 const tarif = $(this).data('tarif') || ''; 134 const modell = $(this).data('modell') || ''; 135 const praemie = $(this).data('praemie') || '';
136 const kassenname = $(this).data('kassenname') || ''; 137 138 // Markiere den geklickten Button 139 $('.tarif-btn').removeClass('clicked'); 140 $(this).addClass('clicked'); 141 142 // Füge versteckte Felder ins Formular ein 143 $('#beratungForm input[name="versicherer_info"]').remove(); 144 $('#beratungForm').append('<input type="hidden" name="versicherer_info" value="' + versicherer + ' - ' + tarif + ' - ' + modell + ' - ' + praemie + ' - ' + kassenname + '">'); 145 146 // Ãffne Modal und verhindere Hintergrund-Scrolling 147 $('#beratungModal').fadeIn(); 148 $('body').addClass('modal-open'); 149 }); 150 151 // Entferne zentralisierte Fehlermeldung beim Tippen in irgendein Formularfeld 152 $('#beratungForm').on('input', 'input, select', function() { 153 // Entferne nur noch die globale Fehlermeldung oberhalb des Formulars 154 $('.form-error.global-form-error').remove(); 155 156 // Entferne auch Feld-Hervorhebungen 157 $('.field-error').removeClass('field-error'); 158 }); 159 160 // Modal schlieÃen beim Klick auf X 161 $('.beratung-close').on('click', function() { 162 $('#beratungModal').fadeOut(); 163 $('body').removeClass('modal-open'); 164 }); 165 166 // Modal schlieÃen beim Klick auÃerhalb 167 $(window).on('click', function(e) { 168 if ($(e.target).is('#beratungModal')) { 169 $('#beratungModal').fadeOut(); 170 $('body').removeClass('modal-open'); 171 } 172 }); 173 174 // ===== FORMULAR SUBMIT MIT ZENTRALISIERTEN FEHLERMELDUNGEN ===== 175 $('#beratungForm').on('submit', function(e) { 176 e.preventDefault(); 177 178 // Entferne alle vorherigen Fehlermeldungen 179 $('.form-error, .erfolgsmeldung').remove(); 180 $('.field-error').removeClass('field-error'); 181 182 const form = $(this); 183 let errorMessages = []; // Array für alle Fehlermeldungen 184 let firstErrorField = null; 185 186 const vorname = form.find('input[name="vorname"]').val().trim(); 187 const nachname = form.find('input[name="nachname"]').val().trim(); 188 const geburtsdatum = form.find('input[name="geburtsdatum"]').val().trim(); 189 const versicherungsbeginn = $('#versicherungsbeginn-hidden').val(); 190 const strasse = form.find('input[name="strasse"]').val().trim(); 191 const ort_adresse = form.find('input[name="ort_adresse"]').val().trim(); 192 const franchise_modal = $('#franchise').val(); 193 const email = form.find('input[name="email"]').val().trim(); 194 const telefon = form.find('input[name="telefon"]').val().trim(); 195 const zusatzversicherung = form.find('input[name="zusatzversicherung"]:checked').val(); 196 const beratung = form.find('input[name="beratung"]:checked').val(); 197 198 // Funktion zum Sammeln von Feldfehlern 199 function addFieldError(inputElement, message) { 200 inputElement.addClass('field-error'); 201 errorMessages.push(message); 202 if (!firstErrorField) { 203 firstErrorField = inputElement; 204 } 205 } 206 207 // Vorname Validierung 208 const vornameInput = form.find('input[name="vorname"]'); 209 if (!vorname) { 210 addFieldError(vornameInput, 'Bitte Vorname eingeben'); 211 } 212 213 // Nachname Validierung 214 const nachnameInput = form.find('input[name="nachname"]'); 215 if (!nachname) { 216 addFieldError(nachnameInput, 'Bitte Nachname eingeben'); 217 } 218 219 // Geburtsdatum Validierung 220 const geburtsdatumInput = form.find('input[name="geburtsdatum"]'); 221 if (!geburtsdatum) { 222 addFieldError(geburtsdatumInput, 'Bitte Geburtsdatum eingeben'); 223 } else { 224 // Prüfe ob gültiges Datum 225 function parseGermanDate(dateStr) { 226 const parts = dateStr.split('.'); 227 if (parts.length !== 3) return null; 228 const day = parseInt(parts[0], 10);
229 const month = parseInt(parts[1], 10) - 1; // JS Monate: 0 = Jan 230 const year = parseInt(parts[2], 10); 231 const date = new Date(year, month, day); 232 return isNaN(date.getTime()) ? null : date; 233 } 234 235 const datum = parseGermanDate(geburtsdatum); 236 const heute = new Date(); 237 const minDatum = new Date(); 238 minDatum.setFullYear(minDatum.getFullYear() - 120); // Max 120 Jahre alt 239 240 if (!datum || datum > heute || datum < minDatum) { 241 addFieldError(geburtsdatumInput, 'Bitte gültiges Geburtsdatum im Format TT.MM.JJJJ eingeben'); 242 } 243 } 244 245 // Versicherungsbeginn Validierung (Datepicker) 246 if (!versicherungsbeginn) { 247 $('.versicherungsbeginn-wrapper').addClass('error'); 248 errorMessages.push('Bitte Versicherungsbeginn auswählen'); 249 if (!firstErrorField) firstErrorField = $('#versicherungsbeginn-display'); 250 } 251 252 // Adresse Validierung 253 if (!strasse) addFieldError(form.find('input[name="strasse"]'), 'Bitte Strasse + Nr. eingeben'); 254 if (!ort_adresse) addFieldError(form.find('input[name="ort_adresse"]'), 'Bitte Ort eingeben'); 255 256 // E-Mail Validierung 257 const emailInput = form.find('input[name="email"]'); 258 const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; 259 260 if (!email) { 261 addFieldError(emailInput, 'Bitte E-Mail-Adresse eingeben'); 262 } else if (!emailPattern.test(email)) { 263 addFieldError(emailInput, 'Bitte gültige E-Mail-Adresse eingeben'); 264 } 265 266 // Telefon Validierung 267 const telefonInput = form.find('input[name="telefon"]'); 268 const telPattern = /^[0-9\-\+\s\(\)]{6,}$/; 269 if (!telefon) { 270 addFieldError(telefonInput, 'Bitte Telefonnummer eingeben'); 271 } else if (!telPattern.test(telefon)) { 272 addFieldError(telefonInput, 'Bitte gültige Telefonnummer eingeben'); 273 } 274 275 // Bei Fehlern: Zeige ALLE Fehlermeldungen in einer zentralen Box 276 if (errorMessages.length > 0) { 277 // Erstelle die zentrale Fehlermeldung mit allen spez
277ifischen Fehlern 278 let errorListHtml = '<ul style="margin: 10px 0 0 0; padding-left: 20px; text-align: left;">'; 279 errorMessages.forEach(function(message) { 280 errorListHtml += '<li style="margin-bottom: 5px;">' + message + '</li>'; 281 }); 282 errorListHtml += '</ul>'; 283 284 const globalError = $('<div class="form-error global-form-error"></div>') 285 .html('<div style="margin-bottom: 10px;"><i class="fa fa-exclamation-triangle"></i> Bitte korrigieren Sie die markierten Felder:</div>' + errorListHtml); 286 287 // Füge die Fehlermeldung VOR dem Formular ein 288 form.before(globalError); 289 290 // Scroll zur globalen Fehlermeldung 291 globalError[0].scrollIntoView({ 292 behavior: 'smooth', 293 block: 'center' 294 }); 295 296 return; 297 } 298 299 // Daten aus angeklicktem Tarif 300 const clicked = $('.tarif-btn.clicked, .tarif-offerte-box.clicked'); 301 const versicherer = clicked.data('versicherer') || ''; 302 const tarif = clicked.data('tarif') || ''; 303 const modell = clicked.data('modell') || ''; 304 const praemie = clicked.data('praemie') || ''; 305 const kassenname = clicked.data('kassenname') || ''; 306 307 $.post(praemien_ajax.ajax_url, { 308 action: 'beratung_anfrage', 309 vorname: vorname, 310 nachname: nachname, 311 geburtsdatum: geburtsdatum, 312 versicherungsbeginn: versicherungsbeginn, 313 strasse: strasse, 314 ort_adresse: ort_adresse, 315 franchise_modal: franchise_modal, 316 email: email, 317 telefon: telefon, 318 zusatzversicherung: zusatzversicherung, 319 beratung: beratung, 320 kassenname: kassenname, 321 versicherer: versicherer, 322 tarif: tarif, 323 modell: modell, 324 praemie: praemie, 325 ort_plz: $('#ort').find(':selected').text(), 326 nonce: $('#beratung_nonce').val() 327 }, function(response) { 328 const res = JSON.parse(response); 329 if (res.success) { 330 const successMsg = $('<div class="erfolgsmeldung"></div>') 331 .html('<i class="fa fa-check-circle"></i> Vielen Dank für Ihre Anfrage, wir werden uns umgehend bei Ihnen melden.'); 332 333 // Erfolgsmeldung oberhalb des Formulars 334 form.before(successMsg); 335 form.trigger('reset'); 336 $('.tarif-btn').removeClass('clicked'); 337 338 // Scroll zur Erfolgsmeldung 339 successMsg[0].scrollIntoView({ behavior: 'smooth', block: 'center' }); 340 341 // Modal nach paar Sekunden schlieÃen 342 setTimeout(function() { 343 $('#beratungModal').fadeOut(); 344 $('body').removeClass('modal-open'); 345 }, 3000); 346 } else { 347 const errorMsg = $('<div class="form-error"></div>') 348 .html('<i class="fa fa-exclamation-triangle"></i> Ein Fehler ist aufgetreten. Bitte versuchen Sie es später erneut.'); 349 350 // Fehlermeldung oberhalb des Formulars 351 form.before(errorMsg); 352 353 // Scroll zur Fehlermeldung 354 errorMsg[0].scrollIntoView({ behavior: 'smooth', block: 'center' }); 355 } 356 }); 357 }); 358 359 // Klick auf die gesamte Offerte-Box 360 $(document).on('click', '.tarif-offerte-box', function(e) { 361 e.preventDefault(); 362 363 // Speichere die Tarif-Daten von der Box 364 const versicherer = $(this).data('versicherer') || ''; 365 const tarif = $(this).data('tarif') || ''; 366 const modell = $(this).data('modell') || ''; 367 const praemie = $(this).data('praemie') || ''; 368 const kassenname = $(this).data('kassenname') || ''; 369 370 // Markiere die geklickte Box 371 $('.tarif-offerte-box').removeClass('clicked'); 372 $(this).addClass('clicked'); 373 374 // Füge versteckte Felder ins Formular ein 375 $('#beratungForm input[name="versicherer_info"]').remove(); 376 $('#beratungForm').append('<input type="hidden" name="versicherer_info" value="' + versicherer + ' - ' + tarif + ' - ' + modell + ' - ' + praemie + ' - ' + kassenname + '">'); 377 378 // Ãffne Modal und verhindere Hintergrund-Scrolling 379 $('#beratungModal').fadeIn(); 380 $('body').addClass('modal-open'); 381 }); 382 383 // Zeige Warnung bei "Nein" für Zusatzversicherung 384 $('input[name="zusatzversicherung"]').on('change', function() { 385 if ($(this).val() === 'nein') { 386 $('#zusatzversicherung-hinweis').slideDown(); 387 } else {
388 $('#zusatzversicherung-hinweis').slideUp(); 389 } 390 }); 391 392 // Entferne den alten tarif-btn Click-Handler, da wir jetzt die Box anklicken 393 $(document).off('click', '.tarif-btn'); 394 395 // ===== CHECKBOX STYLING ===== 396 $('.tarif-choice-btn input').on('change', function() { 397 const parent = $(this).closest('.tarif-choice-btn'); 398 if ($(this).is(':checked')) { 399 parent.addClass('selected'); 400 } else { 401 parent.removeClass('selected'); 402 } 403 }); 404 405 // ===== TOOLTIPS FÃR VERSICHERUNGSMODELLE ===== 406 407 // Tooltip für einzelne Modelle 408 $(document).on('click', '.icon_question_modell, .icon_zusatzversicherung, .icon_franchise', function(e) { 409 410 e.preventDefault(); 411 e.stopPropagation(); 412 413 // Alle anderen Tooltips schlieÃen 414 $('.tooltip-popup').remove(); 415 416 const modell = $(this).data('modell'); 417 const info = tooltipTexte[modell]; 418 419 if (!info) return; 420 421 const offset = $(this).offset(); 422 const $popup = $(` 423 <div class="tooltip-popup"> 424 <h4>${info.titel}</h4> 425 <p>${info.text}</p> 426 </div> 427 `); 428 429 // Position berechnen 430 $popup.css({ 431 top: offset.top + 30 + 'px', 432 left: offset.left - 150 + 'px' 433 }); 434 435 $('body').append($popup); 436 437 // Position anpassen wenn auÃerhalb des Viewports 438 setTimeout(() => { 439 const popupLeft = $popup.offset().left; 440 const popupRight = popupLeft + $popup.outerWidth(); 441 const windowWidth = $(window).width(); 442 443 if (popupRight > windowWidth - 20) { 444 $popup.css('left', offset.left - $popup.outerWidth() + 20 + 'px'); 445 } 446 if (popupLeft < 20) { 447 $popup.css('left', '20px'); 448 } 449 }, 10); 450 }); 451 452 453 // Tooltip schlieÃen bei Klick auÃerhalb 454 $(document).on('click', function(e) { 455 if (!$(e.target).hasClass('icon_question_modell') && 456 !$(e.target).hasClass('info-icon-modelle') && 457 !$(e.target).closest('.tooltip-popup').length) { 458 $('.tooltip-popup').fadeOut(200, function() { 459 $(this).remove(); 460 }); 461 } 462 }); 463 464 // Escape-Taste schlieÃt Tooltip 465 $(document).on('keyup', function(e) { 466 if (e.key === "Escape") { 467 $('.tooltip-popup').remove(); 468 } 469 }); 470 471 // Am Ende der modal.js hinzufügen: 472 473// ===== DROPDOWN-DATEPICKER FUNKTIONALITÃT ===== 474// Am Ende der modal.js hinzufügen: 475 476jQuery(document).ready(function($) { 477 const $display = $('#geburtsdatum-display'); 478 const $dropdown = $('#datepicker-dropdown'); 479 const $hiddenInput = $('#geburtsdatum-hidden'); 480 const $daySelect = $('#day-select'); 481 const $monthSelect = $('#month-select'); 482 const $yearSelect = $('#year-select'); 483 const $wrapper = $('.geburtsdatum-wrapper'); 484 485 const months = [ 486 'Januar', 'Februar', 'März', 'April', 'Mai', 'Juni', 487 'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember' 488 ]; 489 490 // Initialisierung 491 initializeSelects(); 492 493 // Event Listeners 494 $display.on('click', openDropdown); 495 $('.geburtsdatum-icon').on('click', openDropdown); 496 497 $('.btn-ok').on('click', confirmDate); 498 $('.btn-cancel').on('click', closeDropdown); 499 $('.btn-today').on('click', setToday); 500 501 502 // Select Changes - MIT LIVE-PREVIEW 503 [$daySelect, $monthSelect, $yearSelect].forEach($select => { 504 $select.on('change', function() { 505 updateDays(); 506 updateLivePreview(); 507 }); 508 }); 509 510 // Click outside to close 511 $(document).on('click', function(e) { 512 if (!$(e.target).closest('.geburtsdatum-wrapper').length) { 513 closeDropdown(); 514 } 515 }); 516 517 // Escape key to close 518 $(document).on('keyup', function(e) { 519 if (e.key === 'Escape') { 520 closeDropdown(); 521 } 522 }); 523 524 function initializeSelects() { 525 // Tage (1-31) 526 for (let i = 1; i <= 31; i++) { 527 $daySelect.append(`<option value="${i}">${i}</option>`); 528 } 529 530 // Monate 531 months.forEach((month, index) => { 532 $monthSelect.append(`<option value="${index + 1}">${month}</option>`); 533 }); 534 535 // Jahre (aktuelles Jahr bis 1900) 536 const currentYear = new Date().getFullYear(); 537 for (let year = currentYear; year >= 1900; year--) { 538 $yearSelect.append(`<option value="${year}">${year}</option>`); 539 } 540 } 541 542 function openDropdown() { 543 $dropdown.addClass('show'); 544 545 // Wenn bereits ein Datum gesetzt ist, in die Dropdowns laden 546 const currentValue = $hiddenInput.val(); 547 if (currentValue) { 548 const parts = currentValue.split('.'); 549 if (parts.length === 3) { 550 $daySelect.val(parseInt(parts[0])); 551 $monthSelect.val(parseInt(parts[1])); 552 $yearSelect.val(parseInt(parts[2])); 553 updateDays(); 554 return; 555 } 556 } 557 558 // Bei erstem Ãffnen ein sinnvolles Standardjahr setzen 559 if (!$yearSelect.val()) { 560 const defaultYear = new Date().getFullYear() - 30; 561 $yearSelect.val(defaultYear); 562 updateDays(); 563 } 564 } 565 566 function closeDropdown() { 567 $dropdown.removeClass('show'); 568 } 569 570 function updateDays() {
571 const month = parseInt($monthSelect.val()); 572 const year = parseInt($yearSelect.val()); 573 574 if (!month || !year) return; 575 576 // Anzahl Tage im Monat ermitteln 577 const daysInMonth = new Date(year, month, 0).getDate(); 578 579 // Aktuelle Auswahl merken 580 const currentDay = $daySelect.val(); 581 582 // Tage-Dropdown aktualisieren 583 $daySelect.html('<option value="">Tag</option>'); 584 for (let i = 1; i <= daysInMonth; i++) { 585 $daySelect.append(`<option value="${i}">${i}</option>`); 586 } 587 588 // Vorherige Auswahl wiederherstellen (falls möglich) 589 if (currentDay && currentDay <= daysInMonth) { 590 $daySelect.val(currentDay); 591 } 592 } 593 594 // NEUE FUNKTION: Live-Preview 595 function updateLivePreview() { 596 const day = $daySelect.val(); 597 const month = $monthSelect.val(); 598 const year = $yearSelect.val(); 599 600 if (day && month && year) { 601 // Formatierung für Live-Anzeige (grau, da noch nicht bestätigt) 602 const monthName = months[month - 1]; 603 const previewText = `${String(day).padStart(2, '0')}. ${monthName} ${year}`; 604 $display.text(previewText).removeClass('filled').addClass('preview'); 605 } else if (day || month || year) { 606 // Teilweise ausgefüllt 607 let parts = []; 608 if (day) parts.push(day + '.'); 609 if (month) parts.push(months[month - 1]); 610 if (year) parts.push(year); 611 $display.text(parts.join(' ')).removeClass('filled').addClass('preview'); 612 } else { 613 // Zurück zum Placeholder 614 $display.text('Geburtsdatum wählen*').removeClass('filled preview'); 615 } 616 } 617 618 function confirmDate() { 619 const day = $daySelect.val();
620 const month = $monthSelect.val(); 621 const year = $yearSelect.val(); 622 623 if (!day || !month || !year) { 624 alert('Bitte alle Felder auswählen'); 625 return; 626 } 627 628 // Validierung 629 const date = new Date(year, month - 1, day); 630 const today = new Date(); 631 632 if (date > today) { 633 alert('Geburtsdatum kann nicht in der Zukunft liegen'); 634 return; 635 } 636 637 // 16 Jahre Mindestalter 638 const minDate = new Date(); 639 minDate.setFullYear(minDate.getFullYear() - 16); 640 if (date > minDate) { 641 alert('Mindestalter 16 Jahre erforderlich'); 642 return; 643 } 644 645 // Formatierung für finale Anzeige 646 const formattedDate = `${String(day).padStart(2, '0')}.${String(month).padStart(2, '0')}.${year}`; 647 $display.text(formattedDate).removeClass('preview').addClass('filled'); 648 $hiddenInput.val(formattedDate); 649 $wrapper.removeClass('error').addClass('success'); 650 651 closeDropdown(); 652 } 653 654 function setToday() { 655 const today = new Date(); 656 $daySelect.val(today.getDate()); 657 $monthSelect.val(today.getMonth() + 1); 658 $yearSelect.val(today.getFullYear()); 659 updateDays(); 660 updateLivePreview(); 661 } 662 663 // Integration in die Formular-Validierung 664 // Ãberschreibe die ursprüngliche Geburtsdatum-Validierung in der bestehenden Funktion 665 666 // ===== VERSICHERUNGSBEGINN-DATEPICKER FUNKTIONALITÃT ===== 667 const $vbDisplay = $('#versicherungsbeginn-display'); 668 const $vbDropdown = $('#vb-datepicker-dropdown'); 669 const $vbHiddenInput = $('#versicherungsbeginn-hidden'); 670 const $vbDaySelect = $('#vb-day-select'); 671 const $vbMonthSelect = $('#vb-month-select'); 672 const $vbYearSelect = $('#vb-year-select'); 673 const $vbWrapper = $('.versicherungsbeginn-wrapper'); 674 675 // Initialisierung 676 initializeVbSelects(); 677 678 // Event Listeners 679 $vbDisplay.on('click', openVbDropdown); 680 681 $('.vb-btn-ok').on('click', confirmVbDate); 682 $('.vb-btn-cancel').on('click', closeVbDropdown); 683 684 // Select Changes - MIT LIVE-PREVIEW 685 [$vbDaySelect, $vbMonthSelect, $vbYearSelect].forEach($select => { 686 $select.on('change', function() { 687 updateVbDays(); 688 updateVbLivePreview(); 689 }); 690 }); 691 692 // Click outside to close 693 $(document).on('click', function(e) { 694 if (!$(e.target).closest('.versicherungsbeginn-wrapper').length) { 695 closeVbDropdown(); 696 } 697 }); 698 699 function initializeVbSelects() { 700 // Tage (1-31) 701 for (let i = 1; i <= 31; i++) { 702 $vbDaySelect.append(`<option value="${i}">${i}</option>`); 703 } 704 705 // Monate 706 months.forEach((month, index) => { 707 $vbMonthSelect.append(`<option value="${index + 1}">${month}</option>`); 708 }); 709 710 // Jahre: aktuelles Jahr + 2 nächste Jahre 711 const currentYear = new Date().getFullYear(); 712 for (let year = currentYear; year <= currentYear + 2; year++) { 713 $vbYearSelect.append(`<option value="${year}">${year}</option>`); 714 } 715 } 716 717 function openVbDropdown() { 718 $vbDropdown.addClass('show'); 719 720 // Wenn bereits ein Datum gesetzt ist, in die Dropdowns laden 721 const currentValue = $vbHiddenInput.val(); 722 if (currentValue) { 723 const parts = currentValue.split('.'); 724 if (parts.length === 3) { 725 $vbDaySelect.val(parseInt(parts[0])); 726 $vbMonthSelect.val(parseInt(parts[1])); 727 $vbYearSelect.val(parseInt(parts[2])); 728 updateVbDays(); 729 return; 730 } 731 } 732 733 // Bei erstem Ãffnen: nächsten Monat vorschlagen 734 if (!$vbMonthSelect.val()) { 735 const now = new Date(); 736 const nextMonth = now.getMonth() + 2; // +1 weil 0-basiert, +1 für nächsten Monat 737 if (nextMonth <= 12) { 738 $vbMonthSelect.val(nextMonth); 739 $vbYearSelect.val(now.getFullYear()); 740 } else { 741 $vbMonthSelect.val(1); 742 $vbYearSelect.val(now.getFullYear() + 1); 743 } 744 $vbDaySelect.val(1); 745 updateVbDays(); 746 updateVbLivePreview(); 747 } 748 } 749 750 function closeVbDropdown() { 751 $vbDropdown.removeClass('show'); 752 } 753 754 function updateVbDays() {
755 const month = parseInt($vbMonthSelect.val()); 756 const year = parseInt($vbYearSelect.val()); 757 758 if (!month || !year) return; 759 760 const daysInMonth = new Date(year, month, 0).getDate(); 761 const currentDay = $vbDaySelect.val(); 762 763 $vbDaySelect.html('<option value="">Tag</option>'); 764 for (let i = 1; i <= daysInMonth; i++) { 765 $vbDaySelect.append(`<option value="${i}">${i}</option>`); 766 } 767 768 if (currentDay && currentDay <= daysInMonth) { 769 $vbDaySelect.val(currentDay); 770 } 771 } 772 773 function updateVbLivePreview() { 774 const day = $vbDaySelect.val(); 775 const month = $vbMonthSelect.val(); 776 const year = $vbYearSelect.val(); 777 778 if (day && month && year) { 779 const monthName = months[month - 1]; 780 const previewText = `${String(day).padStart(2, '0')}. ${monthName} ${year}`; 781 $vbDisplay.text(previewText).removeClass('filled').addClass('preview'); 782 } else if (day || month || year) { 783 let parts = []; 784 if (day) parts.push(day + '.'); 785 if (month) parts.push(months[month - 1]); 786 if (year) parts.push(year); 787 $vbDisplay.text(parts.join(' ')).removeClass('filled').addClass('preview'); 788 } else { 789 $vbDisplay.text('Versicherungsbeginn*').removeClass('filled preview'); 790 } 791 } 792 793 function confirmVbDate() { 794 const day = $vbDaySelect.val();
795 const month = $vbMonthSelect.val(); 796 const year = $vbYearSelect.val(); 797 798 if (!day || !month || !year) { 799 alert('Bitte alle Felder auswählen'); 800 return; 801 } 802 803 // Validierung: Datum muss in der Zukunft liegen 804 const date = new Date(year, month - 1, day); 805 const today = new Date(); 806 today.setHours(0,0,0,0); 807 808 if (date < today) { 809 alert('Versicherungsbeginn muss in der Zukunft liegen'); 810 return; 811 } 812 813 // Formatierung für finale Anzeige 814 const formattedDate = `${String(day).padStart(2, '0')}.${String(month).padStart(2, '0')}.${year}`; 815 $vbDisplay.text(formattedDate).removeClass('preview').addClass('filled'); 816 $vbHiddenInput.val(formattedDate); 817 $vbWrapper.removeClass('error').addClass('success'); 818 819 closeVbDropdown(); 820 } 821 822 // Integration in die Formular-Validierung 823 // Ãberschreibe die ursprüngliche Geburtsdatum-Validierung in der bestehenden Funktion 824 const originalFormSubmit = $('#beratungForm').off('submit').on('submit', function(e) { 825 e.preventDefault(); 826 827 // Entferne alle vorherigen Fehlermeldungen 828 $('.form-error, .erfolgsmeldung').remove(); 829 $('.field-error').removeClass('field-error'); 830 831 const form = $(this); 832 let errorMessages = []; 833 let firstErrorField = null; 834 835 // Geburtsdatum-Validierung für Dropdown-Datepicker 836 const geburtsdatum = $('#geburtsdatum-hidden').val(); 837 if (!geburtsdatum) { 838 $wrapper.addClass('error'); 839 errorMessages.push('Bitte Geburtsdatum auswählen'); 840 if (!firstErrorField) firstErrorField = $display; 841 } else { 842 $wrapper.removeClass('error'); 843 } 844 845 // Weitere Validierungen wie gewohnt... 846 const vorname = form.find('input[name="vorname"]').val().trim(); 847 const nachname = form.find('input[name="nachname"]').val().trim(); 848 const versicherungsbeginn = $('#versicherungsbeginn-hidden').val(); 849 const strasse = form.find('input[name="strasse"]').val().trim(); 850 const ort_adresse = form.find('input[name="ort_adresse"]').val().trim(); 851 const franchise_modal = $('#franchise').val(); 852 const email = form.find('input[name="email"]').val().trim(); 853 const telefon = form.find('input[name="telefon"]').val().trim(); 854 855 function addFieldError(inputElement, message) { 856 inputElement.addClass('field-error'); 857 errorMessages.push(message); 858 if (!firstErrorField) { 859 firstErrorField = inputElement; 860 } 861 } 862 863 // Alle Validierungen... 864 if (!vorname) addFieldError(form.find('input[name="vorname"]'), 'Bitte Vorname eingeben'); 865 if (!nachname) addFieldError(form.find('input[name="nachname"]'), 'Bitte Nachname eingeben'); 866 867 // Versicherungsbeginn Validierung (Datepicker) 868 if (!versicherungsbeginn) { 869 $('.versicherungsbeginn-wrapper').addClass('error'); 870 errorMessages.push('Bitte Versicherungsbeginn auswählen'); 871 if (!firstErrorField) firstErrorField = $('#versicherungsbeginn-display'); 872 } else { 873 $('.versicherungsbeginn-wrapper').removeClass('error'); 874 } 875 876 // Adresse Validierung 877 if (!strasse) addFieldError(form.find('input[name="strasse"]'), 'Bitte Strasse + Nr. eingeben'); 878 if (!ort_adresse) addFieldError(form.find('input[name="ort_adresse"]'), 'Bitte Ort eingeben'); 879 880 const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; 881 if (!email) { 882 addFieldError(form.find('input[name="email"]'), 'Bitte E-Mail-Adresse eingeben'); 883 } else if (!emailPattern.test(email)) { 884 addFieldError(form.find('input[name="email"]'), 'Bitte gültige E-Mail-Adresse eingeben'); 885 } 886 887 const telPattern = /^[0-9\-\+\s\(\)]{6,}$/; 888 if (!telefon) { 889 addFieldError(form.find('input[name="telefon"]'), 'Bitte Telefonnummer eingeben'); 890 } else if (!telPattern.test(telefon)) { 891 addFieldError(form.find('input[name="telefon"]'), 'Bitte gültige Telefonnummer eingeben'); 892 } 893 894 // Bei Fehlern: Zeige alle Fehlermeldungen 895 if (errorMessages.length > 0) { 896 let errorListHtml = '<ul style="margin: 10px 0 0 0; padding-left: 20px; text-align: left;">';
897 errorMessages.forEach(function(message) { 898 errorListHtml += '<li style="margin-bottom: 5px;">' + message + '</li>'; 899 }); 900 errorListHtml += '</ul>'; 901 902 const globalError = $('<div class="form-error global-form-error"></div>') 903 .html('<div style="margin-bottom: 10px;"><i class="fa fa-exclamation-triangle"></i> Bitte korrigieren Sie die markierten Felder:</div>' + errorListHtml); 904 905 form.before(globalError); 906 globalError[0].scrollIntoView({ behavior: 'smooth', block: 'center' }); 907 return; 908 } 909 910 // Daten sammeln und senden 911 const zusatzversicherung = form.find('input[name="zusatzversicherung"]:checked').val(); 912 const beratung = form.find('input[name="beratung"]:checked').val(); 913 914 const clicked = $('.tarif-btn.clicked, .tarif-offerte-box.clicked'); 915 const versicherer = clicked.data('versicherer') || ''; 916 const tarif = clicked.data('tarif') || ''; 917 const modell = clicked.data('modell') || ''; 918 const praemie = clicked.data('praemie') || ''; 919 const kassenname = clicked.data('kassenname') || ''; 920 921 $.post(praemien_ajax.ajax_url, { 922 action: 'beratung_anfrage', 923 vorname: vorname, 924 nachname: nachname, 925 geburtsdatum: geburtsdatum, 926 versicherungsbeginn: versicherungsbeginn, 927 strasse: strasse, 928 ort_adresse: ort_adresse, 929 franchise_modal: franchise_modal, 930 email: email, 931 telefon: telefon, 932 zusatzversicherung: zusatzversicherung, 933 beratung: beratung, 934 kassenname: kassenname, 935 versicherer: versicherer, 936 tarif: tarif, 937 modell: modell, 938 praemie: praemie, 939 ort_plz: $('#ort').find(':selected').text(), 940 nonce: $('#beratung_nonce').val() 941 }, function(response) { 942 const res = JSON.parse(response); 943 if (res.success) { 944 const successMsg = $('<div class="erfolgsmeldung"></div>') 945 .html('<i class="fa fa-check-circle"></i> Vielen Dank für Ihre Anfrage, wir werden uns umgehend bei Ihnen melden.'); 946 947 form.before(successMsg); 948 form.trigger('reset'); 949 950 // Datepicker zurücksetzen 951 $display.text('Geburtsdatum wählen*').removeClass('filled'); 952 $hiddenInput.val(''); 953 $daySelect.val(''); 954 $monthSelect.val(''); 955 $yearSelect.val(''); 956 $wrapper.removeClass('error success'); 957 958 // Versicherungsbeginn-Datepicker zurücksetzen 959 $('#versicherungsbeginn-display').text('Versicherungsbeginn*').removeClass('filled preview'); 960 $('#versicherungsbeginn-hidden').val(''); 961 $('#vb-day-select').val(''); 962 $('#vb-month-select').val(''); 963 $('#vb-year-select').val(''); 964 $('.versicherungsbeginn-wrapper').removeClass('error success'); 965 966 $('.tarif-btn').removeClass('clicked'); 967 968 successMsg[0].scrollIntoView({ behavior: 'smooth', block: 'center' }); 969 970 setTimeout(function() { 971 $('#beratungModal').fadeOut(); 972 $('body').removeClass('modal-open'); 973 }, 3000); 974 } else { 975 const errorMsg = $('<div class="form-error"></div>') 976 .html('<i class="fa fa-exclamation-triangle"></i> Ein Fehler ist aufgetreten. Bitte versuchen Sie es später erneut.'); 977 978 form.before(errorMsg); 979 errorMsg[0].scrollIntoView({ behavior: 'smooth', block: 'center' }); 980 } 981 }); 982 }); 983}); 984});
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.