Compare commits

...

3 Commits

  1. 14
      wp-content/themes/cosmopet/functions.php
  2. 380
      wp-content/themes/cosmopet/global-functions/metrics-functions.php
  3. 86
      wp-content/themes/cosmopet/global-functions/multilang-functions.php
  4. 387
      wp-content/themes/cosmopet/global-functions/multisite-functions.php
  5. 18
      wp-content/themes/cosmopet/global-functions/seo-functions.php
  6. 661
      wp-content/themes/cosmopet/modules/shop/CosmopetProduct.php
  7. 173
      wp-content/themes/cosmopet/modules/shop/components/cart/assets/js/cart.js
  8. 12
      wp-content/themes/cosmopet/modules/shop/components/catalog/assets/js/gp-main.js
  9. 2
      wp-content/themes/cosmopet/modules/shop/components/catalog/component-controller.php
  10. 95
      wp-content/themes/cosmopet/modules/shop/components/product-card/assets/css/product-card.css
  11. 4
      wp-content/themes/cosmopet/modules/shop/components/product-card/assets/js/product-card.js
  12. 234
      wp-content/themes/cosmopet/modules/shop/components/product-single--new/assets/css/product-style.css
  13. 0
      wp-content/themes/cosmopet/modules/shop/components/product-single--new/assets/img/avg.svg
  14. 0
      wp-content/themes/cosmopet/modules/shop/components/product-single--new/assets/img/beef.svg
  15. 0
      wp-content/themes/cosmopet/modules/shop/components/product-single--new/assets/img/dog-face.svg
  16. 0
      wp-content/themes/cosmopet/modules/shop/components/product-single--new/assets/img/fish.svg
  17. 0
      wp-content/themes/cosmopet/modules/shop/components/product-single--new/assets/img/lamb.svg
  18. 0
      wp-content/themes/cosmopet/modules/shop/components/product-single--new/assets/img/lg.svg
  19. 0
      wp-content/themes/cosmopet/modules/shop/components/product-single--new/assets/img/mini.svg
  20. 0
      wp-content/themes/cosmopet/modules/shop/components/product-single--new/assets/img/pet/cat.png
  21. 0
      wp-content/themes/cosmopet/modules/shop/components/product-single--new/assets/img/pet/cat.png.webp
  22. 0
      wp-content/themes/cosmopet/modules/shop/components/product-single--new/assets/img/pet/dog.png
  23. 0
      wp-content/themes/cosmopet/modules/shop/components/product-single--new/assets/img/pet/dog.png.webp
  24. 0
      wp-content/themes/cosmopet/modules/shop/components/product-single--new/assets/img/pet/mini-dog.png
  25. 0
      wp-content/themes/cosmopet/modules/shop/components/product-single--new/assets/img/pet/mini-dog.png.webp
  26. 0
      wp-content/themes/cosmopet/modules/shop/components/product-single--new/assets/img/svg/main/arrow-back.svg
  27. 0
      wp-content/themes/cosmopet/modules/shop/components/product-single--new/assets/img/svg/main/arrow-breadcrumbs.svg
  28. 0
      wp-content/themes/cosmopet/modules/shop/components/product-single--new/assets/img/svg/main/arrow-left.svg
  29. 0
      wp-content/themes/cosmopet/modules/shop/components/product-single--new/assets/img/svg/main/arrow-right.svg
  30. 0
      wp-content/themes/cosmopet/modules/shop/components/product-single--new/assets/img/svg/main/arrow-selected.svg
  31. 0
      wp-content/themes/cosmopet/modules/shop/components/product-single--new/assets/img/svg/main/black-x.svg
  32. 0
      wp-content/themes/cosmopet/modules/shop/components/product-single--new/assets/img/turkey.svg
  33. 0
      wp-content/themes/cosmopet/modules/shop/components/product-single--new/assets/js/cart-controls.js
  34. 1
      wp-content/themes/cosmopet/modules/shop/components/product-single--new/assets/js/product-single.js
  35. 20
      wp-content/themes/cosmopet/modules/shop/components/product-single--new/component-controller.php
  36. 49
      wp-content/themes/cosmopet/modules/shop/components/product-single/assets/css/cart-loading.css
  37. 0
      wp-content/themes/cosmopet/modules/shop/components/product-single/assets/css/gp-style-core.css
  38. 0
      wp-content/themes/cosmopet/modules/shop/components/product-single/assets/css/gp-style-desktop.css
  39. 0
      wp-content/themes/cosmopet/modules/shop/components/product-single/assets/css/gp-style-mobile.css
  40. 0
      wp-content/themes/cosmopet/modules/shop/components/product-single/assets/css/gp-style-order.css
  41. 0
      wp-content/themes/cosmopet/modules/shop/components/product-single/assets/css/gp-style-tablet.css
  42. 0
      wp-content/themes/cosmopet/modules/shop/components/product-single/assets/css/gp-style-ultra.css
  43. 0
      wp-content/themes/cosmopet/modules/shop/components/product-single/assets/img/pet/cat.png
  44. 0
      wp-content/themes/cosmopet/modules/shop/components/product-single/assets/img/pet/cat.png.webp
  45. 0
      wp-content/themes/cosmopet/modules/shop/components/product-single/assets/img/pet/dog.png
  46. 0
      wp-content/themes/cosmopet/modules/shop/components/product-single/assets/img/pet/dog.png.webp
  47. 0
      wp-content/themes/cosmopet/modules/shop/components/product-single/assets/img/pet/mini-dog.png
  48. 0
      wp-content/themes/cosmopet/modules/shop/components/product-single/assets/img/pet/mini-dog.png.webp
  49. 0
      wp-content/themes/cosmopet/modules/shop/components/product-single/assets/img/svg/main/arrow-back.svg
  50. 0
      wp-content/themes/cosmopet/modules/shop/components/product-single/assets/img/svg/main/arrow-breadcrumbs.svg
  51. 0
      wp-content/themes/cosmopet/modules/shop/components/product-single/assets/img/svg/main/arrow-left.svg
  52. 0
      wp-content/themes/cosmopet/modules/shop/components/product-single/assets/img/svg/main/arrow-right.svg
  53. 0
      wp-content/themes/cosmopet/modules/shop/components/product-single/assets/img/svg/main/arrow-selected.svg
  54. 0
      wp-content/themes/cosmopet/modules/shop/components/product-single/assets/img/svg/main/black-x.svg
  55. 0
      wp-content/themes/cosmopet/modules/shop/components/product-single/assets/js/gp-form.js
  56. 0
      wp-content/themes/cosmopet/modules/shop/components/product-single/assets/js/gp-main.js
  57. 0
      wp-content/themes/cosmopet/modules/shop/components/product-single/assets/js/gp-product.js
  58. 0
      wp-content/themes/cosmopet/modules/shop/components/product-single/assets/js/tabs.js
  59. 0
      wp-content/themes/cosmopet/modules/shop/components/product-single/assets/js/toggle.js
  60. 156
      wp-content/themes/cosmopet/modules/shop/components/product-single/component-controller.php
  61. 152
      wp-content/themes/cosmopet/modules/shop/components/single-product/component-controller.php
  62. 29
      wp-content/themes/cosmopet/modules/shop/components/single-product_new/component-controller.php
  63. 58
      wp-content/themes/cosmopet/modules/shop/module-ajax-controller.php
  64. 2
      wp-content/themes/cosmopet/modules/shop/module-controller.php
  65. 7
      wp-content/themes/cosmopet/page.php
  66. 6
      wp-content/themes/cosmopet/single-blog_author.php
  67. 6
      wp-content/themes/cosmopet/single-post.php
  68. 21
      wp-content/themes/cosmopet/single-product.php
  69. 26
      wp-content/themes/cosmopet/single.php
  70. 23
      wp-content/themes/cosmopet/temp-functions/ajax-logic.php
  71. 63
      wp-content/themes/cosmopet/temp-functions/timber-logic.php
  72. 3
      wp-content/themes/cosmopet/temp-functions/woocommerce-logic.php
  73. 19
      wp-content/themes/cosmopet/templates/profile/profile-orders.twig
  74. 27
      wp-content/themes/cosmopet/templates/profile/profile.twig
  75. 289
      wp-content/themes/cosmopet/templates/shop/single-product_page.twig
  76. 0
      wp-content/themes/cosmopet/views/404.twig
  77. 6
      wp-content/themes/cosmopet/views/_blocks/shop/archive-product-ajaxload.twig
  78. 0
      wp-content/themes/cosmopet/views/_blocks/shop/archive-product-tease.twig
  79. 0
      wp-content/themes/cosmopet/views/_pages/about.twig
  80. 0
      wp-content/themes/cosmopet/views/_pages/front-page.twig
  81. 0
      wp-content/themes/cosmopet/views/_pages/new-front-page.twig
  82. 0
      wp-content/themes/cosmopet/views/_pages/new-front-page/main-about.twig
  83. 0
      wp-content/themes/cosmopet/views/_pages/new-front-page/main-bot.twig
  84. 0
      wp-content/themes/cosmopet/views/_pages/new-front-page/main-cosmopetx.twig
  85. 0
      wp-content/themes/cosmopet/views/_pages/new-front-page/main-food.twig
  86. 0
      wp-content/themes/cosmopet/views/_pages/new-front-page/main-gallery.twig
  87. 0
      wp-content/themes/cosmopet/views/_pages/new-front-page/main-ingredients.twig
  88. 0
      wp-content/themes/cosmopet/views/_pages/new-front-page/main-media.twig
  89. 0
      wp-content/themes/cosmopet/views/_pages/new-front-page/main-quiz.twig
  90. 0
      wp-content/themes/cosmopet/views/_pages/new-front-page/main-reviews.twig
  91. 0
      wp-content/themes/cosmopet/views/_pages/new-front-page/main-slider.twig
  92. 0
      wp-content/themes/cosmopet/views/_pages/new-front-page/main-specialists.twig
  93. 0
      wp-content/themes/cosmopet/views/_pages/new-front-page/main-treats.twig
  94. 0
      wp-content/themes/cosmopet/views/_pages/new-front-page/main-why.twig
  95. 0
      wp-content/themes/cosmopet/views/_pages/policy.twig
  96. 0
      wp-content/themes/cosmopet/views/_pages/production.twig
  97. 26
      wp-content/themes/cosmopet/views/_pages/shop/product-single.twig
  98. 0
      wp-content/themes/cosmopet/views/_pages/wtb.twig
  99. 26
      wp-content/themes/cosmopet/views/blocks/modules/shop/product-single-add_to_cart.twig
  100. 0
      wp-content/themes/cosmopet/views/blog/author-banner.twig
  101. Some files were not shown because too many files have changed in this diff Show More

