{"id":6,"date":"2025-04-06T09:38:29","date_gmt":"2025-04-06T07:38:29","guid":{"rendered":"https:\/\/lp.globalmedia.com.pl\/?page_id=6"},"modified":"2025-04-06T09:38:29","modified_gmt":"2025-04-06T07:38:29","slug":"kalendarz","status":"publish","type":"page","link":"https:\/\/lp.globalmedia.com.pl\/?page_id=6","title":{"rendered":"Kalendarz"},"content":{"rendered":"    <div class=\"glm-calendar-wrapper\">\n        <div class=\"glm-calendar-container-wrapper\">\n            <div id=\"glm-calendar-container\">\n                <div id=\"glm-calendar\"><\/div>\n                <div class=\"glm-calendar-branding\">\n                    <a href=\"https:\/\/www.globalmedia.com.pl\/system-rezerwacji-wynajmu-wordpress\/\" target=\"_blank\" rel=\"noopener\">GLM WP Calendar<\/a>\n                <\/div>\n            <\/div>\n            \n            <!-- Desktop version -->\n            <div id=\"glm-time-slots\" class=\"desktop-only\" style=\"display: none;\">\n                <h3>Dost\u0119pne terminy na dzie\u0144: <span id=\"selected-date\"><\/span><\/h3>\n                <div id=\"loading-spinner\" class=\"loading-spinner\">\n                    <div class=\"spinner\"><\/div>\n                    <p>\u0141adowanie termin\u00f3w...<\/p>\n                <\/div>\n                <div id=\"time-slots-list\"><\/div>\n            <\/div>\n        <\/div>\n\n        <!-- Mobile popup -->\n        <div id=\"mobile-slots-popup\" class=\"mobile-slots-popup\" style=\"display: none;\">\n            <div class=\"mobile-slots-popup-content\">\n                <button class=\"mobile-slots-popup-close\">&times;<\/button>\n                <h3>Dost\u0119pne terminy na dzie\u0144: <span id=\"mobile-selected-date\"><\/span><\/h3>\n                <div id=\"mobile-loading-spinner\" class=\"loading-spinner\">\n                    <div class=\"spinner\"><\/div>\n                    <p>\u0141adowanie termin\u00f3w...<\/p>\n                <\/div>\n                <div id=\"mobile-time-slots-list\"><\/div>\n            <\/div>\n        <\/div>\n    <\/div>\n\n    <div id=\"reservation-form-popup\" class=\"reservation-form-popup\">\n        <div class=\"reservation-form-content\">\n            <button type=\"button\" class=\"reservation-form-close\">&times;<\/button>\n            <h3>Formularz rezerwacji<\/h3>\n            <p>Termin: <span id=\"reservation-datetime\"><\/span><\/p>\n                        <form id=\"glm-reservation-form\">\n                                        <div class=\"form-group\">\n                            <label for=\"firstname\">\n                                Imi\u0119                                                            <\/label>\n                                                            <input type=\"text\" \n                                       id=\"firstname\" \n                                       name=\"firstname\"\n                                       class=\"form-control\"\n                                       >\n                                                    <\/div>\n                                                <div class=\"form-group\">\n                            <label for=\"lastname\">\n                                Nazwisko                                                            <\/label>\n                                                            <input type=\"text\" \n                                       id=\"lastname\" \n                                       name=\"lastname\"\n                                       class=\"form-control\"\n                                       >\n                                                    <\/div>\n                                                <div class=\"form-group\">\n                            <label for=\"phone\">\n                                Telefon                                                            <\/label>\n                                                            <input type=\"tel\" \n                                       id=\"phone\" \n                                       name=\"phone\"\n                                       class=\"form-control\"\n                                       >\n                                                    <\/div>\n                                                <div class=\"form-group\">\n                            <label for=\"email\">\n                                E-mail                                 *                            <\/label>\n                                                            <input type=\"email\" \n                                       id=\"email\" \n                                       name=\"email\"\n                                       class=\"form-control\"\n                                       required>\n                                                    <\/div>\n                                                <div class=\"form-group\">\n                            <label for=\"notes\">\n                                Uwagi                                                            <\/label>\n                                                            <textarea id=\"notes\" \n                                          name=\"notes\" \n                                          rows=\"3\"\n                                          class=\"form-control\"\n                                          ><\/textarea>\n                                                    <\/div>\n                                        <input type=\"hidden\" id=\"selected-slot\" name=\"selected_slot\">\n                <input type=\"hidden\" id=\"selected-date\" name=\"selected_date\">\n                \n                                \n                                <div class=\"reservation-button-container\">\n                    <button type=\"submit\" class=\"submit-reservation\" >\n                        Zarezerwuj termin                    <\/button>\n                                    <\/div>\n            <\/form>\n            \n            <!-- Komunikat sukcesu po wys\u0142aniu rezerwacji -->\n            <div id=\"reservation-success-message\" class=\"reservation-success-message\" style=\"display:none;\">\n                <div class=\"success-icon\">\n                    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" viewBox=\"0 0 52 52\" width=\"70\" height=\"70\">\n                        <circle cx=\"26\" cy=\"26\" r=\"25\" fill=\"none\" stroke=\"#007bff\" stroke-width=\"2\"\/>\n                        <path fill=\"none\" stroke=\"#007bff\" stroke-width=\"3\" d=\"M14.1 27.2l7.1 7.2 16.7-16.8\"\/>\n                    <\/svg>\n                <\/div>\n                <h3>Rezerwacja zosta\u0142a wys\u0142ana!<\/h3>\n                <p>Dzi\u0119kujemy za dokonanie rezerwacji.<\/p>\n                <button type=\"button\" class=\"success-close-button\">OK<\/button>\n            <\/div>\n\n            <style>\n            .form-control {\n                display: block;\n                width: 100%;\n                padding: 0.375rem 0.75rem;\n                font-size: 1rem;\n                line-height: 1.5;\n                color: #495057;\n                background-color: #fff;\n                background-clip: padding-box;\n                border: 1px solid #ced4da;\n                border-radius: 0.25rem;\n                transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n                margin-bottom: 1rem;\n                box-sizing: border-box;\n            }\n\n            .form-control:focus {\n                color: #495057;\n                background-color: #fff;\n                border-color: #80bdff;\n                outline: 0;\n                box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n            }\n            \n            \/* Style dla b\u0142\u0119dnych p\u00f3l formularza *\/\n            .error-field {\n                border-color: #dc3545 !important;\n                box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25) !important;\n                background-color: rgba(220, 53, 69, 0.05) !important;\n            }\n            \n            .error-field:focus {\n                border-color: #dc3545 !important;\n                box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25) !important;\n            }\n\n            select.form-control {\n                height: calc(2.25rem + 2px);\n                padding: 0.375rem 1.75rem 0.375rem 0.75rem;\n                -webkit-appearance: none;\n                -moz-appearance: none;\n                appearance: none;\n                background-image: url(\"data:image\/svg+xml;charset=utf-8,%3Csvg xmlns='http:\/\/www.w3.org\/2000\/svg' viewBox='0 0 4 5'%3E%3Cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'\/%3E%3C\/svg%3E\");\n                background-repeat: no-repeat;\n                background-position: right 0.75rem center;\n                background-size: 8px 10px;\n            }\n\n            .form-group {\n                margin-bottom: 1rem;\n            }\n\n            .form-group label {\n                display: inline-block;\n                margin-bottom: 0.5rem;\n                font-weight: 500;\n                color: #212529;\n            }\n\n            textarea.form-control {\n                height: auto;\n                min-height: 100px;\n                resize: vertical;\n            }\n\n            .submit-reservation {\n                background-color: #007bff;\n                color: #fff;\n                border: none;\n                padding: 0.5rem 1rem;\n                border-radius: 0.25rem;\n                cursor: pointer;\n                font-size: 1rem;\n                transition: background-color 0.15s ease-in-out;\n                width: 100%;\n                max-width: 320px;\n                display: block;\n                margin: 1rem auto 0;\n            }\n\n            .submit-reservation:hover {\n                background-color: #0067eb;\n            }\n            \n            \/* Style dla wy\u0142\u0105czonego przycisku rezerwacji *\/\n            .submit-reservation-disabled {\n                opacity: 0.6;\n                cursor: not-allowed;\n            }\n            \n            .submit-reservation-disabled:hover {\n                background-color: #007bff;\n            }\n\n            .reservation-form-popup {\n                display: none;\n                position: fixed;\n                z-index: 9999;\n                left: 0;\n                top: 0;\n                width: 100%;\n                height: 100%;\n                overflow: auto;\n                background-color: rgba(0,0,0,0.5);\n                opacity: 0;\n                transition: opacity 0.3s ease;\n            }\n            \n            .reservation-form-popup.active {\n                opacity: 1;\n            }\n\n            .reservation-form-content {\n                background-color: #fefefe;\n                margin: 0;\n                padding: 20px;\n                border: 1px solid #888;\n                width: 90%;\n                max-width: 500px;\n                border-radius: 5px;\n                position: absolute;\n                top: 50%;\n                left: 50%;\n                transform: translate(-50%, -50%);\n                max-height: 80vh;\n                overflow-y: auto;\n                text-align: center;\n            }\n\n            .reservation-form-content h3 {\n                margin-top: 0;\n                text-align: center;\n            }\n\n            .reservation-form-content p {\n                text-align: center;\n            }\n\n            .reservation-form-content #glm-reservation-form {\n                text-align: left;\n                width: 100%;\n                max-width: 480px;\n                margin: 0 auto;\n            }\n\n            .reservation-form-close {\n                position: absolute;\n                right: 20px;\n                top: 10px;\n                font-size: 24px;\n                cursor: pointer;\n                background: none;\n                border: none;\n                color: #666;\n            }\n\n            .reservation-form-close:hover {\n                color: #333;\n            }\n\n            #reservation-datetime {\n                font-weight: bold;\n                margin-bottom: 20px;\n                color: #000000;\n                display: inline-block;\n            }\n            \n            \/* Mobile styles *\/\n            @media (max-width: 768px) {\n                .reservation-form-content {\n                    margin: 0;\n                    padding: 15px;\n                    width: 95%;\n                    max-height: 90vh;\n                    top: 50%;\n                    left: 50%;\n                    transform: translate(-50%, -50%);\n                }\n                \n                .reservation-form-close {\n                    right: 10px;\n                    top: 5px;\n                }\n\n                .form-group {\n                    width: 100%;\n                }\n\n                .submit-reservation {\n                    max-width: 100%;\n                }\n            }\n\n            \/* Style dla komunikatu o wy\u0142\u0105czonej mo\u017cliwo\u015bci rezerwacji *\/\n            .reservation-button-container {\n                position: relative;\n                margin: 1rem auto 0;\n                max-width: 320px;\n            }\n            \n            .reservation-disabled-info {\n                margin-top: 10px;\n                color: #721c24;\n                background-color: #f8d7da;\n                border: 1px solid #f5c6cb;\n                border-radius: 4px;\n                padding: 8px 12px;\n                font-size: 14px;\n                text-align: center;\n            }\n            \n            \/* Style dla zg\u00f3d RODO *\/\n            .rodo-consents-section {\n                margin: 20px 0;\n                border-top: 1px solid #eee;\n                padding-top: 15px;\n            }\n            \n            .rodo-consent-checkbox {\n                margin-bottom: 10px;\n            }\n            \n            .consent-wrapper {\n                display: flex;\n                align-items: flex-start;\n            }\n            \n            .consent-checkbox-container {\n                flex-shrink: 0;\n                padding-top: 3px;\n                margin-right: 10px;\n            }\n            \n            .consent-checkbox-container input[type=\"checkbox\"] {\n                width: 18px;\n                height: 18px;\n                margin: 0;\n                visibility: visible !important;\n                opacity: 1 !important;\n                display: inline-block !important;\n                appearance: auto !important;\n                -webkit-appearance: checkbox !important;\n                -moz-appearance: checkbox !important;\n                border: 1px solid #555;\n                background-color: white;\n                border-radius: 3px;\n                position: relative;\n                cursor: pointer;\n                vertical-align: middle;\n            }\n            \n            .consent-text-container {\n                flex-grow: 1;\n                cursor: pointer;\n                font-weight: normal;\n            }\n            \n            .consent-text {\n                font-size: 14px;\n                line-height: 1.4;\n                color: #555;\n            }\n            \n            .required-mark {\n                color: #dc3545;\n                font-weight: bold;\n            }\n            \n            .consent-error {\n                background-color: rgba(220, 53, 69, 0.1);\n                padding: 10px;\n                border-radius: 4px;\n                border-left: 3px solid #dc3545;\n                animation: consent-error-pulse 1s;\n            }\n            \n            @keyframes consent-error-pulse {\n                0% { background-color: rgba(220, 53, 69, 0.1); }\n                50% { background-color: rgba(220, 53, 69, 0.2); }\n                100% { background-color: rgba(220, 53, 69, 0.1); }\n            }\n            \n            \/* Style dla niestandardowego komunikatu sukcesu *\/\n            .reservation-success-message {\n                display: none;\n                position: absolute;\n                top: 50%;\n                left: 50%;\n                transform: translate(-50%, -50%);\n                width: 90%;\n                max-width: 400px;\n                background-color: white;\n                padding: 30px 20px;\n                text-align: center;\n                border-radius: 8px;\n                box-shadow: 0 5px 20px rgba(0,0,0,0.15);\n                z-index: 1000;\n                animation: fadeIn 0.3s ease-out;\n            }\n            \n            @keyframes fadeIn {\n                from { opacity: 0; transform: translate(-50%, -50%) scale(0.9); }\n                to { opacity: 1; transform: translate(-50%, -50%) scale(1); }\n            }\n            \n            .success-icon {\n                margin: 0 auto 20px;\n                width: 70px;\n                height: 70px;\n                animation: scaleIn 0.5s ease-out 0.2s both;\n            }\n            \n            @keyframes scaleIn {\n                from { transform: scale(0); }\n                to { transform: scale(1); }\n            }\n            \n            .reservation-success-message h3 {\n                color: #007bff;\n                margin: 10px 0;\n                font-size: 22px;\n                animation: slideUp 0.4s ease-out 0.3s both;\n            }\n            \n            .reservation-success-message p {\n                color: #666;\n                margin-bottom: 20px;\n                animation: slideUp 0.4s ease-out 0.4s both;\n            }\n            \n            @keyframes slideUp {\n                from { opacity: 0; transform: translateY(20px); }\n                to { opacity: 1; transform: translateY(0); }\n            }\n            \n            .success-close-button {\n                background-color: #007bff;\n                color: white;\n                border: none;\n                padding: 10px 25px;\n                border-radius: 4px;\n                cursor: pointer;\n                font-size: 16px;\n                transition: background-color 0.2s;\n                animation: slideUp 0.4s ease-out 0.5s both;\n            }\n            \n            .success-close-button:hover {\n                background-color: #0067eb;\n            }\n            \n            \/* Overlay dla komunikatu sukcesu *\/\n            .success-overlay {\n                position: absolute;\n                top: 0;\n                left: 0;\n                width: 100%;\n                height: 100%;\n                background-color: rgba(255,255,255,0.9);\n                z-index: 999;\n                animation: fadeIn 0.3s ease-out;\n            }\n            <\/style>\n                    <\/div>\n    <\/div>\n\n    <script>\n    jQuery(document).ready(function($) {\n        \/\/ Globalne zmienne\n        let reservations = [];\n        const emptyDates = []; \/\/ Tablica przechowuj\u0105ca daty, kt\u00f3re nie maj\u0105 rezerwacji\n        const emptyDatesTimestamps = {}; \/\/ Obiekt przechowuj\u0105cy timestampy dodania dat do cache\n        const CACHE_TTL = 60000; \/\/ Czas \u017cycia cache w milisekundach (60 sekund)\n        \n        const showOccupiedSlots = false;\n        const objectId = 1;\n        const preloadDays = 7;\n        \n        \/\/ Data pierwszego pobrania danych\n        const initialLoadDate = new Date();\n        initialLoadDate.setHours(0, 0, 0, 0);\n        \n        \/\/ Funkcja do sprawdzania czy mamy dane dla danej daty\n        function hasReservationsForDate(date) {\n            \/\/ Sprawd\u017a czy mamy jakiekolwiek rezerwacje dla tej daty\n            return reservations.some(r => r.booking_date === date);\n        }\n        \n        \/\/ Funkcja do sprawdzania czy wiemy, \u017ce data nie ma rezerwacji i czy cache jest aktualny\n        function isEmptyDate(date) {\n            const index = emptyDates.indexOf(date);\n            if (index === -1) return false;\n            \n            \/\/ Sprawd\u017a czy cache nie wygas\u0142\n            const timestamp = emptyDatesTimestamps[date] || 0;\n            const now = Date.now();\n            \n            if (now - timestamp > CACHE_TTL) {\n                \/\/ Cache wygas\u0142, usu\u0144 dat\u0119 z cache\n                emptyDates.splice(index, 1);\n                delete emptyDatesTimestamps[date];\n                console.log(`Cache dla daty ${date} wygas\u0142 (${Math.round((now - timestamp)\/1000)}s)`);\n                return false;\n            }\n            \n            return true;\n        }\n        \n        \/\/ Funkcja do sprawdzania czy data mie\u015bci si\u0119 w zakresie preloadDays od pierwszego pobrania\n        function isDateInPreloadRange(date) {\n            const checkDate = new Date(date);\n            const endDate = new Date(initialLoadDate);\n            endDate.setDate(endDate.getDate() + preloadDays - 1);\n            return checkDate >= initialLoadDate && checkDate <= endDate;\n        }\n        \n        \/\/ Funkcja do pobierania rezerwacji z API\n        function fetchReservations(date, forceReload = false) {\n            \/\/ Je\u015bli mamy ju\u017c dane dla tej daty i nie wymuszamy prze\u0142adowania, zwr\u00f3\u0107 istniej\u0105ce dane\n            if (!forceReload && hasReservationsForDate(date)) {\n                const existingReservations = reservations.filter(r => r.booking_date === date);\n                console.log(`Rezerwacje dla ${date}:`, existingReservations.map(r => `${r.booking_time_start}-${r.booking_time_end}`));\n                return Promise.resolve({ success: true, data: existingReservations });\n            }\n            \n            \/\/ Je\u015bli wiemy, \u017ce dla tej daty nie ma rezerwacji i cache jest aktualny, zwr\u00f3\u0107 pust\u0105 tablic\u0119\n            if (!forceReload && isEmptyDate(date)) {\n                console.log(`Brak rezerwacji w dniu ${date} (z cache, pozosta\u0142o ${Math.round((CACHE_TTL - (Date.now() - emptyDatesTimestamps[date]))\/1000)}s)`);\n                return Promise.resolve({ success: true, data: [] });\n            }\n            \n            return $.ajax({\n                url: 'https:\/\/lp.globalmedia.com.pl\/wp-admin\/admin-ajax.php',\n                type: 'POST',\n                data: {\n                    action: 'get_reservations',\n                    date: date,\n                    object_id: objectId,\n                    days: 1,\n                    security: 'fb41ab77e0'\n                }\n            }).then(function(response) {\n                if (response.success) {\n                    const newReservations = response.data || [];\n                    \n                    if (newReservations.length > 0) {\n                        \/\/ Usu\u0144 stare rezerwacje dla tej daty\n                        reservations = [\n                            ...reservations.filter(r => r.booking_date !== date),\n                            ...newReservations\n                        ];\n                        console.log(`Pobrano rezerwacje dla ${date}:`, newReservations.map(r => `${r.booking_time_start}-${r.booking_time_end}`));\n                        \n                        \/\/ Usu\u0144 dat\u0119 z listy pustych dat, je\u015bli by\u0142a tam wcze\u015bniej\n                        const emptyIndex = emptyDates.indexOf(date);\n                        if (emptyIndex !== -1) {\n                            emptyDates.splice(emptyIndex, 1);\n                            delete emptyDatesTimestamps[date];\n                        }\n                    } else {\n                        console.log(`Brak rezerwacji w dniu ${date}, dodaj\u0119 do cache (TTL: ${CACHE_TTL\/1000}s)`);\n                        \/\/ Dodaj dat\u0119 do listy pustych dat z aktualnym timestampem\n                        if (!emptyDates.includes(date)) {\n                            emptyDates.push(date);\n                            emptyDatesTimestamps[date] = Date.now();\n                        } else {\n                            \/\/ Aktualizuj timestamp\n                            emptyDatesTimestamps[date] = Date.now();\n                        }\n                    }\n                }\n                return response;\n            });\n        }\n\n        function isSlotAvailable(startTime, endTime, selectedDate) {\n            \/\/ Konwertuj czasy slotu na timestamp dla \u0142atwiejszego por\u00f3wnania\n            const slotStart = new Date(selectedDate + ' ' + startTime).getTime();\n            const slotEnd = new Date(selectedDate + ' ' + endTime).getTime();\n\n            \/\/ Sprawd\u017a ka\u017cd\u0105 rezerwacj\u0119\n            for (const reservation of reservations) {\n                if (reservation.booking_date === selectedDate) {\n                    const bookingStart = new Date(selectedDate + ' ' + reservation.booking_time_start).getTime();\n                    const bookingEnd = new Date(selectedDate + ' ' + reservation.booking_time_end).getTime();\n\n                    \/\/ Sprawd\u017a czy jest kolizja z rezerwacj\u0105\n                    if ((slotStart < bookingEnd && slotEnd > bookingStart) || \n                        (slotStart === bookingStart && slotEnd === bookingEnd)) {\n                        return false;\n                    }\n                }\n            }\n            return true;\n        }\n        \n        $.datepicker.regional['pl'] = {\n            closeText: 'Zamknij',\n            prevText: 'Poprzedni',\n            nextText: 'Nast\u0119pny',\n            currentText: 'Dzi\u015b',\n            monthNames: ['Stycze\u0144','Luty','Marzec','Kwiecie\u0144','Maj','Czerwiec',\n            'Lipiec','Sierpie\u0144','Wrzesie\u0144','Pa\u017adziernik','Listopad','Grudzie\u0144'],\n            monthNamesShort: ['Sty','Lu','Mar','Kw','Maj','Cze',\n            'Lip','Sie','Wrz','Pa','Lis','Gru'],\n            dayNames: ['Niedziela','Poniedzia\u0142ek','Wtorek','\u015aroda','Czwartek','Pi\u0105tek','Sobota'],\n            dayNamesShort: ['Nie','Pon','Wto','\u015aro','Czw','Pi\u0105','Sob'],\n            dayNamesMin: ['N','P','W','\u015a','C','P','S'],\n            weekHeader: 'Tydz',\n            dateFormat: 'yy-mm-dd',\n            firstDay: 1,\n            isRTL: false,\n            showMonthAfterYear: false,\n            yearSuffix: ''\n        };\n        $.datepicker.setDefaults($.datepicker.regional['pl']);\n        \n        \/\/ POPRAWIONA WERSJA: zapewniamy, \u017ce klucze s\u0105 zawsze stringami\n        const customSlots = {\"1\":[{\"start\":\"9:00\",\"end\":\"10:30\"},{\"start\":\"14:00\",\"end\":\"14:30\"},{\"start\":\"16:00\",\"end\":\"17:30\"}],\"2\":[{\"start\":\"14:00\",\"end\":\"14:30\"},{\"start\":\"16:00\",\"end\":\"17:30\"}],\"3\":[{\"start\":\"14:00\",\"end\":\"14:30\"},{\"start\":\"16:00\",\"end\":\"17:30\"}],\"4\":[{\"start\":\"14:00\",\"end\":\"14:30\"},{\"start\":\"16:00\",\"end\":\"17:30\"}],\"5\":[{\"start\":\"14:00\",\"end\":\"14:30\"},{\"start\":\"16:00\",\"end\":\"17:30\"}]};\n        console.log(\"Raw customSlots po json_encode:\", \"{\\\"1\\\":[{\\\"start\\\":\\\"9:00\\\",\\\"end\\\":\\\"10:30\\\"},{\\\"start\\\":\\\"14:00\\\",\\\"end\\\":\\\"14:30\\\"},{\\\"start\\\":\\\"16:00\\\",\\\"end\\\":\\\"17:30\\\"}],\\\"2\\\":[{\\\"start\\\":\\\"14:00\\\",\\\"end\\\":\\\"14:30\\\"},{\\\"start\\\":\\\"16:00\\\",\\\"end\\\":\\\"17:30\\\"}],\\\"3\\\":[{\\\"start\\\":\\\"14:00\\\",\\\"end\\\":\\\"14:30\\\"},{\\\"start\\\":\\\"16:00\\\",\\\"end\\\":\\\"17:30\\\"}],\\\"4\\\":[{\\\"start\\\":\\\"14:00\\\",\\\"end\\\":\\\"14:30\\\"},{\\\"start\\\":\\\"16:00\\\",\\\"end\\\":\\\"17:30\\\"}],\\\"5\\\":[{\\\"start\\\":\\\"14:00\\\",\\\"end\\\":\\\"14:30\\\"},{\\\"start\\\":\\\"16:00\\\",\\\"end\\\":\\\"17:30\\\"}]}\");\n        const useCustomSlots = true;\n        \n        \/\/ Zawsze definiuj te zmienne, bez wzgl\u0119du na to czy custom_slots jest puste czy nie\n        const startHour = 8;\n        const startMinutes = 0;\n        const endHour = 16;\n        const endMinutes = 0;\n        const slotDuration = 30;\n        \n        const workingDays = [1,2,3,4,5];\n        \n        function formatTime(hour, minutes) {\n            return `${hour.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}`;\n        }\n\n        function generateTimeSlots(dateText) {\n            let availableSlots = 0;\n            let slotsHtml = [];\n            \n            \/\/ Dodaj logowanie dla debugowania\n            console.group(\"generateTimeSlots Debug (\" + dateText + \")\");\n            console.log(\"useCustomSlots:\", useCustomSlots);\n            console.log(\"customSlots:\", customSlots);\n            \n            if (useCustomSlots) {\n                \/\/ Pobierz dzie\u0144 tygodnia (1-7) dla wybranej daty\n                const selectedDate = new Date(dateText);\n                \n                \/\/ Pobierz komponenty daty dla debugowania\n                const year = selectedDate.getFullYear();\n                const month = selectedDate.getMonth() + 1;\n                const day = selectedDate.getDate();\n                \n                \/\/ Pobierz dzie\u0144 tygodnia (0-6, niedziela to 0)\n                const rawDayOfWeek = selectedDate.getDay();\n                \n                \/\/ Konwertuj na format 1-7 (poniedzia\u0142ek to 1, niedziela to 7)\n                const dayOfWeek = rawDayOfWeek === 0 ? 7 : rawDayOfWeek;\n                \n                \/\/ Konwertuj do stringa dla zapewnienia sp\u00f3jno\u015bci mi\u0119dzy \u015brodowiskami\n                const dayOfWeekStr = dayOfWeek.toString();\n                \n                console.log(\"Wybrana data:\", {\n                    pe\u0142naData: selectedDate.toString(),\n                    rok: year,\n                    miesi\u0105c: month,\n                    dzie\u0144: day,\n                    rawDayOfWeek: rawDayOfWeek,\n                    dayOfWeek: dayOfWeek,\n                    dayOfWeekStr: dayOfWeekStr\n                });\n                \n                \/\/ Debugowanie dost\u0119pnych slot\u00f3w\n                console.log(\"Dost\u0119pne sloty dla dni (klucze):\", Object.keys(customSlots));\n                \n                \/\/ NAPRAWIONY KOD: Szukaj slot\u00f3w zawsze po kluczu znakowym\n                let daySlots = [];\n                \n                \/\/ Szukaj po kluczu znakowym\n                if (customSlots[dayOfWeekStr]) {\n                    daySlots = customSlots[dayOfWeekStr];\n                    console.log(`Znaleziono sloty dla dnia tygodnia ${dayOfWeekStr} (klucz znakowy):`, daySlots);\n                } \n                \/\/ W ostateczno\u015bci, sprawd\u017a wszystkie klucze r\u0119cznie\n                else {\n                    console.warn(`Nie znaleziono slot\u00f3w dla dnia tygodnia ${dayOfWeekStr}. Sprawdzam wszystkie klucze...`);\n                    \n                    \/\/ Iteruj przez wszystkie klucze i sprawd\u017a, czy kt\u00f3ry\u015b odpowiada dniowi tygodnia\n                    for (const key in customSlots) {\n                        if (key === dayOfWeekStr || parseInt(key) === dayOfWeek) {\n                            daySlots = customSlots[key];\n                            console.log(`Znaleziono sloty dla dnia tygodnia ${dayOfWeekStr} (klucz ${key}):`, daySlots);\n                            break;\n                        }\n                    }\n                    \n                    if (daySlots.length === 0) {\n                        console.error(`Nie znaleziono slot\u00f3w dla dnia tygodnia ${dayOfWeekStr} po sprawdzeniu wszystkich kluczy:`, Object.keys(customSlots));\n                        \n                        \/\/ AWARYJNE ROZWI\u0104ZANIE: Je\u015bli mamy sloty dla innych dni, u\u017cyj przyk\u0142adowego slotu jako template\n                        let hasFoundAnySlots = false;\n                        for (const key in customSlots) {\n                            if (customSlots[key] && customSlots[key].length > 0) {\n                                console.warn(\"Tworz\u0119 awaryjne sloty na podstawie wzorca z dnia \" + key);\n                                \/\/ Kopiujemy sloty z innego dnia jako przyk\u0142ad\n                                daySlots = [...customSlots[key]];\n                                hasFoundAnySlots = true;\n                                break;\n                            }\n                        }\n                        \n                        if (!hasFoundAnySlots) {\n                            console.error(\"Nie znaleziono \u017cadnych slot\u00f3w w obiekcie customSlots!\");\n                        }\n                    }\n                }\n                \n                slotsHtml = daySlots.map(slot => {\n                    const isAvailable = isSlotAvailable(slot.start, slot.end, dateText);\n                    console.log(`Slot ${slot.start}-${slot.end}: ${isAvailable ? 'dost\u0119pny' : 'niedost\u0119pny'}`);\n                    \n                    \/\/ Je\u015bli slot jest zaj\u0119ty i nie chcemy pokazywa\u0107 zaj\u0119tych slot\u00f3w, pomijamy go\n                    if (!isAvailable && !showOccupiedSlots) {\n                        return '';\n                    }\n                    \n                    if (isAvailable) availableSlots++;\n                    \n                    return `<button class=\"time-slot\" \n                                   style=\"margin: 0;\" \n                                   ${!isAvailable ? 'disabled' : ''}>\n                        ${slot.start} - ${slot.end}\n                        ${!isAvailable ? ' (Zaj\u0119ty)' : ''}\n                    <\/button>`;\n                }).filter(slot => slot !== ''); \/\/ Usu\u0144 puste sloty\n            } else {\n                \/\/ Standardowe sloty\n                if (typeof startHour !== 'undefined' && typeof endHour !== 'undefined' && typeof slotDuration !== 'undefined') {\n                let currentHour = startHour;\n                let currentMinutes = startMinutes;\n                \n                while (currentHour < endHour || (currentHour === endHour && currentMinutes <= endMinutes)) {\n                    const endSlotMinutes = currentMinutes + slotDuration;\n                    const endSlotHour = currentHour + Math.floor(endSlotMinutes \/ 60);\n                    const displayEndMinutes = endSlotMinutes % 60;\n                    \n                    \/\/ Sprawdzamy, czy slot nie przekracza godziny ko\u0144cowej\n                    if (endSlotHour > endHour || (endSlotHour === endHour && displayEndMinutes > endMinutes)) break;\n                    \n                    const startTime = formatTime(currentHour, currentMinutes);\n                    const endTime = formatTime(endSlotHour, displayEndMinutes);\n                    \n                    const isAvailable = isSlotAvailable(startTime, endTime, dateText);\n                    \n                    \/\/ Je\u015bli slot jest zaj\u0119ty i nie chcemy pokazywa\u0107 zaj\u0119tych slot\u00f3w, pomijamy go\n                    if (!isAvailable && !showOccupiedSlots) {\n                        \/\/ Przej\u015bcie do nast\u0119pnego slotu\n                        currentMinutes = endSlotMinutes;\n                        if (currentMinutes >= 60) {\n                            currentHour += Math.floor(currentMinutes \/ 60);\n                            currentMinutes = currentMinutes % 60;\n                        }\n                        continue;\n                    }\n                    \n                    if (isAvailable) availableSlots++;\n                    \n                    slotsHtml.push(\n                        `<button class=\"time-slot\" \n                                 style=\"margin: 0;\" \n                                 ${!isAvailable ? 'disabled' : ''}>\n                            ${startTime} - ${endTime}\n                            ${!isAvailable ? ' (Zaj\u0119ty)' : ''}\n                        <\/button>`\n                    );\n                    \n                    \/\/ Przej\u015bcie do nast\u0119pnego slotu\n                    currentMinutes = endSlotMinutes;\n                    if (currentMinutes >= 60) {\n                        currentHour += Math.floor(currentMinutes \/ 60);\n                        currentMinutes = currentMinutes % 60;\n                    }\n                    }\n                } else {\n                    console.error('Brak zdefiniowanych parametr\u00f3w godzin i d\u0142ugo\u015bci slotu. Sprawd\u017a konfiguracj\u0119 shortcode.');\n                }\n            }\n\n            \/\/ Je\u015bli nie ma dost\u0119pnych termin\u00f3w, zwr\u00f3\u0107 komunikat\n            if (availableSlots === 0) {\n                console.log(\"Brak dost\u0119pnych slot\u00f3w\");\n                console.groupEnd();\n                return ['<div class=\"no-slots-message\">Brak dost\u0119pnych termin\u00f3w w tym dniu<\/div>'];\n            }\n            \n            console.log(`Znaleziono ${availableSlots} dost\u0119pnych slot\u00f3w`);\n            console.groupEnd();\n            return slotsHtml;\n        }\n\n        \/\/ Dodaj funkcj\u0119 debugowania niestandardowych slot\u00f3w\n        function debugCustomSlots() {\n            console.group(\"Debug niestandardowych slot\u00f3w (custom_slots)\");\n            console.log(\"useCustomSlots:\", useCustomSlots);\n            \n            if (useCustomSlots) {\n                console.log(\"Warto\u015b\u0107 customSlots:\", customSlots);\n                console.log(\"Typ customSlots:\", typeof customSlots);\n                console.log(\"Klucze customSlots:\", Object.keys(customSlots));\n                \n                \/\/ Sprawd\u017a dok\u0142adnie ka\u017cdy klucz i jego warto\u015b\u0107\n                for (const key in customSlots) {\n                    const keyType = typeof key;\n                    const parsedKey = parseInt(key);\n                    const slots = customSlots[key];\n                    \n                    console.log(`Klucz: \"${key}\", Typ: ${keyType}, ParsedInt: ${parsedKey}, Liczba slot\u00f3w: ${slots ? slots.length : 0}`);\n                    \n                    if (slots && slots.length > 0) {\n                        console.log(`Przyk\u0142adowy slot dla dnia ${key}:`, slots[0]);\n                    }\n                }\n                \n                \/\/ Poka\u017c informacje o parametrach shortcode\n                console.log(\"Informacje o parametrach shortcode:\");\n                console.log(\"- working_days:\", workingDays);\n                console.log(\"- start_hour:\", startHour);\n                console.log(\"- end_hour:\", endHour);\n                console.log(\"- slot_duration:\", slotDuration);\n            } else {\n                console.log(\"Niestandardowe sloty nie s\u0105 u\u017cywane. Sprawd\u017a parametr 'custom_slots' w shortcode.\");\n            }\n            \n            console.groupEnd();\n        }\n        \n        \/\/ Wywo\u0142aj funkcj\u0119 debugowania po za\u0142adowaniu strony\n        setTimeout(debugCustomSlots, 1000);\n\n        const today = new Date();\n        today.setHours(0, 0, 0, 0);\n        \n        const allowPastMonths = false;\n        \n        const monthsAhead = 3;\n        \n        function showLoadingSpinner() {\n            const isMobile = window.innerWidth <= 768;\n            if (isMobile) {\n                $('#mobile-loading-spinner').show();\n                $('#mobile-time-slots-list').hide();\n                $('#mobile-slots-popup').addClass('active').show();\n            } else {\n                $('#loading-spinner').show();\n                $('#time-slots-list').hide();\n            }\n        }\n\n        function hideLoadingSpinner() {\n            const isMobile = window.innerWidth <= 768;\n            if (isMobile) {\n                $('#mobile-loading-spinner').hide();\n                $('#mobile-time-slots-list').show();\n            } else {\n                $('#loading-spinner').hide();\n                $('#time-slots-list').show();\n            }\n        }\n\n        function showTimeSlots(dateText, slots) {\n            \/\/ U\u017cywamy bezpo\u015brednio dateText - parametr jest ju\u017c w formacie YYYY-MM-DD\n            const formattedDate = dateText;\n            const isMobile = window.innerWidth <= 768;\n            \n            if (isMobile) {\n                \/\/ Zapewnij, \u017ce popup jest widoczny przed animacj\u0105\n                const popup = $('#mobile-slots-popup');\n                popup.show();\n                \n                \/\/ Wymuszenie reflow przed dodaniem klasy active\n                void popup[0].offsetWidth;\n                \n                \/\/ Dodaj klas\u0119 active dla animacji\n                popup.addClass('active');\n                \n                $('#mobile-selected-date').text(formattedDate);\n                $('#mobile-time-slots-list').html(slots.join(''));\n                \n                \/\/ Dostosuj wysoko\u015b\u0107 kontenera slot\u00f3w\n                adjustPopupHeight();\n            } else {\n                \/\/ Desktop version (bez zmian)\n                $('#selected-date').text(formattedDate);\n                $('#time-slots-list').html(slots.join(''));\n                $('#glm-time-slots').show();\n            }\n        }\n\n        \/\/ Funkcja dostosowuj\u0105ca wysoko\u015b\u0107 popupu\n        function adjustPopupHeight() {\n            const popup = $('.mobile-slots-popup-content');\n            const windowHeight = window.innerHeight;\n            const maxHeight = windowHeight * 0.9; \/\/ 90% wysoko\u015bci okna\n            popup.css('max-height', maxHeight + 'px');\n        }\n\n        \/\/ Nas\u0142uchuj zmiany orientacji ekranu\n        $(window).on('resize orientationchange', function() {\n            if (window.innerWidth <= 768) {\n                adjustPopupHeight();\n            }\n        });\n\n        \/\/ Poprawiona obs\u0142uga zamykania popupu\n        $('.mobile-slots-popup-close').on('click', function(e) {\n            e.preventDefault();\n            e.stopPropagation();\n            const popup = $('#mobile-slots-popup');\n            popup.removeClass('active');\n            setTimeout(() => popup.hide(), 300); \/\/ Poczekaj na zako\u0144czenie animacji\n        });\n\n        \/\/ Zamykanie popupu przy klikni\u0119ciu poza nim\n        $('.mobile-slots-popup').on('click', function(e) {\n            if ($(e.target).hasClass('mobile-slots-popup')) {\n                const popup = $(this);\n                popup.removeClass('active');\n                setTimeout(() => popup.hide(), 300);\n            }\n        });\n\n        $('#glm-calendar-container').datepicker({\n            inline: true,\n            showOtherMonths: true,\n            selectOtherMonths: true,\n            minDate: allowPastMonths ? null : new Date(),\n            maxDate: new Date(today.getFullYear(), today.getMonth() + monthsAhead, 0),\n            beforeShowDay: function(date) {\n                const dayOfWeek = date.getDay() || 7;\n                const isWorkingDay = workingDays.includes(dayOfWeek);\n                \n                if (!isWorkingDay) {\n                    return [false, 'ui-state-disabled', 'Dzie\u0144 wolny'];\n                }\n                \n                if (!allowPastMonths && date < today) {\n                    return [false, '', 'Data z przesz\u0142o\u015bci'];\n                }\n                \n                return [true, ''];\n            },\n            onChangeMonthYear: function(year, month, inst) {\n                const maxDate = new Date(today.getFullYear(), today.getMonth() + monthsAhead, 0);\n                \n                if (!allowPastMonths) {\n                    const currentDate = new Date();\n                    if (year < currentDate.getFullYear() || \n                        (year === currentDate.getFullYear() && month < currentDate.getMonth() + 1)) {\n                        setTimeout(function() {\n                            $(this).datepicker('setDate', currentDate);\n                        }.bind(this), 0);\n                    }\n                }\n                \n                const selectedDate = new Date(year, month - 1, 1);\n                if (selectedDate > maxDate) {\n                    setTimeout(function() {\n                        $(this).datepicker('setDate', maxDate);\n                    }.bind(this), 0);\n                }\n            },\n            onSelect: function(dateText, inst) {\n                showLoadingSpinner();\n                \n                if (isDateInPreloadRange(dateText)) {\n                    const timeSlots = generateTimeSlots(dateText);\n                    showTimeSlots(dateText, timeSlots);\n                    hideLoadingSpinner();\n                } else {\n                    fetchReservations(dateText)\n                        .then(function(response) {\n                            if (response.success) {\n                                const timeSlots = generateTimeSlots(dateText);\n                                showTimeSlots(dateText, timeSlots);\n                            } else {\n                                const errorMessage = '<p>Wyst\u0105pi\u0142 b\u0142\u0105d podczas pobierania dost\u0119pno\u015bci.<\/p>';\n                                showTimeSlots(dateText, [errorMessage]);\n                            }\n                            hideLoadingSpinner();\n                        })\n                        .catch(function(error) {\n                            const errorMessage = '<p>Wyst\u0105pi\u0142 b\u0142\u0105d podczas pobierania dost\u0119pno\u015bci.<\/p>';\n                            showTimeSlots(dateText, [errorMessage]);\n                            hideLoadingSpinner();\n                            console.error('B\u0142\u0105d podczas pobierania rezerwacji:', error);\n                        });\n                }\n            }\n        });\n\n        \/\/ Cache initial data without displaying it\n        const formattedToday = $.datepicker.formatDate('yy-mm-dd', today);\n        if (!hasReservationsForDate(formattedToday) && !isEmptyDate(formattedToday)) {\n            fetchReservations(formattedToday, false)\n                .then(function(response) {\n                    if (response.success) {\n                        console.log('Cached initial data for today');\n                    }\n                })\n                .catch(function(error) {\n                    console.error('Error caching initial data:', error);\n                });\n        }\n        \n        \/\/ Wy\u015bwietl informacje o wst\u0119pnie pobranych rezerwacjach\n        console.log(`Za\u0142adowano wst\u0119pne rezerwacje na ${preloadDays} dni do przodu (ustawienie \"Liczba dni do wst\u0119pnego za\u0142adowania\")`);\n        const groupedReservations = {};\n        \n        \/\/ Przygotuj tablic\u0119 dat z przedzia\u0142u wst\u0119pnie za\u0142adowanego\n        const preloadedDates = [];\n        for (let i = 0; i < preloadDays; i++) {\n            const date = new Date(initialLoadDate);\n            date.setDate(date.getDate() + i);\n            const formattedDate = $.datepicker.formatDate('yy-mm-dd', date);\n            preloadedDates.push(formattedDate);\n        }\n        \n        \/\/ Grupuj rezerwacje po datach\n        reservations.forEach(r => {\n            if (!groupedReservations[r.booking_date]) {\n                groupedReservations[r.booking_date] = [];\n            }\n            groupedReservations[r.booking_date].push(`${r.booking_time_start}-${r.booking_time_end}`);\n        });\n        \n        \/\/ Wypisz rezerwacje wed\u0142ug dat\n        Object.keys(groupedReservations).forEach(date => {\n            console.log(`${date}: ${groupedReservations[date].length} rezerwacji:`, groupedReservations[date]);\n            \n            \/\/ Usu\u0144 dat\u0119 z listy preloadedDates\n            const index = preloadedDates.indexOf(date);\n            if (index !== -1) {\n                preloadedDates.splice(index, 1);\n            }\n        });\n        \n        \/\/ Daty bez rezerwacji dodaj do cache'u\n        if (preloadedDates.length > 0) {\n            console.log(`Nast\u0119puj\u0105ce dni nie maj\u0105 rezerwacji (dodaj\u0119 do cache):`, preloadedDates);\n            \n            \/\/ Dodaj daty bez rezerwacji do cache'u\n            preloadedDates.forEach(date => {\n                if (!emptyDates.includes(date)) {\n                    emptyDates.push(date);\n                    emptyDatesTimestamps[date] = Date.now();\n                }\n            });\n        }\n\n        \/\/ Dodaj obs\u0142ug\u0119 klikni\u0119cia z animacj\u0105\n        $(document).on('click', '.time-slot:not(:disabled)', function() {\n            const selectedTime = $(this).text().trim().replace(' (Zaj\u0119ty)', '');\n            const isMobile = window.innerWidth <= 768;\n            \n            \/\/ Zamknij najpierw popup z terminami na mobile\n            if (isMobile) {\n                $('#mobile-slots-popup').removeClass('active');\n                setTimeout(() => $('#mobile-slots-popup').hide(), 300);\n            }\n            \n            \/\/ Ustaw dane terminu\n            const selectedDate = isMobile ? $('#mobile-selected-date').text() : $('#selected-date').text();\n            \n            \/\/ Poka\u017c dat\u0119 i godzin\u0119 w formularzu\n            $('#reservation-datetime').text(`${selectedDate}, ${selectedTime}`);\n            \n            \/\/ Data jest ju\u017c w formacie ISO YYYY-MM-DD, nie wymaga parsowania\n            try {\n            \/\/ Zapisz warto\u015bci w ukrytych polach formularza\n            $('#selected-slot').val(selectedTime);\n                $('#selected-date').val(selectedDate);\n                \n                console.log(`Wybrana data: ${selectedDate}, czas: ${selectedTime}`);\n                \n            } catch (error) {\n                console.error('B\u0142\u0105d podczas przetwarzania daty:', error);\n                \/\/ U\u017cyj aktualnej daty jako fallback\n                const now = new Date();\n                const formattedDate = `${now.getFullYear()}-${(now.getMonth() + 1).toString().padStart(2, '0')}-${now.getDate().toString().padStart(2, '0')}`;\n                $('#selected-date').val(formattedDate);\n            }\n            \n            \/\/ Poka\u017c formularz\n            const popup = $('#reservation-form-popup');\n            popup.css('display', 'block');\n            \n            \/\/ Wymuszenie reflow przed dodaniem klasy active\n            void popup[0].offsetWidth;\n            \n            popup.addClass('active');\n        });\n\n        \/\/ Zamykanie formularza\n        $('.reservation-form-close').on('click', function() {\n            const popup = $('#reservation-form-popup');\n            popup.removeClass('active');\n            setTimeout(() => popup.hide(), 300);\n        });\n\n        \/\/ Zamykanie formularza przy klikni\u0119ciu poza nim\n        $('#reservation-form-popup').on('click', function(e) {\n            if ($(e.target).hasClass('reservation-form-popup')) {\n                const popup = $(this);\n                popup.removeClass('active');\n                setTimeout(() => popup.hide(), 300);\n            }\n        });\n\n        const allowReservation = '1';\n        \n        if (allowReservation === '1') {\n            \/\/ Obs\u0142uga klikni\u0119cia w slot czasowy ju\u017c jest zdefiniowana wcze\u015bniej\n\n            \/\/ Zamykanie formularza\n            $('.reservation-form-close').on('click', function() {\n                $('#reservation-form-popup').removeClass('active').hide();\n            });\n\n            \/\/ Obs\u0142uga wysy\u0142ki formularza\n            $('#glm-reservation-form').on('submit', function(e) {\n                e.preventDefault();\n                \n                \/\/ Sprawd\u017a, czy przycisk nie jest wy\u0142\u0105czony\n                if ($(this).find('button[type=\"submit\"]').prop('disabled')) {\n                    return false;\n                }\n                \n                \/\/ Sprawd\u017a czy pola selected_date i selected_slot maj\u0105 warto\u015bci\n                const selectedDate = $('#selected-date').val();\n                const selectedSlot = $('#selected-slot').val();\n                \n                if (!selectedDate || !selectedSlot) {\n                    alert('Brak wybranych danych terminu. Spr\u00f3buj ponownie wybra\u0107 dat\u0119 i godzin\u0119.');\n                    glm_wp_calendar_client_log_error('Brak danych terminu w formularzu', {\n                        selected_date: selectedDate,\n                        selected_slot: selectedSlot\n                    });\n                    return false;\n                }\n                \n                \/\/ Walidacja wszystkich wymaganych p\u00f3l formularza\n                let allRequiredFieldsValid = true;\n                let firstInvalidField = null;\n                \n                \/\/ Sprawd\u017a wszystkie wymagane pola w formularzu\n                $(this).find('input[required], textarea[required], select[required]').each(function() {\n                    if (!$(this).val()) {\n                        allRequiredFieldsValid = false;\n                        \/\/ Zapisz pierwsze nieprawid\u0142owe pole, aby przesun\u0105\u0107 do niego fokus\n                        if (!firstInvalidField) {\n                            firstInvalidField = $(this);\n                        }\n                        \/\/ Dodaj klas\u0119 b\u0142\u0119du do pola\n                        $(this).addClass('error-field');\n                    } else {\n                        \/\/ Usu\u0144 klas\u0119 b\u0142\u0119du, je\u015bli pole jest poprawne\n                        $(this).removeClass('error-field');\n                    }\n                });\n                \n                if (!allRequiredFieldsValid) {\n                    alert('Prosz\u0119 wype\u0142ni\u0107 wszystkie wymagane pola formularza.');\n                    \/\/ Przesu\u0144 fokus do pierwszego nieprawid\u0142owego pola\n                    if (firstInvalidField) {\n                        firstInvalidField.focus();\n                    }\n                    return false;\n                }\n                \n                \/\/ Sprawd\u017a czy wszystkie wymagane zgody RODO s\u0105 zaznaczone\n                let allRequiredConsentsChecked = true;\n                $(this).find('input[required][name^=\"rodo_consent\"]').each(function() {\n                    if (!$(this).is(':checked')) {\n                        allRequiredConsentsChecked = false;\n                        \/\/ Pod\u015bwietl niezaznaczone wymagane zgody\n                        $(this).closest('.rodo-consent-checkbox').addClass('consent-error');\n                    } else {\n                        $(this).closest('.rodo-consent-checkbox').removeClass('consent-error');\n                    }\n                });\n                \n                if (!allRequiredConsentsChecked) {\n                    alert('Prosz\u0119 zaznaczy\u0107 wszystkie wymagane zgody RODO.');\n                    return false;\n                }\n                \n                \/\/ U\u017cycie FormData zamiast obiektu JavaScript\n                const formData = new FormData(this);\n                \n                \/\/ Dodaj dodatkowe dane\n                formData.append('action', 'submit_reservation');\n                formData.append('security', 'cf07573f09');\n                formData.append('object_id', '1');\n                formData.append('notification_enabled', false);\n                formData.append('notification_email', '');\n                formData.append('page_url', window.location.href);\n                \n                \/\/ Upewnij si\u0119, \u017ce selected_date i selected_slot s\u0105 w FormData\n                formData.set('selected_date', selectedDate);\n                formData.set('selected_slot', selectedSlot);\n                \n                \/\/ Przetw\u00f3rz slot czasowy, aby doda\u0107 czasy rozpocz\u0119cia i zako\u0144czenia\n                if (selectedSlot) {\n                    \/\/ Funkcja pomocnicza do przetwarzania slot\u00f3w czasowych\n                    function parseTimeSlot(slot) {\n                        const result = {\n                            start: '',\n                            end: ''\n                        };\n                        \n                        if (!slot) return result;\n                        \n                        \/\/ Usu\u0144 bia\u0142e znaki i podziel na cz\u0119\u015bci\n                        const parts = slot.trim().split('-');\n                        \n                        if (parts.length >= 2) {\n                            result.start = parts[0].trim();\n                            result.end = parts[1].trim();\n                        } else if (parts.length === 1) {\n                            result.start = parts[0].trim();\n                        }\n                        \n                        return result;\n                    }\n                    \n                    const slotTimes = parseTimeSlot(selectedSlot);\n                    \n                    \/\/ Dodaj czasy rozpocz\u0119cia i zako\u0144czenia do formularza\n                    formData.append('selected_slot_start', slotTimes.start);\n                    formData.append('selected_slot_end', slotTimes.end);\n                    \n                    console.log('Przetworzony slot czasowy:', {\n                        original: selectedSlot,\n                        start: slotTimes.start,\n                        end: slotTimes.end\n                    });\n                }\n                \n                \/\/ Logowanie formularza\n                console.group('Dane formularza:');\n                for (let pair of formData.entries()) {\n                    console.log(pair[0] + ': ' + pair[1]);\n                }\n                \n                \/\/ Zbierz informacje o wszystkich zgodach (nie tylko zaznaczonych)\n                const consentsStatus = {};\n                $(this).find('input[name^=\"rodo_consent\"]').each(function() {\n                    \/\/ Pobierz prawid\u0142owy ID zgody - format: rodo_consent[consent_XXXXX]\n                    const inputName = $(this).attr('name');\n                    const matches = inputName.match(\/rodo_consent\\[(.*?)\\]\/);\n                    \n                    if (matches && matches[1]) {\n                        const consentId = matches[1];\n                        console.log('Przetwarzanie zgody:', consentId, 'z pola:', inputName);\n                        \n                        consentsStatus[consentId] = {\n                            required: $(this).prop('required'),\n                            checked: $(this).is(':checked'),\n                            text: $(`label[for=\"consent_${consentId}\"]`).text().trim()\n                        };\n                    } else {\n                        console.warn('Nieprawid\u0142owy format nazwy pola zgody:', inputName);\n                    }\n                });\n                \n                console.log('Status zg\u00f3d:', consentsStatus);\n                console.groupEnd();\n                \n                \/\/ Dodaj statusy zg\u00f3d do formularza\n                formData.append('consents_status', JSON.stringify(consentsStatus));\n                \n                \/\/ Wy\u0142\u0105cz przycisk submit, aby zapobiec wielokrotnemu klikni\u0119ciu\n                const submitButton = $(this).find('button[type=\"submit\"]');\n                const originalButtonText = submitButton.text();\n                submitButton.prop('disabled', true).text('Wysy\u0142anie...');\n\n                $.ajax({\n                    url: ajaxurl,\n                    type: 'POST',\n                    data: formData,\n                    processData: false,\n                    contentType: false,\n                    success: function(response) {\n                        \/\/ Odblokuj przycisk submit\n                        submitButton.prop('disabled', false).text(originalButtonText);\n                        \n                        if (response.success) {\n                            \/\/ Wy\u015bwietl odpowied\u017a z webhooka w konsoli\n                            if (response.data) {\n                                console.group('Odpowied\u017a z formularza rezerwacji:');\n                                \n                                \/\/ Informacje o powiadomieniu email\n                                if (response.data.email_status) {\n                                    console.group('Status powiadomienia email:');\n                                    console.log('W\u0142\u0105czone:', response.data.email_status.enabled);\n                                    if (response.data.email_status.enabled) {\n                                        console.log('Wys\u0142ane:', response.data.email_status.sent);\n                                        console.log('Adres odbiorcy:', response.data.email_status.to);\n                                    }\n                                    console.groupEnd();\n                                }\n                                \n                                \/\/ Informacje o webhooku\n                                if (response.data.webhook_status) {\n                                    console.group('Status webhooka:');\n                                    console.log('Status:', response.data.webhook_status);\n                                    \n                                    if (response.data.webhook_status === 'success') {\n                                        console.log('Kod odpowiedzi:', response.data.webhook_response.code);\n                                        console.log('Tre\u015b\u0107 odpowiedzi:', response.data.webhook_response.body);\n                                    } else if (response.data.webhook_status === 'error') {\n                                        console.error('B\u0142\u0105d:', response.data.webhook_error);\n                                    } else if (response.data.webhook_status === 'disabled') {\n                                        console.log('Webhook jest wy\u0142\u0105czony w ustawieniach');\n                                    }\n                                    \n                                    console.groupEnd();\n                                }\n                                \n                                console.groupEnd();\n                            }\n                            \n                            \/\/ Poka\u017c niestandardowy komunikat sukcesu zamiast alertu\n                            const formContent = $('#glm-reservation-form').closest('.reservation-form-content');\n                            \n                            \/\/ Dodaj overlay, aby zas\u0142oni\u0107 formularz\n                            formContent.append('<div class=\"success-overlay\"><\/div>');\n                            \n                            \/\/ Poka\u017c komunikat sukcesu\n                            $('#reservation-success-message').fadeIn(300);\n                            \n                            \/\/ Obs\u0142uga przycisku zamkni\u0119cia\n                            $('.success-close-button').one('click', function() {\n                                $('#reservation-success-message').fadeOut(200);\n                                $('.success-overlay').fadeOut(200);\n                                \n                                \/\/ Po animacji zamknij popup i zresetuj formularz\n                                setTimeout(function() {\n                            $('#reservation-form-popup').removeClass('active').hide();\n                            $('#glm-reservation-form')[0].reset();\n                                    $('.success-overlay').remove();\n                                }, 250);\n                                \n                            \/\/ Od\u015bwie\u017c kalendarz\n                                const currentDate = $('#glm-calendar-container').datepicker('getDate');\n                                if (currentDate) {\n                                    const dateText = $.datepicker.formatDate('yy-mm-dd', currentDate);\n                                    \n                                    \/\/ Wymusza prze\u0142adowanie rezerwacji dla wybranej daty\n                                    fetchReservations(dateText, true)\n                                        .then(function() {\n                                            \/\/ Wywo\u0142aj ponownie onSelect dla bie\u017c\u0105cej daty, aby od\u015bwie\u017cy\u0107 terminarz\n                                            $('#glm-calendar-container').datepicker('setDate', currentDate);\n                                        })\n                                        .catch(function(error) {\n                                            console.error('B\u0142\u0105d podczas od\u015bwie\u017cania kalendarza:', error);\n                                        });\n                                }\n                            });\n                        } else {\n                            \/\/ Wy\u015bwietl szczeg\u00f3\u0142owy komunikat b\u0142\u0119du, je\u015bli jest dost\u0119pny\n                            let errorMessage = 'Wyst\u0105pi\u0142 b\u0142\u0105d podczas wysy\u0142ania rezerwacji. Spr\u00f3buj ponownie.';\n                            \n                            if (response.data && typeof response.data === 'string') {\n                                errorMessage = response.data;\n                            } else if (response.data && response.data.message) {\n                                errorMessage = response.data.message;\n                            }\n                            \n                            console.error('B\u0142\u0105d podczas wysy\u0142ania rezerwacji:', response.data);\n                            alert(errorMessage);\n                            \n                            \/\/ W przypadku b\u0142\u0119du zwi\u0105zanego ze zgodami RODO, pod\u015bwietl problematyczne zgody\n                            if (response.data && response.data.missing_consents) {\n                                response.data.missing_consents.forEach(function(consentId) {\n                                    $(`input[name=\"rodo_consent[${consentId}]\"]`).closest('.rodo-consent-checkbox').addClass('consent-error');\n                                });\n                            }\n                        }\n                    },\n                    error: function(xhr, status, error) {\n                        \/\/ Odblokuj przycisk submit\n                        submitButton.prop('disabled', false).text(originalButtonText);\n                        \n                        console.error('B\u0142\u0105d AJAX podczas wysy\u0142ania rezerwacji:', status, error);\n                        \n                        \/\/ Pr\u00f3ba wydobycia dodatkowych informacji z odpowiedzi\n                        let errorMessage = 'Wyst\u0105pi\u0142 b\u0142\u0105d podczas wysy\u0142ania rezerwacji. Spr\u00f3buj ponownie.';\n                        \n                        try {\n                            if (xhr.responseJSON && xhr.responseJSON.data) {\n                                if (typeof xhr.responseJSON.data === 'string') {\n                                    errorMessage = xhr.responseJSON.data;\n                                } else if (xhr.responseJSON.data.message) {\n                                    errorMessage = xhr.responseJSON.data.message;\n                                }\n                            }\n                        } catch (e) {\n                            console.error('B\u0142\u0105d podczas przetwarzania odpowiedzi:', e);\n                        }\n                        \n                        alert(errorMessage);\n                    }\n                });\n            });\n        }\n    });\n    <\/script>\n    \n","protected":false},"excerpt":{"rendered":"","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"saved_in_kubio":false,"zakra_sidebar_layout":"customizer","zakra_remove_content_margin":false,"zakra_sidebar":"customizer","zakra_transparent_header":"customizer","zakra_logo":0,"zakra_main_header_style":"default","zakra_menu_item_color":"","zakra_menu_item_hover_color":"","zakra_menu_item_active_color":"","zakra_menu_active_style":"","zakra_page_header":true,"footnotes":""},"class_list":["post-6","page","type-page","status-publish","hentry"],"kubio_ai_page_context":{"short_desc":"","purpose":"general"},"_links":{"self":[{"href":"https:\/\/lp.globalmedia.com.pl\/index.php?rest_route=\/wp\/v2\/pages\/6","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/lp.globalmedia.com.pl\/index.php?rest_route=\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/lp.globalmedia.com.pl\/index.php?rest_route=\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/lp.globalmedia.com.pl\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/lp.globalmedia.com.pl\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=6"}],"version-history":[{"count":1,"href":"https:\/\/lp.globalmedia.com.pl\/index.php?rest_route=\/wp\/v2\/pages\/6\/revisions"}],"predecessor-version":[{"id":9,"href":"https:\/\/lp.globalmedia.com.pl\/index.php?rest_route=\/wp\/v2\/pages\/6\/revisions\/9"}],"wp:attachment":[{"href":"https:\/\/lp.globalmedia.com.pl\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=6"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}