From a332e5b62a5c5ddcfcdf46e007310c2b882dc4cd Mon Sep 17 00:00:00 2001 From: GP_DV Date: Wed, 14 May 2025 20:03:51 +0300 Subject: [PATCH] single product page --- wp-content/themes/cosmopet/functions.php | 155 +++- .../assets/css/gp-style-core.css | 552 +++++++++++++ .../assets/css/gp-style-desktop.css | 722 ++++++++++++++++++ .../assets/css/gp-style-mobile.css | 38 + .../assets/css/gp-style-tablet.css | 348 +++++++++ .../assets/img/svg/main/arrow-breadcrumbs.svg | 3 + .../assets/img/svg/main/arrow-left.svg | 5 + .../assets/img/svg/main/arrow-right.svg | 5 + .../assets/img/svg/main/black-x.svg | 11 + .../single-product/assets/js/gp-main.js | 348 +++++++++ .../single-product/assets/js/gp-product.js | 110 +++ .../single-product/assets/js/tabs.js | 67 ++ .../single-product/assets/js/toggle.js | 89 +++ .../single-product/component-controller.php | 74 ++ .../single-product/component-template.twig | 447 +++++++++++ .../modules/shop/module-controller.php | 56 ++ wp-content/themes/cosmopet/single-product.php | 60 ++ .../static/shop/img/svg/main/arrow-left.svg | 5 + .../static/shop/img/svg/main/arrow-right.svg | 5 + .../static/shop/img/svg/main/black-x.svg | 11 + .../templates/shop/single-product.php | 63 ++ .../archive-product-tease.twig | 198 ++--- .../cosmopet/woocommerce/single-product.php | 19 + 23 files changed, 3291 insertions(+), 100 deletions(-) create mode 100644 wp-content/themes/cosmopet/modules/shop/components/single-product/assets/css/gp-style-core.css create mode 100644 wp-content/themes/cosmopet/modules/shop/components/single-product/assets/css/gp-style-desktop.css create mode 100644 wp-content/themes/cosmopet/modules/shop/components/single-product/assets/css/gp-style-mobile.css create mode 100644 wp-content/themes/cosmopet/modules/shop/components/single-product/assets/css/gp-style-tablet.css create mode 100644 wp-content/themes/cosmopet/modules/shop/components/single-product/assets/img/svg/main/arrow-breadcrumbs.svg create mode 100644 wp-content/themes/cosmopet/modules/shop/components/single-product/assets/img/svg/main/arrow-left.svg create mode 100644 wp-content/themes/cosmopet/modules/shop/components/single-product/assets/img/svg/main/arrow-right.svg create mode 100644 wp-content/themes/cosmopet/modules/shop/components/single-product/assets/img/svg/main/black-x.svg create mode 100644 wp-content/themes/cosmopet/modules/shop/components/single-product/assets/js/gp-main.js create mode 100644 wp-content/themes/cosmopet/modules/shop/components/single-product/assets/js/gp-product.js create mode 100644 wp-content/themes/cosmopet/modules/shop/components/single-product/assets/js/tabs.js create mode 100644 wp-content/themes/cosmopet/modules/shop/components/single-product/assets/js/toggle.js create mode 100644 wp-content/themes/cosmopet/modules/shop/components/single-product/component-controller.php create mode 100644 wp-content/themes/cosmopet/modules/shop/components/single-product/component-template.twig create mode 100644 wp-content/themes/cosmopet/modules/shop/module-controller.php create mode 100644 wp-content/themes/cosmopet/single-product.php create mode 100644 wp-content/themes/cosmopet/static/shop/img/svg/main/arrow-left.svg create mode 100644 wp-content/themes/cosmopet/static/shop/img/svg/main/arrow-right.svg create mode 100644 wp-content/themes/cosmopet/static/shop/img/svg/main/black-x.svg create mode 100644 wp-content/themes/cosmopet/templates/shop/single-product.php create mode 100644 wp-content/themes/cosmopet/woocommerce/single-product.php diff --git a/wp-content/themes/cosmopet/functions.php b/wp-content/themes/cosmopet/functions.php index 93ed2b9..0eab026 100644 --- a/wp-content/themes/cosmopet/functions.php +++ b/wp-content/themes/cosmopet/functions.php @@ -535,12 +535,19 @@ function get_products() { } function get_product_info ($id, $type) { - $product = wc_get_product( $id ); + if (!$id) { + return ''; + } + $product = wc_get_product($id); + if (!$product) { + return ''; + } if ($type == 'price') { return $product->get_price(); } elseif ($type == 'weight') { - return $product->get_weight() . ' кг'; + return $product->get_weight() ? $product->get_weight() . ' кг' : ''; } + return ''; } function get_add_to_cart_button ($id) { @@ -550,6 +557,10 @@ function get_add_to_cart_button ($id) { } function get_collection_siblings ($term) { + if (!$term) { + return []; + } + $args = array( 'posts_per_page' => -1, 'post_type' => 'product', @@ -782,3 +793,143 @@ function create_likes_table() { dbDelta($sql); } add_action('after_switch_theme', 'create_likes_table'); + + + +add_filter('woocommerce_product_data_tabs', function($tabs) { + $tabs['composition_tab'] = array( + 'label' => 'Состав', + 'target' => 'composition_product_data', + 'class' => array('composition_tab'), + 'priority' => 60, + ); + $tabs['feeding_tab'] = array( + 'label' => 'Рекомендации по кормлению', + 'target' => 'feeding_product_data', + 'class' => array('feeding_tab'), + 'priority' => 61, + ); + $tabs['important_tab'] = array( + 'label' => 'Важно', + 'target' => 'important_product_data', + 'class' => array('important_tab'), + 'priority' => 62, + ); + return $tabs; +}); + + +add_action('woocommerce_product_data_panels', function() { + global $post; + $composition = get_post_meta($post->ID, '_composition', true); + echo '
'; + woocommerce_wp_textarea_input([ + 'id' => '_composition', + 'label' => 'Состав', + 'desc_tip' => true, + 'description' => 'Введите состав товара', + 'value' => $composition + ]); + echo '
'; +}); + + +add_action('woocommerce_product_data_panels', function() { + global $post; + $feeding = get_post_meta($post->ID, '_feeding_recommendations', true); + echo '
'; + woocommerce_wp_textarea_input([ + 'id' => '_feeding_recommendations', + 'label' => 'Рекомендации по кормлению', + 'desc_tip' => true, + 'description' => 'Введите рекомендации по кормлению', + 'value' => $feeding + ]); + echo '
'; +}); + + +add_action('woocommerce_product_data_panels', function() { + global $post; + $important = get_post_meta($post->ID, '_important', true); + echo '
'; + woocommerce_wp_textarea_input([ + 'id' => '_important', + 'label' => 'Важно', + 'desc_tip' => true, + 'description' => 'Введите важную информацию', + 'value' => $important + ]); + echo '
'; +}); + +add_action('woocommerce_process_product_meta', function($post_id) { + if (isset($_POST['_composition'])) { + update_post_meta($post_id, '_composition', sanitize_textarea_field($_POST['_composition'])); + } + if (isset($_POST['_feeding_recommendations'])) { + update_post_meta($post_id, '_feeding_recommendations', sanitize_textarea_field($_POST['_feeding_recommendations'])); + } + if (isset($_POST['_important'])) { + update_post_meta($post_id, '_important', sanitize_textarea_field($_POST['_important'])); + } +}); +// Добавление поля для выбора рекомендуемых товаров +function register_recommended_products_acf_field() { + if (function_exists('acf_add_local_field_group')) { + acf_add_local_field_group(array( + 'key' => 'group_recommended_products', + 'title' => 'Рекомендуемые товары', + 'fields' => array( + array( + 'key' => 'field_recommended_products', + 'label' => 'Выберите рекомендуемые товары', + 'name' => 'recommended_products', + 'type' => 'relationship', + 'instructions' => 'Выберите товары, которые будут отображаться в секции "вашему питомцу может понравиться"', + 'required' => 0, + 'conditional_logic' => 0, + 'post_type' => array( + 0 => 'product', + ), + 'filters' => array( + 0 => 'search', + 1 => 'taxonomy', + ), + 'return_format' => 'object', + 'min' => '', + 'max' => 8, + ), + ), + 'location' => array( + array( + array( + 'param' => 'post_type', + 'operator' => '==', + 'value' => 'product', + ), + ), + ), + 'menu_order' => 0, + 'position' => 'normal', + 'style' => 'default', + 'label_placement' => 'top', + 'instruction_placement' => 'label', + 'hide_on_screen' => '', + )); + } +} +add_action('acf/init', 'register_recommended_products_acf_field'); + +add_action('wp_footer', 'remove_view_cart_button_js'); +function remove_view_cart_button_js() { + ?> + + + + diff --git a/wp-content/themes/cosmopet/modules/shop/components/single-product/assets/img/svg/main/arrow-left.svg b/wp-content/themes/cosmopet/modules/shop/components/single-product/assets/img/svg/main/arrow-left.svg new file mode 100644 index 0000000..465c268 --- /dev/null +++ b/wp-content/themes/cosmopet/modules/shop/components/single-product/assets/img/svg/main/arrow-left.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/wp-content/themes/cosmopet/modules/shop/components/single-product/assets/img/svg/main/arrow-right.svg b/wp-content/themes/cosmopet/modules/shop/components/single-product/assets/img/svg/main/arrow-right.svg new file mode 100644 index 0000000..8952470 --- /dev/null +++ b/wp-content/themes/cosmopet/modules/shop/components/single-product/assets/img/svg/main/arrow-right.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/wp-content/themes/cosmopet/modules/shop/components/single-product/assets/img/svg/main/black-x.svg b/wp-content/themes/cosmopet/modules/shop/components/single-product/assets/img/svg/main/black-x.svg new file mode 100644 index 0000000..cb3041d --- /dev/null +++ b/wp-content/themes/cosmopet/modules/shop/components/single-product/assets/img/svg/main/black-x.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/wp-content/themes/cosmopet/modules/shop/components/single-product/assets/js/gp-main.js b/wp-content/themes/cosmopet/modules/shop/components/single-product/assets/js/gp-main.js new file mode 100644 index 0000000..a1bc687 --- /dev/null +++ b/wp-content/themes/cosmopet/modules/shop/components/single-product/assets/js/gp-main.js @@ -0,0 +1,348 @@ +'use strict'; + +// header +toggleOpenX('.lang', '.lang__open', '.lang__list', '.lang__content', false); +toggleHeader('#pc-menu','.header__menu-block','.header__pc-menu', '.white', 'white'); +toggleHeader('#phone-menu','.header__menu-block','.header__phone-menu', '.white', 'white'); +// header + +// modal +modalOpen('.button--filter', '.modal__filter'); +modalOpen('.basket-open', '.modal__basket'); +modalOpen('.open-to-know', '.modal__to-know'); +modalClose('.modal__close'); + +let modal = document.querySelector('.modal'); + +modal.onclick = function (event) { + let target = event.target; + + if (target.classList.contains('modal')) { + let aside = target.querySelector('.modal__aside'), + modalItem = target.querySelector('.modal__item.active'); + + aside.style.width = '0px'; + setTimeout(() => { + modalItem.style.cssText = ''; + modalItem.classList.remove('active'); + target.classList.remove('active'); + }, 300); + } +} + +// modal + +// toggle +toggleOpenX('.toggle', '.toggle__title', '.toggle__content', '.toggle__block-content', true); +// toggle + +// // radio-button +// let radioButtons = document.querySelectorAll('.radio-button'); + +// radioButtons.forEach(radioBlock => { +// let buttons = radioBlock.querySelectorAll('.button'); + +// buttons.forEach(button => { +// let input = radioBlock.querySelector('.radio-button__input'); + +// button.onclick = function (e) { +// e.preventDefault(); + +// buttons.forEach(thisButton => { +// if (thisButton.classList.contains('active')) { +// thisButton.classList.remove('active') +// } +// }) + +// let text = button.textContent.trim(); + +// button.classList.toggle('active'); + +// input.value = text; +// } +// }) +// }) +// // radio-button + +// overlay +let products = document.querySelectorAll('.product__item'); + +products.forEach(productItem => { + let button = productItem.querySelector('.open-overlay'), + overlay = productItem.querySelector('.product-item__overlay'); + + + if (button) { + button.onclick = function (e) { + document.querySelectorAll('.product__item').forEach(e => { + if (e.classList.contains('active')) { + e.classList.remove('active'); + } + }); + document.querySelectorAll('.product-item__overlay').forEach(e => { + if (e.classList.contains('active')) { + e.classList.remove('active'); + } + }); + + productItem.classList.toggle('active'); + overlay.classList.toggle('active'); + } + } + +}) +// overlay + + +// select +toggleOpenX('.select', '.select__state' , '.state__content', '.state__block', true); + +let selects = document.querySelectorAll('.select'); + +selects.forEach(select => { + let state = select.querySelector('.select__state'), + content = select.querySelector('.state__block'), + buttons = select.querySelectorAll('.state__button'); + + buttons.forEach(e => { + let button = e; + + e.onclick = function (event) { + event.preventDefault(); + + buttons.forEach(element => { + if (element.classList.contains('active')) { + element.classList.remove('active'); + } + }) + + let text = e.textContent.trim(); + state.value = text; + + button.classList.add('active'); + content.style.height = 0; + select.classList.remove('active'); + } + }) +}) +// select + +// counter +let counters = document.querySelectorAll('.counter'); + +counters.forEach(e => { + let minus = e.querySelector('.minus'), + plus = e.querySelector('.plus'), + input = e.querySelector('.counter__input'); + + minus.onclick = function (e) { + e.preventDefault(); + + let number = input.value; + + if (number >= 2){ + input.value = Number(number) - 1; + } + } + + plus.onclick = function (e) { + e.preventDefault(); + + let number = input.value; + + if (number <= 99) { + input.value = Number(number) + 1; + } + } +}) +// counter + +// checkbox +let checkbox = document.querySelectorAll('.checkbox'); + +checkbox.forEach(e => { + e.onclick = function (event) { + let input = e.querySelector('.checkbox__input'); + + if (!e.classList.contains('active')) { + input.checked = 1; + }else{ + input.checked = 0; + } + e.classList.toggle('active'); + } +}) +// checkbox + + +// function +function modalOpen(buttonElement, contentElement){ + let modal = document.querySelector('.modal'), + aside = document.querySelector('.modal__aside'), + elements = document.querySelectorAll(buttonElement), + device = window.screen.width; + + elements.forEach(e => { + let thisContentElement = document.querySelector(contentElement); + + e.onclick = function () { + modal.classList.add('active'); + thisContentElement.classList.add('active'); + + let width = thisContentElement.clientWidth; + + setTimeout(() => { + if (device <= 720) { + aside.style.width = `${device}px`; + thisContentElement.style.opacity = 1; + thisContentElement.style.filter = 'blur(0px)'; + }else{ + aside.style.width = `${width}px`; + thisContentElement.style.opacity = 1; + thisContentElement.style.filter = 'blur(0px)'; + } + }, 10); + } + }) +} + +function modalClose(buttonElement) { + let modal = document.querySelector('.modal'), + aside = document.querySelector('.modal__aside'), + asideItems = document.querySelectorAll('.modal__item'), + elements = document.querySelectorAll(buttonElement); + + elements.forEach(e => { + e.onclick = function () { + aside.style.width = '0px'; + + asideItems.forEach(e => { + if (e.classList.contains('active')) { + e.style.filter = 'blur(10px)'; + } + }); + + setTimeout(() => { + asideItems.forEach(e => { + if (e.classList.contains('active')) { + e.classList.remove('active'); + } + }); + + modal.classList.remove('active'); + }, 300); + } + }) +} + +function toggleOpenX(mainElement, buttonElement ,heightElement, contentElement, close) { + let elements = document.querySelectorAll(mainElement); + + elements.forEach(e => { + let thisMainElement = e, + thisButtonElement = e.querySelector(buttonElement), + thisHeightElement = e.querySelector(heightElement), + thisContentElement = e.querySelector(contentElement); + + thisButtonElement.onclick = function (e) { + let height = thisHeightElement.clientHeight; + + if (close == true && !thisMainElement.classList.contains('active')) { + elements.forEach(e => { + if (e.classList.contains('active')) { + e.classList.remove('active'); + e.querySelector(contentElement).style.height = null + } + }) + } + + if (!thisMainElement.classList.contains('active')) { + thisContentElement.style.height = `${height}px`; + thisMainElement.classList.add('active'); + }else{ + thisContentElement.style.height = null; + thisMainElement.classList.remove('active'); + } + } + + }); +} + +function toggleHeader(button, content, blockheight, removeBlock, removeClass) { + let thisButton = document.querySelector(button), + thisContent = document.querySelector(content), + thisRemoveBlock = document.querySelector(removeBlock) || '', + thisBlockheight = document.querySelector(blockheight); + + thisButton.onclick = function () { + let height = thisBlockheight.clientHeight; + + if (!thisContent.classList .contains('open')) { + thisContent.style.height = `${height}px`; + thisContent.classList .add('open'); + + if (removeBlock) { + thisRemoveBlock.classList.remove(removeClass); + } + }else{ + thisContent.style.height = null; + thisContent.classList .remove('open'); + + if (removeBlock) { + if (window.scrollY <= 25) { + thisRemoveBlock.classList.add(removeClass); + } + } + } + } +} +// function + +// resize +window.addEventListener('resize', (e) => { + let width = window.screen.width; + + // media + modalOpen('.button--filter', '.modal__filter'); + modalOpen('.basket-open', '.modal__basket'); + modalOpen('.open-to-know', '.modal__to-know'); + modalClose('.modal__close'); + + let modalItem = document.querySelectorAll('.modal__item'); + + // if (width <= 720) { + modalItem.forEach(modal => { + if (modal.classList.contains('active')) { + let aside = document.querySelector('.modal__aside'); + + if (width <= 720) { + aside.style.width = `${width}px` + }else{ + let openAside = document.querySelector('.modal__item.active'), + newWidth = openAside.clientWidth; + + aside.style.width = `${newWidth}px` + } + } + }) + // } +}); +// resize + +// scroll + + +if (document.querySelector('.header').classList.contains('white')) { + window.addEventListener("scroll", function (e) { + let header = document.querySelector('.header'); + let scroll = window.scrollY; + + if (scroll >= 25) { + header.classList.remove('white') + }else{ + header.classList.add('white') + } + + }); +} +// scroll diff --git a/wp-content/themes/cosmopet/modules/shop/components/single-product/assets/js/gp-product.js b/wp-content/themes/cosmopet/modules/shop/components/single-product/assets/js/gp-product.js new file mode 100644 index 0000000..2508270 --- /dev/null +++ b/wp-content/themes/cosmopet/modules/shop/components/single-product/assets/js/gp-product.js @@ -0,0 +1,110 @@ +'use strict'; + +// slider gallery modal +const gallery = new Swiper('.gallery__slider', { + spaceBetween: 100, + + pagination: { + el: '.swiper-pagination', + }, + + navigation: { + nextEl: '.swiper-button-next', + prevEl: '.swiper-button-prev', + }, + + scrollbar: { + el: '.swiper-scrollbar', + }, +}); + +let paginationButtons = document.querySelectorAll('.gallery-pagination__item'); + +paginationButtons.forEach(button => { + let index = button.dataset.countImg; + + button.onclick = function () { + gallery.slideTo(index); + } +}) + +// open gallery +let detailImage= document.querySelectorAll('.detail__image'); +detailImage.forEach(button => { + let index = button.dataset.countImg; + + button.onclick = function () { + gallery.slideTo(index); + + document.querySelector('.gallery').classList.add('active'); + } +}) + +// close gallery + +document.querySelector('.gallery__close').onclick = function () { + document.querySelector('.gallery').classList.remove('active'); +} +// slider gallery modal + +// slider gallery main phone + +// createGalleryPhone('.detail__images', '.detail-images__wrapper', '.detail__image', 980); + +const detailPhone = new Swiper('.detail__images-phone', { + spaceBetween: 100, + + pagination: { + el: '.swiper-pagination', + }, + + navigation: { + nextEl: '.swiper-button-next', + prevEl: '.swiper-button-prev', + }, + + scrollbar: { + el: '.swiper-scrollbar', + }, +}); + +let detailImagesPhones = document.querySelectorAll('.detail-images-phone__image-block'); + +detailImagesPhones.forEach(button => { + button.onclick = function (e) { + let index = button.dataset.countImg; + + gallery.slideTo(index); + + document.querySelector('.gallery').classList.add('active'); + } +}) + +// slider gallery main phone + +// detail catalog +const detailCatalot = new Swiper('.detail__catalot', { + // Navigation arrows + navigation: { + nextEl: '.detail-catalot-control__button.next', + prevEl: '.detail-catalot-control__button.prev', + }, + breakpoints: { + 1600: { + slidesPerView: 4, + }, + 1200: { + slidesPerView: 3, + }, + 780: { + slidesPerView: 2, + }, + 100: { + slidesPerView: 1.1, + spaceBetween: 20 + }, + } + + +}); +// detail catalog \ No newline at end of file diff --git a/wp-content/themes/cosmopet/modules/shop/components/single-product/assets/js/tabs.js b/wp-content/themes/cosmopet/modules/shop/components/single-product/assets/js/tabs.js new file mode 100644 index 0000000..10c8130 --- /dev/null +++ b/wp-content/themes/cosmopet/modules/shop/components/single-product/assets/js/tabs.js @@ -0,0 +1,67 @@ +const rootSelectorTabs = '[data-js-tabs]'; + +class Tabs{ + // элементы для поиска + selectors = { + root: rootSelectorTabs, + button: '[data-js-tabs-button]', + content: '[data-js-tabs-content]', + } + // класс отображения состояния + stateClasses = { + isActive: 'active', + } + + constructor(rootElement){ + this.rootElement = rootElement; + this.buttonElements = this.rootElement.querySelectorAll(this.selectors.button); + this.contentElements = this.rootElement.querySelectorAll(this.selectors.content); + this.state = { + activeMenuIndex: [...this.buttonElements] + .findIndex((buttonElement) => buttonElement.classList.contains(this.stateClasses.isActive)), + }; + this.limitTabsIndex = this.buttonElements.length - 1; + this.bindEvents(); + } + + updateUI(){ + const { activeMenuIndex } = this.state; + + this.buttonElements.forEach((buttonElement, index) => { + const isActive = index === activeMenuIndex; + + buttonElement.classList.toggle(this.stateClasses.isActive, isActive); + }) + + this.contentElements.forEach((contentElement, index) => { + const isActive = index === activeMenuIndex; + + contentElement.classList.toggle(this.stateClasses.isActive, isActive); + }) + } + + onButtonClick(buttonIndex){ + this.state.activeMenuIndex = buttonIndex; + this.updateUI(); + } + + bindEvents(){ + this.buttonElements.forEach((buttonElement, index) => { + buttonElement.addEventListener('click', () => this.onButtonClick(index)) + }) + } +} + +class TabsCollection { + constructor(){ + this.init(); + } + + init(){ + document.querySelectorAll(rootSelectorTabs).forEach((element) => { + new Tabs(element); + }); + } +} + +export default TabsCollection; \ No newline at end of file diff --git a/wp-content/themes/cosmopet/modules/shop/components/single-product/assets/js/toggle.js b/wp-content/themes/cosmopet/modules/shop/components/single-product/assets/js/toggle.js new file mode 100644 index 0000000..bb162e9 --- /dev/null +++ b/wp-content/themes/cosmopet/modules/shop/components/single-product/assets/js/toggle.js @@ -0,0 +1,89 @@ +const rootSelectorToggles = '[data-js-toggle]'; + +class Toggle{ + // элементы для поиска + selectors = { + root: rootSelectorToggles, + button: '[data-js-toggle-button]', + wrapper: '[data-js-toggle-wrapper]', + content: '[data-js-toggle-content]', + } + // класс отображения состояния + stateClasses = { + isActive: 'active', + } + + constructor(rootElement){ + this.rootElement = rootElement; + this.buttonElements = this.rootElement.querySelectorAll(this.selectors.button); + this.wrapperElements = this.rootElement.querySelectorAll(this.selectors.wrapper); + this.contentElements = this.rootElement.querySelectorAll(this.selectors.content); + this.state = { + activeToggleIndex: [...this.buttonElements] + .findIndex((buttonElement) => buttonElement.classList.contains(this.stateClasses.isActive)), + }; + this.bindEvents(); + } + + updateUI(newHeight){ + const { activeToggleIndex } = this.state; + + this.buttonElements.forEach((buttonElement, index) => { + const isActive = index === activeToggleIndex; + + buttonElement.classList.toggle(this.stateClasses.isActive, isActive); + }) + + this.wrapperElements.forEach((wrapperElement, index) => { + const isActive = index === activeToggleIndex, + newHeight = this.contentElements[index].offsetHeight; + + wrapperElement.classList.toggle(this.stateClasses.isActive, isActive); + + if (isActive) { + wrapperElement.style.height = `${newHeight}px`; + + setTimeout(() => { + if (wrapperElement.classList.contains('active')) { + wrapperElement.style.height = `auto`; + } + }, 300); + }else{ + wrapperElement.style.height = `${newHeight}px`; + + setTimeout(() => { + if (!wrapperElement.classList.contains('active')) { + wrapperElement.style.height = `${0}px`; + } + }, 10); + } + }) + + } + + onButtonClick(buttonIndex){ + this.state.activeToggleIndex = (buttonIndex === this.state.activeToggleIndex) ? -1 : buttonIndex; + + this.updateUI(); + } + + bindEvents(){ + this.buttonElements.forEach((buttonElement, index) => { + buttonElement.addEventListener('click', () => this.onButtonClick(index)) + }) + } +} + +class TogglesCollection{ + constructor(){ + this.init(); + } + + init(){ + document.querySelectorAll(rootSelectorToggles).forEach((element) => { + new Toggle(element); + }); + } +} + +export default TogglesCollection; \ No newline at end of file diff --git a/wp-content/themes/cosmopet/modules/shop/components/single-product/component-controller.php b/wp-content/themes/cosmopet/modules/shop/components/single-product/component-controller.php new file mode 100644 index 0000000..e5aca87 --- /dev/null +++ b/wp-content/themes/cosmopet/modules/shop/components/single-product/component-controller.php @@ -0,0 +1,74 @@ +get_attributes(); + + if (!empty($product_attributes)) { + foreach ($product_attributes as $taxonomy => $attribute) { + if ($attribute->is_taxonomy()) { + $terms = wc_get_product_terms($product_id, $taxonomy, ['fields' => 'all']); + if (!empty($terms)) { + $attr_values = []; + foreach ($terms as $term) { + $attr_values[] = [ + 'name' => $term->name, + 'slug' => $term->slug, + 'term_id' => $term->term_id, + 'link' => get_term_link($term->term_id, $taxonomy), + ]; + } + $attributes[wc_attribute_label($taxonomy)] = $attr_values; + } + } else { + $attributes[wc_attribute_label($taxonomy)] = $attribute->get_options(); + } + } + } + + $context['product_attributes'] = $attributes; + + if ($product->is_type('variable')) { + $available_variations = $product->get_available_variations(); + $variations_data = []; + + foreach ($available_variations as $variation) { + $variation_id = $variation['variation_id']; + $variation_obj = wc_get_product($variation_id); + + $variations_data[] = [ + 'variation_id' => $variation_id, + 'price' => $variation_obj->get_price(), + 'regular_price' => $variation_obj->get_regular_price(), + 'sale_price' => $variation_obj->get_sale_price(), + 'attributes' => $variation['attributes'] + ]; + } + + $context['variations'] = $variations_data; + } + + $meta_fields = [ + 'composition' => get_post_meta($product_id, '_composition', true), + 'feeding_recommendations' => get_post_meta($product_id, '_feeding_recommendations', true), + 'feeding_recommendations_table' => get_field('feeding_recommendations_table', $product_id), + + 'nutritional_value' => get_post_meta($product_id, '_nutritional_value', true), + 'vitamins' => get_post_meta($product_id, '_vitamins', true), + 'additives' => get_post_meta($product_id, '_additives', true), + 'energy_value' => get_post_meta($product_id, '_energy_value', true), + 'important' => get_post_meta($product_id, '_important', true), + ]; + + $context['product_meta'] = $meta_fields; + } + } + + return $context; +}); \ No newline at end of file diff --git a/wp-content/themes/cosmopet/modules/shop/components/single-product/component-template.twig b/wp-content/themes/cosmopet/modules/shop/components/single-product/component-template.twig new file mode 100644 index 0000000..7fd37a6 --- /dev/null +++ b/wp-content/themes/cosmopet/modules/shop/components/single-product/component-template.twig @@ -0,0 +1,447 @@ + + + {% set bodyClass = 'bg-white' %} + + {% set mainClass = 'wrapper' %} + + {% extends 'layout.twig' %} + + {% block content %} +
+ + + + {{ function('pll_e', 'к каталогу') }} + + +
+
+ {% for image in gallery_images %} +
+ {{ image.alt }} +
+ {% endfor %} +
+
+ +
+
+ {% if product_attributes.Flavor is defined and product_attributes.Flavor|length > 0 %} +
+ {{ product_attributes.Flavor[0].name }} +
+ {% endif %} + {% if product_attributes.Вкус is defined and product_attributes.Вкус|length > 0 %} + + {% endif %} + + {% if product_attributes.Тег is defined and product_attributes.Тег|length > 0 %} + + {% endif %} + {% if product.is_on_sale() %} +
+ {{ function('pll_e', 'Распродажа') }} % +
+ {% endif %} +
+

+ {{ product.get_title }} +

+ +
+
+ {% for image in gallery_images %} +
+
+ {{ image.alt }} +
+
+ {% endfor %} +
+
+
+ +
+

+ {{ product.get_price }} +

+ + {% if product.is_on_sale() %} +
+

+ {{ product.get_regular_price }} +

+

+ {{ ((product.get_regular_price - product.get_price) / product.get_regular_price * 100)|round }} +

+
+ {% endif %} +
+
+ {% set collection = fn('wc_get_product_terms', product.id, 'pa_collection') %} + {% if collection %} + {% set siblings = function('get_collection_siblings', collection[0].term_id) %} + {% if siblings %} +
+ {% for sibling in siblings %} + {% set weight = sibling.post_title|split(', ')|last %} + {% set current_weight = function('get_product_info', product.id, 'weight') %} + {% set class = weight == current_weight ? 'active' : '' %} + + {{ weight|upper }} + + {% endfor %} + +
+ {% endif %} + {% endif %} + + + +
+ +
+
+ +
+
+

+ {{ function('pll_e', 'ОПИСАНИЕ') }} +

+
+
+

+ {{ product.get_description() }} +

+
+
+
+ + {% if product_meta.composition %} +
+

+ {{ function('pll_e', 'СОСТАВ') }} +

+
+
+

+ {{ product_meta.composition }} +

+
+
+
+ {% endif %} + + {% if product_meta.feeding_recommendations_table %} +
+

+ {{ function('pll_e', 'РЕКОМЕНДАЦИИ ПО КОРМЛЕНИЮ') }} +

+
+
+
+ {% if product_meta.feeding_recommendations_table.header %} +
+

{{ product_meta.feeding_recommendations_table.header.0 is iterable ? product_meta.feeding_recommendations_table.header.0|join(', ') : product_meta.feeding_recommendations_table.header.0 }}

+ {% for row in product_meta.feeding_recommendations_table.body %} +
+

{{ row.0 is iterable ? row.0|join(', ') : row.0 }}

+
+
+ {% endfor %} +
+
+

{{ product_meta.feeding_recommendations_table.header.1 is iterable ? product_meta.feeding_recommendations_table.header.1|join(', ') : product_meta.feeding_recommendations_table.header.1 }}

+ {% for row in product_meta.feeding_recommendations_table.body %} +
+

{{ row.1 is iterable ? row.1|join(', ') : row.1 }}

+
+
+ {% endfor %} +
+
+

{{ product_meta.feeding_recommendations_table.header.2 is iterable ? product_meta.feeding_recommendations_table.header.2|join(', ') : product_meta.feeding_recommendations_table.header.2 }}

+ {% for row in product_meta.feeding_recommendations_table.body %} +
+

{{ row.2 is iterable ? row.2|join(', ') : row.2 }}

+
+ {% endfor %} +
+ {% endif %} +
+
+
+
+ {% elseif product_meta.feeding_recommendations %} +
+

+ {{ function('pll_e', 'РЕКОМЕНДАЦИИ ПО КОРМЛЕНИЮ') }} +

+
+
+ {{ product_meta.feeding_recommendations }} +
+
+
+ {% endif %} + + {% if product_meta.nutritional_value or product_meta.vitamins or product_meta.additives or product_meta.energy_value %} +
+

+ {{ function('pll_e', 'ПИЩЕВАЯ ЦЕННОСТЬ') }} +

+
+
+ {% if product_meta.nutritional_value and product_meta.vitamins %} +
+
+

{{ function('pll_e', 'ПИЩЕВАЯ ЦЕННОСТЬ') }}

+ {{ product_meta.nutritional_value }} +
+
+

{{ function('pll_e', 'ВИТАМИНЫ НА КГ') }}

+ {{ product_meta.vitamins }} +
+
+ {% endif %} + + {% if product_meta.additives or product_meta.energy_value %} +
+ {% if product_meta.additives %} +
+

{{ function('pll_e', 'ПИТАТЕЛЬНЫЕ ДОБАВКИ НА КГ') }}

+ {{ product_meta.additives }} +
+ {% endif %} + + {% if product_meta.energy_value %} +
+

{{ function('pll_e', 'ЭНЕРГЕТИЧЕСКАЯ ЦЕННОСТЬ НА 100 ГРАММ') }}

+
+
+
+

{{ product_meta.energy_value }}

+
+
+
+
+ {% endif %} +
+ {% endif %} +
+
+
+ {% endif %} + + +
+ + +
+
+

{{ function('pll_e', 'Важно') }}

+

+ {{ product_meta.important }} +

+
+
+
+ + {% set recommended_products = function('get_field', 'recommended_products', product.id) %} + {% set related_products = recommended_products ? recommended_products : function('wc_get_related_products', product.id, 4) %} + {% if related_products %} +
+
+
+

+ {{ function('pll_e', 'вашему питомцу может понравиться') }} +

+ +
+ + + +
+
+
+ {% for related_product in related_products %} +
+ {% set post_id = related_product.ID is defined ? related_product.ID : related_product %} + {% set wc_product = fn('wc_get_product', post_id) %} + {% if wc_product %} +
+
+ {% if wc_product.get_date_created|date('Y-m-d') >= criteria_for_new_product %} + + {{ function('pll_e', 'Новинка') }} + + {% endif %} + + {% if wc_product.is_on_sale() %} + + {{ function('pll_e', 'Распродажа %') }} + + {% endif %} +
+ + {{ wc_product.get_name() }} + +
+
+ {% set compound = fn('wc_get_product_terms', post_id, 'pa_compound') %} + {% for option in compound %} + {% set term = get_term(option) %} + {{ term.name }} + {% endfor %} +
+ {{ wc_product.get_name() }} +
+

{{ wc_product.get_price() }}

+
+
+ +
+
+
+
+ {{ wc_product.get_name() }} +
    + {% set features = fn('wc_get_product_terms', post_id, 'pa_features') %} + {% for option in features %} + {% set term = get_term(option) %} +
  • {{ term.name }}
  • + {% endfor %} +
+
+ +
+
+
+

Объем

+ +
+ {% set cur_weight = function('get_product_info', post_id, 'weight') %} + +
+
    + {% set collection = fn('wc_get_product_terms', post_id, 'pa_collection') %} + {% for option in collection %} + {% set term = get_term(option) %} + {% if term %} + {% set siblings = function('get_collection_siblings', term.term_id) %} + + {% for sibling in siblings %} + {% set weight = function('get_product_info', sibling.ID, 'weight') %} + + {% set class = '' %} + {% if weight == cur_weight %} + {% set class = 'active' %} + {% endif %} +
  • + +
  • + {% endfor %} + {% endif %} + {% endfor %} +
+
+
+
+
+

Количество

+ +
+ + + +
+
+
+

+ {{ wc_product.get_price() }} +

+
+
+ {{ function('get_add_to_cart_button', post_id) }} +
+ +
+
+
+
+ {% endif %} +
+ {% endfor %} +
+
+
+ {% endif %} +
+ + + +
+ {% endblock %} \ No newline at end of file diff --git a/wp-content/themes/cosmopet/modules/shop/module-controller.php b/wp-content/themes/cosmopet/modules/shop/module-controller.php new file mode 100644 index 0000000..9c541f6 --- /dev/null +++ b/wp-content/themes/cosmopet/modules/shop/module-controller.php @@ -0,0 +1,56 @@ +get_gallery_image_ids(); + $gallery_images = []; + + $main_image_id = $product->get_image_id(); + if ($main_image_id) { + $gallery_images[] = [ + 'id' => $main_image_id, + 'src' => wp_get_attachment_image_url($main_image_id, 'full'), + 'alt' => get_post_meta($main_image_id, '_wp_attachment_image_alt', true) + ]; + } + + foreach ($attachment_ids as $attachment_id) { + $gallery_images[] = [ + 'id' => $attachment_id, + 'src' => wp_get_attachment_image_url($attachment_id, 'full'), + 'alt' => get_post_meta($attachment_id, '_wp_attachment_image_alt', true) + ]; + } + + $context['gallery_images'] = $gallery_images; + + $related_products_ids = wc_get_related_products($product_id, 5); + if (!empty($related_products_ids)) { + $related_products = []; + foreach ($related_products_ids as $related_id) { + $related_product = wc_get_product($related_id); + if ($related_product) { + $related_products[] = $related_product; + } + } + $context['related_products'] = $related_products; + } + } + } + + return $context; + }); \ No newline at end of file diff --git a/wp-content/themes/cosmopet/single-product.php b/wp-content/themes/cosmopet/single-product.php new file mode 100644 index 0000000..ad3e75f --- /dev/null +++ b/wp-content/themes/cosmopet/single-product.php @@ -0,0 +1,60 @@ + '', + 'wrap_before' => '', + 'wrap_after' => '', + 'before' => '', + 'after' => '', + 'home' => _x('Home', 'breadcrumb', 'woocommerce'), + ); + + $breadcrumbs = new WC_Breadcrumb(); + $breadcrumbs->generate(); + + $formatted_breadcrumbs = array(); + foreach ($breadcrumbs->get_breadcrumb() as $crumb) { + $formatted_breadcrumbs[] = array( + 'text' => $crumb[0], + 'url' => $crumb[1] + ); + } + + $context['wc_breadcrumbs'] = $formatted_breadcrumbs; + } + + $product_id = get_the_ID(); + $product = wc_get_product($product_id); + + $context['product'] = $product; + + $context['related_products'] = array(); + $related_products_ids = wc_get_related_products($product_id, 5); + + if ($related_products_ids) { + foreach ($related_products_ids as $related_id) { + $related_product = wc_get_product($related_id); + if ($related_product) { + $context['related_products'][] = $related_product; + } + } + } + + Timber::render('modules/shop/components/single-product/component-template.twig', $context); +} \ No newline at end of file diff --git a/wp-content/themes/cosmopet/static/shop/img/svg/main/arrow-left.svg b/wp-content/themes/cosmopet/static/shop/img/svg/main/arrow-left.svg new file mode 100644 index 0000000..465c268 --- /dev/null +++ b/wp-content/themes/cosmopet/static/shop/img/svg/main/arrow-left.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/wp-content/themes/cosmopet/static/shop/img/svg/main/arrow-right.svg b/wp-content/themes/cosmopet/static/shop/img/svg/main/arrow-right.svg new file mode 100644 index 0000000..8952470 --- /dev/null +++ b/wp-content/themes/cosmopet/static/shop/img/svg/main/arrow-right.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/wp-content/themes/cosmopet/static/shop/img/svg/main/black-x.svg b/wp-content/themes/cosmopet/static/shop/img/svg/main/black-x.svg new file mode 100644 index 0000000..cb3041d --- /dev/null +++ b/wp-content/themes/cosmopet/static/shop/img/svg/main/black-x.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/wp-content/themes/cosmopet/templates/shop/single-product.php b/wp-content/themes/cosmopet/templates/shop/single-product.php new file mode 100644 index 0000000..59884cb --- /dev/null +++ b/wp-content/themes/cosmopet/templates/shop/single-product.php @@ -0,0 +1,63 @@ + '', + 'wrap_before' => '', + 'wrap_after' => '', + 'before' => '', + 'after' => '', + 'home' => _x('Home', 'breadcrumb', 'woocommerce'), + ); + + $breadcrumbs = new WC_Breadcrumb(); + $breadcrumbs->generate(); + + $formatted_breadcrumbs = array(); + foreach ($breadcrumbs->get_breadcrumb() as $crumb) { + $formatted_breadcrumbs[] = array( + 'text' => $crumb[0], + 'url' => $crumb[1] + ); + } + + $context['wc_breadcrumbs'] = $formatted_breadcrumbs; + } + + + $product_id = get_the_ID(); + $product = wc_get_product($product_id); + + $context['product'] = $product; + + $context['related_products'] = array(); + $related_products_ids = wc_get_related_products($product_id, 5); + + if ($related_products_ids) { + foreach ($related_products_ids as $related_id) { + $related_product = wc_get_product($related_id); + if ($related_product) { + $context['related_products'][] = $related_product; + } + } + } + + Timber::render('modules/shop/components/single-product/component-template.twig', $context); +} \ No newline at end of file diff --git a/wp-content/themes/cosmopet/woocommerce/archive-product/archive-product-tease.twig b/wp-content/themes/cosmopet/woocommerce/archive-product/archive-product-tease.twig index 4a39255..306fa6e 100644 --- a/wp-content/themes/cosmopet/woocommerce/archive-product/archive-product-tease.twig +++ b/wp-content/themes/cosmopet/woocommerce/archive-product/archive-product-tease.twig @@ -1,115 +1,117 @@ -{% set cur_product = fn('wc_get_product', product.id) %} -{% set attrs = post.product.get_attributes() %} -{% set cur_weight = function('get_product_info', post.id, 'weight') %} +{% if post.id is defined and post.id %} + {% set cur_product = fn('wc_get_product', post.id) %} + {% set attrs = post.product.get_attributes() %} + {% set cur_weight = function('get_product_info', post.id, 'weight') %} -
-
- {% if post.date('Y-m-d') >= criteria_for_new_product %} - - {{ function('pll_e', 'Новинка') }} - - {% endif %} +
+
+ {% if post.date('Y-m-d') >= criteria_for_new_product %} + + {{ function('pll_e', 'Новинка') }} + + {% endif %} - {% if post._sale_price %} - - {{ function('pll_e', 'Распродажа %') }} - - {% endif %} -
- - {{ post.title }} - -
-
- {% set compound = fn('wc_get_product_terms', post.id, 'pa_compound') %} - {% for option in compound %} - - {% set term = get_term(option) %} - {{ term.name }} - {% endfor %} -
- {{ post.title }} -
-