@ -23,3 +23,17 @@ require_once __DIR__ . '/temp-functions/login-logic.php';
// include_module('footer');
// include_module('author');
// include_module('layout');
include_once('modules/shop/CosmopetProduct.php');
add_filter('timber/post/classmap', function ($classmap) {
$classmap['product'] = CosmopetProduct::class;
return $classmap;
}, 100);
function allow_svg_upload($mimes) {
$mimes['svg'] = 'image/svg+xml';
return $mimes;
}
add_filter('upload_mimes', 'allow_svg_upload');

@ -0,0 +1,380 @@
<?
/**
* Добавление событий контрибуции для FP Pixel
* только на боевом сайте АЕ
*/
if ($site_env->site_mode == 'production' && $site_env->site_region == 'ae') {
// Проверка на тестовый режим для метрик
// TO_DO: дуюлирует логику is_gp_test_mode в wp-content/themes/cosmopet/global-functions/multisite-functions.php
function is_gp_test_mode()
{
if (isset($_GET['gp-test']) && $_GET['gp-test'] == '1') {
return true;
}
if (is_user_logged_in() && current_user_can('administrator')) {
return true;
}
return false;
}
add_action('wp_footer', 'add_facebook_pixel_events');
function add_facebook_pixel_events()
{
if (is_gp_test_mode())
return;
global $product;
// 1. ViewContent
if (is_product() && $product && $product->get_price() > 0) {
?>
<script>
document.addEventListener('DOMContentLoaded', function() {
fbq('track', 'ViewContent', {
content_ids: ['<?php echo $product->get_id(); ?>'],
content_type: 'product',
value: <?php echo $product->get_price(); ?>,
currency: '<?php echo get_woocommerce_currency(); ?>'
});
});
</script>
<?php
}
// 2. InitiateCheckout
if (is_checkout() && !is_wc_endpoint_url('order-received') && WC()->cart && WC()->cart->get_cart_contents_count() > 0) {
?>
<script>
document.addEventListener('DOMContentLoaded', function() {
fbq('track', 'InitiateCheckout');
});
</script>
<?php
}
// 3. AddToCart
if (is_product() || is_shop() || is_cart()) {
?>
<script>
document.addEventListener('DOMContentLoaded', function() {
jQuery(function($) {
$(document.body).on('added_to_cart', function(event, fragments, cart_hash, $button) {
var productId = $button.data('product_id') || '';
var quantity = $button.data('quantity') || 1;
var productName = $button.data('product_sku') ||
$button.closest('.product').find('.woocommerce-loop-product__title').text().trim() || 'Unknown';
var priceElement = $button.closest('.product').find('.price .amount').text().replace(/[^0-9.]/g, '') || '0.00';
var currency = '<?php echo get_woocommerce_currency(); ?>';
fbq('track', 'AddToCart', {
content_ids: [productId],
content_type: 'product',
value: parseFloat(priceElement) * quantity,
currency: currency
});
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({
'event': 'add_to_cart',
'ecommerce': {
'currency': currency,
'value': parseFloat(priceElement) * quantity,
'items': [{
'item_id': productId,
'item_name': productName,
'price': parseFloat(priceElement),
'quantity': quantity
}]
}
});
});
});
});
</script>
<?php
}
// 4. AddPaymentInfo
if (is_checkout() && !is_wc_endpoint_url('order-received') && WC()->cart && WC()->cart->get_cart_contents_count() > 0) {
$currency = get_woocommerce_currency();
$cart_total = WC()->cart->get_total('edit');
?>
<script>
document.addEventListener('DOMContentLoaded', function() {
fbq('track', 'AddPaymentInfo', {
value: <?php echo $cart_total; ?>,
currency: '<?php echo $currency; ?>'
});
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({
'event': 'add_payment_info',
'ecommerce': {
'currency': '<?php echo $currency; ?>',
'value': <?php echo $cart_total; ?>
}
});
});
</script>
<?php
}
// 5. Purchase
if (is_wc_endpoint_url('order-received')) {
$order_id = absint(get_query_var('order-received'));
if (!$order_id)
return;
$order = wc_get_order($order_id);
if (!$order || ($order->get_status() !== 'processing' && $order->get_status() !== 'completed'))
return;
$items = [];
foreach ($order->get_items() as $item) {
$product = $item->get_product();
$items[] = [
'item_id' => $product->get_id(),
'item_name' => $product->get_name(),
'price' => $product->get_price(),
'quantity' => $item->get_quantity()
];
}
?>
<script>
document.addEventListener('DOMContentLoaded', function() {
fbq('track', 'Purchase', {
value: <?php echo $order->get_total(); ?>,
currency: '<?php echo $order->get_currency(); ?>',
content_ids: [<?php echo implode(',', array_column($items, 'item_id')); ?>],
content_type: 'product'
});
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({
'event': 'purchase',
'ecommerce': {
'currency': '<?php echo $order->get_currency(); ?>',
'value': <?php echo $order->get_total(); ?>,
'items': <?php echo json_encode($items); ?>
}
});
});
</script>
<?php
}
}
add_action('woocommerce_thankyou', 'send_purchase_to_metrika');
function send_purchase_to_metrika($order_id)
{
if (is_gp_test_mode())
return;
if (!$order_id)
return;
$order = wc_get_order($order_id);
if ($order->get_status() !== 'processing' && $order->get_status() !== 'completed')
return;
$items = [];
foreach ($order->get_items() as $item) {
$product = $item->get_product();
$items[] = [
'id' => $product->get_id(),
'name' => $product->get_name(),
'price' => $product->get_price(),
'quantity' => $item->get_quantity()
];
}
$currency = $order->get_currency();
?>
<script>
window.dataLayer = window.dataLayer || [];
dataLayer.push({
'ecommerce': {
'purchase': {
'actionField': {
'id': '<?php echo $order_id; ?>',
'revenue': '<?php echo $order->get_total(); ?>',
'currency': '<?php echo $currency; ?>'
},
'products': <?php echo json_encode($items); ?>
}
}
});
yaCounter96481053.reachGoal('purchase', {
'order_id': '<?php echo $order_id; ?>',
'order_price': '<?php echo $order->get_total(); ?>',
'currency': '<?php echo $currency; ?>',
'items': <?php echo json_encode($items); ?>
});
fbq('track', 'Purchase', {
value: <?php echo $order->get_total(); ?>,
currency: '<?php echo $currency; ?>',
content_ids: [<?php echo implode(',', array_column($items, 'id')); ?>],
content_type: 'product'
});
</script>
<?php
}
}
// Функция событий Meta Pixel
add_action('wp_head', function () {
global $site_env;
$show_pixel = false;
// Для продакшена
if ($site_env->site_mode === 'production' && $site_env->site_region === 'ae') {
$show_pixel = true;
}
// Для тестовых стендов AE
if ($site_env->site_mode === 'develope' && $site_env->site_region === 'ae') {
$show_pixel = true;
}
// Дополнительные проверки
$skip_tracking = false;
// Исключаем админов
if (current_user_can('administrator')) {
$skip_tracking = true;
}
// Исключаем gp-test режим
if ($site_env->is_gp_test_mode) {
$skip_tracking = true;
}
// Исключаем если есть параметр gp-test=1 в URL
if (isset($_GET['gp-test']) && $_GET['gp-test'] == '1') {
$skip_tracking = true;
}
if ($show_pixel && !$skip_tracking) {
$context = [
'currency' => get_woocommerce_currency(),
];
// Определяем тип страницы и подготавливаем данные
if (is_product()) {
// Страница товара - ViewContent
global $product;
if ($product && $product instanceof WC_Product) {
$context['pixel_event_type'] = 'ViewContent';
$context['product'] = [
'id' => $product->get_id(),
'name' => $product->get_name(),
'price' => floatval($product->get_price()),
'sku' => $product->get_sku(),
'category' => wp_get_post_terms($product->get_id(), 'product_cat', ['fields' => 'names'])[0] ?? '',
];
}
} elseif (is_checkout() && !is_order_received_page()) {
// Страница чекаута - InitiateCheckout
$context['pixel_event_type'] = 'InitiateCheckout';
if (WC()->cart && !WC()->cart->is_empty()) {
$context['cart_total'] = floatval(WC()->cart->get_total('edit'));
$context['cart_items'] = [];
$context['content_ids'] = [];
foreach (WC()->cart->get_cart() as $cart_item) {
$product = $cart_item['data'];
if ($product && $product instanceof WC_Product) {
$item_data = [
'item_id' => $product->get_id(),
'item_name' => $product->get_name(),
'price' => floatval($product->get_price()),
'quantity' => intval($cart_item['quantity']),
'category' => wp_get_post_terms($product->get_id(), 'product_cat', ['fields' => 'names'])[0] ?? '',
];
$context['cart_items'][] = $item_data;
$context['content_ids'][] = $product->get_id();
}
}
}
} elseif (is_order_received_page()) {
// Страница благодарности - Purchase
$order_id = get_query_var('order-received');
if ($order_id) {
$order = wc_get_order($order_id);
if ($order && $order instanceof WC_Order) {
$context['pixel_event_type'] = 'Purchase';
$order_items = [];
$content_ids = [];
foreach ($order->get_items() as $item) {
if ($item instanceof WC_Order_Item_Product) {
$product = $item->get_product();
if ($product && $product instanceof WC_Product) {
$order_items[] = [
'item_id' => $product->get_id(),
'item_name' => $product->get_name(),
'price' => floatval($item->get_total() / $item->get_quantity()),
'quantity' => intval($item->get_quantity()),
'category' => wp_get_post_terms($product->get_id(), 'product_cat', ['fields' => 'names'])[0] ?? '',
];
$content_ids[] = $product->get_id();
}
}
}
$context['order'] = [
'id' => $order->get_id(),
'total' => floatval($order->get_total()),
'currency' => $order->get_currency(),
'items' => $order_items,
'content_ids' => $content_ids,
'order_key' => $order->get_order_key(),
];
}
}
} else {
// Все остальные страницы - PageView + подготовка для AddToCart
$context['pixel_event_type'] = 'PageView';
$context['enable_addtocart'] = true; // Включаем обработчик AddToCart
// Дополнительные данные для PageView
global $wp_query;
$context['page_data'] = [
'page_title' => wp_get_document_title(),
'page_url' => home_url(add_query_arg(array(), $wp_query->request)),
'page_type' => get_page_type(),
];
}
// Рендерим шаблон
\Timber\Timber::render('templates/head-pixel-functions.twig', $context);
}
});
function get_page_type()
{
if (is_home() || is_front_page()) {
return 'home';
} elseif (is_shop()) {
return 'shop';
} elseif (is_product_category()) {
return 'category';
} elseif (is_product_tag()) {
return 'tag';
} elseif (is_search()) {
return 'search';
} elseif (is_cart()) {
return 'cart';
} elseif (is_account_page()) {
return 'account';
} else {
return 'other';
}
}

