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 %}
+
+
+ {% if wc_breadcrumbs %}
+ {% for crumb in wc_breadcrumbs %}
+
+ {{ crumb.text }}
+
+ {% endfor %}
+ {% endif %}
+
+
+
+ {{ function('pll_e', 'к каталогу') }}
+
+
+
+
+ {% for image in gallery_images %}
+
+

+
+ {% 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 %}
+
+
+

+
+
+ {% 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 %}
+
+
+
+
+
+
+ {{ 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 %}
+
+
+
+
+ {% 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 %}
+
+
+
+
+
+
+ {% 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() }}
+
+
+
+
+
+
+
+ {% endif %}
+
+ {% endfor %}
+
+
+
+ {% endif %}
+
+
+
+
+
+
+
+
+
+
+ {% for image in gallery_images %}
+
+
+

+
+
+ {% endfor %}
+
+
+
+
+
+
+
+
+
+
+
+ {% 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 %}
-
-
-
-
-
-
- {% 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 %}
-
-
-
-
-
-