Task 7719 | натяжка верстки single товара.

web_10
parent 7499a8aa24
commit 8e354cb729
  1. 6
      wp-content/themes/cosmopet/functions.php
  2. 267
      wp-content/themes/cosmopet/modules/shop/CosmopetProduct.php
  3. 173
      wp-content/themes/cosmopet/modules/shop/components/cart/assets/js/cart.js
  4. 12
      wp-content/themes/cosmopet/modules/shop/components/catalog/assets/js/gp-main.js
  5. 2
      wp-content/themes/cosmopet/modules/shop/components/catalog/component-controller.php
  6. 69
      wp-content/themes/cosmopet/modules/shop/components/product-card/assets/css/product-card.css
  7. 4
      wp-content/themes/cosmopet/modules/shop/components/product-card/assets/js/product-card.js
  8. 143
      wp-content/themes/cosmopet/modules/shop/components/product-single--new/assets/css/product-style.css
  9. 0
      wp-content/themes/cosmopet/modules/shop/components/product-single--new/assets/img/avg.svg
  10. 0
      wp-content/themes/cosmopet/modules/shop/components/product-single--new/assets/img/beef.svg
  11. 0
      wp-content/themes/cosmopet/modules/shop/components/product-single--new/assets/img/dog-face.svg
  12. 0
      wp-content/themes/cosmopet/modules/shop/components/product-single--new/assets/img/fish.svg
  13. 0
      wp-content/themes/cosmopet/modules/shop/components/product-single--new/assets/img/lamb.svg
  14. 0
      wp-content/themes/cosmopet/modules/shop/components/product-single--new/assets/img/lg.svg
  15. 0
      wp-content/themes/cosmopet/modules/shop/components/product-single--new/assets/img/mini.svg
  16. 0
      wp-content/themes/cosmopet/modules/shop/components/product-single--new/assets/img/pet/cat.png
  17. 0
      wp-content/themes/cosmopet/modules/shop/components/product-single--new/assets/img/pet/cat.png.webp
  18. 0
      wp-content/themes/cosmopet/modules/shop/components/product-single--new/assets/img/pet/dog.png
  19. 0
      wp-content/themes/cosmopet/modules/shop/components/product-single--new/assets/img/pet/dog.png.webp
  20. 0
      wp-content/themes/cosmopet/modules/shop/components/product-single--new/assets/img/pet/mini-dog.png
  21. 0
      wp-content/themes/cosmopet/modules/shop/components/product-single--new/assets/img/pet/mini-dog.png.webp
  22. 0
      wp-content/themes/cosmopet/modules/shop/components/product-single--new/assets/img/svg/main/arrow-back.svg
  23. 0
      wp-content/themes/cosmopet/modules/shop/components/product-single--new/assets/img/svg/main/arrow-breadcrumbs.svg
  24. 0
      wp-content/themes/cosmopet/modules/shop/components/product-single--new/assets/img/svg/main/arrow-left.svg
  25. 0
      wp-content/themes/cosmopet/modules/shop/components/product-single--new/assets/img/svg/main/arrow-right.svg
  26. 0
      wp-content/themes/cosmopet/modules/shop/components/product-single--new/assets/img/svg/main/arrow-selected.svg
  27. 0
      wp-content/themes/cosmopet/modules/shop/components/product-single--new/assets/img/svg/main/black-x.svg
  28. 0
      wp-content/themes/cosmopet/modules/shop/components/product-single--new/assets/img/turkey.svg
  29. 0
      wp-content/themes/cosmopet/modules/shop/components/product-single--new/assets/js/cart-controls.js
  30. 1
      wp-content/themes/cosmopet/modules/shop/components/product-single--new/assets/js/product-single.js
  31. 20
      wp-content/themes/cosmopet/modules/shop/components/product-single--new/component-controller.php
  32. 49
      wp-content/themes/cosmopet/modules/shop/components/product-single/assets/css/cart-loading.css
  33. 0
      wp-content/themes/cosmopet/modules/shop/components/product-single/assets/css/gp-style-core.css
  34. 0
      wp-content/themes/cosmopet/modules/shop/components/product-single/assets/css/gp-style-desktop.css
  35. 0
      wp-content/themes/cosmopet/modules/shop/components/product-single/assets/css/gp-style-mobile.css
  36. 0
      wp-content/themes/cosmopet/modules/shop/components/product-single/assets/css/gp-style-order.css
  37. 0
      wp-content/themes/cosmopet/modules/shop/components/product-single/assets/css/gp-style-tablet.css
  38. 0
      wp-content/themes/cosmopet/modules/shop/components/product-single/assets/css/gp-style-ultra.css
  39. 0
      wp-content/themes/cosmopet/modules/shop/components/product-single/assets/img/pet/cat.png
  40. 0
      wp-content/themes/cosmopet/modules/shop/components/product-single/assets/img/pet/cat.png.webp
  41. 0
      wp-content/themes/cosmopet/modules/shop/components/product-single/assets/img/pet/dog.png
  42. 0
      wp-content/themes/cosmopet/modules/shop/components/product-single/assets/img/pet/dog.png.webp
  43. 0
      wp-content/themes/cosmopet/modules/shop/components/product-single/assets/img/pet/mini-dog.png
  44. 0
      wp-content/themes/cosmopet/modules/shop/components/product-single/assets/img/pet/mini-dog.png.webp
  45. 0
      wp-content/themes/cosmopet/modules/shop/components/product-single/assets/img/svg/main/arrow-back.svg
  46. 0
      wp-content/themes/cosmopet/modules/shop/components/product-single/assets/img/svg/main/arrow-breadcrumbs.svg
  47. 0
      wp-content/themes/cosmopet/modules/shop/components/product-single/assets/img/svg/main/arrow-left.svg
  48. 0
      wp-content/themes/cosmopet/modules/shop/components/product-single/assets/img/svg/main/arrow-right.svg
  49. 0
      wp-content/themes/cosmopet/modules/shop/components/product-single/assets/img/svg/main/arrow-selected.svg
  50. 0
      wp-content/themes/cosmopet/modules/shop/components/product-single/assets/img/svg/main/black-x.svg
  51. 0
      wp-content/themes/cosmopet/modules/shop/components/product-single/assets/js/gp-form.js
  52. 0
      wp-content/themes/cosmopet/modules/shop/components/product-single/assets/js/gp-main.js
  53. 0
      wp-content/themes/cosmopet/modules/shop/components/product-single/assets/js/gp-product.js
  54. 0
      wp-content/themes/cosmopet/modules/shop/components/product-single/assets/js/tabs.js
  55. 0
      wp-content/themes/cosmopet/modules/shop/components/product-single/assets/js/toggle.js
  56. 156
      wp-content/themes/cosmopet/modules/shop/components/product-single/component-controller.php
  57. 152
      wp-content/themes/cosmopet/modules/shop/components/single-product/component-controller.php
  58. 13
      wp-content/themes/cosmopet/modules/shop/components/single-product_new/component-controller.php
  59. 58
      wp-content/themes/cosmopet/modules/shop/module-ajax-controller.php
  60. 1
      wp-content/themes/cosmopet/modules/shop/module-controller.php
  61. 6
      wp-content/themes/cosmopet/page.php
  62. 6
      wp-content/themes/cosmopet/single-blog_author.php
  63. 6
      wp-content/themes/cosmopet/single-post.php
  64. 22
      wp-content/themes/cosmopet/single-product.php
  65. 26
      wp-content/themes/cosmopet/single.php
  66. 35
      wp-content/themes/cosmopet/temp-functions/timber-logic.php
  67. 6
      wp-content/themes/cosmopet/views/_blocks/shop/archive-product-ajaxload.twig
  68. 4
      wp-content/themes/cosmopet/views/_pages/shop/product-single.twig
  69. 26
      wp-content/themes/cosmopet/views/blocks/modules/shop/product-single-add_to_cart.twig
  70. 0
      wp-content/themes/cosmopet/views/shop/single-product_page.twig
  71. 10
      wp-content/themes/cosmopet/views/woocommerce/archive-product.twig
  72. 73
      wp-content/themes/cosmopet/views/woocommerce/single-product.twig
  73. 52
      wp-content/themes/cosmopet/woocommerce.php
  74. 5
      wp-content/themes/cosmopet/woocommerce/archive-product.php