@ -1842,50 +1842,50 @@ pll_register_string('This item is not on our website yet, but it will be here ve
add_action('wp_head', function () {
if (function_exists('pll_current_language')) {
$lang = pll_current_language();
$translations = [
'en' => [
'name_required' => 'The name is required.',
'email_invalid' => 'Email is incorrect.',
'phone_invalid' => 'The phone number is incorrect.',
'error_coupon_empty' => 'Please enter the coupon code.',
'error_coupon_invalid' => 'Error! The coupon code is invalid or does not exist.',
'error_generic' => 'An error occurred. Please try again.',
'shipping_method_error' => 'No pickup point selected for delivery.',
'shipping_address_error' => 'Delivery address not provided',
'email_taken_error' => 'This email is already registered. Please log in or provide a different email.',
'phone_number_error' => 'Incorrect phone number entered.',
'shipping_method_missing' => 'No delivery method selected. Please check your address or contact the site administration for help.',
'change_address' => 'Change address',
'shipping_updated' => 'Shipping methods will be updated during checkout.',
'coupon_applied' => 'Coupon applied: ',
$translations = [
'en' => [
'name_required' => 'The name is required.',
'email_invalid' => 'Email is incorrect.',
'phone_invalid' => 'The phone number is incorrect.',
'error_coupon_empty' => 'Please enter the coupon code.',
'error_coupon_invalid' => 'Error! The coupon code is invalid or does not exist.',
'error_generic' => 'An error occurred. Please try again.',
'shipping_method_error' => 'No pickup point selected for delivery.',
'shipping_address_error' => 'Delivery address not provided',
'email_taken_error' => 'This email is already registered. Please log in or provide a different email.',
'phone_number_error' => 'Incorrect phone number entered.',
'shipping_method_missing' => 'No delivery method selected. Please check your address or contact the site administration for help.',
'change_address' => 'Change address',
'shipping_updated' => 'Shipping methods will be updated during checkout.',
'coupon_applied' => 'Coupon applied: ',
// 🔽 Новые строки из JS
'Choose pick-up' => 'Choose pick-up',
'Re-select pick-up' => 'Re-select pick-up',
'There are no CDEK pick-up points available in this direction, please select another delivery method' =>
'There are no CDEK pick-up points available in this direction, please select another delivery method'
],
'ru' => [
'name_required' => 'Поле имени обязательно для заполнения.',
'email_invalid' => 'Введите корректный email.',
'phone_invalid' => 'Введите корректный номер телефона.',
'error_coupon_empty' => 'Пожалуйста, введите код купона.',
'error_coupon_invalid' => 'Ошибка! Промокод введен неверно или не существует.',
'error_generic' => 'Произошла ошибка. Попробуйте ещё раз.',
'shipping_method_error' => 'Не выбран ПВЗ для доставки заказа.',
'shipping_address_error' => 'Не указан адрес доставки',
'email_taken_error' => 'Этот email уже зарегистрирован. Войдите в свой аккаунт или укажите другой E-mail.',
'phone_number_error' => 'Неправильно введен номер',
'shipping_method_missing' => 'Не выбран метод доставки. Пожалуйста перепроверьте ваш адрес или обратитесь за помощью к администрации сайта.',
'change_address' => 'Изменить адрес',
'shipping_updated' => 'Методы доставки будут обновлены в процессе оформления заказа.',
'coupon_applied' => 'Применен промокод: ',
'Choose pick-up' => 'Выбрать пункт выдачи',
'Re-select pick-up' => 'Выбрать другой пункт выдачи',
'There are no CDEK pick-up points available in this direction, please select another delivery method' =>
'В этом направлении нет доступных пунктов выдачи CDEK, пожалуйста, выберите другой способ доставки'
]
];
// 🔽 Новые строки из JS
'Choose pick-up' => 'Choose pick-up',
'Re-select pick-up' => 'Re-select pick-up',
'There are no CDEK pick-up points available in this direction, please select another delivery method' =>
'There are no CDEK pick-up points available in this direction, please select another delivery method'
],
'ru' => [
'name_required' => 'Поле имени обязательно для заполнения.',
'email_invalid' => 'Введите корректный email.',
'phone_invalid' => 'Введите корректный номер телефона.',
'error_coupon_empty' => 'Пожалуйста, введите код купона.',
'error_coupon_invalid' => 'Ошибка! Промокод введен неверно или не существует.',
'error_generic' => 'Произошла ошибка. Попробуйте ещё раз.',
'shipping_method_error' => 'Не выбран ПВЗ для доставки заказа.',
'shipping_address_error' => 'Не указан адрес доставки',
'email_taken_error' => 'Этот email уже зарегистрирован. Войдите в свой аккаунт или укажите другой E-mail.',
'phone_number_error' => 'Неправильно введен номер',
'shipping_method_missing' => 'Не выбран метод доставки. Пожалуйста перепроверьте ваш адрес или обратитесь за помощью к администрации сайта.',
'change_address' => 'Изменить адрес',
'shipping_updated' => 'Методы доставки будут обновлены в процессе оформления заказа.',
'coupon_applied' => 'Применен промокод: ',
'Choose pick-up' => 'Выбрать пункт выдачи',
'Re-select pick-up' => 'Выбрать другой пункт выдачи',
'There are no CDEK pick-up points available in this direction, please select another delivery method' =>
'В этом направлении нет доступных пунктов выдачи CDEK, пожалуйста, выберите другой способ доставки'
]
];

@ -36,390 +36,3 @@ add_filter('timber/twig', function (\Twig\Environment $twig) {
add_filter('woocommerce_currency_symbol', function($currency_symbol, $currency) {
return $currency === 'AED' ? 'AED' : $currency_symbol;
}, 10, 2);
add_filter('wpseo_canonical', '__return_false');
add_filter('wpseo_opengraph_url', '__return_false');
add_filter('wpseo_add_x_default_hreflang', '__return_false');
add_filter('wpseo_disable_adjacent_rel_links', '__return_true');
add_action('wp_head', function() {
if (!is_admin()) {
static $canonical_added = false;
if ($canonical_added) return;
$canonical_added = true;
$current_url = strtok(trailingslashit(home_url($_SERVER['REQUEST_URI'])), '?');
echo '<link rel="canonical" href="' . esc_url($current_url) . '" />' . "\n";
}
});
/**
* Добавление событий контрибуции для FP Pixel
* только на боевом сайте АЕ
*/
if ($site_env->site_mode == 'production' && $site_env->site_region == 'ae') {
// Проверка на тестовый режим для метрик
function is_gp_test_mode() {
if (isset($_GET['gp-test']) && $_GET['gp-test'] == '1') {
return true;
}
if (is_user_logged_in() && current_user_can('administrator')) {
return true;
}
return false;
}
add_action('wp_footer', 'add_facebook_pixel_events');
function add_facebook_pixel_events() {
if (is_gp_test_mode()) return;
global $product;
// 1. ViewContent
if (is_product() && $product && $product->get_price() > 0) {
?>
<script>
document.addEventListener('DOMContentLoaded', function() {
fbq('track', 'ViewContent', {
content_ids: ['<?php echo $product->get_id(); ?>'],
content_type: 'product',
value: <?php echo $product->get_price(); ?>,
currency: '<?php echo get_woocommerce_currency(); ?>'
});
});
</script>
<?php
}
// 2. InitiateCheckout
if (is_checkout() && !is_wc_endpoint_url('order-received') && WC()->cart && WC()->cart->get_cart_contents_count() > 0) {
?>
<script>
document.addEventListener('DOMContentLoaded', function() {
fbq('track', 'InitiateCheckout');
});
</script>
<?php
}
// 3. AddToCart
if (is_product() || is_shop() || is_cart()) {
?>
<script>
document.addEventListener('DOMContentLoaded', function() {
jQuery(function($) {
$(document.body).on('added_to_cart', function(event, fragments, cart_hash, $button) {
var productId = $button.data('product_id') || '';
var quantity = $button.data('quantity') || 1;
var productName = $button.data('product_sku') ||
$button.closest('.product').find('.woocommerce-loop-product__title').text().trim() || 'Unknown';
var priceElement = $button.closest('.product').find('.price .amount').text().replace(/[^0-9.]/g, '') || '0.00';
var currency = '<?php echo get_woocommerce_currency(); ?>';
fbq('track', 'AddToCart', {
content_ids: [productId],
content_type: 'product',
value: parseFloat(priceElement) * quantity,
currency: currency
});
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({
'event': 'add_to_cart',
'ecommerce': {
'currency': currency,
'value': parseFloat(priceElement) * quantity,
'items': [{
'item_id': productId,
'item_name': productName,
'price': parseFloat(priceElement),
'quantity': quantity
}]
}
});
});
});
});
</script>
<?php
}
// 4. AddPaymentInfo
if (is_checkout() && !is_wc_endpoint_url('order-received') && WC()->cart && WC()->cart->get_cart_contents_count() > 0) {
$currency = get_woocommerce_currency();
$cart_total = WC()->cart->get_total('edit');
?>
<script>
document.addEventListener('DOMContentLoaded', function() {
fbq('track', 'AddPaymentInfo', {
value: <?php echo $cart_total; ?>,
currency: '<?php echo $currency; ?>'
});
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({
'event': 'add_payment_info',
'ecommerce': {
'currency': '<?php echo $currency; ?>',
'value': <?php echo $cart_total; ?>
}
});
});
</script>
<?php
}
// 5. Purchase
if (is_wc_endpoint_url('order-received')) {
$order_id = absint(get_query_var('order-received'));
if (!$order_id) return;
$order = wc_get_order($order_id);
if (!$order || ($order->get_status() !== 'processing' && $order->get_status() !== 'completed')) return;
$items = [];
foreach ($order->get_items() as $item) {
$product = $item->get_product();
$items[] = [
'item_id' => $product->get_id(),
'item_name' => $product->get_name(),
'price' => $product->get_price(),
'quantity' => $item->get_quantity()
];
}
?>
<script>
document.addEventListener('DOMContentLoaded', function() {
fbq('track', 'Purchase', {
value: <?php echo $order->get_total(); ?>,
currency: '<?php echo $order->get_currency(); ?>',
content_ids: [<?php echo implode(',', array_column($items, 'item_id')); ?>],
content_type: 'product'
});
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({
'event': 'purchase',
'ecommerce': {
'currency': '<?php echo $order->get_currency(); ?>',
'value': <?php echo $order->get_total(); ?>,
'items': <?php echo json_encode($items); ?>
}
});
});
</script>
<?php
}
}
add_action('woocommerce_thankyou', 'send_purchase_to_metrika');
function send_purchase_to_metrika($order_id) {
if (is_gp_test_mode()) return;
if (!$order_id) return;
$order = wc_get_order($order_id);
if ($order->get_status() !== 'processing' && $order->get_status() !== 'completed') return;
$items = [];
foreach ($order->get_items() as $item) {
$product = $item->get_product();
$items[] = [
'id' => $product->get_id(),
'name' => $product->get_name(),
'price' => $product->get_price(),
'quantity' => $item->get_quantity()
];
}
$currency = $order->get_currency();
?>
<script>
window.dataLayer = window.dataLayer || [];
dataLayer.push({
'ecommerce': {
'purchase': {
'actionField': {
'id': '<?php echo $order_id; ?>',
'revenue': '<?php echo $order->get_total(); ?>',
'currency': '<?php echo $currency; ?>'
},
'products': <?php echo json_encode($items); ?>
}
}
});
yaCounter96481053.reachGoal('purchase', {
'order_id': '<?php echo $order_id; ?>',
'order_price': '<?php echo $order->get_total(); ?>',
'currency': '<?php echo $currency; ?>',
'items': <?php echo json_encode($items); ?>
});
fbq('track', 'Purchase', {
value: <?php echo $order->get_total(); ?>,
currency: '<?php echo $currency; ?>',
content_ids: [<?php echo implode(',', array_column($items, 'id')); ?>],
content_type: 'product'
});
</script>
<?php
}
}
// Функция событий Meta Pixel
add_action('wp_head', function() {
global $site_env;
$show_pixel = false;
// Для продакшена
if ($site_env->site_mode === 'production' && $site_env->site_region === 'ae') {
$show_pixel = true;
}
// Для тестовых стендов AE
if ($site_env->site_mode === 'develope' && $site_env->site_region === 'ae') {
$show_pixel = true;
}
// Дополнительные проверки
$skip_tracking = false;
// Исключаем админов
if (current_user_can('administrator')) {
$skip_tracking = true;
}
// Исключаем gp-test режим
if ($site_env->is_gp_test_mode) {
$skip_tracking = true;
}
// Исключаем если есть параметр gp-test=1 в URL
if (isset($_GET['gp-test']) && $_GET['gp-test'] == '1') {
$skip_tracking = true;
}
if ($show_pixel && !$skip_tracking) {
$context = [
'currency' => get_woocommerce_currency(),
];
// Определяем тип страницы и подготавливаем данные
if (is_product()) {
// Страница товара - ViewContent
global $product;
if ($product && $product instanceof WC_Product) {
$context['pixel_event_type'] = 'ViewContent';
$context['product'] = [
'id' => $product->get_id(),
'name' => $product->get_name(),
'price' => floatval($product->get_price()),
'sku' => $product->get_sku(),
'category' => wp_get_post_terms($product->get_id(), 'product_cat', ['fields' => 'names'])[0] ?? '',
];
}
}
elseif (is_checkout() && !is_order_received_page()) {
// Страница чекаута - InitiateCheckout
$context['pixel_event_type'] = 'InitiateCheckout';
if (WC()->cart && !WC()->cart->is_empty()) {
$context['cart_total'] = floatval(WC()->cart->get_total('edit'));
$context['cart_items'] = [];
$context['content_ids'] = [];
foreach (WC()->cart->get_cart() as $cart_item) {
$product = $cart_item['data'];
if ($product && $product instanceof WC_Product) {
$item_data = [
'item_id' => $product->get_id(),
'item_name' => $product->get_name(),
'price' => floatval($product->get_price()),
'quantity' => intval($cart_item['quantity']),
'category' => wp_get_post_terms($product->get_id(), 'product_cat', ['fields' => 'names'])[0] ?? '',
];
$context['cart_items'][] = $item_data;
$context['content_ids'][] = $product->get_id();
}
}
}
}
elseif (is_order_received_page()) {
// Страница благодарности - Purchase
$order_id = get_query_var('order-received');
if ($order_id) {
$order = wc_get_order($order_id);
if ($order && $order instanceof WC_Order) {
$context['pixel_event_type'] = 'Purchase';
$order_items = [];
$content_ids = [];
foreach ($order->get_items() as $item) {
if ($item instanceof WC_Order_Item_Product) {
$product = $item->get_product();
if ($product && $product instanceof WC_Product) {
$order_items[] = [
'item_id' => $product->get_id(),
'item_name' => $product->get_name(),
'price' => floatval($item->get_total() / $item->get_quantity()),
'quantity' => intval($item->get_quantity()),
'category' => wp_get_post_terms($product->get_id(), 'product_cat', ['fields' => 'names'])[0] ?? '',
];
$content_ids[] = $product->get_id();
}
}
}
$context['order'] = [
'id' => $order->get_id(),
'total' => floatval($order->get_total()),
'currency' => $order->get_currency(),
'items' => $order_items,
'content_ids' => $content_ids,
'order_key' => $order->get_order_key(),
];
}
}
}
else {
// Все остальные страницы - PageView + подготовка для AddToCart
$context['pixel_event_type'] = 'PageView';
$context['enable_addtocart'] = true; // Включаем обработчик AddToCart
// Дополнительные данные для PageView
global $wp_query;
$context['page_data'] = [
'page_title' => wp_get_document_title(),
'page_url' => home_url(add_query_arg(array(), $wp_query->request)),
'page_type' => get_page_type(),
];
}
// Рендерим шаблон
\Timber\Timber::render('templates/head-pixel-functions.twig', $context);
}
});
function get_page_type() {
if (is_home() || is_front_page()) {
return 'home';
} elseif (is_shop()) {
return 'shop';
} elseif (is_product_category()) {
return 'category';
} elseif (is_product_tag()) {
return 'tag';
} elseif (is_search()) {
return 'search';
} elseif (is_cart()) {
return 'cart';
} elseif (is_account_page()) {
return 'account';
} else {
return 'other';
}
}

@ -0,0 +1,18 @@
<?
add_filter('wpseo_canonical', '__return_false');
add_filter('wpseo_opengraph_url', '__return_false');
add_filter('wpseo_add_x_default_hreflang', '__return_false');
add_filter('wpseo_disable_adjacent_rel_links', '__return_true');
/* Добавление canonical-ссылок */
add_action('wp_head', function () {
if (!is_admin()) {
static $canonical_added = false;
if ($canonical_added)
return;
$canonical_added = true;
$current_url = strtok(trailingslashit(home_url($_SERVER['REQUEST_URI'])), '?');
echo '<link rel="canonical" href="' . esc_url($current_url) . '" />' . "\n";
}
});

@ -0,0 +1,661 @@
<?php
use Timber;
use Timber\PostCollection;
use Timber\Integrations\WooCommerce\Product as TimberProduct;
class CosmopetProduct extends TimberProduct {
protected $sibling_categories = [
'pa_compound',
'pa_dog-size',
'pa_reproductive-status',
'pa_age-of-the-cat',
'pa_age-of-the-dog',
'pa_series',
];
public function __construct ($pid = null) {
parent::__construct($pid);
}
public function getComposition() {
return get_post_meta($this->id, '_composition', true);
}
public function getImageGallery($size) {
// Инициализируем массив для результатов
$images_array = array();
// Получаем ID основного изображения товара
$thumbnail_id = get_post_thumbnail_id($this->id);
// Добавляем основное изображение
if ($thumbnail_id) {
$images_array[] = array(
'url' => wp_get_attachment_image_url($thumbnail_id, $size),
'alt' => get_post_meta($thumbnail_id, '_wp_attachment_image_alt', true)
);
} else {
// Используем заглушку, если нет основного изображения
$images_array[] = array(
'url' => wc_placeholder_img_src(),
'alt' => __('Placeholder image', 'woocommerce')
);
}
// Получаем галерею изображений
if ($this->product && method_exists($this->product, 'get_gallery_image_ids')) {
$gallery_image_ids = $this->product->get_gallery_image_ids();
} else {
$gallery_image_ids = [];
}
// Добавляем изображения из галереи
if (!empty($gallery_image_ids)) {
foreach ($gallery_image_ids as $image_id) {
$image_url = wp_get_attachment_image_url($image_id, $size);
if ($image_url) {
$images_array[] = array(
'url' => $image_url,
'alt' => get_post_meta($image_id, '_wp_attachment_image_alt', true)
);
}
}
}
return $images_array;
}
public function get_weight() {
$custom_measurement = get_post_meta($this->id, '_custom_measurement', true) ?: pll__('кг');
$custom_size = get_post_meta($this->id, '_size_for_display', true) ?: '';
if ($custom_size){
return $custom_size ? $custom_size . ' ' . $custom_measurement : '';
}
else{
if ($this->product && method_exists($this->product, 'get_weight')) {
return $this->product->get_weight() ? $this->product->get_weight() . ' ' . $custom_measurement : '';
}
return '';
}
return '';
}
public function getNumericWeight() {
$custom_size = get_post_meta($this->id, '_size_for_display', true) ?: '';
if ($custom_size){
return floatval($custom_size);
}
else{
if ($this->product && method_exists($this->product, 'get_weight')) {
return floatval($this->product->get_weight());
}
return 0;
}
return 0;
}
/**
* Получает CSS класс вкуса для товара
* @return string CSS класс вкуса (например, --food-fish, --treats-beef)
*/
public function getTasteClass() {
// Получаем категории товара
$product_categories = $this->product->get_category_ids();
$is_food = false;
$is_treats = false;
// Определяем тип товара (корм или лакомство)
foreach ($product_categories as $cat) {
if ($cat == 365) {
$is_food = true;
} elseif ($cat == 367) {
$is_treats = true;
}
}
// Определяем префикс типа товара
$type_prefix = '';
if ($is_food) {
$type_prefix = 'food';
} elseif ($is_treats) {
$type_prefix = 'treats';
} else {
$type_prefix = 'acc'; // Дефолтный тип
}
// Получаем термины атрибута compound (вкус)
$compound_terms = get_the_terms($this->id, 'pa_compound');
$taste_suffix = '';
if ($compound_terms && !is_wp_error($compound_terms)) {
foreach ($compound_terms as $term) {
switch ($term->slug) {
case 'govyadina':
$taste_suffix = 'beef';
break;
case 'indejka':
$taste_suffix = 'turkey';
break;
case 'krolik':
$taste_suffix = 'rabbit';
break;
case 'losos':
$taste_suffix = 'salmon';
break;
case 'ryba':
$taste_suffix = 'fish';
break;
case 'utka':
$taste_suffix = 'duck';
break;
case 'yagnenok':
$taste_suffix = 'lamb';
break;
}
// Если нашли вкус, прерываем цикл
if ($taste_suffix) {
break;
}
}
}
// Если вкус не найден, используем дефолтные суффиксы по типу товара
if (!$taste_suffix) {
if ($is_food) {
$taste_suffix = 'fish';
} elseif ($is_treats) {
$taste_suffix = 'beef';
} else {
$taste_suffix = ''; // Дефолтный суффикс
}
}
return '--' . $type_prefix . '-' . $taste_suffix;
}
public function getSizeSiblings() {
$term = get_the_terms($this->id, 'pa_collection');
if ($term) {
$term = $term[0]->term_id;
} else {
return [];
}
$args = array(
'posts_per_page' => -1,
'post_type' => 'product',
'order' => 'ASC',
'orderby' => 'name', // Fixed parameter name (was 'order_by')
'tax_query' => [
[
'taxonomy' => 'pa_collection',
'terms' => $term,
'field' => 'id',
]
],
);
$siblings = Timber::get_posts($args);
// Create array with products and their weights
$siblings_with_weights = [];
foreach ($siblings as $sibling) {
$weight = $sibling->get_weight();
$siblings_with_weights[] = [
'post' => $sibling,
'weight' => floatval($weight) // Ensure weight is treated as a number
];
}
// Sort by weight
usort($siblings_with_weights, function($a, $b) {
return $a['weight'] <=> $b['weight'];
});
// Extract just the posts in sorted order
$sorted_siblings = array_map(function($item) {
return $item['post'];
}, $siblings_with_weights);
return $sorted_siblings;
}
protected function get_category_products(){
// Получаем все товары с похожими атрибутами
$args = array(
'posts_per_page' => -1,
'post_type' => 'product',
'order' => 'ASC',
'orderby' => 'name',
'post_status' => 'publish'
);
error_log("get_category_products: Запрос товаров");
$posts = Timber::get_posts($args);
error_log("get_category_products: Получено товаров: " . (is_array($posts) ? count($posts) : 'не массив'));
// Преобразуем в массив, если это PostCollection или PostQuery
if ($posts instanceof \Timber\PostCollection || $posts instanceof \Timber\PostQuery) {
$result = $posts->get_posts();
error_log("get_category_products: Преобразовано в массив: " . count($result));
return $result;
}
$result = is_array($posts) ? $posts : [];
error_log("get_category_products: Возвращаем: " . count($result));
return $result;
}
public function getProductConstructor() {
$all_products = $this->get_category_products();
$constructor = [];
// Получаем атрибуты текущего товара
$current_product_attributes = [];
$current_weight = $this->getNumericWeight();
// Получаем значения атрибутов текущего товара
foreach ($this->sibling_categories as $taxonomy) {
$terms = get_the_terms($this->id, $taxonomy);
if ($terms && !is_wp_error($terms)) {
$current_product_attributes[$taxonomy] = array_map(function($term) {
return $term->term_id;
}, $terms);
}
}
// Получаем упорядоченные таксономии
$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' => []
];
}
}
// ОБХОДИМ МАССИВ ТОВАРОВ ТОЛЬКО ОДИН РАЗ
foreach ($all_products as $product) {
// Получаем все атрибуты сравниваемого товара за один раз
$compare_attributes = [];
$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[$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;
// Проверяем все атрибуты кроме текущей таксономии
foreach ($this->sibling_categories as $compare_taxonomy) {
if ($compare_taxonomy === $taxonomy) {
continue; // Пропускаем проверяемую категорию
}
// Если у текущего товара есть значение для этой категории
if (isset($current_product_attributes[$compare_taxonomy])) {
// Проверяем, есть ли у сравниваемого товара значение для этой категории
if (!isset($compare_attributes[$compare_taxonomy])) {
$attributes_match = false;
break;
}
// Проверяем, совпадают ли значения
$current_values = $current_product_attributes[$compare_taxonomy];
$compare_values = $compare_attributes[$compare_taxonomy];
if (array_diff($current_values, $compare_values) !== [] ||
array_diff($compare_values, $current_values) !== []) {
$attributes_match = false;
break;
}
}
// Если у текущего товара НЕТ значения для этой категории,
// то у сравниваемого товара тоже не должно быть значения
else {
if (isset($compare_attributes[$compare_taxonomy])) {
$attributes_match = false;
break;
}
}
}
// Проверяем вес (числовое сравнение)
if (abs($current_weight - $compare_weight) > 0.01) {
$attributes_match = false;
}
// Если все атрибуты и вес совпадают, добавляем товар в группу
if ($attributes_match) {
$product_terms = get_the_terms($product->id, $taxonomy);
if ($product_terms && !is_wp_error($product_terms)) {
foreach ($product_terms as $term) {
$option_key = $term->term_id;
if (!isset($constructor[$taxonomy]['options'][$option_key])) {
$constructor[$taxonomy]['options'][$option_key] = [
'term_id' => $term->term_id,
'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;
}
}
}
}
}
// Сортируем опции по порядку терминов для каждой таксономии
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;
}
}
// Сортируем итоговый конструктор по порядку таксономий
$ordered_constructor = [];
foreach ($this->sibling_categories as $taxonomy) {
if (isset($constructor[$taxonomy])) {
$ordered_constructor[$taxonomy] = $constructor[$taxonomy];
}
}
return $ordered_constructor;
}
protected function getAttributeLabel($taxonomy) {
// Fallback на старые метки
$labels = [
'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);
}
protected function getCurrentAttributeValue($taxonomy) {
$terms = get_the_terms($this->id, $taxonomy);
if ($terms && !is_wp_error($terms)) {
return $terms[0]->term_id;
}
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) {
$terms = get_the_terms($this->id, $taxonomy);
if ($terms && !is_wp_error($terms)) {
$attributes[$taxonomy] = [];
foreach ($terms as $term) {
$attributes[$taxonomy][] = [
'term_id' => $term->term_id,
'name' => $term->name,
'slug' => $term->slug
];
}
}
}
return $attributes;
}
public function getPrice(){
$price = $this->product->get_price() . ' ' . get_woocommerce_currency_symbol();
return $price;
}
public function getRegularPrice(){
$regular_price = $this->product->get_regular_price() . ' ' . get_woocommerce_currency_symbol();
if ($regular_price != $this->getPrice()){
return $regular_price;
}
else{
return '';
}
}
public function getPriceDifferenceInPercent(){
$regular_price = $this->product->get_regular_price();
$price = $this->product->get_price();
$difference = ($regular_price - $price) / $regular_price * 100;
$difference = round($difference, 0) . '%';
return $difference;
}
public function getReviews(){
$reviews = get_posts(array(
'post_type' => 'p_reviews',
'numberposts' => -1,
'meta_query' => array(
array(
'key' => 'products',
'value' => '"' . $this->id . '"',
'compare' => 'LIKE'
)
)
));
return $reviews;
}
public function getRelatedProducts(){
$related_products = [];
$related_products_ids = $this->product->get_upsell_ids();
if ($related_products_ids) {
foreach ($related_products_ids as $related_id) {
$related_product = Timber::get_post($related_id);
if ($related_product) {
$related_products[] = $related_product;
}
}
}
return $related_products;
}
};

@ -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);
?>

@ -31,12 +31,12 @@
}
.main-food_products-card.acc{
.main-food_products-card.--acc-{
background: #e7e7e7;
}
.main-food_products-card.acc .main-food_products-cardbottom-btn,
.main-food_products-card.acc .main-food_products-card-slider .swiper-pagination-bullet.swiper-pagination-bullet-active{
.main-food_products-card.--acc- .main-food_products-cardbottom-btn,
.main-food_products-card.--acc- .main-food_products-card-slider .swiper-pagination-bullet.swiper-pagination-bullet-active{
background: radial-gradient(278.91% 196.13% at 128.36% -48.29%, #ee6868 0%, #569ef0 57.69%);
}
.main-food_products-card-img {
@ -80,38 +80,40 @@ background: radial-gradient(278.91% 196.13% at 128.36% -48.29%, #ee6868 0%, #569
color: var(--accent-color);
}
.food.lamb.main-food_products-card {
.--food-lamb.main-food_products-card {
--accent-color: #863300;
}
.food.turkey.main-food_products-card {
.--food-turkey.main-food_products-card {
--accent-color: #CD0003;
}
.food.beef.main-food_products-card{
.--food-beef.main-food_products-card{
--accent-color: #7e0092;
}
.food.fish.main-food_products-card{
.--food-fish.main-food_products-card{
--accent-color: #48c1ed;
}
.treats.lamb.main-food_products-card {
.--treats-lamb.main-food_products-card {
--accent-color: #44006c;
}
.treats.rabbit.main-food_products-card {
.--treats-rabbit.main-food_products-card {
--accent-color: #601094;
}
.treats.beef.main-food_products-card{
.--treats-beef.main-food_products-card{
--accent-color: #004c31;
}
.treats.salmon.main-food_products-card{
.--treats-salmon.main-food_products-card{
--accent-color: #7c007a;
}
.treats.duck.main-food_products-card{
.--treats-duck.main-food_products-card{
--accent-color: #326e70;
}
@ -275,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'))
});
});

@ -51,10 +51,76 @@
background-color: var(--bg-color);
}
.product_main.--fish{
.product_main.--food-fish{
--bg-color: #C1DDE6;
--accent-color: #48c1ed;
}
.product_main.--food-lamb{
--bg-color: #E6D7C1;
--accent-color: #863300;
}
.product_main.--food-turkey{
--bg-color: #F3D7D2;
--accent-color: #CD0003;
}
.product_main.--food-beef{
--bg-color: #E6C1D7;
--accent-color: #7e0092;
}
.product_main.--food-salmon{
--bg-color: #F3D7C1;
--accent-color: #ff6b35;
}
.product_main.--food-rabbit{
--bg-color: #E6D7C1;
--accent-color: #8b4513;
}
.product_main.--food-duck{
--bg-color: #F3E6D7;
--accent-color: #d2691e;
}
.product_main.--treats-lamb{
--bg-color: #E6D7F3;
--accent-color: #44006c;
}
.product_main.--treats-rabbit{
--bg-color: #F3D7E6;
--accent-color: #601094;
}
.product_main.--treats-beef{
--bg-color: #D7F3E6;
--accent-color: #004c31;
}
.product_main.--treats-salmon{
--bg-color: #F3D7F3;
--accent-color: #7c007a;
}
.product_main.--treats-duck{
--bg-color: #D7F3F3;
--accent-color: #326e70;
}
.product_main.--treats-fish{
--bg-color: #D7E6F3;
--accent-color: #0066cc;
}
.product_main.--treats-turkey{
--bg-color: #F3E6D7;
--accent-color: #cc3300;
}
/* product info */
.product-info{
width: 364px;
@ -122,7 +188,9 @@
.product-values-item-val{
border: 1px solid #000;
border-radius: 100px;
width: 40px;
min-width: 40px;
padding-left: 5px;
padding-right: 5px;
height: 40px;
flex-shrink: 0;
display: flex;
@ -204,6 +272,7 @@ font-weight: 700;
.product-constructor__block-item svg, .product-constructor__block-item path{
fill: rgba(18, 18, 18, 0.75);
fill-opacity: 1;
}
.product-constructor__block-item.active{
color: #fff;
@ -255,7 +324,7 @@ color: #fa0505;
margin-top: 8px;
}
.product-btn{
.product-btn, .button{
border: 2px solid #fff;
border-radius: 100px;
padding: 12px 16px;
@ -271,6 +340,18 @@ margin-top: 8px;
margin-top: 20px;
background-color: var(--accent-color);
}
.button::before{
display: none;
}
.to-know{
font-size: 24px;
text-transform: uppercase;
font-weight: 700;
margin-top: 20px;
text-decoration: underline;
}
/* product price */
@ -596,6 +677,7 @@ line-height: 143%;
.product-show{
display: flex;
flex-grow: 1;
flex-direction: column;
}
@media (max-width: 576px) {
.product_main {
@ -671,3 +753,149 @@ line-height: 143%;
margin-top: 60px;
}
}
.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,29 +0,0 @@
<?php
$context = Timber::get_context();
include_component('shop', 'reviews');
include_component('shop', 'product-card');
$reviews = get_posts(array(
'post_type' => 'p_reviews',
'numberposts' => -1,
));
$product_id = get_the_ID();
$product = wc_get_product($product_id);
$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;
}
}
}
$context['reviews'] = $reviews;
Timber::render('shop/single-product_page.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) {
@ -401,3 +402,4 @@ function map_attr_slugs_to_class($slug) {
return $slug_map[$slug] ?? $slug; // Fallback to original slug if not found
}

@ -5,6 +5,10 @@
* This is the template that renders pages using Timber (Twig).
*/
/* TO_DO сюда перенести логику home.php */
$context = Timber::context();
$context['post'] = Timber::get_post();
if (is_account_page() && !is_wc_endpoint_url()){
include_module('profile');
@ -27,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,21 +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');
}
}

@ -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 );

@ -1,5 +1,8 @@
<?
/* TO_DO удалить файл, перенеся логику подключения файлов ajax-controller
в логику связанную с модулями ( что сейчас в temp-functions/modules-logic.php) */
// Функция для рекурсивного подключения файлов
function requireAjaxControllers($baseDir) {
$modules = glob($baseDir . '/*', GLOB_ONLYDIR);
@ -14,16 +17,16 @@ die();
require_once $moduleController;
}
$componentsDir = $module . '/components';
if (is_dir($componentsDir)) {
$components = glob($componentsDir . '/*', GLOB_ONLYDIR);
foreach ($components as $component) {
$componentController = $component . '/component-ajax-controller.php';
if (file_exists($componentController)) {
require_once $componentController;
}
}
}
// $componentsDir = $module . '/components';
// if (is_dir($componentsDir)) {
// $components = glob($componentsDir . '/*', GLOB_ONLYDIR);
// foreach ($components as $component) {
// $componentController = $component . '/component-ajax-controller.php';
// if (file_exists($componentController)) {
// require_once $componentController;
// }
// }
// }
}
}

@ -6,31 +6,37 @@ use Timber\Integrations\WooCommerce\Product as TimberProduct;
Timber::$dirname = [
[
'modules',
'templates'
],
'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,34 +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);
class WooProduct extends Timber\Post {
protected $wc_product;
public function __construct ($pid = null) {
parent::__construct($pid);
$this->wc_product = wc_get_product($this->ID);
}
public function price() {
return $this->wc_product->get_price();
}
public function get_price_html() {
return $this->wc_product->get_price_html();
}
public function get_attr() {
return $this->wc_product->get_attribute('pa_compound');
}
};
add_filter('timber/post/classmap', function ($classmap) {
$custom_classmap = [
'product' => WooProduct::class,
];
return array_merge($classmap, $custom_classmap);
});

@ -16,6 +16,9 @@ add_action('after_setup_theme', function() {
/* TO_DO | убюрать в ajax-контроллер модуля shop + переименовать в что-то связанное с ajax и "листинг" */
//Ajax подгрузка товаров в архиве
add_action( 'wp_ajax_nopriv_get_products', 'get_products' );
add_action( 'wp_ajax_get_products', 'get_products' );

@ -1,19 +0,0 @@
{% extends 'profile/profile-base.twig' %}
{% block profile_content%}
<div class="cabinet__orders cabinet-content {% if fn('is_wc_endpoint_url') %}active hide{% endif %}">
<!-- <div class="cabinet-card cabinet-card--green-white cabinet__subscription-pc">
<div class="cabinet-card__content">
<p class="cabinet-card__title">{{ function('pll_e', 'Feed subscription') }}</p>
<div class="cabinet-card__element">
<p class="cabinet-card__text">{{ function('pll_e', 'Weekly food delivery for your pet') }}</p>
</div>
<div class="cabinet-card__element">
<button class="button button--gradient button--high-46 form-sub__btn">{{ function('pll_e', 'Get details') }}</button>
</div>
</div>
</div> -->
{% include 'profile/order-list.twig' %}
</div>
</div>
{% endblock %}

@ -1,27 +0,0 @@
{% set profile_pg = 1 %}
{% extends 'profile/profile-base.twig' %}
{% block profile_content%}
<div class="cabinet__orders cabinet-content {% if fn('is_wc_endpoint_url') %}active hide{% endif %}">
<!-- <div class="cabinet-card cabinet-card--green-white cabinet__subscription-pc">
<div class="cabinet-card__content">
<p class="cabinet-card__title">{{ function('pll_e', 'Feed subscription') }}</p>
<div class="cabinet-card__element">
<p class="cabinet-card__text">{{ function('pll_e', 'Weekly food delivery for your pet') }}</p>
</div>
<div class="cabinet-card__element">
<button class="button button--gradient button--high-46 form-sub__btn">{{ function('pll_e', 'Get details') }}</button>
</div>
</div>
</div> -->
{% include 'profile/order-list.twig' %}
</div>
</div>
</main>
{% endblock %}

@ -1,289 +0,0 @@
{% set bodyClass = 'bg-white' %}
{% set mainClass = '' %}
{% extends 'layout.twig' %}
{% block content %}
<div class="product-single">
<div class="wrapper">
<div class="breadcrumbs">
<a href="https://cosmopet-test-dumb.cp.good-production.xyz/" class="breadcrumbs__item">
Главная
</a>
<a href="https://cosmopet-test-dumb.cp.good-production.xyz/product-category/dlia-sobak/korm-dlya-sobak/" class="breadcrumbs__item">
Корм для собак
</a>
<a href="" class="breadcrumbs__item">
{{ post.title }}
</a>
</div>
<div class="product_main --fish">
<div class="product-info">
<h1 class="product-title --pc">
Сухой корм,
для&nbsp;средних
и&nbsp;крупных пород
</h1>
<div class="product-contains">
<h2 class="product-block-title">СОСТАВ</h2>
<div class="product-contains-text">Животные белки 49% (Дегидрированная индейка 33%, белковый концентрат из Черной львинки 10%, печеночный гидролизат 5%, сухая плазма крови 1%), цельный рис, горошек, жир индейки, автолизат пивных дрожжей, витаминно-минеральный комплекс, лососевое масло (источник незаменимых Омега-3 жирных кислот), клетчатка гороха, таурин, комплекс пребиотиков (натуральные пребиотики FOS и MOS), экстракт граната, юкка Шидигера, экстракт розмарина (натуральный антиоксидант).</div>
</div>
<div class="product-values">
<h2 class="product-values-title">ПИЩЕВАЯ ЦЕННОСТЬ</h2>
<div class="product-values-list">
<div class="product-values-item">
<div class="product-values-item__name">Сырой белок</div>
<div class="product-values-item-val">14%</div>
</div>
<div class="product-values-item">
<div class="product-values-item__name">Сырой жир</div>
<div class="product-values-item-val">8%</div>
</div>
<div class="product-values-item">
<div class="product-values-item__name">Сырая клетчатка</div>
<div class="product-values-item-val">26%</div>
</div>
</div>
<div class="product-vitamins">
<div class="product-vitamins-title">
Витамины на кг
</div>
<div class="product-vitamins-list">
<div class="product-vitamins-item">A, ME — 34000</div>
<div class="product-vitamins-item">D3, ME — 1600</div>
</div>
</div>
</div>
</div>
{% set thumbnail = product.get_image_id() ? function('wp_get_attachment_image_url', product.get_image_id(), '') : function('wc_placeholder_img_src') %}
{% set images = product.get_gallery_image_ids() %}
<div class="product-show">
<div class="product-gallery">
<div style="--swiper-navigation-color: #fff; --swiper-pagination-color: #fff" class="swiper product-gallery__mainSlider">
<div class="swiper-wrapper">
<div class="swiper-slide">
<img src="{{ thumbnail }}" alt="">
</div>
{% if images %}
{% for image in images %}
<div class="swiper-slide"><img src="{{ function('wp_get_attachment_image_url', image, '') }}" alt=""></div>
{% endfor %}
{% endif %}
</div>
</div>
<div thumbsSlider="" class="swiper product-gallery__thumbsSlider">
<div class="swiper-wrapper">
<div class="swiper-slide">
<img src="{{ thumbnail }}" alt="">
</div>
{% if images %}
{% for image in images %}
<div class="swiper-slide"><img src="{{ function('wp_get_attachment_image_url', image, '') }}" alt=""></div>
{% endfor %}
{% endif %}
</div>
<div class="product-gallery-arrows">
<button class="product-gallery-arrow product-gallery-prev"></button>
<button class="product-gallery-arrow product-gallery-next"></button>
</div>
</div>
<div class="product-gallery-bullets"></div>
</div>
<h1 class="product-title --m">
Сухой корм,
для&nbsp;средних
и&nbsp;крупных пород
</h1>
</div>
<div class="product-constructor">
<div class="product-constructor__block">
<div class="product-constructor__block-title">ВКУС КОРМА</div>
<div class="product-constructor__block-list">
<a href="" class="product-constructor__block-item">
<img src="/wp-content/themes/cosmopet/modules/shop/components/single-product_new/assets/img/beef.svg" alt="" class="product-constructor__block-item-icon svg">
<div class="product-constructor__block-item-name">Говядина</div>
</a>
<a href="" class="product-constructor__block-item active">
<img src="/wp-content/themes/cosmopet/modules/shop/components/single-product_new/assets/img/fish.svg" alt="" class="product-constructor__block-item-icon active svg">
<div class="product-constructor__block-item-name">Рыба</div>
</a>
<a href="" class="product-constructor__block-item">
<img src="/wp-content/themes/cosmopet/modules/shop/components/single-product_new/assets/img/turkey.svg" alt="" class="product-constructor__block-item-icon svg">
<div class="product-constructor__block-item-name">Индейка</div>
</a>
<a href="" class="product-constructor__block-item">
<img src="/wp-content/themes/cosmopet/modules/shop/components/single-product_new/assets/img/lamb.svg" alt="" class="product-constructor__block-item-icon svg">
<div class="product-constructor__block-item-name">Ягненок</div>
</a>
</div>
</div>
<div class="product-constructor__block">
<div class="product-constructor__block-title">РАЗМЕР СОБАКИ</div>
<div class="product-constructor__block-list">
<a href="" class="product-constructor__block-item active">
<img src="/wp-content/themes/cosmopet/modules/shop/components/single-product_new/assets/img/mini.svg" alt="" class="product-constructor__block-item-icon svg">
<div class="product-constructor__block-item-name">Мини</div>
</a>
<a href="" class="product-constructor__block-item">
<img src="/wp-content/themes/cosmopet/modules/shop/components/single-product_new/assets/img/avg.svg" alt="" class="product-constructor__block-item-icon active svg">
<div class="product-constructor__block-item-name">Мелкие</div>
</a>
<a href="" class="product-constructor__block-item">
<img src="/wp-content/themes/cosmopet/modules/shop/components/single-product_new/assets/img/lg.svg" alt="" class="product-constructor__block-item-icon svg">
<div class="product-constructor__block-item-name">Средние и крупные</div>
</a>
</div>
</div>
<div class="product-constructor__block">
<div class="product-constructor__block-title">ВОЗРАСТ СОБАКИ</div>
<div class="product-constructor__block-list">
<a href="" class="product-constructor__block-item active">
<img src="/wp-content/themes/cosmopet/modules/shop/components/single-product_new/assets/img/dog-face.svg" alt="" class="product-constructor__block-item-icon svg">
<div class="product-constructor__block-item-name">до 1 года</div>
</a>
<a href="" class="product-constructor__block-item">
<img src="/wp-content/themes/cosmopet/modules/shop/components/single-product_new/assets/img/dog-face.svg" alt="" class="product-constructor__block-item-icon active svg">
<div class="product-constructor__block-item-name">1-7 лет</div>
</a>
<a href="" class="product-constructor__block-item">
<img src="/wp-content/themes/cosmopet/modules/shop/components/single-product_new/assets/img/dog-face.svg" alt="" class="product-constructor__block-item-icon svg">
<div class="product-constructor__block-item-name">7+ лет</div>
</a>
</div>
</div>
<div class="product-constructor__block">
<div class="product-constructor__block-title">ВЕС УПАКОВКИ</div>
<div class="product-constructor__block-list">
<a href="" class="product-constructor__block-item active">
<div class="product-constructor__block-item-name">0.8 кг</div>
</a>
<a href="" class="product-constructor__block-item">
<div class="product-constructor__block-item-name">2 кг</div>
</a>
<a href="" class="product-constructor__block-item">
<div class="product-constructor__block-item-name">5 кг</div>
</a>
</div>
</div>
<div class="product-constructor__block">
<div class="product-constructor__block-title">СЕРИЯ</div>
<div class="product-constructor__block-list">
<a href="" class="product-constructor__block-item active">
<div class="product-constructor__block-item-name">Стандартная</div>
</a>
<a href="" class="product-constructor__block-item">
<div class="product-constructor__block-item-name">PRO - с пробиотиками</div>
</a>
</div>
</div>
<div class="product-price">
<div class="product-price-main">
1303
</div>
<div class="product-price-disc">
<div class="product-price-old">1449₽</div>
<div class="product-price-percent">
- 10%
</div>
</div>
</div>
<button class="product-btn">
<span>Добавить в корзину</span>
</button>
</div>
</div>
<div class="product-desc">
<div class="product-block-title">
ОПИСАНИЕ
</div>
<div class="product-desc-text">
Этот легкоусвояемый рацион содержит исключительно натуральные ингредиенты. Он обогащён белком, витаминами, минералами и антиоксидантами. Благодаря своей высокопитательной рецептуре, Cosmocat способствует здоровому росту и развитию котёнка
</div>
</div>
</div>
<div class="product-reviews">
<div class="product-reviews__head wrapper">
<div class="product-block-title">
Отзывы Специалистов
</div>
<div class="product-block-arrows">
<button class="slider-button-prev slider-button product-reviews-prev"></button>
<button class="slider-button-next slider-button product-reviews-next"></button>
</div>
</div>
<div class="product-reviews-slider swiper">
<div class="swiper-wrapper">
{% for slide in reviews %}
{% include "shop/reviews-slide_element.twig" with {slide: slide} %}
{% endfor %}
</div>
</div>
<div class="product-block-arrows--m">
<button class="slider-button-prev slider-button product-reviews-prev"></button>
<button class="slider-button-next slider-button product-reviews-next"></button>
</div>
</div>
{% if related_products %}
<div class="product-similar ">
<div class="product-similar__head wrapper">
<div class="product-block-title">
вашему питомцу может понравиться
</div>
<div class="product-block-arrows">
<button class="slider-button-prev slider-button product-similar-prev"></button>
<button class="slider-button-next slider-button product-similar-next"></button>
</div>
</div>
<div class="product-similar-slider wrapper">
<div class="swiper-wrapper">
{% for related_product in related_products %}
{% set categories = related_product.get_category_ids() %}
{% set section = '' %}
{% for cat in categories %}
{% if cat == 365 %}
{% set section = 'food swiper-slide' %}
{% elseif cat == 367 %}
{% set section = 'treats swiper-slide' %}
{% endif %}
{% endfor %}
{% if section == '' %}
{% set section = 'acc swiper-slide' %}
{% endif %}
{% include 'shop/product-card_element.twig' with {_product : fn('wc_get_product', related_product), section: section} %}
{% endfor %}
</div>
</div>
<div class="product-block-arrows--m">
<button class="slider-button-prev slider-button product-similar-prev"></button>
<button class="slider-button-next slider-button product-similar-next"></button>
</div>
</div>
{% endif %}
</div>
{% for slide in reviews %}
{% include 'modal/rewiew_modal.twig' with {slide : slide} %}
{% endfor %}
{% endblock %}

@ -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,17 +147,19 @@
{% endif %}
</form>
{% set s_in_stock = TimberPost(product.id).meta('_stock_status') == 'instock' %}
{% if s_in_stock %}
{{ function('do_action', 'woocommerce_' ~ product.get_type() ~ '_add_to_cart') }}
{% else %}
{# Start | Вывод кнопки узнать о поступлении если товара нет в наличии #}
<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>
{# End | Вывод кнопки узнать о поступлении если товара нет в наличии #}
{% endif %}
<div id="product-buy-block-wrapper">
{% if s_in_stock %}
{{ function('do_action', 'woocommerce_' ~ product.get_type() ~ '_add_to_cart') }}
{% else %}
{# Start | Вывод кнопки узнать о поступлении если товара нет в наличии #}
<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>
{# 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 %}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save