{{ post._price() }}

+ {% if post._sale_price %} + + {{ function('pll_e', 'Распродажа %') }} + + {% endif %}
-
- -
-
-
-
- {{ post.title }} -
    - {% set features = fn('wc_get_product_terms', post.id, 'pa_features') %} - {% for option in features %} + + {{ post.title }} + +
    +
    + {% set compound = fn('wc_get_product_terms', post.id, 'pa_compound') %} + {% for option in compound %} + {% set term = get_term(option) %} -
  • {{ term.name }}
  • + {{ term.name }} {% endfor %} -
+
+ {{ post.title }} +
+

{{ post._price() }}

+
+
+ +
- -
-
-
-

Объем

+
+
+ {{ post.title }} +
    + {% set features = fn('wc_get_product_terms', post.id, 'pa_features') %} + {% for option in features %} + {% set term = get_term(option) %} +
  • {{ term.name }}
  • + {% endfor %} +
+
+ + +
+
+

Объем

-
- -
-
    - {% set collection = fn('wc_get_product_terms', post.id, 'pa_collection') %} - {% for option in collection %} - {% set term = get_term(option) %} - {% set siblings = function('get_collection_siblings' , term.id) %} +
    + +
    +
      + {% set collection = fn('wc_get_product_terms', post.id, 'pa_collection') %} + {% for option in collection %} + {% set term = get_term(option) %} + {% set siblings = function('get_collection_siblings' , term.id) %} - {% for sibling in siblings %} + {% for sibling in siblings %} - {% set weight = function('get_product_info', sibling.ID, 'weight') %} - - {% set class = '' %} - {% if weight == cur_weight %} - {% set class = 'active' %} - {% endif %} -
    • - -
    • + {% set weight = function('get_product_info', sibling.ID, 'weight') %} + + {% set class = '' %} + {% if weight == cur_weight %} + {% set class = 'active' %} + {% endif %} +
    • + +
    • + {% endfor %} {% endfor %} - {% endfor %} -
    +
+
-
-
-

Количество

+
+

Количество

-
- - - +
+ + + +
-
-

- {{ post._price() }} -

-
-
- {{ function('get_add_to_cart_button', post.id) }} -
-
- -

{{ function('pll_e', 'Подробнее') }}

-
+

+ {{ post._price() }} +

+
+
+ {{ function('get_add_to_cart_button', post.id) }} +
+
-
- + +
-
\ No newline at end of file +{% endif %} \ No newline at end of file diff --git a/wp-content/themes/cosmopet/woocommerce/single-product.php b/wp-content/themes/cosmopet/woocommerce/single-product.php new file mode 100644 index 0000000..16272d7 --- /dev/null +++ b/wp-content/themes/cosmopet/woocommerce/single-product.php @@ -0,0 +1,19 @@ +