@ -31,3 +31,9 @@ add_filter('timber/post/classmap', function ($classmap) {
return $classmap;
}, 100);
function allow_svg_upload($mimes) {
$mimes['svg'] = 'image/svg+xml';
return $mimes;
}
add_filter('upload_mimes', 'allow_svg_upload');

@ -7,10 +7,11 @@ use Timber\Integrations\WooCommerce\Product as TimberProduct;
class CosmopetProduct extends TimberProduct {
protected $sibling_categories = [
'pa_age-of-the-cat',
'pa_age-of-the-dog',
'pa_compound',
'pa_dog-size',
'pa_reproductive-status',
'pa_age-of-the-cat',
'pa_age-of-the-dog',
'pa_series',
];
@ -269,48 +270,49 @@ class CosmopetProduct extends TimberProduct {
}
}
// Отладочная информация
error_log("=== Конструктор товара ===");
error_log("Текущий товар ID: " . $this->id);
error_log("Текущий вес (числовой): " . $current_weight);
error_log("Всего товаров в коллекции: " . count($all_products));
error_log("Атрибуты текущего товара: " . print_r($current_product_attributes, true));
// Группируем товары по атрибутам
foreach ($this->sibling_categories as $taxonomy) {
// Проверяем, есть ли у текущего товара значение для этой категории
if (!isset($current_product_attributes[$taxonomy])) {
error_log("Пропускаем $taxonomy - нет значения у текущего товара");
continue; // Пропускаем, если у товара нет значения для этой категории
}
// Получаем упорядоченные таксономии
$ordered_taxonomies = $this->getOrderedTaxonomies();
// Инициализируем конструктор для всех таксономий
foreach ($this->sibling_categories as $taxonomy) {
if (isset($current_product_attributes[$taxonomy])) {
$constructor[$taxonomy] = [
'taxonomy' => $taxonomy,
'label' => $this->getAttributeLabel($taxonomy),
'current_value' => $this->getCurrentAttributeValue($taxonomy),
'options' => []
];
}
}
error_log("Обрабатываем атрибут: $taxonomy");
error_log("Атрибуты текущего товара для проверки: " . print_r($current_product_attributes, true));
// ОБХОДИМ МАССИВ ТОВАРОВ ТОЛЬКО ОДИН РАЗ
foreach ($all_products as $product) {
// Получаем атрибуты сравниваемого товара
// Получаем все атрибуты сравниваемого товара за один раз
$compare_attributes = [];
foreach ($this->sibling_categories as $compare_taxonomy) {
$terms = get_the_terms($product->id, $compare_taxonomy);
$compare_weight = $product->getNumericWeight();
foreach ($this->sibling_categories as $taxonomy) {
$terms = get_the_terms($product->id, $taxonomy);
if ($terms && !is_wp_error($terms)) {
$compare_attributes[$compare_taxonomy] = array_map(function($term) {
$compare_attributes[$taxonomy] = array_map(function($term) {
return $term->term_id;
}, $terms);
}
}
// Проверяем, подходит ли товар для каждой таксономии
foreach ($this->sibling_categories as $taxonomy) {
// Пропускаем, если у текущего товара нет значения для этой категории
if (!isset($current_product_attributes[$taxonomy])) {
continue;
}
// Проверяем, совпадают ли все остальные атрибуты и вес
$attributes_match = true;
$differences = [];
// Проверяем только атрибуты из sibling_categories
// Проверяем все атрибуты кроме текущей таксономии
foreach ($this->sibling_categories as $compare_taxonomy) {
if ($compare_taxonomy === $taxonomy) {
continue; // Пропускаем проверяемую категорию
@ -321,7 +323,6 @@ class CosmopetProduct extends TimberProduct {
// Проверяем, есть ли у сравниваемого товара значение для этой категории
if (!isset($compare_attributes[$compare_taxonomy])) {
$attributes_match = false;
$differences[] = "Нет атрибута $compare_taxonomy у сравниваемого товара";
break;
}
@ -332,7 +333,6 @@ class CosmopetProduct extends TimberProduct {
if (array_diff($current_values, $compare_values) !== [] ||
array_diff($compare_values, $current_values) !== []) {
$attributes_match = false;
$differences[] = "Не совпадают значения для $compare_taxonomy";
break;
}
}
@ -341,22 +341,18 @@ class CosmopetProduct extends TimberProduct {
else {
if (isset($compare_attributes[$compare_taxonomy])) {
$attributes_match = false;
$differences[] = "У сравниваемого товара есть атрибут $compare_taxonomy, а у текущего нет";
break;
}
}
}
// Проверяем вес (числовое сравнение)
$compare_weight = $product->getNumericWeight();
if (abs($current_weight - $compare_weight) > 0.01) { // Используем небольшой допуск для float
if (abs($current_weight - $compare_weight) > 0.01) {
$attributes_match = false;
$differences[] = "Не совпадает вес: текущий '$current_weight' vs сравниваемый '$compare_weight'";
}
// Если все атрибуты и вес совпадают, добавляем товар в группу
if ($attributes_match) {
error_log("Товар {$product->id} подходит для атрибута $taxonomy");
$product_terms = get_the_terms($product->id, $taxonomy);
if ($product_terms && !is_wp_error($product_terms)) {
foreach ($product_terms as $term) {
@ -364,41 +360,53 @@ class CosmopetProduct extends TimberProduct {
if (!isset($constructor[$taxonomy]['options'][$option_key])) {
$constructor[$taxonomy]['options'][$option_key] = [
'term_id' => $term->term_id,
'name' => $term->name,
'name' => $this->getTermName($term->term_id, $taxonomy),
'slug' => $term->slug,
'icon' => $this->getTermIcon($term->term_id, $taxonomy),
'products' => []
];
}
$constructor[$taxonomy]['options'][$option_key]['products'][] = $product;
}
}
} else {
error_log("Товар {$product->id} НЕ подходит для атрибута $taxonomy. Причины: " . implode(', ', $differences));
}
}
}
error_log("Найдено опций для $taxonomy: " . count($constructor[$taxonomy]['options']));
// Сортируем опции по порядку терминов для каждой таксономии
foreach ($constructor as $taxonomy => $taxonomy_data) {
if (isset($ordered_taxonomies[$taxonomy])) {
$ordered_options = [];
foreach ($ordered_taxonomies[$taxonomy] as $term) {
if (isset($constructor[$taxonomy]['options'][$term->term_id])) {
$ordered_options[$term->term_id] = $constructor[$taxonomy]['options'][$term->term_id];
}
}
$constructor[$taxonomy]['options'] = $ordered_options;
}
// Показываем детали найденных опций
foreach ($constructor[$taxonomy]['options'] as $option_id => $option) {
error_log("Опция $option_id ({$option['name']}): " . count($option['products']) . " товаров");
foreach ($option['products'] as $product) {
error_log(" - Товар {$product->id}: {$product->title}");
}
// Сортируем итоговый конструктор по порядку таксономий
$ordered_constructor = [];
foreach ($this->sibling_categories as $taxonomy) {
if (isset($constructor[$taxonomy])) {
$ordered_constructor[$taxonomy] = $constructor[$taxonomy];
}
}
error_log("Итоговый конструктор: " . print_r($constructor, true));
return $constructor;
return $ordered_constructor;
}
protected function getAttributeLabel($taxonomy) {
// Fallback на старые метки
$labels = [
'pa_age-of-the-cat' => 'ВОЗРАСТ КОШКИ',
'pa_age-of-the-dog' => 'ВОЗРАСТ СОБАКИ',
'pa_compound' => 'ВКУС КОРМА',
'pa_reproductive-status' => 'РЕПРОДУКТИВНЫЙ СТАТУС',
'pa_series' => 'СЕРИЯ',
'pa_age-of-the-cat' => pll__('ВОЗРАСТ КОШКИ'),
'pa_age-of-the-dog' => pll__('ВОЗРАСТ СОБАКИ'),
'pa_dog-size' => pll__('РАЗМЕР СОБАКИ'),
'pa_compound' => pll__('ВКУС КОРМА'),
'pa_reproductive-status' => pll__('ВИД КОШКИ'),
'pa_series' => pll__('СЕРИЯ'),
];
return $labels[$taxonomy] ?? wc_attribute_label($taxonomy);
@ -412,6 +420,171 @@ class CosmopetProduct extends TimberProduct {
return null;
}
/**
* Получает таксономии в правильном порядке с учетом сортировки
* @return array Массив таксономий с их порядком
*/
protected function getOrderedTaxonomies() {
$ordered_taxonomies = [];
foreach ($this->sibling_categories as $taxonomy) {
// Получаем все термины для данной таксономии
$terms = get_terms([
'taxonomy' => $taxonomy,
'hide_empty' => false,
'orderby' => 'menu_order',
'order' => 'ASC'
]);
// Если menu_order не работает, пробуем сортировку по name
if (is_wp_error($terms) || empty($terms)) {
$terms = get_terms([
'taxonomy' => $taxonomy,
'hide_empty' => false,
'orderby' => 'name',
'order' => 'ASC'
]);
}
if (!is_wp_error($terms) && !empty($terms)) {
$ordered_taxonomies[$taxonomy] = $terms;
}
}
return $ordered_taxonomies;
}
/**
* Получает таксономии в правильном порядке для отображения
* @return array Массив таксономий в порядке отображения
*/
public function getOrderedSiblingCategories() {
return $this->sibling_categories;
}
/**
* Получает таксономии с их метками в правильном порядке
* @return array Массив таксономий с метками
*/
public function getOrderedTaxonomiesWithLabels() {
$taxonomies_with_labels = [];
foreach ($this->sibling_categories as $taxonomy) {
$label = $this->getAttributeLabel($taxonomy);
$taxonomies_with_labels[$taxonomy] = $label;
}
return $taxonomies_with_labels;
}
/**
* Получает иконку термина из ACF поля
* @param int $term_id ID термина
* @param string $taxonomy Таксономия
* @return string|false URL иконки или false если иконка не найдена
*/
public function getTermIcon($term_id, $taxonomy) {
// Проверяем, есть ли ACF поле 'icon' у термина
$icon = get_field('icon', $taxonomy . '_' . $term_id);
if ($icon) {
return $icon;
}
// Если ACF поле не найдено, возвращаем false
return false;
}
/**
* Получает название термина с приоритетом описания
* @param int $term_id ID термина
* @param string $taxonomy Таксономия
* @return string Название термина
*/
public function getTermName($term_id, $taxonomy) {
$term = get_term($term_id, $taxonomy);
if (!$term || is_wp_error($term)) {
return '';
}
// Сначала пытаемся получить описание термина
$description = get_term_meta($term_id, 'description', true);
// Также пробуем получить из ACF поля
$acf_description = get_field('description', $taxonomy . '_' . $term_id);
// Пробуем получить из стандартного поля термина
$term_description = $term->description;
// Пробуем ACF поле сначала
if (!empty($acf_description)) {
return $acf_description;
}
// Потом стандартное поле термина
if (!empty($term_description)) {
return $term_description;
}
// Потом обычное мета-поле
if (!empty($description)) {
return $description;
}
return $term->name;
}
/**
* Получает количество товара в корзине
* @return int Количество товара в корзине
*/
public function getCartQuantity() {
if (!function_exists('WC') || !WC()->cart) {
return 0;
}
$cart = WC()->cart;
$cart_item_key = $this->getCartItemKey();
if ($cart_item_key) {
$cart_item = $cart->get_cart_item($cart_item_key);
return $cart_item ? $cart_item['quantity'] : 0;
}
return 0;
}
/**
* Получает ключ товара в корзине
* @return string|false Ключ товара в корзине или false если товар не найден
*/
public function getCartItemKey() {
if (!function_exists('WC') || !WC()->cart) {
return false;
}
$cart = WC()->cart;
foreach ($cart->get_cart() as $cart_item_key => $cart_item) {
if ($cart_item['product_id'] == $this->id) {
return $cart_item_key;
}
}
return false;
}
/**
* Проверяет, есть ли товар в корзине
* @return bool True если товар в корзине, false если нет
*/
public function isInCart() {
return $this->getCartQuantity() > 0;
}
public function getAllAttributes() {
$attributes = [];
foreach ($this->sibling_categories as $taxonomy) {

@ -1,4 +1,26 @@
jQuery(document).ready(function($) {
$(document).on('click', '.product-incart__btn--plus', function(e) {
e.preventDefault();
const key = $(this).data('key');
const quantity = parseInt($(this).siblings('.product-incart__count').text()) + 1;
updateCart(key, quantity);
$('.product-incart__wrap').addClass('loading');
});
// // Уменьшение количества
$(document).on('click', '.product-incart__btn--minus', function(e) {
e.preventDefault();
const key = $(this).data('key');
const quantity = parseInt($(this).siblings('.product-incart__count').text()) - 1;
$('.product-incart__wrap').addClass('loading');
if (quantity <= 0) {
removeItem(key); // Вызываем удаление, если количество становится 0
} else {
updateCart(key, quantity); // Обновляем количество, если больше 0
}
});
// Открытие/закрытие модалки
// $(document).on('click', '.cart-contents, .continue-shopping, .modal__close', function(e) {
// e.preventDefault();
@ -19,7 +41,7 @@ jQuery(document).ready(function($) {
});
$(document).on('click', '.modal__basket .counter__button.plus', function(e) {
$(document).on('click', '.modal__basket .counter__button.plus, .product-incart__btn--plus', function(e) {
e.preventDefault();
const key = $(this).data('key');
const input = $(this).siblings('.counter__input');
@ -28,7 +50,7 @@ jQuery(document).ready(function($) {
});
// // Уменьшение количества
$(document).on('click', '.modal__basket .counter__button.minus', function(e) {
$(document).on('click', '.modal__basket .counter__button.minus, .product-incart__btn--minus', function(e) {
e.preventDefault();
const key = $(this).data('key');
const input = $(this).siblings('.counter__input');
@ -144,6 +166,9 @@ jQuery(document).ready(function($) {
updateCartFragment();
updateNotices();
// Обновляем блок покупки на single странице
updateProductBuyBlock();
// $('[data-key="' + key + '"]').remove()
}
}
@ -182,6 +207,9 @@ jQuery(document).ready(function($) {
updateCartFragment();
updateNotices();
// Обновляем блок покупки на single странице
updateProductBuyBlock();
}
}
});
@ -241,10 +269,13 @@ jQuery(document).ready(function($) {
}
// Обновление корзины при добавлении товара
$(document.body).on('added_to_cart', function() {
$(document.body).on('added_to_cart', function(e, fragments, cart_hash, $button) {
updateCartFragment();
updateNotices();
openBasketOnFirstAdd();
// Обновляем блок покупки на single странице
updateProductBuyBlock();
});
// Функция обновления количества
@ -262,11 +293,16 @@ jQuery(document).ready(function($) {
},
complete: function() {
$('#modal-basket').removeClass('loading');
$('.product-incart__wrap').removeClass('loading');
},
success: function(response) {
if (response.success) {
updateCartFragment(update_full);
updateNotices();
// Обновляем блок покупки на single странице
updateProductBuyBlock();
} else {
console.error('Ошибка при обновлении корзины');
}
@ -398,6 +434,9 @@ jQuery(document).ready(function($) {
updateCartFragment();
updateNotices();
openBasketOnFirstAdd();
// Обновляем блок с кнопкой покупки на single странице
updateProductBuyBlock();
}
},
complete: function() {
@ -412,8 +451,136 @@ jQuery(document).ready(function($) {
});
// ===== ФУНКЦИИ ДЛЯ SINGLE СТРАНИЦЫ ===
// Обновление количества через input на single странице
$(document).on('change', '.product-incart__count', function () {
const $count = $(this);
const $wrap = $count.closest('.product-incart__wrap');
const key = $count.data('key');
const quantity = parseInt($count.text(), 10);
if (quantity > 0) {
if (key) {
updateCart(key, quantity);
}
} else {
if (key) {
removeItem(key);
}
}
});
// Увеличение количества на single странице
$(document).on('click', '.product-incart__btn--plus', function(e) {
e.preventDefault();
const $btn = $(this);
const $wrap = $btn.closest('.product-incart__wrap');
const $count = $wrap.find('.product-incart__count');
const key = $btn.data('key');
const currentQuantity = parseInt($count.text());
const newQuantity = currentQuantity + 1;
// Показываем загрузку
$wrap.addClass('loading');
// Обновляем количество через updateCart
if (key) {
updateCart(key, newQuantity);
}
});
// Уменьшение количества на single странице
$(document).on('click', '.product-incart__btn--minus', function(e) {
e.preventDefault();
const $btn = $(this);
const $wrap = $btn.closest('.product-incart__wrap');
const $count = $wrap.find('.product-incart__count');
const key = $btn.data('key');
const currentQuantity = parseInt($count.text());
const newQuantity = currentQuantity - 1;
if (newQuantity <= 0) {
// Показываем загрузку
$wrap.addClass('loading');
// Удаляем товар из корзины
if (key) {
removeItem(key);
}
} else {
// Показываем загрузку
$wrap.addClass('loading');
// Обновляем количество через updateCart
if (key) {
updateCart(key, newQuantity);
}
}
});
// Функция обновления блока с кнопкой покупки
function updateProductBuyBlock() {
console.log('Updating product buy block');
// Проверяем, что мы на странице нового шаблона (не старого)
if ($('.detail').length > 0) {
console.log('Old template detected, skipping buy block update');
return;
}
const $container = $('#product-buy-block-wrapper');
if (!$container.length) {
console.log('Product buy block wrapper not found');
return;
}
// Получаем product_id из текущей страницы
const productId = $container.find('[name="add-to-cart"]').val() ||
$container.find('.product-incart__wrap').data('product-id') ||
$('.product-single').data('product-id');
if (!productId) {
console.log('Product ID not found');
return;
}
console.log('Product ID:', productId);
$.ajax({
url: woocommerce_params.ajax_url,
type: 'POST',
data: {
action: 'update_product_buy_block',
product_id: productId
},
success: function(response) {
console.log('Update product buy block response:', response);
if (response.success) {
// Обновляем содержимое контейнера
$container.html(response.data.html);
console.log('HTML updated successfully');
// Если товар добавлен в корзину, обновляем фрагменты корзины
if (response.data.in_cart) {
updateCartFragment();
}
console.log('Product buy block updated successfully');
} else {
console.error('Ошибка обновления блока покупки:', response);
}
},
error: function(xhr, status, error) {
console.error('AJAX ошибка обновления блока покупки:', error);
console.error('Response:', xhr.responseText);
}
});
}
});

@ -107,6 +107,18 @@ jQuery(document).ready(function ($) {
$('#load-more-products').detach();
$('.product__main').append(data);
$('#load-more-products').prependTo('.product__footer');
var productSwiper = new Swiper('.main-food_products-card-slider', {
effect: 'fade',
fadeEffect: { crossFade: true },
loop: true,
pagination: {
el: '.swiper-dots',
clickable: true,
bulletClass: 'swiper-pagination-bullet',
bulletActiveClass: 'swiper-pagination-bullet-active',
},
});
},
error: function(data) {
console.log(data);

@ -137,5 +137,5 @@ $context['posts'] = Timber::get_posts($args);
$context['count'] = count(Timber::get_posts($count_args));
$context['sidebar_filters'] = Timber::get_widgets('sidebar_filters');
Timber::render('_pages/shop/archive-product.twig', $context);
Timber::render('woocommerce/archive-product.twig', $context);
?>

@ -277,3 +277,72 @@ background: radial-gradient(278.91% 196.13% at 128.36% -48.29%, #ee6868 0%, #569
width: 100%;
max-width: 350px;
}
.product-archive-card{
width: calc(25% - 7.5px);
}
@media (max-width: 1200px) {
.product-archive-card{
width: calc(33.333% - 6.666px);
}
}
@media (max-width: 640px) {
.product-archive-card{
width: calc(50% - 5px);
}
}
.product__main{
gap: 10px;
}
/* Стили для загрузки на карточках товаров */
.product-incart__wrap.loading {
position: relative;
pointer-events: none;
opacity: 0.7;
}
.product-incart__wrap.loading::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(255, 255, 255, 0.8);
z-index: 10;
}
.product-incart__wrap.loading::after {
content: '';
position: absolute;
top: 50%;
left: 50%;
width: 20px;
height: 20px;
margin: -10px 0 0 -10px;
border: 2px solid #f3f3f3;
border-top: 2px solid #3498db;
border-radius: 50%;
animation: spin 1s linear infinite;
z-index: 11;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
/* Стили для кнопок во время загрузки */
.product-incart__wrap.loading .product-incart__btn {
opacity: 0.5;
pointer-events: none;
}
/* Стили для счетчика во время загрузки */
.product-incart__wrap.loading .product-incart__count {
opacity: 0.5;
}

@ -13,7 +13,7 @@ jQuery(document).ready(function() {
},
});
jQuery('[data-image-set]').on('click', function() {
jQuery('body').on('click', '[data-image-set]', function() {
// Получаем родительский элемент карточки продукта
var productCard = jQuery(this).closest('.main-food_products-card');
jQuery(productCard).find('.main-food_products-card__weight-item').removeClass('active')
@ -55,3 +55,5 @@ jQuery(document).ready(function() {
jQuery(productCard).find('.main-food_products-cardbottom-btn').attr('href', '?add-to-cart=' + jQuery(this).data('product_id'))
});
});

@ -677,6 +677,7 @@ line-height: 143%;
.product-show{
display: flex;
flex-grow: 1;
flex-direction: column;
}
@media (max-width: 576px) {
.product_main {
@ -756,3 +757,145 @@ line-height: 143%;
.stock, .quantity{
display: none;
}
.product-incart__wrap{
display: flex;
align-items: center;
gap: 10px;
margin-top: 20px;
}
.product-incart__btn{
height: 50px;
width: 50px;
position: relative;
border-radius: 50%;
background-color: var(--accent-color);
}
.product-incart__btn--minus::before{
content: '';
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 16px;
height: 2px;
background-color: #fff;
}
.product-incart__btn--plus::before{
content: '';
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 16px;
height: 2px;
background-color: #fff;
}
.product-incart__btn--plus::after{
content: '';
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
height: 16px;
width: 2px;
background-color: #fff;
}
.product-incart{
display: flex;
align-items: center;
justify-content: space-between;
align-items: center;
padding: 23px 5px;
gap: 10px;
margin: 0 auto;
width: 159px;
height: 60px;
background: color-mix(in srgb, var(--accent-color) 25%, transparent);
border: 2px solid #FFFFFF;
border-radius: 100px;
flex: none;
order: 0;
flex-grow: 0;
font-weight: 900;
font-size: 12px;
color: #fff;
}
.product-incart__label{
border: 2px solid #fff;
border-radius: 100px;
padding: 12px 16px;
width: 100%;
height: 60px;
display: flex;
align-items: center;
justify-content: center;
font-weight: 900;
font-size: 12px;
text-align: center;
color: #fff;
background-color: var(--accent-color);
}
.product-incart-btn{
}
/* Стили для загрузки корзины на single странице */
.product-incart__wrap.loading {
position: relative;
pointer-events: none;
opacity: 0.7;
}
.product-incart__wrap.loading::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: var(--bg-color);
opacity: .7;
z-index: 10;
}
.product-incart__wrap.loading::after {
content: '';
position: absolute;
top: 50%;
left: 50%;
width: 20px;
height: 20px;
margin: -10px 0 0 -10px;
border: 2px solid #f3f3f3;
border-top: 2px solid #3498db;
border-radius: 50%;
animation: spin 1s linear infinite;
z-index: 11;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
/* Стили для кнопок во время загрузки */
.product-incart__wrap.loading .product-incart__btn {
opacity: 0.5;
pointer-events: none;
}
/* Стили для счетчика во время загрузки */
.product-incart__wrap.loading .product-incart__count {
opacity: 0.5;
}

@ -0,0 +1,20 @@
<?php
use Timber\Timber;
global $post;
$context = Timber::get_context();
// Получаем текущий продукт
$product_id = get_the_ID();
$product = wc_get_product($product_id);
// Добавляем продукт в контекст
$context['product'] = $product;
$context['post'] = Timber::get_post();
include_module('shop');
include_component('shop', 'reviews');
?>
<?php
Timber::render('woocommerce/single-product.twig', $context);

@ -0,0 +1,49 @@
/* Стили для загрузки корзины на single странице */
.product-incart__wrap.loading {
position: relative;
pointer-events: none;
opacity: 0.7;
}
.product-incart__wrap.loading::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(255, 255, 255, 0.8);
z-index: 10;
}
.product-incart__wrap.loading::after {
content: '';
position: absolute;
top: 50%;
left: 50%;
width: 20px;
height: 20px;
margin: -10px 0 0 -10px;
border: 2px solid #f3f3f3;
border-top: 2px solid #3498db;
border-radius: 50%;
animation: spin 1s linear infinite;
z-index: 11;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
/* Стили для кнопок во время загрузки */
.product-incart__wrap.loading .product-incart__btn {
opacity: 0.5;
pointer-events: none;
}
/* Стили для счетчика во время загрузки */
.product-incart__wrap.loading .product-incart__count {
opacity: 0.5;
}

@ -0,0 +1,156 @@
<?php
$product;
$product_type;
$context_for_twig;
// Получаем данные продукта
$product_id = get_the_ID();
$product = wc_get_product($product_id);
// Проверяем, что продукт существует
if (!$product) {
return; // Просто выходим, если продукт не найден
}
$attributes = [];
$product_attributes = $product->get_attributes();
/* Получение категорий */
$tags = get_the_terms($product_id, 'product_cat');
if (!empty($tags) && !is_wp_error($tags)) {
foreach ($tags as $tag) {
$context_for_twig['product_tags'][] = $tag->name;
}
}
/* Получение атрибутов */
if (!empty($product_attributes)) {
foreach ($product_attributes as $taxonomy => $attribute) {
if ($attribute->is_taxonomy()) {
$terms = wc_get_product_terms($product_id, $taxonomy, ['fields' => 'all']);
// $tags = wc_get_product_terms( $product_id, 'pa_catalog_tags' );
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_for_twig['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_for_twig['variations'] = $variations_data;
}
/* ACF-поля и мета-данные продукта */
$meta_fields = [
'composition' => get_post_meta($product_id, '_composition', true),
'feeding_recommendations' => get_post_meta($product_id, '_feeding_recommendations', true), // Ранее выводились HTML-полем «Рекомендации по кормлению»
'feeding_recommendations_table' => get_field('feeding_recommendations_table', get_post(get_field('p_tables_field', $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_for_twig['product_meta'] = $meta_fields;
/* Товар оформляется по подписке? */
$context_for_twig['is_subscription'] = $product->is_type( array( 'subscription', 'subscription_variation', 'variable-subscription' )) ? true : false;
$context = Timber::get_context();
$post = Timber::get_post();
$context['post'] = $post;
$context['wc_breadcrumbs'] = array();
if (function_exists('woocommerce_breadcrumb')) {
$args = array(
'delimiter' => '',
'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;
}
$context['product'] = $product;
$context['related_products'] = array();
$related_products_ids = $product->get_upsell_ids();
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;
}
}
}
if (class_exists('WCS_ATT_Product_Schemes')) {
$schemes = WCS_ATT_Product_Schemes::get_subscription_schemes($product);
$context['has_subscription_options'] = !empty($schemes);
} else {
$context['has_subscription_options'] = false;
}
Timber::render('_pages/shop/product-single.twig', $context);
// Подключение стилей для загрузки корзины
add_action('wp_enqueue_scripts', 'single_product_cart_styles');
function single_product_cart_styles() {
if (is_product()) {
wp_enqueue_style(
'single-product-cart-loading',
get_template_directory_uri() . '/modules/shop/components/single-product/assets/css/cart-loading.css',
array(),
'1.0.0'
);
}
}

@ -1,152 +0,0 @@
<?php
$product;
$product_type;
$context_for_twig;
if (function_exists('is_product') && is_product()) {
$product_id = get_the_ID();
$product = wc_get_product($product_id);
$attributes = [];
$product_attributes = $product->get_attributes();
/* Получение категорий */
$tags = get_the_terms($product_id, 'product_cat');
if (!empty($tags) && !is_wp_error($tags)) {
foreach ($tags as $tag) {
$context_for_twig['product_tags'][] = $tag->name;
}
}
/* Получение атрибутов */
if (!empty($product_attributes)) {
foreach ($product_attributes as $taxonomy => $attribute) {
if ($attribute->is_taxonomy()) {
$terms = wc_get_product_terms($product_id, $taxonomy, ['fields' => 'all']);
// $tags = wc_get_product_terms( $product_id, 'pa_catalog_tags' );
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_for_twig['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_for_twig['variations'] = $variations_data;
}
/* ACF-поля и мета-данные продукта */
$meta_fields = [
'composition' => get_post_meta($product_id, '_composition', true),
'feeding_recommendations' => get_post_meta($product_id, '_feeding_recommendations', true), // Ранее выводились HTML-полем «Рекомендации по кормлению»
'feeding_recommendations_table' => get_field('feeding_recommendations_table', get_post(get_field('p_tables_field', $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_for_twig['product_meta'] = $meta_fields;
/* Товар оформляется по подписке? */
$context_for_twig['is_subscription'] = $product->is_type( array( 'subscription', 'subscription_variation', 'variable-subscription' )) ? true : false;
}
add_filter('timber/context', function ($context) use ($context_for_twig) {
return array_merge($context, $context_for_twig);
});
$context = Timber::get_context();
$post = Timber::get_post();
$context['post'] = $post;
$context['wc_breadcrumbs'] = array();
if (function_exists('woocommerce_breadcrumb')) {
$args = array(
'delimiter' => '',
'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 = $product->get_upsell_ids();
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;
}
}
}
if (class_exists('WCS_ATT_Product_Schemes')) {
$schemes = WCS_ATT_Product_Schemes::get_subscription_schemes($product);
$context['has_subscription_options'] = !empty($schemes);
} else {
$context['has_subscription_options'] = false;
}
Timber::render('_pages/shop/product-single.twig', $context);

@ -1,13 +0,0 @@
<?php
use Timber\Timber;
global $post;
$context = Timber::get_context();
include_module('shop');
include_component('shop', 'reviews');
include_component('shop', 'product-card');
?>
<?php
Timber::render('shop/single-product.twig', $context);

@ -1,5 +1,8 @@
<?php
// Импортируем класс CosmopetProduct
require_once __DIR__ . '/CosmopetProduct.php';
/* Страница Checkout */
add_action( 'wp_ajax_apply_coupon', 'custom_apply_coupon' );
add_action( 'wp_ajax_nopriv_apply_coupon', 'custom_apply_coupon' );
@ -171,3 +174,58 @@ function woocommerce_ajax_add_to_cart() {
wp_die();
}
/**
* Обработчик AJAX для обновления блока с кнопкой покупки
*/
add_action('wp_ajax_update_product_buy_block', 'update_product_buy_block_handler');
add_action('wp_ajax_nopriv_update_product_buy_block', 'update_product_buy_block_handler');
function update_product_buy_block_handler() {
error_log('update_product_buy_block_handler called');
if (!isset($_POST['product_id'])) {
error_log('No product_id provided');
wp_send_json_error('Не указан ID товара');
return;
}
$product_id = intval($_POST['product_id']);
error_log('Product ID: ' . $product_id);
$product = Timber::get_post($product_id);
if (!$product) {
error_log('Product not found');
wp_send_json_error('Товар не найден');
return;
}
// Получаем количество товара в корзине через метод класса
$cart_quantity = $product->getCartQuantity();
error_log('Cart quantity: ' . $cart_quantity);
// Подготавливаем контекст для Twig
$context = Timber::context();
$context['post'] = $product;
$context['product'] = $product->product;
$context['cart_quantity'] = $cart_quantity;
$context['in_cart'] = $cart_quantity > 0;
if($cart_quantity > 0){
$html = Timber::compile('blocks/modules/shop/product-single-add_to_cart.twig', $context);
}
else{
$GLOBALS['product'] = $product->product;
ob_start();
do_action( 'woocommerce_' . $product->product->get_type() . '_add_to_cart' );
$html = ob_get_clean();
}
// Рендерим Twig шаблон
error_log('HTML length: ' . strlen($html));
wp_send_json_success(array(
'html' => $html,
'quantity' => $cart_quantity,
'in_cart' => $cart_quantity > 0
));
}

@ -386,6 +386,7 @@ function conditional_dequeue_woocommerce_styles() {
}
add_action('wp_enqueue_scripts', 'conditional_dequeue_woocommerce_styles', 999);
include_component('shop', 'product-card');
//Функция для получения названия класса по аттрибуту
function map_attr_slugs_to_class($slug) {

@ -7,6 +7,9 @@
/* TO_DO сюда перенести логику home.php */
$context = Timber::context();
$context['post'] = Timber::get_post();
if (is_account_page() && !is_wc_endpoint_url()){
include_module('profile');
$orders_pg = false;
@ -28,8 +31,5 @@ elseif (is_account_page() && isset($wp->query_vars['view-subscription'])){
}
else{
$context = Timber::context();
$context['post'] = Timber::get_post();
Timber::render('page.twig', $context);
}

@ -1,6 +0,0 @@
<?php
include_module('author');
include_component('author', 'author-single');
?>

@ -1,6 +0,0 @@
<?php
include_module('blog');
include_component('blog', 'single');
?>

@ -1,22 +0,0 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
if (is_product()) {
include_module('shop');
global $product;
// Get the product ID
$product_id = get_the_ID();
if ($product_id == 1105){
include_component('shop', 'single-product_new');
}
else{
// include_component('shop', 'single-product');
include_component('shop', 'single-product_new');
}
}

@ -0,0 +1,26 @@
<?php
/**
* The Template for displaying all single posts
*
* @link https://developer.wordpress.org/themes/basics/template-hierarchy/
*/
namespace App;
use Timber\Timber;
$context = Timber::context();
$post = $context['post'];
if ( is_singular('post') ) {
include_module('blog');
include_component('blog', 'single');
} elseif ( is_singular('blog_author') ) {
include_module('author');
include_component('author', 'author-single');
} else {
// Для других типов записей
$templates = [ 'views/single-' . get_post_type() . '.twig', 'views/single.twig' ];
}
Timber::render( $templates, $context );

@ -6,31 +6,37 @@ use Timber\Integrations\WooCommerce\Product as TimberProduct;
Timber::$dirname = [
[
'modules',
'views'
],
'views',
'modules'
];
// Настройка путей для WooCommerce шаблонов
add_filter('timber/loader/paths', function($paths) {
$paths[] = get_template_directory() . '/views/woocommerce';
return $paths;
});
add_filter( 'timber/integrations', function ( array $integrations ): array {
$integrations[] = new \Timber\Integrations\WooCommerce\WooCommerceIntegration();
return $integrations;
} );
add_action('timber/init', function() {
// Инициализируем WooCommerce интеграцию
if (class_exists('Timber\Integrations\WooCommerce\WooCommerce')) {
Timber\Integrations\WooCommerce\WooCommerce::init();
}
});
// Add the function to the Timber context
add_filter('timber/context', function($context) {
$context['template_path'] = get_template_directory_uri();
$current_url = "http://" . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
$context['current_url'] = htmlspecialchars($current_url);
// Добавляем WooCommerce контекст если WooCommerce активен
if (class_exists('WooCommerce')) {
// WooCommerce автоматически добавляет свой контекст через интеграцию
// Но мы можем его расширить
if (is_woocommerce()) {
$context['is_woocommerce'] = true;
}
}
return $context;
});
@ -38,6 +44,13 @@ add_filter('timber/context', function($context) {
add_action('init', function() {
add_filter('timber/context', function($context) {
$context['current_lang'] = pll_current_language();
// Добавляем функцию для получения иконки термина
$context['get_term_icon'] = function($term_id, $taxonomy) {
$product = new CosmopetProduct();
return $product->getTermIcon($term_id, $taxonomy);
};
return $context;
});
}, 1);

@ -1,5 +1,9 @@
{% for post in posts %}
{% include '_blocks/shop/archive-product-tease.twig' with {post: post} %}
{% set class = post.getTasteClass %}
{% set section = class ~ ' product-archive-card' %}
{% include 'shop/product-card.twig' with {_product : fn('wc_get_product', post.id), section: section} %}
{% endfor %}
{% if not ended %}

@ -19,7 +19,7 @@
{{ function('pll_e', 'к каталогу') }}
</a>
<div class="detail">
<div class="detail" data-product-id="{{ product.id }}">
<div class="detail__images">
{% for image in gallery_images %}
<div class="detail__image detail__image--width-perc-{% if loop.index == 1 or loop.index == 4 %}100{% else %}50{% endif %}" data-count-img="{{ loop.index0 }}">
@ -147,6 +147,7 @@
{% endif %}
</form>
{% set s_in_stock = TimberPost(product.id).meta('_stock_status') == 'instock' %}
<div id="product-buy-block-wrapper">
{% if s_in_stock %}
{{ function('do_action', 'woocommerce_' ~ product.get_type() ~ '_add_to_cart') }}
{% else %}
@ -158,6 +159,7 @@
</div>
{# End | Вывод кнопки узнать о поступлении если товара нет в наличии #}
{% endif %}
</div>
{# End | Кнопка добавить в корзину + варинанты подписки на товар #}
{# Start | Табы с информацией #}

@ -0,0 +1,26 @@
{% set in_stock = post.meta('_stock_status') == 'instock' %}
{% set cart_quantity = post.getCartQuantity %}
{% if in_stock %}
{% if post.getCartQuantity == 0 %}
{{ function('do_action', 'woocommerce_' ~ post.product.get_type() ~ '_add_to_cart') }}
{% else %}
<div class="product-incart__wrap" data-product-id="{{ post.id }}">
<div class="product-incart counter">
<button class="product-incart__btn product-incart__btn--minus" data-key="{{post.getCartItemKey}}" data-action="decrease"></button>
<div class="product-incart__count" data-key="{{post.getCartItemKey}}">{{ cart_quantity }}</div>
<button class="product-incart__btn product-incart__btn--plus" data-key="{{post.getCartItemKey}}" data-action="increase"></button>
</div>
<div class="product-incart__label">
{{fn('pll_e', 'В корзине')}}
</div>
</div>
{% endif %}
{% else %}
<div class="detail-block-form__item detail-block-form__item--tn">
<button type="button" data-pname="{{ product.title }}" class="to-know open-to-know" >
<p>{{ function('pll_e', 'Узнать о поступлении') }}</p>
</button>
</div>
{% endif %}

@ -55,7 +55,15 @@
{% if posts|length > 0 %}
{% for post in posts %}
{% include '_blocks/shop/archive-product-tease.twig' with {post: post} %}
{% set class = post.getTasteClass %}
{% set section = class ~ ' product-archive-card' %}
<!-- TO_DO (избавиться от wc_get_product) -->
{% include 'shop/product-card.twig' with {_product : fn('wc_get_product', post.id), section: section} %}
{% endfor %}
{% else %}
<div class="not-found">

@ -3,7 +3,7 @@
{% extends 'layout.twig' %}
{% block content %}
<div class="product-single">
<div class="product-single" data-product-id="{{ post.id }}">
<div class="wrapper">
<div class="breadcrumbs">
<a href="{{ fn('home_url') }}" class="breadcrumbs__item">
@ -29,11 +29,14 @@
<h2 class="product-block-title">{{ fn('pll_e', 'СОСТАВ') }}</h2>
<div class="product-contains-text">{{ post.getComposition }}</div>
</div>
{% set nutritional_value = post.nutritional_value %}
{% set vitamins = post.vitamins %}
{% if nutritional_value or vitamins %}
<div class="product-values">
{% if nutritional_value %}
<h2 class="product-values-title">{{ fn('pll_e', 'ПИЩЕВАЯ ЦЕННОСТЬ') }}</h2>
<div class="product-values-list">
{% set nutritional_value = post.nutritional_value %}
{% if nutritional_value.protein %}
<div class="product-values-item">
<div class="product-values-item__name">{{ fn('pll_e', 'Сырой белок') }}</div>
@ -89,6 +92,7 @@
</div>
{% endif %}
</div>
{% endif %}
{% set vitamins = post.vitamins %}
{% if vitamins %}
<div class="product-vitamins">
@ -134,6 +138,7 @@
</div>
{% endif %}
</div>
{% endif %}
</div>
<div class="product-show">
<div class="product-gallery">
@ -170,38 +175,22 @@
<div class="product-constructor">
{% set constructor = post.getProductConstructor %}
{% for taxonomy, attribute_data in constructor %}
{% if taxonomy != 'pa_series' %}
<div class="product-constructor__block">
<div class="product-constructor__block-title">{{attribute_data.label}}</div>
<div class="product-constructor__block-list">
{% for option_id, option in attribute_data.options %}
{% set first_product = option.products[0] %}
<a href="{{first_product.link}}" class="product-constructor__block-item{% if attribute_data.current_value == option.term_id %} active{% endif %}">
{% if taxonomy == 'pa_compound' %}
{% set icon_map = {
'govyadina': 'beef',
'indejka': 'turkey',
'krolik': 'rabbit',
'losos': 'salmon',
'ryba': 'fish',
'utka': 'duck',
'yagnenok': 'lamb'
} %}
{% set icon_class = icon_map[option.slug] ?? 'default' %}
<img src="/wp-content/themes/cosmopet/modules/shop/components/single-product_new/assets/img/{{icon_class}}.svg" alt="" class="product-constructor__block-item-icon{% if attribute_data.current_value == option.term_id %} active{% endif %} svg">
{% elseif taxonomy == 'pa_age-of-the-dog' %}
<img src="/wp-content/themes/cosmopet/modules/shop/components/single-product_new/assets/img/dog-face.svg" alt="" class="product-constructor__block-item-icon{% if attribute_data.current_value == option.term_id %} active{% endif %} svg">
{% elseif taxonomy == 'pa_age-of-the-cat' %}
<img src="/wp-content/themes/cosmopet/modules/shop/components/single-product_new/assets/img/cat-face.svg" alt="" class="product-constructor__block-item-icon{% if attribute_data.current_value == option.term_id %} active{% endif %} svg">
{% elseif taxonomy == 'pa_reproductive-status' %}
<img src="/wp-content/themes/cosmopet/modules/shop/components/single-product_new/assets/img/heart.svg" alt="" class="product-constructor__block-item-icon{% if attribute_data.current_value == option.term_id %} active{% endif %} svg">
{% elseif taxonomy == 'pa_series' %}
<img src="/wp-content/themes/cosmopet/modules/shop/components/single-product_new/assets/img/star.svg" alt="" class="product-constructor__block-item-icon{% if attribute_data.current_value == option.term_id %} active{% endif %} svg">
{% if option.icon %}
<img src="{{option.icon}}" alt="" class="product-constructor__block-item-icon{% if attribute_data.current_value == option.term_id %} active{% endif %} svg">
{% endif %}
<div class="product-constructor__block-item-name">{{option.name}}</div>
</a>
{% endfor %}
</div>
</div>
{% endif %}
{% endfor %}
{% set size_siblings = post.getSizeSiblings %}
@ -218,6 +207,26 @@
</div>
{% endif %}
{# Отображаем pa_series после вариантов веса #}
{% for taxonomy, attribute_data in constructor %}
{% if taxonomy == 'pa_series' %}
<div class="product-constructor__block">
<div class="product-constructor__block-title">{{attribute_data.label}}</div>
<div class="product-constructor__block-list">
{% for option_id, option in attribute_data.options %}
{% set first_product = option.products[0] %}
<a href="{{first_product.link}}" class="product-constructor__block-item{% if attribute_data.current_value == option.term_id %} active{% endif %}">
{% if option.icon %}
<img src="{{option.icon}}" alt="" class="product-constructor__block-item-icon{% if attribute_data.current_value == option.term_id %} active{% endif %} svg">
{% endif %}
<div class="product-constructor__block-item-name">{{option.name}}</div>
</a>
{% endfor %}
</div>
</div>
{% endif %}
{% endfor %}
<div class="product-price">
<div class="product-price-main">
{{post.getPrice}}
@ -231,16 +240,11 @@
</div>
{% endif %}
</div>
{% set in_stock = post.meta('_stock_status') == 'instock' %}
{% if in_stock %}
{{ function('do_action', 'woocommerce_' ~ product.get_type() ~ '_add_to_cart') }}
{% else %}
<div class="detail-block-form__item detail-block-form__item--tn">
<button type="button" data-pname="{{ product.title }}" class="to-know open-to-know" >
<p>{{ function('pll_e', 'Узнать о поступлении') }}</p>
</button>
<div id="product-buy-block-wrapper">
{% include 'blocks/modules/shop/product-single-add_to_cart.twig' %}
</div>
{% endif %}
</div>
</div>
@ -254,7 +258,8 @@
</div>
</div>
</div>
{% set reviews = post.getReviews %}
{% if reviews %}
<div class="product-reviews">
<div class="product-reviews__head wrapper">
<div class="product-block-title">
@ -267,7 +272,7 @@
</div>
<div class="product-reviews-slider swiper">
<div class="swiper-wrapper">
{% for slide in post.getReviews %}
{% for slide in reviews %}
{% include "shop/reviews-slide_element.twig" with {slide: slide} %}
{% endfor %}
</div>
@ -278,7 +283,7 @@
<button class="slider-button-next slider-button product-reviews-next"></button>
</div>
</div>
{% endif %}
{% if post.getRelatedProducts %}
<div class="product-similar ">
<div class="product-similar__head wrapper">

@ -0,0 +1,52 @@
<?php
/**
* The template for displaying WooCommerce pages
*/
if (!defined('ABSPATH')) {
exit;
}
$context = Timber::context();
// Добавляем WooCommerce контекст
if (class_exists('WooCommerce')) {
$context['is_woocommerce'] = true;
$context['is_product'] = is_product();
$context['is_shop'] = is_shop();
// Получаем текущий продукт если это страница товара
if (is_product()) {
$context['product'] = wc_get_product(get_the_ID());
}
}
// Определяем какой шаблон использовать
if (is_product()) {
$product_id = get_the_ID();
$categories = get_the_terms($product_id, 'product_cat');
$use_new_template = false;
if ($categories && !is_wp_error($categories)) {
foreach ($categories as $category) {
if (in_array($category->slug, ['korm', 'lakomstva'])) {
$use_new_template = true;
break;
}
}
}
if ($use_new_template) {
include_component('shop', 'product-single--new');
} else {
include_component('shop', 'product-single');
}
} elseif (is_shop()) {
include_component('shop', 'catalog');
} elseif (is_product_category()) {
include_component('shop', 'catalog');
} else {
include_component('shop', 'catalog');
}
// Рендеринг уже происходит внутри компонентов, поэтому здесь ничего не нужно

@ -1,5 +0,0 @@
<?php
include_component('shop', 'catalog')
?>
Loading…
Cancel
Save