diff --git a/2026-04-19_16-19-23.png b/2026-04-19_16-19-23.png new file mode 100644 index 00000000..ee04a37d Binary files /dev/null and b/2026-04-19_16-19-23.png differ diff --git a/2026-04-19_16-24-26.png b/2026-04-19_16-24-26.png new file mode 100644 index 00000000..7b0d65dd Binary files /dev/null and b/2026-04-19_16-24-26.png differ diff --git a/wp-content/themes/twentytwentyfour/assets/css/test1-landing.css b/wp-content/themes/twentytwentyfour/assets/css/test1-landing.css index ed888288..6f9b17aa 100644 --- a/wp-content/themes/twentytwentyfour/assets/css/test1-landing.css +++ b/wp-content/themes/twentytwentyfour/assets/css/test1-landing.css @@ -1306,7 +1306,6 @@ line-height: 1.7; } - .cart-drawer, .modal-backdrop { position: fixed; inset: 0; @@ -1316,13 +1315,11 @@ transition: opacity 0.24s ease; } - .cart-drawer.open, .modal-backdrop.open { pointer-events: auto; opacity: 1; } - .cart-drawer::before, .modal-backdrop::before { content: ""; position: absolute; @@ -1331,7 +1328,6 @@ backdrop-filter: blur(10px); } - .cart-panel, .modal-card { position: absolute; background: rgba(255, 255, 255, 0.94); @@ -1340,23 +1336,6 @@ backdrop-filter: blur(24px); } - .cart-panel { - top: 0; - right: 0; - width: min(440px, 100%); - height: 100%; - padding: 22px; - display: grid; - grid-template-rows: auto auto 1fr auto; - gap: 16px; - transform: translateX(100%); - transition: transform 0.28s ease; - } - - .cart-drawer.open .cart-panel { - transform: translateX(0); - } - .modal-card { top: 50%; left: 50%; @@ -1371,7 +1350,6 @@ transform: translate(-50%, -50%) scale(1); } - .drawer-head, .modal-head { display: flex; justify-content: space-between; @@ -1379,7 +1357,6 @@ align-items: start; } -.drawer-head h3, .modal-head h3 { margin: 0; font-size: 28px; @@ -1403,84 +1380,6 @@ font-weight: 800; } - .cart-list { - display: grid; - align-content: start; - gap: 12px; - overflow: auto; - padding-right: 4px; - } - - .cart-item { - display: grid; - grid-template-columns: minmax(0, 1fr) auto; - gap: 14px; - padding: 16px; - border-radius: 20px; - background: rgba(255, 255, 255, 0.76); - border: 1px solid rgba(18, 48, 89, 0.08); - } - - .cart-item strong { - display: block; - margin-bottom: 6px; - } - - .cart-item span { - color: var(--muted); - font-size: 14px; - } - - .cart-item button { - justify-self: end; - color: #d53b5c; - font-weight: 800; - } - - .cart-summary { - display: grid; - gap: 12px; - padding: 18px; - border-radius: 22px; - background: linear-gradient(135deg, rgba(13, 79, 214, 0.08), rgba(60, 200, 255, 0.12)); - border: 1px solid rgba(18, 48, 89, 0.08); - } - - .summary-row { - display: flex; - align-items: center; - justify-content: space-between; - gap: 12px; - font-weight: 700; - } - - .summary-row strong { - font-size: 28px; - color: var(--primary-strong); - } - - .cart-actions { - display: grid; - grid-template-columns: repeat(2, minmax(0, 1fr)); - gap: 12px; - } - - .cart-actions .ghost-button, - .cart-actions .button { - justify-content: center; - text-align: center; - } - - .empty-state { - padding: 20px; - border-radius: 22px; - background: rgba(255, 255, 255, 0.72); - border: 1px dashed rgba(18, 48, 89, 0.14); - text-align: center; - color: var(--muted); - line-height: 1.7; - } - .success-note { display: none; margin-top: 14px; @@ -1627,9 +1526,6 @@ grid-column: auto; } - .cart-actions { - grid-template-columns: 1fr; - } } @media (prefers-reduced-motion: reduce) { diff --git a/wp-content/themes/twentytwentyfour/assets/images/2026-04-19_15-38-52.png b/wp-content/themes/twentytwentyfour/assets/images/2026-04-19_15-38-52.png new file mode 100644 index 00000000..cd8a9b26 Binary files /dev/null and b/wp-content/themes/twentytwentyfour/assets/images/2026-04-19_15-38-52.png differ diff --git a/wp-content/themes/twentytwentyfour/assets/js/test1-landing.js b/wp-content/themes/twentytwentyfour/assets/js/test1-landing.js index 1433a970..273e0b60 100644 --- a/wp-content/themes/twentytwentyfour/assets/js/test1-landing.js +++ b/wp-content/themes/twentytwentyfour/assets/js/test1-landing.js @@ -1,343 +1,216 @@ const mobileToggle = document.getElementById('mobileToggle') - const mobilePanel = document.getElementById('mobilePanel') - const cartDrawer = document.getElementById('cartDrawer') - const cartOpenButton = document.getElementById('cartOpenButton') - const mobileCartOpenButton = document.getElementById('mobileCartOpenButton') - const cartCloseButton = document.getElementById('cartCloseButton') - const modalBackdrop = document.getElementById('orderModal') - const modalCloseButton = document.getElementById('modalCloseButton') - const viewCartButton = document.getElementById('viewCartButton') - const checkoutButton = document.getElementById('checkoutButton') - const orderButtons = document.querySelectorAll('[data-open-order]') - const addToCartButtons = document.querySelectorAll('.add-to-cart') - const faqItems = document.querySelectorAll('.faq-item') - const cartCount = document.getElementById('cartCount') - const mobileCartCount = document.getElementById('mobileCartCount') - const cartItemsCount = document.getElementById('cartItemsCount') - const cartTotal = document.getElementById('cartTotal') - const cartList = document.getElementById('cartList') - const orderForm = document.getElementById('orderForm') - const contactForm = document.getElementById('contactForm') - const orderItemsField = document.getElementById('orderItemsField') - const orderSuccess = document.getElementById('orderSuccess') - const contactSuccess = document.getElementById('contactSuccess') - const wooCommerceConfig = window.test1WooCommerce || null - const cartPageUrl = (wooCommerceConfig && wooCommerceConfig.cartUrl) ? wooCommerceConfig.cartUrl : '/cart/' - const checkoutPageUrl = (wooCommerceConfig && wooCommerceConfig.checkoutUrl) ? wooCommerceConfig.checkoutUrl : '/checkout/' - const cartPanel = cartDrawer?.querySelector('.cart-panel') - const modalCard = modalBackdrop?.querySelector('.modal-card') +const mobilePanel = document.getElementById('mobilePanel') +const modalBackdrop = document.getElementById('orderModal') +const modalCloseButton = document.getElementById('modalCloseButton') +const orderButtons = document.querySelectorAll('[data-open-order]') +const faqItems = document.querySelectorAll('.faq-item') +const orderForm = document.getElementById('orderForm') +const contactForm = document.getElementById('contactForm') +const orderItemsField = document.getElementById('orderItemsField') +const orderSuccess = document.getElementById('orderSuccess') +const contactSuccess = document.getElementById('contactSuccess') +const modalCard = modalBackdrop?.querySelector('.modal-card') - let cart = [] - let lastCartTrigger = null - let lastModalTrigger = null +let lastModalTrigger = null - const getFocusableElements = container => { - if (!container) { - return [] - } +const getFocusableElements = container => { + if (!container) { + return [] + } - return Array.from(container.querySelectorAll('a[href], button:not([disabled]), textarea:not([disabled]), input:not([disabled]), select:not([disabled]), [tabindex]:not([tabindex="-1"])')) - .filter(element => !element.hasAttribute('hidden') && !element.closest('[hidden]')) + return Array.from(container.querySelectorAll('a[href], button:not([disabled]), textarea:not([disabled]), input:not([disabled]), select:not([disabled]), [tabindex]:not([tabindex="-1"])')) + .filter(element => !element.hasAttribute('hidden') && !element.closest('[hidden]')) +} + +const focusFirstElement = container => { + const [first] = getFocusableElements(container) + + if (first) { + first.focus() + } +} + +const openMobileMenu = () => { + if (!mobilePanel || !mobileToggle) { + return + } + + mobilePanel.classList.add('active') + mobileToggle.setAttribute('aria-expanded', 'true') + document.body.classList.add('menu-open') +} + +const closeMobileMenu = () => { + if (!mobilePanel || !mobileToggle) { + return + } + + mobilePanel.classList.remove('active') + mobileToggle.setAttribute('aria-expanded', 'false') + document.body.classList.remove('menu-open') +} + +const openModal = triggerElement => { + if (!modalBackdrop) { + return + } + + lastModalTrigger = triggerElement || document.activeElement + + if (orderItemsField) { + orderItemsField.value = '' + } + + modalBackdrop.classList.add('open') + modalBackdrop.setAttribute('aria-hidden', 'false') + document.body.classList.add('modal-open') + focusFirstElement(modalCard) +} + +const closeModal = () => { + if (!modalBackdrop) { + return + } + + const shouldRestoreFocus = modalBackdrop.classList.contains('open') + + modalBackdrop.classList.remove('open') + modalBackdrop.setAttribute('aria-hidden', 'true') + document.body.classList.remove('modal-open') + + if (shouldRestoreFocus && lastModalTrigger && typeof lastModalTrigger.focus === 'function') { + lastModalTrigger.focus() + } + + lastModalTrigger = null +} + +const submitLeadForm = (formElement, successElement, formType, onSuccess) => { + if (!formElement || !successElement) { + return + } + + formElement.addEventListener('submit', async event => { + event.preventDefault() + + const leadFormConfig = window.test1LeadForm || null + + if (!leadFormConfig?.ajaxUrl || !leadFormConfig?.action || !leadFormConfig?.nonce) { + window.alert('Форма временно недоступна. Обновите страницу и попробуйте снова.') + return } - const focusFirstElement = container => { - const [first] = getFocusableElements(container) + const formData = new window.FormData(formElement) + formData.append('action', leadFormConfig.action) + formData.append('nonce', leadFormConfig.nonce) + formData.append('form_type', formType) + formData.append('page_url', window.location.href) - if (first) { - first.focus() - } - } - - const openMobileMenu = () => { - mobilePanel.classList.add('active') - mobileToggle.setAttribute('aria-expanded', 'true') - document.body.classList.add('menu-open') - } - - const closeMobileMenu = () => { - mobilePanel.classList.remove('active') - mobileToggle.setAttribute('aria-expanded', 'false') - document.body.classList.remove('menu-open') - } - - const openCart = triggerElement => { - lastCartTrigger = triggerElement || document.activeElement - cartDrawer.classList.add('open') - cartDrawer.setAttribute('aria-hidden', 'false') - document.body.classList.add('modal-open') - focusFirstElement(cartPanel) - } - - const closeCart = () => { - const shouldRestoreFocus = cartDrawer.classList.contains('open') - cartDrawer.classList.remove('open') - cartDrawer.setAttribute('aria-hidden', 'true') - document.body.classList.remove('modal-open') - - if (shouldRestoreFocus && lastCartTrigger && typeof lastCartTrigger.focus === 'function') { - lastCartTrigger.focus() - } - - lastCartTrigger = null - } - - const openModal = triggerElement => { - lastModalTrigger = triggerElement || document.activeElement - orderItemsField.value = cart.length - ? cart.map(item => `${item.name} (${item.volume}) - ${item.price} ₽`).join('\n') - : '' - modalBackdrop.classList.add('open') - modalBackdrop.setAttribute('aria-hidden', 'false') - document.body.classList.add('modal-open') - focusFirstElement(modalCard) - } - - const closeModal = () => { - const shouldRestoreFocus = modalBackdrop.classList.contains('open') - modalBackdrop.classList.remove('open') - modalBackdrop.setAttribute('aria-hidden', 'true') - document.body.classList.remove('modal-open') - - if (shouldRestoreFocus && lastModalTrigger && typeof lastModalTrigger.focus === 'function') { - lastModalTrigger.focus() - } - - lastModalTrigger = null - } - - const renderCart = () => { - const total = cart.reduce((sum, item) => sum + item.price, 0) - const count = cart.length - - cartCount.textContent = count - mobileCartCount.textContent = count - cartItemsCount.textContent = count - cartTotal.textContent = `${total} ₽` - - if (!count) { - cartList.innerHTML = '
Корзина пока пуста. Добавьте воду 19 литров, компактные форматы или минеральную воду из каталога.
' - return - } - - cartList.innerHTML = cart.map((item, index) => ` -
-
- ${item.name} - ${item.volume} · ${item.price} ₽ -
- -
- `).join('') - - cartList.querySelectorAll('[data-remove-index]').forEach(button => { - button.addEventListener('click', () => { - const index = Number(button.dataset.removeIndex) - cart.splice(index, 1) - renderCart() - }) + try { + const response = await window.fetch(leadFormConfig.ajaxUrl, { + method: 'POST', + credentials: 'same-origin', + body: formData, }) - } - const submitLeadForm = (formElement, successElement, formType, onSuccess) => { - if (!formElement || !successElement) { - return + const payload = await response.json().catch(() => null) + + if (!response.ok || !payload?.success) { + throw new Error('submit_failed') } - formElement.addEventListener('submit', async event => { - event.preventDefault() - - const leadFormConfig = window.test1LeadForm || null - - if (!leadFormConfig?.ajaxUrl || !leadFormConfig?.action || !leadFormConfig?.nonce) { - window.alert('Форма временно недоступна. Обновите страницу и попробуйте снова.') - return - } - - const formData = new window.FormData(formElement) - formData.append('action', leadFormConfig.action) - formData.append('nonce', leadFormConfig.nonce) - formData.append('form_type', formType) - formData.append('page_url', window.location.href) - - try { - const response = await window.fetch(leadFormConfig.ajaxUrl, { - method: 'POST', - credentials: 'same-origin', - body: formData, - }) - - const payload = await response.json().catch(() => null) - - if (!response.ok || !payload?.success) { - throw new Error('submit_failed') - } - - successElement.classList.add('visible') - if (typeof onSuccess === 'function') { - onSuccess() - } - - setTimeout(() => { - formElement.reset() - successElement.classList.remove('visible') - }, 2200) - } catch (error) { - window.alert('Не удалось отправить заявку. Попробуйте еще раз.') - } - }) - } - - mobileToggle.addEventListener('click', () => { - const isOpen = mobilePanel.classList.contains('active') - if (isOpen) { - closeMobileMenu() - return - } - openMobileMenu() - }) - - document.querySelectorAll('.mobile-nav a').forEach(link => { - link.addEventListener('click', closeMobileMenu) - }) - - addToCartButtons.forEach(button => { - button.addEventListener('click', () => { - const productId = Number(button.dataset.productId || 0) - - if (wooCommerceConfig && productId && wooCommerceConfig.addToCartUrl && wooCommerceConfig.checkoutUrl) { - button.disabled = true - - fetch(wooCommerceConfig.addToCartUrl, { - method: 'POST', - headers: { - 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' - }, - body: new URLSearchParams({ - product_id: String(productId), - quantity: '1' - }) - }) - .then(async response => { - let payload = null - - try { - payload = await response.json() - } catch (error) { - payload = null - } - - if (!response.ok) { - throw new Error('add_to_cart_failed') - } - - if (payload && payload.error && payload.product_url) { - window.location.href = payload.product_url - return - } - - cart.push({ - name: button.dataset.name, - volume: button.dataset.volume, - price: Number(button.dataset.price) - }) - renderCart() - openCart(button) - button.disabled = false - }) - .catch(() => { - button.disabled = false - }) - - return - } - - cart.push({ - name: button.dataset.name, - volume: button.dataset.volume, - price: Number(button.dataset.price) - }) - renderCart() - openCart(button) - }) - }) - - cartOpenButton.addEventListener('click', () => openCart(cartOpenButton)) - mobileCartOpenButton.addEventListener('click', () => { - closeMobileMenu() - openCart(mobileCartOpenButton) - }) - cartCloseButton.addEventListener('click', closeCart) - viewCartButton.addEventListener('click', () => { - window.location.href = cartPageUrl - }) - checkoutButton.addEventListener('click', () => { - window.location.href = checkoutPageUrl - }) - - orderButtons.forEach(button => { - button.addEventListener('click', () => openModal(button)) - }) - - modalCloseButton.addEventListener('click', closeModal) - - cartDrawer.addEventListener('click', event => { - if (event.target === cartDrawer) { - closeCart() - } - }) - - modalBackdrop.addEventListener('click', event => { - if (event.target === modalBackdrop) { - closeModal() - } - }) - - faqItems.forEach(item => { - const trigger = item.querySelector('.faq-question') - const answer = item.querySelector('.faq-answer') - - if (!trigger || !answer) { - return + successElement.classList.add('visible') + if (typeof onSuccess === 'function') { + onSuccess() } - trigger.addEventListener('click', () => { - const isOpen = item.classList.contains('open') - faqItems.forEach(entry => { - const entryTrigger = entry.querySelector('.faq-question') - const entryAnswer = entry.querySelector('.faq-answer') - - entry.classList.remove('open') - entryTrigger?.setAttribute('aria-expanded', 'false') - - if (entryAnswer) { - entryAnswer.hidden = true - } - }) - if (!isOpen) { - item.classList.add('open') - trigger.setAttribute('aria-expanded', 'true') - answer.hidden = false - } - }) - }) - - submitLeadForm(orderForm, orderSuccess, 'landing_order', () => { - cart = [] - renderCart() setTimeout(() => { - closeModal() - }, 700) - }) + formElement.reset() + successElement.classList.remove('visible') + }, 2200) + } catch (error) { + window.alert('Не удалось отправить заявку. Попробуйте еще раз.') + } + }) +} - submitLeadForm(contactForm, contactSuccess, 'landing_contacts') +if (mobileToggle && mobilePanel) { + mobileToggle.addEventListener('click', () => { + const isOpen = mobilePanel.classList.contains('active') - document.addEventListener('keydown', event => { - if (event.key !== 'Escape') { - return - } - closeCart() - closeModal() + if (isOpen) { closeMobileMenu() + return + } + + openMobileMenu() + }) + + document.querySelectorAll('.mobile-nav a').forEach(link => { + link.addEventListener('click', closeMobileMenu) + }) +} + +orderButtons.forEach(button => { + button.addEventListener('click', () => openModal(button)) +}) + +if (modalCloseButton) { + modalCloseButton.addEventListener('click', closeModal) +} + +if (modalBackdrop) { + modalBackdrop.addEventListener('click', event => { + if (event.target === modalBackdrop) { + closeModal() + } + }) +} + +faqItems.forEach(item => { + const trigger = item.querySelector('.faq-question') + const answer = item.querySelector('.faq-answer') + + if (!trigger || !answer) { + return + } + + trigger.addEventListener('click', () => { + const isOpen = item.classList.contains('open') + + faqItems.forEach(entry => { + const entryTrigger = entry.querySelector('.faq-question') + const entryAnswer = entry.querySelector('.faq-answer') + + entry.classList.remove('open') + entryTrigger?.setAttribute('aria-expanded', 'false') + + if (entryAnswer) { + entryAnswer.hidden = true + } }) - renderCart() + if (!isOpen) { + item.classList.add('open') + trigger.setAttribute('aria-expanded', 'true') + answer.hidden = false + } + }) +}) + +submitLeadForm(orderForm, orderSuccess, 'landing_order', () => { + setTimeout(() => { + closeModal() + }, 700) +}) + +submitLeadForm(contactForm, contactSuccess, 'landing_contacts') + +document.addEventListener('keydown', event => { + if (event.key !== 'Escape') { + return + } + + closeModal() + closeMobileMenu() +}) diff --git a/wp-content/themes/twentytwentyfour/functions.php b/wp-content/themes/twentytwentyfour/functions.php index 86fbffac..22d3e6be 100644 --- a/wp-content/themes/twentytwentyfour/functions.php +++ b/wp-content/themes/twentytwentyfour/functions.php @@ -503,17 +503,6 @@ function twentytwentyfour_test1_assets() { ) ); - if ( class_exists( 'WooCommerce' ) ) { - wp_localize_script( - 'twentytwentyfour-test1-script', - 'test1WooCommerce', - array( - 'addToCartUrl' => WC_AJAX::get_endpoint( 'add_to_cart' ), - 'cartUrl' => wc_get_cart_url(), - 'checkoutUrl' => wc_get_checkout_url(), - ) - ); - } } if ( is_page( 'faq' ) ) { diff --git a/wp-content/themes/twentytwentyfour/page-test1.php b/wp-content/themes/twentytwentyfour/page-test1.php index feb0e57b..138fa000 100644 --- a/wp-content/themes/twentytwentyfour/page-test1.php +++ b/wp-content/themes/twentytwentyfour/page-test1.php @@ -50,6 +50,8 @@ function twentytwentyfour_test1_catalog_markup() { if ( '' === $volume ) { $volume = __( 'Товар', 'twentytwentyfour' ); } + $add_to_cart_url = $product->add_to_cart_url(); + $add_to_cart_text = $product->add_to_cart_text(); ?>
@@ -66,14 +68,18 @@ function twentytwentyfour_test1_catalog_markup() {
- + is_purchasable() && $product->is_in_stock() ) : ?> + + + +
@@ -183,7 +189,7 @@ get_header( 'test1' ); для кулера
290 ₽за бутыль
- + В каталог
@@ -195,7 +201,7 @@ get_header( 'test1' ); компактно
95 ₽за бутылку
- + В каталог
@@ -207,7 +213,7 @@ get_header( 'test1' ); мероприятия
39 ₽от 0.5 л
- + В каталог
@@ -219,7 +225,7 @@ get_header( 'test1' ); рестораны
68 ₽за 1.5 л
- + В каталог
@@ -231,7 +237,7 @@ get_header( 'test1' ); на выбор
45 ₽за 0.5 л
- + В каталог
19 л, мягкий вкус, ежедневное потребление
290 ₽
- + В каталог
@@ -271,7 +277,7 @@ get_header( 'test1' ); 19 л, артезианская вода, усиленный контроль
340 ₽
- + В каталог
@@ -279,7 +285,7 @@ get_header( 'test1' ); 2 бутыли 19 л + помпа для первого заказа
690 ₽
- + В каталог
@@ -687,32 +693,6 @@ get_header( 'test1' ); - -