PageSourceSearch

https://einwandern-schweiz.ch/wp-content/plugins/praemienrechner-2025/assets/js/modal.js?ver=1771327217

js einwandern-schweiz.ch collected 2026-10-04 05:55:40 UTC 38,384 bytes, 984 lines download raw bytes

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.