feat: integrate yandex metrika ecommerce
This commit is contained in:
@@ -7,6 +7,7 @@ use Cart\Currency;
|
||||
use Config;
|
||||
use Language;
|
||||
use Loader;
|
||||
use ModelCatalogCategory;
|
||||
use ModelCatalogProduct;
|
||||
use ModelDesignBanner;
|
||||
use ModelSettingSetting;
|
||||
@@ -27,6 +28,7 @@ use Url;
|
||||
* @property ModelCatalogProduct $model_catalog_product
|
||||
* @property ModelSettingSetting $model_setting_setting
|
||||
* @property ModelDesignBanner $model_design_banner
|
||||
* @property ModelCatalogCategory $model_catalog_category
|
||||
*/
|
||||
class OcRegistryDecorator
|
||||
{
|
||||
|
||||
@@ -55,7 +55,7 @@ class ProductsHandler
|
||||
], Response::HTTP_NOT_FOUND);
|
||||
} catch (Exception $exception) {
|
||||
$this->logger->logException($exception);
|
||||
throw new RuntimeException('Error get product with id ' . $productId, 500, $exception);
|
||||
throw new RuntimeException('Error get product with id ' . $productId, 500);
|
||||
}
|
||||
|
||||
return new JsonResponse([
|
||||
|
||||
@@ -56,6 +56,7 @@ class SettingsHandler
|
||||
'store_enabled' => $this->settings->get('store_enabled'),
|
||||
'feature_coupons' => $this->settings->get('feature_coupons') ?? false,
|
||||
'feature_vouchers' => $this->settings->get('feature_vouchers') ?? false,
|
||||
'currency_code' => $this->settings->get('oc_default_currency', 'RUB'),
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
@@ -195,6 +195,7 @@ class OrderCreateService
|
||||
'total' => $orderData['total'],
|
||||
'final_total_numeric' => $orderData['total_numeric'],
|
||||
'currency' => $currencyCode,
|
||||
'products' => $products,
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ use Openguru\OpenCartFramework\OpenCart\Decorators\OcRegistryDecorator;
|
||||
use Openguru\OpenCartFramework\QueryBuilder\Builder;
|
||||
use Openguru\OpenCartFramework\QueryBuilder\JoinClause;
|
||||
use Openguru\OpenCartFramework\QueryBuilder\RawExpression;
|
||||
use Openguru\OpenCartFramework\QueryBuilder\Table;
|
||||
use Openguru\OpenCartFramework\Support\Arr;
|
||||
use Openguru\OpenCartFramework\Support\PaginationHelper;
|
||||
|
||||
@@ -61,6 +62,8 @@ class ProductsService
|
||||
$filters = $params['filters'] ?? [];
|
||||
|
||||
$customerGroupId = (int) $this->settings->get('oc_customer_group_id');
|
||||
$currency = $this->settings->get('oc_default_currency');
|
||||
|
||||
$specialPriceSql = "(SELECT price
|
||||
FROM oc_product_special ps
|
||||
WHERE ps.product_id = products.product_id
|
||||
@@ -78,6 +81,8 @@ class ProductsService
|
||||
'products.price' => 'price',
|
||||
'products.image' => 'product_image',
|
||||
'products.tax_class_id' => 'tax_class_id',
|
||||
'manufacturer.name' => 'manufacturer_name',
|
||||
'category_description.name' => 'category_name',
|
||||
new RawExpression($specialPriceSql),
|
||||
])
|
||||
->from(db_table('product'), 'products')
|
||||
@@ -88,6 +93,20 @@ class ProductsService
|
||||
->where('product_description.language_id', '=', $languageId);
|
||||
}
|
||||
)
|
||||
->leftJoin(new Table(db_table('manufacturer'), 'manufacturer'), function (JoinClause $join) {
|
||||
$join->on('products.manufacturer_id', '=', 'manufacturer.manufacturer_id');
|
||||
})
|
||||
->leftJoin(new Table(db_table('product_to_category'), 'product_to_category'), function (JoinClause $join) {
|
||||
$join->on('products.product_id', '=', 'product_to_category.product_id')
|
||||
->where('product_to_category.main_category', '=', 1);
|
||||
})
|
||||
->leftJoin(
|
||||
new Table(db_table('category_description'), 'category_description'),
|
||||
function (JoinClause $join) use ($languageId) {
|
||||
$join->on('product_to_category.category_id', '=', 'category_description.category_id')
|
||||
->where('category_description.language_id', '=', $languageId);
|
||||
}
|
||||
)
|
||||
->where('products.status', '=', 1)
|
||||
->whereRaw('products.date_available < NOW()')
|
||||
->when($search, function (Builder $query) use ($search) {
|
||||
@@ -136,7 +155,7 @@ class ProductsService
|
||||
}
|
||||
|
||||
return [
|
||||
'data' => array_map(function ($product) use ($productsImagesMap, $imageWidth, $imageHeight) {
|
||||
'data' => array_map(function ($product) use ($productsImagesMap, $imageWidth, $imageHeight, $currency) {
|
||||
$allImages = [];
|
||||
|
||||
$image = $this->ocImageTool->resize(
|
||||
@@ -151,24 +170,24 @@ class ProductsService
|
||||
'alt' => Utils::htmlEntityEncode($product['product_name']),
|
||||
];
|
||||
|
||||
$price = $this->currency->format(
|
||||
$this->tax->calculate(
|
||||
$product['price'],
|
||||
$product['tax_class_id'],
|
||||
$this->settings->get('oc_config_tax'),
|
||||
),
|
||||
$this->settings->get('oc_default_currency'),
|
||||
$priceNumeric = $this->tax->calculate(
|
||||
$product['price'],
|
||||
$product['tax_class_id'],
|
||||
$this->settings->get('oc_config_tax'),
|
||||
);
|
||||
$price = $this->currency->format($priceNumeric, $currency);
|
||||
|
||||
$special = false;
|
||||
$specialPriceNumeric = null;
|
||||
if ($product['special'] && (float) $product['special'] >= 0) {
|
||||
$specialPriceNumeric = $this->tax->calculate(
|
||||
$product['special'],
|
||||
$product['tax_class_id'],
|
||||
$this->settings->get('oc_config_tax'),
|
||||
);
|
||||
$special = $this->currency->format(
|
||||
$this->tax->calculate(
|
||||
$product['special'],
|
||||
$product['tax_class_id'],
|
||||
$this->settings->get('oc_config_tax'),
|
||||
),
|
||||
$this->settings->get('oc_default_currency'),
|
||||
$specialPriceNumeric,
|
||||
$currency,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -183,6 +202,11 @@ class ProductsService
|
||||
'price' => $price,
|
||||
'special' => $special,
|
||||
'images' => $allImages,
|
||||
'special_numeric' => $specialPriceNumeric,
|
||||
'price_numeric' => $priceNumeric,
|
||||
'final_price_numeric' => $specialPriceNumeric ?: $priceNumeric,
|
||||
'manufacturer_name' => $product['manufacturer_name'],
|
||||
'category_name' => $product['category_name'],
|
||||
];
|
||||
}, $products),
|
||||
|
||||
@@ -400,8 +424,27 @@ class ProductsService
|
||||
|
||||
$data['recurrings'] = $this->oc->model_catalog_product->getProfiles($productId);
|
||||
|
||||
$data['category'] = $this->getProductMainCategory($productId);
|
||||
$data['id'] = $productId;
|
||||
|
||||
$this->oc->model_catalog_product->updateViewed($productId);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
private function getProductMainCategory(int $productId): ?array
|
||||
{
|
||||
return $this->queryBuilder->newQuery()
|
||||
->select([
|
||||
'category_description.category_id' => 'id',
|
||||
'category_description.name' => 'name',
|
||||
])
|
||||
->from(db_table('category_description'), 'category_description')
|
||||
->join(new Table(db_table('product_to_category'), 'product_to_category'), function (JoinClause $join) {
|
||||
$join->on('product_to_category.category_id', '=', 'category_description.category_id')
|
||||
->where('product_to_category.main_category', '=', 1);
|
||||
})
|
||||
->where('product_to_category.product_id', '=', $productId)
|
||||
->firstOrNull();
|
||||
}
|
||||
}
|
||||
0
module/oc_telegram_shop/upload/oc_telegram_shop/src/Support/Utils.php
Normal file → Executable file
0
module/oc_telegram_shop/upload/oc_telegram_shop/src/Support/Utils.php
Normal file → Executable file
0
module/oc_telegram_shop/upload/oc_telegram_shop/tests/Unit/RequestTest.php
Normal file → Executable file
0
module/oc_telegram_shop/upload/oc_telegram_shop/tests/Unit/RequestTest.php
Normal file → Executable file
@@ -7,11 +7,11 @@
|
||||
class="products-grid grid grid-cols-2 gap-x-5 gap-y-5 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 xl:gap-x-8"
|
||||
>
|
||||
<RouterLink
|
||||
v-for="product in products"
|
||||
v-for="(product, index) in products"
|
||||
:key="product.id"
|
||||
class="product-grid-card group"
|
||||
:to="`/product/${product.id}`"
|
||||
@click="haptic"
|
||||
@click="productClick(product, index)"
|
||||
>
|
||||
<ProductImageSwiper :images="product.images"/>
|
||||
<h3 class="product-title mt-4 text-sm">{{ product.name }}</h3>
|
||||
@@ -54,7 +54,9 @@ import ProductImageSwiper from "@/components/ProductImageSwiper.vue";
|
||||
import {useSettingsStore} from "@/stores/SettingsStore.js";
|
||||
import {ref} from "vue";
|
||||
import {useIntersectionObserver} from '@vueuse/core';
|
||||
import {useYaMetrikaStore} from "@/stores/yaMetrikaStore.js";
|
||||
|
||||
const yaMetrika = useYaMetrikaStore();
|
||||
const settings = useSettingsStore();
|
||||
const bottom = ref(null);
|
||||
|
||||
@@ -87,8 +89,26 @@ const props = defineProps({
|
||||
}
|
||||
});
|
||||
|
||||
function haptic() {
|
||||
function productClick(product, index) {
|
||||
window.Telegram.WebApp.HapticFeedback.selectionChanged();
|
||||
yaMetrika.dataLayerPush({
|
||||
"ecommerce": {
|
||||
"currencyCode": settings.currency_code,
|
||||
"click": {
|
||||
"products": [
|
||||
{
|
||||
"id": product.id,
|
||||
"name": product.name,
|
||||
"price": product.final_price_numeric,
|
||||
"brand": product.manufacturer_name,
|
||||
"category": product.category_name,
|
||||
"list": "Главная страница",
|
||||
"position": index,
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
useIntersectionObserver(
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export const YA_METRIKA_GOAL = {
|
||||
ADD_TO_CART: 'add_to_cart',
|
||||
PRODUCT_OPEN_EXTERNAL: 'product_open_external',
|
||||
CREATE_ORDER: 'create_order',
|
||||
ORDER_CREATED_SUCCESS: 'order_created_success',
|
||||
VIEW_PRODUCT: 'view_product',
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import {defineStore} from "pinia";
|
||||
import {isNotEmpty} from "@/helpers.js";
|
||||
import {addToCart, cartEditItem, cartRemoveItem, getCart, setCoupon, setVoucher} from "@/utils/ftch.js";
|
||||
import {useYaMetrikaStore} from "@/stores/yaMetrikaStore.js";
|
||||
import {useSettingsStore} from "@/stores/SettingsStore.js";
|
||||
|
||||
export const useCartStore = defineStore('cart', {
|
||||
state: () => ({
|
||||
@@ -79,12 +81,27 @@ export const useCartStore = defineStore('cart', {
|
||||
}
|
||||
},
|
||||
|
||||
async removeItem(rowId) {
|
||||
async removeItem(cartItem, rowId, index = 0) {
|
||||
try {
|
||||
this.isLoading = true;
|
||||
const formData = new FormData();
|
||||
formData.append('key', rowId);
|
||||
await cartRemoveItem(formData);
|
||||
useYaMetrikaStore().dataLayerPush({
|
||||
"ecommerce": {
|
||||
"currencyCode": useSettingsStore().currency_code,
|
||||
"remove": {
|
||||
"products": [
|
||||
{
|
||||
"id": cartItem.product_id,
|
||||
"name": cartItem.name,
|
||||
"quantity": cartItem.quantity,
|
||||
"position": index
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
});
|
||||
await this.getProducts();
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
|
||||
@@ -2,6 +2,9 @@ import {defineStore} from "pinia";
|
||||
import {isNotEmpty} from "@/helpers.js";
|
||||
import {storeOrder} from "@/utils/ftch.js";
|
||||
import {useCartStore} from "@/stores/CartStore.js";
|
||||
import {YA_METRIKA_GOAL} from "@/constants/yaMetrikaGoals.js";
|
||||
import {useYaMetrikaStore} from "@/stores/yaMetrikaStore.js";
|
||||
import {useSettingsStore} from "@/stores/SettingsStore.js";
|
||||
|
||||
export const useCheckoutStore = defineStore('checkout', {
|
||||
state: () => ({
|
||||
@@ -56,6 +59,37 @@ export const useCheckoutStore = defineStore('checkout', {
|
||||
const response = await storeOrder(this.customer);
|
||||
this.order = response.data;
|
||||
|
||||
if (! this.order.id) {
|
||||
console.debug(response.data);
|
||||
throw new Error('Ошибка создания заказа.');
|
||||
}
|
||||
|
||||
const yaMetrika = useYaMetrikaStore();
|
||||
yaMetrika.reachGoal(YA_METRIKA_GOAL.ORDER_CREATED_SUCCESS, {
|
||||
price: this.order?.final_total_numeric,
|
||||
currency: this.order?.currency,
|
||||
});
|
||||
yaMetrika.dataLayerPush({
|
||||
"ecommerce": {
|
||||
"currencyCode": useSettingsStore().currency_code,
|
||||
"purchase": {
|
||||
"actionField": {
|
||||
"id": this.order.id,
|
||||
'revenue': this.order?.final_total_numeric,
|
||||
},
|
||||
"products": this.order.products ? this.order.products.map((product, index) => {
|
||||
return {
|
||||
id: product.product_id,
|
||||
name: product.name,
|
||||
price: product.total_numeric,
|
||||
position: index,
|
||||
quantity: product.quantif,
|
||||
};
|
||||
}) : [],
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
await window.Telegram.WebApp.HapticFeedback.notificationOccurred('success');
|
||||
await useCartStore().getProducts();
|
||||
} catch (error) {
|
||||
|
||||
@@ -17,6 +17,7 @@ export const useSettingsStore = defineStore('settings', {
|
||||
ya_metrika_enabled: false,
|
||||
feature_coupons: false,
|
||||
feature_vouchers: false,
|
||||
currency_code: null,
|
||||
theme: {
|
||||
light: 'light', dark: 'dark', variables: {
|
||||
'--product_list_title_max_lines': 2,
|
||||
@@ -44,6 +45,7 @@ export const useSettingsStore = defineStore('settings', {
|
||||
this.store_enabled = settings.store_enabled;
|
||||
this.feature_coupons = settings.feature_coupons;
|
||||
this.feature_vouchers = settings.feature_vouchers;
|
||||
this.currency_code = settings.currency_code;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -11,8 +11,7 @@ export const useYaMetrikaStore = defineStore('ya_metrika', {
|
||||
|
||||
actions: {
|
||||
pushHit(url, params = {}) {
|
||||
const settings = useSettingsStore();
|
||||
if (!settings.ya_metrika_enabled) {
|
||||
if (!useSettingsStore().ya_metrika_enabled) {
|
||||
console.debug('[ym] Yandex Metrika disabled in settings.');
|
||||
return;
|
||||
}
|
||||
@@ -39,8 +38,7 @@ export const useYaMetrikaStore = defineStore('ya_metrika', {
|
||||
},
|
||||
|
||||
reachGoal(target, params = {}) {
|
||||
const settings = useSettingsStore();
|
||||
if (!settings.ya_metrika_enabled) {
|
||||
if (!useSettingsStore().ya_metrika_enabled) {
|
||||
console.debug('[ym] Yandex Metrika disabled in settings.');
|
||||
return;
|
||||
}
|
||||
@@ -61,6 +59,11 @@ export const useYaMetrikaStore = defineStore('ya_metrika', {
|
||||
},
|
||||
|
||||
initUserParams() {
|
||||
if (!useSettingsStore().ya_metrika_enabled) {
|
||||
console.debug('[ym] Yandex Metrika disabled in settings.');
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof window.ym === 'function' && window.YA_METRIKA_ID !== undefined) {
|
||||
let tgID = null;
|
||||
|
||||
@@ -95,6 +98,9 @@ export const useYaMetrikaStore = defineStore('ya_metrika', {
|
||||
window.ym(window.YA_METRIKA_ID, item.event, item.payload.url, item.payload.params);
|
||||
} else if (item.event === 'reachGoal') {
|
||||
window.ym(window.YA_METRIKA_ID, item.event, item.payload.target, item.payload.params);
|
||||
} else if (item.event === 'dataLayer') {
|
||||
console.debug('[ym] queue dataLayer push: ', item.payload);
|
||||
window.dataLayer.push(item.payload);
|
||||
} else {
|
||||
console.error('[ym] Unsupported queue event: ', item.event);
|
||||
}
|
||||
@@ -102,5 +108,23 @@ export const useYaMetrikaStore = defineStore('ya_metrika', {
|
||||
|
||||
console.debug('[ym] Queue processing complete. Size: ', this.queue.length);
|
||||
},
|
||||
|
||||
dataLayerPush(object) {
|
||||
if (!useSettingsStore().ya_metrika_enabled) {
|
||||
console.debug('[ym] Yandex Metrika disabled in settings.');
|
||||
return;
|
||||
}
|
||||
|
||||
if (Array.isArray(window.dataLayer)) {
|
||||
console.debug('[ym] dataLayer push: ', object);
|
||||
window.dataLayer.push(object);
|
||||
} else {
|
||||
console.debug('[ym] dataLayer inaccessible. Put to queue');
|
||||
this.queue.push({
|
||||
event: 'dataLayer',
|
||||
payload: object,
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@@ -24,6 +24,7 @@ export function injectYaMetrika() {
|
||||
console.debug('[Init] Detected Yandex.Metrika ID:', window.YA_METRIKA_ID);
|
||||
const yaMetrika = useYaMetrikaStore();
|
||||
yaMetrika.initUserParams();
|
||||
window.dataLayer = window.dataLayer || [];
|
||||
yaMetrika.processQueue();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
|
||||
<div v-if="cart.items.length > 0">
|
||||
<div
|
||||
v-for="item in cart.items"
|
||||
v-for="(item, index) in cart.items"
|
||||
:key="item.cart_id"
|
||||
class="card card-border bg-base-100 card-sm mb-3"
|
||||
:class="item.stock === false ? 'border-error' : ''"
|
||||
@@ -62,7 +62,7 @@
|
||||
v-model="item.quantity"
|
||||
@update:modelValue="cart.setQuantity(item.cart_id, $event)"
|
||||
/>
|
||||
<button class="btn btn-error" @click="removeItem(item.cart_id)" :disabled="cart.isLoading">
|
||||
<button class="btn btn-error" @click="removeItem(item, item.cart_id, index)" :disabled="cart.isLoading">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="m14.74 9-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 0 1-2.244 2.077H8.084a2.25 2.25 0 0 1-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 0 0-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 0 1 3.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 0 0-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 0 0-7.5 0" />
|
||||
</svg>
|
||||
@@ -162,8 +162,8 @@ const lastTotal = computed(() => {
|
||||
return cart.totals.at(-1) ?? null;
|
||||
});
|
||||
|
||||
function removeItem(cartId) {
|
||||
cart.removeItem(cartId);
|
||||
function removeItem(cartItem, cartId, index) {
|
||||
cart.removeItem(cartItem, cartId, index);
|
||||
window.Telegram.WebApp.HapticFeedback.notificationOccurred('error');
|
||||
}
|
||||
|
||||
|
||||
@@ -74,6 +74,7 @@ import {computed, onMounted, ref} from "vue";
|
||||
import {IMaskComponent} from "vue-imask";
|
||||
import {useYaMetrikaStore} from "@/stores/yaMetrikaStore.js";
|
||||
import {YA_METRIKA_GOAL} from "@/constants/yaMetrikaGoals.js";
|
||||
import {useSettingsStore} from "@/stores/SettingsStore.js";
|
||||
|
||||
const checkout = useCheckoutStore();
|
||||
const yaMetrika = useYaMetrikaStore();
|
||||
@@ -92,11 +93,9 @@ async function onCreateBtnClick() {
|
||||
price: checkout.order?.final_total_numeric,
|
||||
currency: checkout.order?.currency,
|
||||
});
|
||||
|
||||
await checkout.makeOrder();
|
||||
yaMetrika.reachGoal(YA_METRIKA_GOAL.ORDER_CREATED_SUCCESS, {
|
||||
price: checkout.order?.final_total_numeric,
|
||||
currency: checkout.order?.currency,
|
||||
});
|
||||
|
||||
router.push({name: 'order_created'});
|
||||
} catch {
|
||||
error.value = 'Невозможно создать заказ.';
|
||||
|
||||
@@ -39,6 +39,7 @@ import {useProductFiltersStore} from "@/stores/ProductFiltersStore.js";
|
||||
import Banner from "@/components/Banner.vue";
|
||||
import {useYaMetrikaStore} from "@/stores/yaMetrikaStore.js";
|
||||
import {YA_METRIKA_GOAL} from "@/constants/yaMetrikaGoals.js";
|
||||
import {useSettingsStore} from "@/stores/SettingsStore.js";
|
||||
|
||||
defineOptions({
|
||||
name: 'Home'
|
||||
@@ -48,12 +49,14 @@ const router = useRouter();
|
||||
const filtersStore = useProductFiltersStore();
|
||||
const yaMetrika = useYaMetrikaStore();
|
||||
const haptic = window.Telegram.WebApp.HapticFeedback;
|
||||
const settings = useSettingsStore();
|
||||
|
||||
const products = ref([]);
|
||||
const hasMore = ref(false);
|
||||
const isLoading = ref(false);
|
||||
const isLoadingMore = ref(false);
|
||||
const page = ref(1);
|
||||
const perPage = 20;
|
||||
|
||||
function showFilters() {
|
||||
haptic.impactOccurred('soft');
|
||||
@@ -67,11 +70,31 @@ async function fetchProducts() {
|
||||
console.debug('Home: Fetch products from server using filters: ', toRaw(filtersStore.applied));
|
||||
const response = await ftch('products', null, toRaw({
|
||||
page: page.value,
|
||||
perPage: perPage,
|
||||
filters: filtersStore.applied,
|
||||
}));
|
||||
products.value = response.data;
|
||||
hasMore.value = response.meta.hasMore;
|
||||
console.debug('Home: Products for main page loaded.');
|
||||
|
||||
yaMetrika.dataLayerPush({
|
||||
ecommerce: {
|
||||
currencyCode: settings.currency_code,
|
||||
impressions: products.value.map((product, index) => {
|
||||
return {
|
||||
id: product.id,
|
||||
name: product.name,
|
||||
price: product.final_price_numeric,
|
||||
brand: product.manufacturer_name,
|
||||
category: product.category_name,
|
||||
list: 'Главная страница',
|
||||
position: index,
|
||||
discount: product.price_numeric - product.final_price_numeric,
|
||||
quantity: product.product_quantity,
|
||||
};
|
||||
}),
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
} finally {
|
||||
|
||||
@@ -224,6 +224,25 @@ async function onCartBtnClick() {
|
||||
price: product.value.final_price_numeric,
|
||||
currency: product.value.currency,
|
||||
});
|
||||
yaMetrika.dataLayerPush({
|
||||
"ecommerce": {
|
||||
"currencyCode": settings.currency_code,
|
||||
"add": {
|
||||
"products": [
|
||||
{
|
||||
"id": product.value?.id,
|
||||
"name": product.value?.name,
|
||||
"price": product.value?.final_price_numeric,
|
||||
"brand": product.value?.manufacturer,
|
||||
"category": product.value?.category?.name,
|
||||
"quantity": 1,
|
||||
"list": "Выдача категории",
|
||||
"position": 2
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
window.Telegram.WebApp.HapticFeedback.selectionChanged();
|
||||
await router.push({'name': 'cart'});
|
||||
@@ -240,6 +259,11 @@ function openProductInMarketplace() {
|
||||
return;
|
||||
}
|
||||
|
||||
yaMetrika.reachGoal(YA_METRIKA_GOAL.PRODUCT_OPEN_EXTERNAL, {
|
||||
price: product.value?.final_price_numeric,
|
||||
currency: product.value?.currency,
|
||||
});
|
||||
|
||||
window.Telegram.WebApp.openLink(product.value.share, {try_instant_view: false});
|
||||
}
|
||||
|
||||
@@ -285,6 +309,23 @@ onMounted(async () => {
|
||||
price: data.final_price_numeric,
|
||||
currency: data.currency,
|
||||
});
|
||||
|
||||
yaMetrika.dataLayerPush({
|
||||
"ecommerce": {
|
||||
"currencyCode": settings.currency_code,
|
||||
"detail": {
|
||||
"products": [
|
||||
{
|
||||
"id": data.product_id,
|
||||
"name": data.name,
|
||||
"price": data.final_price_numeric,
|
||||
"brand": data.manufacturer,
|
||||
"category": data.category?.name,
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
} finally {
|
||||
|
||||
Reference in New Issue
Block a user