(function () { 'use strict'; var REST_URL = (window.careiReservation && window.careiReservation.restUrl) || '/wp-json/carei/v1/'; var NONCE = (window.careiReservation && window.careiReservation.nonce) || ''; // ─── API Helpers ────────────────────────────────────────────── function apiGet(endpoint) { return fetch(REST_URL + endpoint, { method: 'GET', headers: { 'X-WP-Nonce': NONCE, 'Content-Type': 'application/json' } }).then(function (r) { if (!r.ok) throw new Error('API error: ' + r.status); return r.json(); }); } function apiPost(endpoint, data) { return fetch(REST_URL + endpoint, { method: 'POST', headers: { 'X-WP-Nonce': NONCE, 'Content-Type': 'application/json' }, body: JSON.stringify(data) }).then(function (r) { if (!r.ok) throw new Error('API error: ' + r.status); return r.json(); }); } // ─── DOM Refs ───────────────────────────────────────────────── var overlay, form, segmentSelect, dateFrom, dateTo, daysCount; var pickupSelect, returnSelect, returnWrap, sameReturnCheck; var extrasContainer, errorSummary; function initRefs() { overlay = document.querySelector('[data-carei-modal]'); form = document.getElementById('carei-reservation-form'); segmentSelect = document.getElementById('carei-segment'); dateFrom = document.getElementById('carei-date-from'); dateTo = document.getElementById('carei-date-to'); daysCount = document.getElementById('carei-days-count'); pickupSelect = document.getElementById('carei-pickup-branch'); returnSelect = document.getElementById('carei-return-branch'); returnWrap = document.getElementById('carei-return-wrap'); sameReturnCheck = document.getElementById('carei-same-return'); extrasContainer = document.getElementById('carei-extras-container'); errorSummary = document.getElementById('carei-error-summary'); } // ─── Modal Open/Close ───────────────────────────────────────── function initModal() { var triggers = document.querySelectorAll('[data-carei-open-modal]'); var closeBtns = document.querySelectorAll('[data-carei-close-modal]'); triggers.forEach(function (btn) { btn.addEventListener('click', function (e) { e.preventDefault(); openModal(); }); }); closeBtns.forEach(function (btn) { btn.addEventListener('click', closeModal); }); if (overlay) { overlay.addEventListener('click', function (e) { if (e.target === overlay) closeModal(); }); } document.addEventListener('keydown', function (e) { if (e.key === 'Escape' && overlay && overlay.classList.contains('is-open')) { closeModal(); } }); } var dataLoaded = false; function openModal() { if (!overlay) return; overlay.classList.add('is-open'); document.body.style.overflow = 'hidden'; if (!dataLoaded) { loadBranches(); loadAllCarClasses(); setDefaultDates(); dataLoaded = true; } } function closeModal() { if (!overlay) return; overlay.classList.remove('is-open'); document.body.style.overflow = ''; } // ─── Default Dates ──────────────────────────────────────────── function setDefaultDates() { if (!dateFrom || !dateTo) return; var tomorrow = new Date(); tomorrow.setDate(tomorrow.getDate() + 1); tomorrow.setHours(10, 0, 0, 0); var dayAfter = new Date(); dayAfter.setDate(dayAfter.getDate() + 2); dayAfter.setHours(10, 0, 0, 0); dateFrom.value = formatDatetimeLocal(tomorrow); dateTo.value = formatDatetimeLocal(dayAfter); updateDaysCount(); } function formatDatetimeLocal(d) { var y = d.getFullYear(); var m = String(d.getMonth() + 1).padStart(2, '0'); var day = String(d.getDate()).padStart(2, '0'); var h = String(d.getHours()).padStart(2, '0'); var min = String(d.getMinutes()).padStart(2, '0'); return y + '-' + m + '-' + day + 'T' + h + ':' + min; } // ─── Days Count ─────────────────────────────────────────────── function updateDaysCount() { if (!dateFrom || !dateTo || !daysCount) return; var from = new Date(dateFrom.value); var to = new Date(dateTo.value); if (isNaN(from.getTime()) || isNaN(to.getTime()) || to <= from) { daysCount.innerHTML = 'Wybrano: 0 dni'; return; } var diff = Math.ceil((to - from) / (1000 * 60 * 60 * 24)); var label = diff === 1 ? 'dzień' : 'dni'; daysCount.innerHTML = 'Wybrano: ' + diff + ' ' + label + ''; } // ─── Load Branches ──────────────────────────────────────────── function loadBranches() { if (!pickupSelect) return; setSelectLoading(pickupSelect, true); apiGet('branches') .then(function (branches) { if (!Array.isArray(branches)) { branches = []; } populateSelect(pickupSelect, branches.map(function (b) { var label = b.description || b.name; if (b.city) label += ' — ' + b.city; return { value: b.name, label: label }; }), 'Miejsce odbioru'); // Copy to return branch if (returnSelect) { populateSelect(returnSelect, branches.map(function (b) { var label = b.description || b.name; if (b.city) label += ' — ' + b.city; return { value: b.name, label: label }; }), 'Miejsce zwrotu'); } setSelectLoading(pickupSelect, false); }) .catch(function (err) { console.error('Failed to load branches:', err); setSelectLoading(pickupSelect, false); }); } // ─── Load All Car Classes (on modal open) ──────────────────── function loadAllCarClasses() { if (!segmentSelect) return; setSelectLoading(segmentSelect, true); apiGet('car-classes-all') .then(function (classes) { if (!Array.isArray(classes) || classes.length === 0) { populateSelect(segmentSelect, [], 'Brak segmentów'); setSelectLoading(segmentSelect, false); return; } populateSelect(segmentSelect, classes.map(function (c) { var val = typeof c === 'string' ? c : (c.name || c); var label = typeof c === 'string' ? ('Segment ' + c) : (c.description || c.name || c); return { value: val, label: label }; }), 'Wybierz segment pojazdu'); setSelectLoading(segmentSelect, false); }) .catch(function (err) { console.error('Failed to load car classes:', err); populateSelect(segmentSelect, [], 'Błąd ładowania'); setSelectLoading(segmentSelect, false); }); } // ─── Load Available Car Classes (filtered by dates+branch) ─── function loadCarClasses() { if (!segmentSelect || !dateFrom || !dateTo || !pickupSelect) return; var fromVal = dateFrom.value; var toVal = dateTo.value; var branch = pickupSelect.value; if (!fromVal || !toVal || !branch) return; // Convert datetime-local to API format: YYYY-MM-DDTHH:MM:SS var apiFrom = fromVal.replace('T', 'T') + ':00'; var apiTo = toVal.replace('T', 'T') + ':00'; setSelectLoading(segmentSelect, true); apiPost('car-classes', { dateFrom: apiFrom, dateTo: apiTo, branchName: branch }) .then(function (classes) { if (!Array.isArray(classes) || classes.length === 0) { populateSelect(segmentSelect, [], 'Brak dostępnych klas'); setSelectLoading(segmentSelect, false); return; } populateSelect(segmentSelect, classes.map(function (c) { // classes might be strings or objects var val = typeof c === 'string' ? c : (c.name || c); var label = typeof c === 'string' ? ('Segment ' + c) : (c.description || c.name || c); return { value: val, label: label }; }), 'Wybierz segment pojazdu'); setSelectLoading(segmentSelect, false); }) .catch(function (err) { console.error('Failed to load car classes:', err); populateSelect(segmentSelect, [], 'Błąd ładowania'); setSelectLoading(segmentSelect, false); }); } // ─── Load Extras from Pricelist ─────────────────────────────── function loadExtras() { if (!segmentSelect || !dateFrom || !dateTo || !pickupSelect || !extrasContainer) return; var category = segmentSelect.value; var fromVal = dateFrom.value; var toVal = dateTo.value; var branch = pickupSelect.value; if (!category || !fromVal || !toVal || !branch) return; var apiFrom = fromVal + ':00'; var apiTo = toVal + ':00'; apiPost('pricelist', { category: category, dateFrom: apiFrom, dateTo: apiTo, pickUpLocation: branch }) .then(function (pricelists) { if (!Array.isArray(pricelists) || pricelists.length === 0) return; var pricelist = pricelists[0]; var items = pricelist.additionalItems; if (!Array.isArray(items) || items.length === 0) return; extrasContainer.innerHTML = ''; items.forEach(function (item) { var price = parseFloat(item.price || item.minPrice || 0); var priceLabel = price > 0 ? (price.toFixed(0) + ' zł') : 'Gratis'; var card = document.createElement('div'); card.className = 'carei-form__extra-card'; card.innerHTML = ''; extrasContainer.appendChild(card); }); }) .catch(function (err) { console.error('Failed to load pricelist:', err); // Keep static fallback extras }); } // ─── Select Helpers ─────────────────────────────────────────── function populateSelect(select, options, placeholder) { select.innerHTML = ''; var ph = document.createElement('option'); ph.value = ''; ph.disabled = true; ph.selected = true; ph.textContent = placeholder || 'Wybierz...'; select.appendChild(ph); options.forEach(function (opt) { var o = document.createElement('option'); o.value = opt.value; o.textContent = opt.label; select.appendChild(o); }); } function setSelectLoading(select, loading) { var wrap = select.closest('.carei-form__select-wrap'); if (!wrap) return; if (loading) { wrap.classList.add('carei-form__select-wrap--loading'); } else { wrap.classList.remove('carei-form__select-wrap--loading'); } } // ─── Same Return Location ───────────────────────────────────── function initSameReturn() { if (!sameReturnCheck || !returnWrap) return; sameReturnCheck.addEventListener('change', function () { if (sameReturnCheck.checked) { returnWrap.classList.remove('is-visible'); } else { returnWrap.classList.add('is-visible'); } }); } // ─── Validation ─────────────────────────────────────────────── var requiredFields = [ { id: 'carei-segment', type: 'select', msg: 'Wybierz segment pojazdu' }, { id: 'carei-date-from', type: 'input', msg: 'Podaj datę rozpoczęcia' }, { id: 'carei-date-to', type: 'input', msg: 'Podaj datę zakończenia' }, { id: 'carei-pickup-branch', type: 'select', msg: 'Wybierz miejsce odbioru' }, { id: 'carei-firstname', type: 'input', msg: 'Podaj imię' }, { id: 'carei-lastname', type: 'input', msg: 'Podaj nazwisko' }, { id: 'carei-email', type: 'email', msg: 'Podaj poprawny adres e-mail' }, { id: 'carei-phone', type: 'phone', msg: 'Podaj numer telefonu (min. 9 cyfr)' }, { id: 'carei-privacy', type: 'checkbox', msg: 'Wymagana zgoda na Politykę Prywatności' } ]; function validateForm() { var valid = true; // Clear previous errors form.querySelectorAll('.carei-form__field--error').forEach(function (el) { el.classList.remove('carei-form__field--error'); }); form.querySelectorAll('.carei-form__checkbox-label--error').forEach(function (el) { el.classList.remove('carei-form__checkbox-label--error'); }); form.querySelectorAll('.carei-form__error-msg').forEach(function (el) { el.remove(); }); requiredFields.forEach(function (f) { var el = document.getElementById(f.id); if (!el) return; var hasError = false; if (f.type === 'checkbox') { if (!el.checked) hasError = true; } else if (f.type === 'email') { var emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; if (!el.value.trim() || !emailRegex.test(el.value.trim())) hasError = true; } else if (f.type === 'phone') { var digits = el.value.replace(/\D/g, ''); if (digits.length < 9) hasError = true; } else if (f.type === 'select') { if (!el.value) hasError = true; } else { if (!el.value.trim()) hasError = true; } if (hasError) { valid = false; markFieldError(el, f.msg, f.type); } }); // Check dateTo > dateFrom if (dateFrom && dateTo && dateFrom.value && dateTo.value) { if (new Date(dateTo.value) <= new Date(dateFrom.value)) { valid = false; markFieldError(dateTo, 'Data zakończenia musi być po dacie rozpoczęcia', 'input'); } } // Return branch required if different location if (sameReturnCheck && !sameReturnCheck.checked && returnSelect) { if (!returnSelect.value) { valid = false; markFieldError(returnSelect, 'Wybierz miejsce zwrotu', 'select'); } } if (errorSummary) { errorSummary.style.display = valid ? 'none' : 'block'; } return valid; } function markFieldError(el, msg, type) { if (type === 'checkbox') { var label = el.closest('.carei-form__checkbox-label'); if (label) label.classList.add('carei-form__checkbox-label--error'); } else { var field = el.closest('.carei-form__field'); if (!field) { // For phone in wrap field = el.closest('.carei-form__phone-wrap'); if (field) field = field.closest('.carei-form__field'); } if (field) { field.classList.add('carei-form__field--error'); var errEl = document.createElement('span'); errEl.className = 'carei-form__error-msg'; errEl.textContent = msg; field.appendChild(errEl); } } } function initClearErrors() { if (!form) return; form.addEventListener('focusin', function (e) { var field = e.target.closest('.carei-form__field'); if (field && field.classList.contains('carei-form__field--error')) { field.classList.remove('carei-form__field--error'); var errMsg = field.querySelector('.carei-form__error-msg'); if (errMsg) errMsg.remove(); } // Checkbox var label = e.target.closest('.carei-form__checkbox-label'); if (label) label.classList.remove('carei-form__checkbox-label--error'); }); // Also on change for checkboxes form.addEventListener('change', function (e) { if (e.target.type === 'checkbox') { var label = e.target.closest('.carei-form__checkbox-label'); if (label) label.classList.remove('carei-form__checkbox-label--error'); } }); } // ─── Form Submit ────────────────────────────────────────────── function initSubmit() { if (!form) return; form.addEventListener('submit', function (e) { e.preventDefault(); if (!validateForm()) return; var formData = collectFormData(); console.log('Carei Reservation — Form data:', formData); // Phase 3 will handle actual API submission var submitBtn = form.querySelector('.carei-form__submit'); if (submitBtn) { submitBtn.disabled = true; submitBtn.innerHTML = ' Wysyłanie...'; // Re-enable after 2s (temporary — Phase 3 will handle properly) setTimeout(function () { submitBtn.disabled = false; submitBtn.innerHTML = ' Wyślij'; }, 2000); } }); } function collectFormData() { var extras = []; form.querySelectorAll('input[name="extras[]"]:checked').forEach(function (cb) { extras.push({ id: cb.value, price: parseFloat(cb.getAttribute('data-price') || 0) }); }); return { segment: segmentSelect ? segmentSelect.value : '', dateFrom: dateFrom ? dateFrom.value : '', dateTo: dateTo ? dateTo.value : '', pickupBranch: pickupSelect ? pickupSelect.value : '', sameReturn: sameReturnCheck ? sameReturnCheck.checked : true, returnBranch: (sameReturnCheck && !sameReturnCheck.checked && returnSelect) ? returnSelect.value : '', extras: extras, firstName: val('carei-firstname'), lastName: val('carei-lastname'), email: val('carei-email'), phone: '+48' + (document.getElementById('carei-phone') ? document.getElementById('carei-phone').value.replace(/\D/g, '') : ''), message: val('carei-message'), privacy: document.getElementById('carei-privacy') ? document.getElementById('carei-privacy').checked : false }; } function val(id) { var el = document.getElementById(id); return el ? el.value.trim() : ''; } // ─── Event Listeners for Dynamic Loading ────────────────────── function initDynamicLoading() { // Date change → update days count if (dateFrom) dateFrom.addEventListener('change', updateDaysCount); if (dateTo) dateTo.addEventListener('change', updateDaysCount); // When segment + dates + branch all set → load extras from pricelist if (segmentSelect) segmentSelect.addEventListener('change', loadExtras); if (pickupSelect) pickupSelect.addEventListener('change', loadExtras); if (dateFrom) dateFrom.addEventListener('change', loadExtras); if (dateTo) dateTo.addEventListener('change', loadExtras); } // ─── HTML Escape Helpers ────────────────────────────────────── function escHtml(str) { var div = document.createElement('div'); div.textContent = str || ''; return div.innerHTML; } function escAttr(str) { return (str || '').replace(/&/g, '&').replace(/"/g, '"').replace(/'/g, ''').replace(//g, '>'); } // ─── Init ───────────────────────────────────────────────────── function init() { initRefs(); if (!overlay || !form) return; initModal(); initSameReturn(); initDynamicLoading(); initClearErrors(); initSubmit(); } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } })();