document.addEventListener('DOMContentLoaded', () => { const nameInput = document.getElementById('name'); const phoneInput = document.getElementById('phone'); const emailInput = document.getElementById('email'); const vkInput = document.getElementById('vk'); const agreeOfertaInput = document.getElementById('agreeOferta'); const agreePolicyInput = document.getElementById('agreePolicy'); const agreeNewsletterInput = document.getElementById('agreeNewsletter'); const form = document.getElementById('ikpForm'); const formContainer = document.getElementById('formContainer'); const successContainer = document.getElementById('successContainer'); const resetBtn = document.getElementById('resetBtn'); // Service display elements const serviceDisplay = document.getElementById('serviceDisplay'); const serviceNameDisplay = document.getElementById('serviceNameDisplay'); const servicePriceDisplay = document.getElementById('servicePriceDisplay'); const serviceIdInput = document.getElementById('serviceId'); // Parse URL query parameters const urlParams = new URLSearchParams(window.location.search); const serviceIdParam = urlParams.get('service_id'); const successParam = urlParams.get('success'); // If redirected back from payment successfully if (successParam === 'true') { formContainer.style.display = 'none'; successContainer.style.display = 'flex'; successContainer.style.opacity = '1'; } let services = {}; const servicesUrl = window.location.protocol === 'file:' ? 'http://localhost:8000/api/services' : '/api/services'; fetch(servicesUrl) .then(response => { if (!response.ok) { throw new Error('Не удалось загрузить список услуг'); } return response.json(); }) .then(data => { if (data.status === 'success') { services = data.services; initializeServiceDisplay(); } else { console.error('Error loading services:', data.message); fallbackServiceDisplay(); } }) .catch(err => { console.error('Error fetching services:', err); fallbackServiceDisplay(); }); function initializeServiceDisplay() { if (serviceIdParam && services[serviceIdParam]) { const selectedService = services[serviceIdParam]; serviceNameDisplay.textContent = selectedService.name; const price = selectedService.price_numeric || parseFloat(selectedService.price) || 0; servicePriceDisplay.textContent = price.toLocaleString('ru-RU') + ' ₽'; serviceIdInput.value = serviceIdParam; serviceDisplay.style.display = 'block'; } else { // Default to web_1 serviceIdInput.value = "web_1"; if (services["web_1"]) { const selectedService = services["web_1"]; serviceNameDisplay.textContent = selectedService.name; const price = selectedService.price_numeric || parseFloat(selectedService.price) || 0; servicePriceDisplay.textContent = price.toLocaleString('ru-RU') + ' ₽'; serviceDisplay.style.display = 'block'; } } } function fallbackServiceDisplay() { services = { "web_1": { name: "Разовая тренировка", price_numeric: 870 }, "web_2": { name: "Абонемент на 4 занятия (Групповой)", price_numeric: 2790 }, "web_3": { name: "Абонемент на 8 занятий (Групповой)", price_numeric: 4990 }, "web_4": { name: "Абонемент на 4 занятия (Индивидуальный)", price_numeric: 3290 }, "web_5": { name: "Абонемент на 8 занятий (Индивидуальный)", price_numeric: 5990 } }; initializeServiceDisplay(); } // List of fields to manage focus and validation state const fields = [ { input: nameInput, check: checkName, dirty: false }, { input: phoneInput, check: checkPhone, dirty: false }, { input: emailInput, check: checkEmail, dirty: false }, { input: vkInput, check: checkVK, dirty: false }, { input: agreeOfertaInput, check: checkOferta, dirty: false }, { input: agreePolicyInput, check: checkPolicy, dirty: false } ]; /* ==================================================== Validation Functions ==================================================== */ function showError(input, show) { const group = input.closest('.form-group') || input.closest('.checkbox-group'); if (show) { group.classList.add('has-error'); } else { group.classList.remove('has-error'); } } function checkName() { const val = nameInput.value.trim(); const isValid = val.length >= 2; showError(nameInput, !isValid); return isValid; } function checkPhone() { const val = phoneInput.value; const digits = val.replace(/\D/g, ''); const isValid = digits.length === 11 && val.startsWith('+7') && val.length === 18; showError(phoneInput, !isValid); return isValid; } function checkEmail() { const val = emailInput.value.trim(); const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/; const isValid = emailRegex.test(val); showError(emailInput, !isValid); return isValid; } function checkVK() { const val = vkInput.value.trim().toLowerCase(); const isValid = val.length > 0; showError(vkInput, !isValid); return isValid; } if (agreeOfertaInput) { agreeOfertaInput.addEventListener('change', checkOferta); } if (agreePolicyInput) { agreePolicyInput.addEventListener('change', checkPolicy); } function checkOferta() { const isValid = agreeOfertaInput.checked; showError(agreeOfertaInput, !isValid); return isValid; } function checkPolicy() { const isValid = agreePolicyInput.checked; showError(agreePolicyInput, !isValid); return isValid; } /* ==================================================== Interaction Listeners (UX enhancements) ==================================================== */ fields.forEach(field => { field.input.addEventListener('blur', () => { field.dirty = true; field.check(); }); const eventType = field.input.type === 'checkbox' ? 'change' : 'input'; field.input.addEventListener(eventType, () => { if (field.dirty) { field.check(); } }); }); /* ==================================================== Phone Input Mask Implementation ==================================================== */ phoneInput.addEventListener('input', (e) => { let value = e.target.value; let digits = value.replace(/\D/g, ''); if (digits.startsWith('7') || digits.startsWith('8')) { digits = digits.substring(1); } digits = digits.substring(0, 10); let formatted = ''; if (digits.length > 0) { formatted = '+7 (' + digits.substring(0, 3); } if (digits.length >= 3) { formatted += ') '; } if (digits.length > 3) { formatted += digits.substring(3, 6); } if (digits.length > 6) { formatted += '-' + digits.substring(6, 8); } if (digits.length > 8) { formatted += '-' + digits.substring(8, 10); } e.target.value = formatted; const phoneField = fields.find(item => item.input === phoneInput); if (phoneField && phoneField.dirty) { checkPhone(); } }); phoneInput.addEventListener('keydown', (e) => { if (e.key === 'Backspace') { const val = e.target.value; if (val === '+7 ' || val === '+7' || val === '+') { e.target.value = ''; } } }); phoneInput.addEventListener('focus', (e) => { if (!e.target.value) { e.target.value = '+7 '; } }); phoneInput.addEventListener('blur', (e) => { if (e.target.value === '+7 ' || e.target.value === '+7') { e.target.value = ''; } const phoneField = fields.find(item => item.input === phoneInput); if (phoneField) { phoneField.dirty = true; checkPhone(); } }); /* ==================================================== Form Submission ==================================================== */ form.addEventListener('submit', (e) => { e.preventDefault(); fields.forEach(field => { field.dirty = true; }); const isNameValid = checkName(); const isPhoneValid = checkPhone(); const isEmailValid = checkEmail(); const isVKValid = checkVK(); const isOfertaValid = checkOferta(); const isPolicyValid = checkPolicy(); if (isNameValid && isPhoneValid && isEmailValid && isVKValid && isOfertaValid && isPolicyValid) { const formData = { name: nameInput.value.trim(), phone: phoneInput.value, email: emailInput.value.trim(), vk: vkInput.value.trim(), agreeOferta: agreeOfertaInput.checked, agreePolicy: agreePolicyInput.checked, agreeNewsletter: agreeNewsletterInput.checked, serviceId: serviceIdInput.value || "web_1" }; const submitBtn = document.getElementById('submitBtn'); const originalBtnText = submitBtn.textContent; submitBtn.disabled = true; submitBtn.textContent = 'Отправка...'; const submitUrl = window.location.protocol === 'file:' ? 'http://localhost:8000/submit' : '/submit'; fetch(submitUrl, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(formData) }) .then(response => { return response.json().then(data => { if (!response.ok) { throw new Error(data.message || 'Ошибка сервера'); } return data; }).catch(err => { if (!response.ok) { throw new Error('Ошибка сети или сервера'); } throw err; }); }) .then(data => { if (data.payment_url) { // Redirect directly to YooKassa payment gateway window.location.href = data.payment_url; return; } // Fallback success UI container (if YooKassa keys not configured in server) formContainer.style.transition = 'opacity 0.3s ease, transform 0.3s ease'; formContainer.style.opacity = '0'; formContainer.style.transform = 'translateY(-10px)'; setTimeout(() => { formContainer.style.display = 'none'; successContainer.style.display = 'flex'; successContainer.style.opacity = '0'; successContainer.style.transform = 'translateY(10px)'; successContainer.offsetHeight; successContainer.style.transition = 'opacity 0.4s ease, transform 0.4s ease'; successContainer.style.opacity = '1'; successContainer.style.transform = 'translateY(0)'; }, 300); form.reset(); fields.forEach(field => { field.dirty = false; }); serviceDisplay.style.display = 'none'; }) .catch(error => { console.error('Error submitting form:', error); alert('Произошла ошибка при отправке заявки: ' + error.message + '. Пожалуйста, убедитесь, что сервер server.py запущен.'); }) .finally(() => { submitBtn.disabled = false; submitBtn.textContent = originalBtnText; }); } else { const firstInvalid = fields.find(field => { if (field.input === nameInput) return !isNameValid; if (field.input === phoneInput) return !isPhoneValid; if (field.input === emailInput) return !isEmailValid; if (field.input === vkInput) return !isVKValid; if (field.input === agreeOfertaInput) return !isOfertaValid; if (field.input === agreePolicyInput) return !isPolicyValid; return false; }); if (firstInvalid) { firstInvalid.input.focus(); } } }); /* ==================================================== Reset / Back to Form Handler ==================================================== */ resetBtn.addEventListener('click', () => { successContainer.style.transition = 'opacity 0.3s ease, transform 0.3s ease'; successContainer.style.opacity = '0'; successContainer.style.transform = 'translateY(10px)'; // Remove parameters from URL to prevent showing success container on reload const newUrl = window.location.protocol + "//" + window.location.host + window.location.pathname + "?service_id=" + serviceIdInput.value; window.history.replaceState({ path: newUrl }, '', newUrl); setTimeout(() => { successContainer.style.display = 'none'; formContainer.style.display = 'block'; formContainer.style.opacity = '0'; formContainer.style.transform = 'translateY(-10px)'; formContainer.offsetHeight; formContainer.style.transition = 'opacity 0.4s ease, transform 0.4s ease'; formContainer.style.opacity = '1'; formContainer.style.transform = 'translateY(0)'; if (serviceIdInput.value && services[serviceIdInput.value]) { serviceDisplay.style.display = 'block'; } }, 300); }); });