feat: WIP add yandex metrika goals
This commit is contained in:
@@ -547,17 +547,18 @@ HTML,
|
|||||||
],
|
],
|
||||||
|
|
||||||
'orders' => [
|
'orders' => [
|
||||||
'module_tgshop_order_customer_group_id' => [
|
|
||||||
'type' => 'select',
|
|
||||||
'options' => $this->getCustomerGroups(),
|
|
||||||
'help' => 'Группа покупателей, которая будет назначена для заказов, оформленных через Telegram-магазин.',
|
|
||||||
],
|
|
||||||
|
|
||||||
'module_tgshop_order_default_status_id' => [
|
'module_tgshop_order_default_status_id' => [
|
||||||
'type' => 'select',
|
'type' => 'select',
|
||||||
'options' => $this->getOrderStatuses(),
|
'options' => $this->getOrderStatuses(),
|
||||||
'help' => 'Статус, с которым будут создаваться заказы через Telegram по умолчанию.',
|
'help' => 'Статус, с которым будут создаваться заказы через Telegram по умолчанию.',
|
||||||
],
|
],
|
||||||
|
|
||||||
|
'module_tgshop_order_customer_group_id' => [
|
||||||
|
'hidden' => true,
|
||||||
|
'type' => 'select',
|
||||||
|
'options' => $this->getCustomerGroups(),
|
||||||
|
'help' => 'Группа покупателей, которая будет назначена для заказов, оформленных через Telegram-магазин.',
|
||||||
|
],
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,8 +45,7 @@ class ControllerExtensionTgshopHandle extends Controller
|
|||||||
'app_debug' => $appDebug,
|
'app_debug' => $appDebug,
|
||||||
'oc_config_tax' => $this->config->get('config_tax'),
|
'oc_config_tax' => $this->config->get('config_tax'),
|
||||||
'oc_default_currency' => $this->config->get('config_currency'),
|
'oc_default_currency' => $this->config->get('config_currency'),
|
||||||
// ID группы покупателей, которая будет использоаваться в заказах через Телеграм.
|
'oc_customer_group_id' => $this->config->get('config_customer_group_id'),
|
||||||
'oc_customer_group_id' => $this->config->get('module_tgshop_order_customer_group_id'),
|
|
||||||
// ID магазина, для которого будут создаваться заказы из Телеграм
|
// ID магазина, для которого будут создаваться заказы из Телеграм
|
||||||
'oc_store_id' => 0,
|
'oc_store_id' => 0,
|
||||||
// Название магазина, для которого будут создаваться заказы из Телеграм
|
// Название магазина, для которого будут создаваться заказы из Телеграм
|
||||||
|
|||||||
@@ -177,6 +177,7 @@ class OrderCreateService
|
|||||||
$this->cartService->flush();
|
$this->cartService->flush();
|
||||||
|
|
||||||
$orderData['order_id'] = $orderId;
|
$orderData['order_id'] = $orderId;
|
||||||
|
$orderData['total_numeric'] = $orderData['total'] ?? 0;
|
||||||
$orderData['total'] = $cart['total_text'] ?? '';
|
$orderData['total'] = $cart['total_text'] ?? '';
|
||||||
|
|
||||||
$this->sendNotifications($orderData, $data['tgData']);
|
$this->sendNotifications($orderData, $data['tgData']);
|
||||||
@@ -192,6 +193,8 @@ class OrderCreateService
|
|||||||
'id' => $orderData['order_id'],
|
'id' => $orderData['order_id'],
|
||||||
'created_at' => $dateTimeFormatted,
|
'created_at' => $dateTimeFormatted,
|
||||||
'total' => $orderData['total'],
|
'total' => $orderData['total'],
|
||||||
|
'final_total_numeric' => $orderData['total_numeric'],
|
||||||
|
'currency' => $currencyCode,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -214,6 +214,7 @@ class ProductsService
|
|||||||
$configTax = $this->oc->config->get('config_tax');
|
$configTax = $this->oc->config->get('config_tax');
|
||||||
|
|
||||||
$product_info = $this->oc->model_catalog_product->getProduct($productId);
|
$product_info = $this->oc->model_catalog_product->getProduct($productId);
|
||||||
|
$currency = $this->oc->session->data['currency'];
|
||||||
|
|
||||||
if (! $product_info) {
|
if (! $product_info) {
|
||||||
throw new EntityNotFoundException('Product with id ' . $productId . ' not found');
|
throw new EntityNotFoundException('Product with id ' . $productId . ' not found');
|
||||||
@@ -280,24 +281,24 @@ class ProductsService
|
|||||||
|
|
||||||
$data['images'] = $images;
|
$data['images'] = $images;
|
||||||
|
|
||||||
$data['price'] = $this->currency->format(
|
$productPrice = $this->tax->calculate(
|
||||||
$this->tax->calculate(
|
|
||||||
$product_info['price'],
|
$product_info['price'],
|
||||||
$product_info['tax_class_id'],
|
$product_info['tax_class_id'],
|
||||||
$configTax,
|
$configTax,
|
||||||
),
|
|
||||||
$this->oc->session->data['currency']
|
|
||||||
);
|
);
|
||||||
|
$data['price'] = $this->currency->format($productPrice, $currency);
|
||||||
|
$data['currency'] = $currency;
|
||||||
|
$data['final_price_numeric'] = $productPrice;
|
||||||
|
|
||||||
if (! is_null($product_info['special']) && (float) $product_info['special'] >= 0) {
|
if (! is_null($product_info['special']) && (float) $product_info['special'] >= 0) {
|
||||||
$data['special'] = $this->currency->format(
|
$productSpecialPrice = $this->tax->calculate(
|
||||||
$this->tax->calculate(
|
|
||||||
$product_info['special'],
|
$product_info['special'],
|
||||||
$product_info['tax_class_id'],
|
$product_info['tax_class_id'],
|
||||||
$configTax,
|
$configTax,
|
||||||
),
|
|
||||||
$this->oc->session->data['currency']
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
$data['special'] = $this->currency->format($productSpecialPrice, $currency);
|
||||||
|
$data['final_price_numeric'] = $productSpecialPrice;
|
||||||
$tax_price = (float) $product_info['special'];
|
$tax_price = (float) $product_info['special'];
|
||||||
} else {
|
} else {
|
||||||
$data['special'] = false;
|
$data['special'] = false;
|
||||||
@@ -305,7 +306,7 @@ class ProductsService
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($configTax) {
|
if ($configTax) {
|
||||||
$data['tax'] = $this->currency->format($tax_price, $this->oc->session->data['currency']);
|
$data['tax'] = $this->currency->format($tax_price, $currency);
|
||||||
} else {
|
} else {
|
||||||
$data['tax'] = false;
|
$data['tax'] = false;
|
||||||
}
|
}
|
||||||
@@ -323,7 +324,7 @@ class ProductsService
|
|||||||
$product_info['tax_class_id'],
|
$product_info['tax_class_id'],
|
||||||
$configTax,
|
$configTax,
|
||||||
),
|
),
|
||||||
$this->oc->session->data['currency']
|
$currency
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -341,7 +342,7 @@ class ProductsService
|
|||||||
$product_info['tax_class_id'],
|
$product_info['tax_class_id'],
|
||||||
$configTax ? 'P' : false
|
$configTax ? 'P' : false
|
||||||
),
|
),
|
||||||
$this->oc->session->data['currency']
|
$currency
|
||||||
);
|
);
|
||||||
|
|
||||||
$product_option_value_data[] = array(
|
$product_option_value_data[] = array(
|
||||||
|
|||||||
14
spa/src/constants/yaMetrikaGoals.js
Normal file
14
spa/src/constants/yaMetrikaGoals.js
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
export const YA_METRIKA_GOAL = {
|
||||||
|
ADD_TO_CART: 'add_to_cart',
|
||||||
|
CREATE_ORDER: 'create_order',
|
||||||
|
ORDER_CREATED_SUCCESS: 'order_created_success',
|
||||||
|
VIEW_PRODUCT: 'view_product',
|
||||||
|
VIEW_CART: 'view_cart',
|
||||||
|
VIEW_CHECKOUT: 'view_checkout',
|
||||||
|
VIEW_HOME: 'view_home',
|
||||||
|
VIEW_FILTERS: 'view_filters',
|
||||||
|
FILTERS_APPLY: 'filters_apply',
|
||||||
|
FILTERS_RESET: 'filters_reset',
|
||||||
|
VIEW_SEARCH: 'view_search',
|
||||||
|
PERFORM_SEARCH: 'perform_search',
|
||||||
|
};
|
||||||
@@ -35,3 +35,9 @@ export const router = createRouter({
|
|||||||
history: createWebHashHistory('/image/catalog/tgshopspa/'),
|
history: createWebHashHistory('/image/catalog/tgshopspa/'),
|
||||||
routes,
|
routes,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
router.beforeEach((to, from, next) => {
|
||||||
|
const ym = useYaMetrikaStore();
|
||||||
|
ym.prevPath = from.path;
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
|||||||
@@ -38,5 +38,24 @@ export const useCategoriesStore = defineStore('categories', {
|
|||||||
this.isLoading = false;
|
this.isLoading = false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
async findCategoryById(id, list = []) {
|
||||||
|
if (! id) return null;
|
||||||
|
|
||||||
|
if (list && list.length === 0) {
|
||||||
|
await this.fetchCategories();
|
||||||
|
list = this.categories;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const cat of list) {
|
||||||
|
if (parseInt(cat.id) === parseInt(id)) return cat;
|
||||||
|
if (cat.children?.length) {
|
||||||
|
const found = await this.findCategoryById(id, cat.children);
|
||||||
|
if (found) return found;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import {defineStore} from "pinia";
|
import {defineStore} from "pinia";
|
||||||
import ftch from "@/utils/ftch.js";
|
import ftch from "@/utils/ftch.js";
|
||||||
|
import {YA_METRIKA_GOAL} from "@/constants/yaMetrikaGoals.js";
|
||||||
|
import {useYaMetrikaStore} from "@/stores/yaMetrikaStore.js";
|
||||||
|
|
||||||
export const useSearchStore = defineStore('search', {
|
export const useSearchStore = defineStore('search', {
|
||||||
state: () => ({
|
state: () => ({
|
||||||
@@ -31,6 +33,10 @@ export const useSearchStore = defineStore('search', {
|
|||||||
return this.reset();
|
return this.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
useYaMetrikaStore().reachGoal(YA_METRIKA_GOAL.PERFORM_SEARCH, {
|
||||||
|
keyword: this.search,
|
||||||
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.isLoading = true;
|
this.isLoading = true;
|
||||||
this.products = await ftch('products', {
|
this.products = await ftch('products', {
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
import {defineStore} from "pinia";
|
import {defineStore} from "pinia";
|
||||||
import {useSettingsStore} from "@/stores/SettingsStore.js";
|
import {useSettingsStore} from "@/stores/SettingsStore.js";
|
||||||
|
import sha256 from 'crypto-js/sha256';
|
||||||
|
import {toRaw} from "vue";
|
||||||
|
|
||||||
export const useYaMetrikaStore = defineStore('ya_metrika', {
|
export const useYaMetrikaStore = defineStore('ya_metrika', {
|
||||||
state: () => ({
|
state: () => ({
|
||||||
queue: [],
|
queue: [],
|
||||||
|
prevPath: null,
|
||||||
}),
|
}),
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
@@ -16,6 +19,8 @@ export const useYaMetrikaStore = defineStore('ya_metrika', {
|
|||||||
|
|
||||||
const fullUrl = `/#${url}`;
|
const fullUrl = `/#${url}`;
|
||||||
|
|
||||||
|
params.referer = params.referer ?? this.prevPath;
|
||||||
|
|
||||||
if (typeof window.ym === 'function' && window.YA_METRIKA_ID !== undefined) {
|
if (typeof window.ym === 'function' && window.YA_METRIKA_ID !== undefined) {
|
||||||
console.debug('[ym] Hit ', fullUrl);
|
console.debug('[ym] Hit ', fullUrl);
|
||||||
console.debug('[ym] ID ', window.YA_METRIKA_ID);
|
console.debug('[ym] ID ', window.YA_METRIKA_ID);
|
||||||
@@ -33,6 +38,49 @@ export const useYaMetrikaStore = defineStore('ya_metrika', {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
reachGoal(target, params = {}) {
|
||||||
|
const settings = useSettingsStore();
|
||||||
|
if (!settings.ya_metrika_enabled) {
|
||||||
|
console.debug('[ym] Yandex Metrika disabled in settings.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof window.ym === 'function' && window.YA_METRIKA_ID !== undefined) {
|
||||||
|
console.debug('[ym] reachGoal ', target, ' params: ', params);
|
||||||
|
window.ym(window.YA_METRIKA_ID, 'reachGoal', target, params);
|
||||||
|
} else {
|
||||||
|
console.debug('[ym] Yandex Metrika is not initialized. Pushed to queue.');
|
||||||
|
this.queue.push({
|
||||||
|
event: 'reachGoal',
|
||||||
|
payload: {
|
||||||
|
target,
|
||||||
|
params
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
initUserParams() {
|
||||||
|
if (typeof window.ym === 'function' && window.YA_METRIKA_ID !== undefined) {
|
||||||
|
let tgID = null;
|
||||||
|
|
||||||
|
if (window?.Telegram?.WebApp?.initDataUnsafe?.user?.id) {
|
||||||
|
tgID = sha256(window.Telegram.WebApp.initDataUnsafe.user.id).toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
const userParams = {
|
||||||
|
tg_id: tgID,
|
||||||
|
language: window.Telegram?.WebApp?.initDataUnsafe?.user?.language_code || 'unknown',
|
||||||
|
platform: window.Telegram?.WebApp?.platform || 'unknown',
|
||||||
|
};
|
||||||
|
|
||||||
|
window.ym(window.YA_METRIKA_ID, 'userParams', userParams);
|
||||||
|
console.debug('[ym] User params initialized: ', userParams);
|
||||||
|
} else {
|
||||||
|
console.debug('[ym] Yandex Metrika is not initialized. Could not init user params.');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
processQueue() {
|
processQueue() {
|
||||||
if (this.queue.length === 0) {
|
if (this.queue.length === 0) {
|
||||||
return;
|
return;
|
||||||
@@ -43,8 +91,10 @@ export const useYaMetrikaStore = defineStore('ya_metrika', {
|
|||||||
while (this.queue.length > 0) {
|
while (this.queue.length > 0) {
|
||||||
const item = this.queue.shift();
|
const item = this.queue.shift();
|
||||||
if (item.event === 'hit') {
|
if (item.event === 'hit') {
|
||||||
console.debug('[ym] Queue ', item);
|
console.debug('[ym] Queue ', toRaw(item));
|
||||||
window.ym(window.YA_METRIKA_ID, item.event, item.payload.url, item.payload.params);
|
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 {
|
} else {
|
||||||
console.error('[ym] Unsupported queue event: ', item.event);
|
console.error('[ym] Unsupported queue event: ', item.event);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ export function injectYaMetrika() {
|
|||||||
window.YA_METRIKA_ID = getMetrikaId();
|
window.YA_METRIKA_ID = getMetrikaId();
|
||||||
console.debug('[Init] Detected Yandex.Metrika ID:', window.YA_METRIKA_ID);
|
console.debug('[Init] Detected Yandex.Metrika ID:', window.YA_METRIKA_ID);
|
||||||
const yaMetrika = useYaMetrikaStore();
|
const yaMetrika = useYaMetrikaStore();
|
||||||
|
yaMetrika.initUserParams();
|
||||||
yaMetrika.processQueue();
|
yaMetrika.processQueue();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -139,9 +139,13 @@ import OptionRadio from "@/components/ProductOptions/Cart/OptionRadio.vue";
|
|||||||
import OptionCheckbox from "@/components/ProductOptions/Cart/OptionCheckbox.vue";
|
import OptionCheckbox from "@/components/ProductOptions/Cart/OptionCheckbox.vue";
|
||||||
import OptionText from "@/components/ProductOptions/Cart/OptionText.vue";
|
import OptionText from "@/components/ProductOptions/Cart/OptionText.vue";
|
||||||
import {computed, onMounted} from "vue";
|
import {computed, onMounted} from "vue";
|
||||||
import {useRouter} from "vue-router";
|
import {useRoute, useRouter} from "vue-router";
|
||||||
import {useSettingsStore} from "@/stores/SettingsStore.js";
|
import {useSettingsStore} from "@/stores/SettingsStore.js";
|
||||||
|
import {useYaMetrikaStore} from "@/stores/yaMetrikaStore.js";
|
||||||
|
import {YA_METRIKA_GOAL} from "@/constants/yaMetrikaGoals.js";
|
||||||
|
|
||||||
|
const route = useRoute();
|
||||||
|
const yaMetrika = useYaMetrikaStore();
|
||||||
const cart = useCartStore();
|
const cart = useCartStore();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const settings = useSettingsStore();
|
const settings = useSettingsStore();
|
||||||
@@ -169,11 +173,15 @@ function goToCheckout() {
|
|||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
window.document.title = 'Корзина покупок';
|
window.document.title = 'Корзина покупок';
|
||||||
|
yaMetrika.pushHit(route.path, {
|
||||||
|
title: 'Корзина покупок',
|
||||||
|
});
|
||||||
|
yaMetrika.reachGoal(YA_METRIKA_GOAL.VIEW_CART);
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.btn-checkout {
|
.btn-checkout {
|
||||||
bottom: calc(var(--spacing, 0px) * 22px + var(--tg-safe-area-inset-bottom, 0px))
|
bottom: calc(var(--spacing, 0px) * 22 + var(--tg-safe-area-inset-bottom, 0px))
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
@@ -58,9 +58,11 @@ import {router} from "@/router.js";
|
|||||||
import {useCategoriesStore} from "@/stores/CategoriesStore.js";
|
import {useCategoriesStore} from "@/stores/CategoriesStore.js";
|
||||||
import {useRoute} from "vue-router";
|
import {useRoute} from "vue-router";
|
||||||
import CategoryItem from "@/components/CategoriesList/CategoryItem.vue";
|
import CategoryItem from "@/components/CategoriesList/CategoryItem.vue";
|
||||||
|
import {useYaMetrikaStore} from "@/stores/yaMetrikaStore.js";
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const categoriesStore = useCategoriesStore();
|
const categoriesStore = useCategoriesStore();
|
||||||
|
const yaMetrika = useYaMetrikaStore();
|
||||||
|
|
||||||
const parentId = computed(() => route.params.id ? Number(route.params.id) : null);
|
const parentId = computed(() => route.params.id ? Number(route.params.id) : null);
|
||||||
|
|
||||||
@@ -106,6 +108,9 @@ function showProductsInParentCategory() {
|
|||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
window.document.title = 'Каталог';
|
window.document.title = 'Каталог';
|
||||||
|
yaMetrika.pushHit(route.path, {
|
||||||
|
title: 'Каталог',
|
||||||
|
});
|
||||||
await categoriesStore.fetchCategories();
|
await categoriesStore.fetchCategories();
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -69,11 +69,15 @@
|
|||||||
import {useCheckoutStore} from "@/stores/CheckoutStore.js";
|
import {useCheckoutStore} from "@/stores/CheckoutStore.js";
|
||||||
import TgInput from "@/components/Form/TgInput.vue";
|
import TgInput from "@/components/Form/TgInput.vue";
|
||||||
import TgTextarea from "@/components/Form/TgTextarea.vue";
|
import TgTextarea from "@/components/Form/TgTextarea.vue";
|
||||||
import {useRouter} from "vue-router";
|
import {useRoute, useRouter} from "vue-router";
|
||||||
import {computed, onMounted, ref} from "vue";
|
import {computed, onMounted, ref} from "vue";
|
||||||
import {IMaskComponent} from "vue-imask";
|
import {IMaskComponent} from "vue-imask";
|
||||||
|
import {useYaMetrikaStore} from "@/stores/yaMetrikaStore.js";
|
||||||
|
import {YA_METRIKA_GOAL} from "@/constants/yaMetrikaGoals.js";
|
||||||
|
|
||||||
const checkout = useCheckoutStore();
|
const checkout = useCheckoutStore();
|
||||||
|
const yaMetrika = useYaMetrikaStore();
|
||||||
|
const route = useRoute();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const error = ref(null);
|
const error = ref(null);
|
||||||
|
|
||||||
@@ -84,7 +88,15 @@ const btnText = computed(() => {
|
|||||||
async function onCreateBtnClick() {
|
async function onCreateBtnClick() {
|
||||||
try {
|
try {
|
||||||
error.value = null;
|
error.value = null;
|
||||||
|
yaMetrika.reachGoal(YA_METRIKA_GOAL.CREATE_ORDER, {
|
||||||
|
price: checkout.order?.final_total_numeric,
|
||||||
|
currency: checkout.order?.currency,
|
||||||
|
});
|
||||||
await checkout.makeOrder();
|
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'});
|
router.push({name: 'order_created'});
|
||||||
} catch {
|
} catch {
|
||||||
error.value = 'Невозможно создать заказ.';
|
error.value = 'Невозможно создать заказ.';
|
||||||
@@ -93,5 +105,9 @@ async function onCreateBtnClick() {
|
|||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
window.document.title = 'Оформление заказа';
|
window.document.title = 'Оформление заказа';
|
||||||
|
yaMetrika.pushHit(route.path, {
|
||||||
|
title: 'Оформление заказа',
|
||||||
|
});
|
||||||
|
yaMetrika.reachGoal(YA_METRIKA_GOAL.VIEW_CHECKOUT);
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -49,8 +49,10 @@ import {nextTick, onMounted} from "vue";
|
|||||||
import {useProductFiltersStore} from "@/stores/ProductFiltersStore.js";
|
import {useProductFiltersStore} from "@/stores/ProductFiltersStore.js";
|
||||||
import ProductPrice from "@/components/ProductFilters/Components/ProductPrice.vue";
|
import ProductPrice from "@/components/ProductFilters/Components/ProductPrice.vue";
|
||||||
import ForMainPage from "@/components/ProductFilters/Components/ForMainPage.vue";
|
import ForMainPage from "@/components/ProductFilters/Components/ForMainPage.vue";
|
||||||
import {useRouter} from "vue-router";
|
import {useRoute, useRouter} from "vue-router";
|
||||||
import ProductCategory from "@/components/ProductFilters/Components/ProductCategory/ProductCategory.vue";
|
import ProductCategory from "@/components/ProductFilters/Components/ProductCategory/ProductCategory.vue";
|
||||||
|
import {useYaMetrikaStore} from "@/stores/yaMetrikaStore.js";
|
||||||
|
import {YA_METRIKA_GOAL} from "@/constants/yaMetrikaGoals.js";
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: 'Filters'
|
name: 'Filters'
|
||||||
@@ -63,6 +65,8 @@ const componentMap = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
const yaMetrika = useYaMetrikaStore();
|
||||||
|
const route = useRoute();
|
||||||
const emit = defineEmits(['close', 'apply']);
|
const emit = defineEmits(['close', 'apply']);
|
||||||
|
|
||||||
const filtersStore = useProductFiltersStore();
|
const filtersStore = useProductFiltersStore();
|
||||||
@@ -72,6 +76,7 @@ const applyFilters = async () => {
|
|||||||
filtersStore.applied = JSON.parse(JSON.stringify(filtersStore.draft));
|
filtersStore.applied = JSON.parse(JSON.stringify(filtersStore.draft));
|
||||||
console.debug('Filters: apply filters. Hash for router: ', filtersStore.paramsHashForRouter);
|
console.debug('Filters: apply filters. Hash for router: ', filtersStore.paramsHashForRouter);
|
||||||
haptic.impactOccurred('soft');
|
haptic.impactOccurred('soft');
|
||||||
|
yaMetrika.reachGoal(YA_METRIKA_GOAL.FILTERS_APPLY);
|
||||||
await nextTick();
|
await nextTick();
|
||||||
router.back();
|
router.back();
|
||||||
}
|
}
|
||||||
@@ -80,6 +85,7 @@ const resetFilters = async () => {
|
|||||||
filtersStore.applied = filtersStore.default;
|
filtersStore.applied = filtersStore.default;
|
||||||
console.debug('Filters: reset filters. Hash for router: ', filtersStore.paramsHashForRouter);
|
console.debug('Filters: reset filters. Hash for router: ', filtersStore.paramsHashForRouter);
|
||||||
haptic.notificationOccurred('success');
|
haptic.notificationOccurred('success');
|
||||||
|
yaMetrika.reachGoal(YA_METRIKA_GOAL.FILTERS_RESET);
|
||||||
await nextTick();
|
await nextTick();
|
||||||
window.scrollTo(0, 0);
|
window.scrollTo(0, 0);
|
||||||
router.back();
|
router.back();
|
||||||
@@ -89,6 +95,9 @@ onMounted(async () => {
|
|||||||
console.debug('Filters: OnMounted');
|
console.debug('Filters: OnMounted');
|
||||||
window.document.title = 'Фильтры';
|
window.document.title = 'Фильтры';
|
||||||
|
|
||||||
|
yaMetrika.pushHit(route.path, {title: 'Фильтры'});
|
||||||
|
yaMetrika.reachGoal(YA_METRIKA_GOAL.VIEW_FILTERS);
|
||||||
|
|
||||||
if (filtersStore.applied?.rules) {
|
if (filtersStore.applied?.rules) {
|
||||||
console.debug('Filters: Found applied filters.');
|
console.debug('Filters: Found applied filters.');
|
||||||
filtersStore.draft = JSON.parse(JSON.stringify(filtersStore.applied));
|
filtersStore.draft = JSON.parse(JSON.stringify(filtersStore.applied));
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ import ftch from "@/utils/ftch.js";
|
|||||||
import {useProductFiltersStore} from "@/stores/ProductFiltersStore.js";
|
import {useProductFiltersStore} from "@/stores/ProductFiltersStore.js";
|
||||||
import Banner from "@/components/Banner.vue";
|
import Banner from "@/components/Banner.vue";
|
||||||
import {useYaMetrikaStore} from "@/stores/yaMetrikaStore.js";
|
import {useYaMetrikaStore} from "@/stores/yaMetrikaStore.js";
|
||||||
|
import {YA_METRIKA_GOAL} from "@/constants/yaMetrikaGoals.js";
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: 'Home'
|
name: 'Home'
|
||||||
@@ -99,7 +100,10 @@ async function onLoadMore() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onActivated(() => {
|
onActivated(() => {
|
||||||
yaMetrika.pushHit('/');
|
yaMetrika.pushHit('/', {
|
||||||
|
title: 'Главная страница',
|
||||||
|
});
|
||||||
|
yaMetrika.reachGoal(YA_METRIKA_GOAL.VIEW_HOME);
|
||||||
});
|
});
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
|
|||||||
@@ -39,10 +39,17 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import {useCheckoutStore} from "@/stores/CheckoutStore.js";
|
import {useCheckoutStore} from "@/stores/CheckoutStore.js";
|
||||||
import {onMounted} from "vue";
|
import {onMounted} from "vue";
|
||||||
|
import {useYaMetrikaStore} from "@/stores/yaMetrikaStore.js";
|
||||||
|
import {useRoute} from "vue-router";
|
||||||
|
|
||||||
const checkout = useCheckoutStore();
|
const checkout = useCheckoutStore();
|
||||||
|
const yaMetrika = useYaMetrikaStore();
|
||||||
|
const route = useRoute();
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(() => {
|
||||||
window.document.title = 'Заказ оформлен';
|
window.document.title = 'Заказ оформлен';
|
||||||
|
yaMetrika.pushHit(route.path, {
|
||||||
|
title: 'Заказ оформлен',
|
||||||
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -106,7 +106,7 @@
|
|||||||
class="btn btn-primary btn-lg w-full"
|
class="btn btn-primary btn-lg w-full"
|
||||||
:class="isInCart ? 'btn-success' : 'btn-primary'"
|
:class="isInCart ? 'btn-success' : 'btn-primary'"
|
||||||
:disabled="cart.isLoading || canAddToCart === false"
|
:disabled="cart.isLoading || canAddToCart === false"
|
||||||
@click="actionBtnClick"
|
@click="onCartBtnClick"
|
||||||
>
|
>
|
||||||
<span v-if="cart.isLoading" class="loading loading-spinner loading-sm"></span>
|
<span v-if="cart.isLoading" class="loading loading-spinner loading-sm"></span>
|
||||||
{{ btnText }}
|
{{ btnText }}
|
||||||
@@ -156,7 +156,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import {computed, nextTick, onMounted, onUnmounted, ref} from "vue";
|
import {computed, onMounted, onUnmounted, ref} from "vue";
|
||||||
import {useRoute, useRouter} from 'vue-router'
|
import {useRoute, useRouter} from 'vue-router'
|
||||||
import ProductOptions from "../components/ProductOptions/ProductOptions.vue";
|
import ProductOptions from "../components/ProductOptions/ProductOptions.vue";
|
||||||
import {useCartStore} from "../stores/CartStore.js";
|
import {useCartStore} from "../stores/CartStore.js";
|
||||||
@@ -168,6 +168,7 @@ import LoadingFullScreen from "@/components/LoadingFullScreen.vue";
|
|||||||
import ProductNotFound from "@/components/ProductNotFound.vue";
|
import ProductNotFound from "@/components/ProductNotFound.vue";
|
||||||
import {useSettingsStore} from "@/stores/SettingsStore.js";
|
import {useSettingsStore} from "@/stores/SettingsStore.js";
|
||||||
import {useYaMetrikaStore} from "@/stores/yaMetrikaStore.js";
|
import {useYaMetrikaStore} from "@/stores/yaMetrikaStore.js";
|
||||||
|
import {YA_METRIKA_GOAL} from "@/constants/yaMetrikaGoals.js";
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const productId = computed(() => route.params.id);
|
const productId = computed(() => route.params.id);
|
||||||
@@ -211,7 +212,7 @@ function closeFullScreen() {
|
|||||||
document.body.style.overflow = '';
|
document.body.style.overflow = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
async function actionBtnClick() {
|
async function onCartBtnClick() {
|
||||||
try {
|
try {
|
||||||
error.value = '';
|
error.value = '';
|
||||||
|
|
||||||
@@ -219,6 +220,10 @@ async function actionBtnClick() {
|
|||||||
await cart.addProduct(productId.value, product.value.name, product.value.price, quantity.value, product.value.options);
|
await cart.addProduct(productId.value, product.value.name, product.value.price, quantity.value, product.value.options);
|
||||||
isInCart.value = true;
|
isInCart.value = true;
|
||||||
window.Telegram.WebApp.HapticFeedback.notificationOccurred('success');
|
window.Telegram.WebApp.HapticFeedback.notificationOccurred('success');
|
||||||
|
yaMetrika.reachGoal(YA_METRIKA_GOAL.ADD_TO_CART, {
|
||||||
|
price: product.value.final_price_numeric,
|
||||||
|
currency: product.value.currency,
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
window.Telegram.WebApp.HapticFeedback.selectionChanged();
|
window.Telegram.WebApp.HapticFeedback.selectionChanged();
|
||||||
await router.push({'name': 'cart'});
|
await router.push({'name': 'cart'});
|
||||||
@@ -275,6 +280,11 @@ onMounted(async () => {
|
|||||||
'Цена': data.price,
|
'Цена': data.price,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
yaMetrika.reachGoal(YA_METRIKA_GOAL.VIEW_PRODUCT, {
|
||||||
|
price: data.final_price_numeric,
|
||||||
|
currency: data.currency,
|
||||||
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
:hasMore="productsStore.products.meta.hasMore"
|
:hasMore="productsStore.products.meta.hasMore"
|
||||||
:isLoading="productsStore.isLoading"
|
:isLoading="productsStore.isLoading"
|
||||||
:isLoadingMore="productsStore.isLoadingMore"
|
:isLoadingMore="productsStore.isLoadingMore"
|
||||||
|
:categoryName="category?.name"
|
||||||
@loadMore="productsStore.loadMore"
|
@loadMore="productsStore.loadMore"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -12,9 +13,11 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import ProductsList from "@/components/ProductsList.vue";
|
import ProductsList from "@/components/ProductsList.vue";
|
||||||
import {onMounted} from "vue";
|
import {onMounted, ref} from "vue";
|
||||||
import {useRoute} from "vue-router";
|
import {useRoute} from "vue-router";
|
||||||
import {useProductsStore} from "@/stores/ProductsStore.js";
|
import {useProductsStore} from "@/stores/ProductsStore.js";
|
||||||
|
import {useCategoriesStore} from "@/stores/CategoriesStore.js";
|
||||||
|
import {useYaMetrikaStore} from "@/stores/yaMetrikaStore.js";
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: 'Products'
|
name: 'Products'
|
||||||
@@ -22,12 +25,22 @@ defineOptions({
|
|||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const productsStore = useProductsStore();
|
const productsStore = useProductsStore();
|
||||||
|
const categoriesStore = useCategoriesStore();
|
||||||
|
const yaMetrika = useYaMetrikaStore();
|
||||||
|
|
||||||
const categoryId = route.params.category_id ?? null;
|
const categoryId = route.params.category_id ?? null;
|
||||||
|
const category = ref(null);
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
console.debug("Category Products Mounted");
|
console.debug("[Category] Category Products Mounted");
|
||||||
console.debug("Load products for category: ", categoryId);
|
console.debug("[Category] Load products for category: ", categoryId);
|
||||||
|
category.value = await categoriesStore.findCategoryById(categoryId);
|
||||||
|
console.debug("[Category] Category Name: ", category.value?.name);
|
||||||
|
|
||||||
|
window.document.title = `${category.value?.name ?? 'Неизвестная категория'}`;
|
||||||
|
yaMetrika.pushHit(route.path, {
|
||||||
|
title: `${category.value?.name ?? 'Неизвестная категория'}`,
|
||||||
|
});
|
||||||
|
|
||||||
if (productsStore.filtersFullUrl === route.fullPath) {
|
if (productsStore.filtersFullUrl === route.fullPath) {
|
||||||
await productsStore.loadProducts(productsStore.filters ?? {
|
await productsStore.loadProducts(productsStore.filters ?? {
|
||||||
|
|||||||
@@ -74,12 +74,21 @@
|
|||||||
import {useSearchStore} from "@/stores/SearchStore.js";
|
import {useSearchStore} from "@/stores/SearchStore.js";
|
||||||
import {useDebounceFn} from "@vueuse/core";
|
import {useDebounceFn} from "@vueuse/core";
|
||||||
import {onMounted, ref} from "vue";
|
import {onMounted, ref} from "vue";
|
||||||
|
import {useYaMetrikaStore} from "@/stores/yaMetrikaStore.js";
|
||||||
|
import {useRoute} from "vue-router";
|
||||||
|
import {YA_METRIKA_GOAL} from "@/constants/yaMetrikaGoals.js";
|
||||||
|
|
||||||
|
const route = useRoute();
|
||||||
|
const yaMetrika = useYaMetrikaStore();
|
||||||
const searchStore = useSearchStore();
|
const searchStore = useSearchStore();
|
||||||
const searchInput = ref(null);
|
const searchInput = ref(null);
|
||||||
const debouncedSearch = useDebounceFn(() => searchStore.performSearch(), 500);
|
const debouncedSearch = useDebounceFn(() => searchStore.performSearch(), 500);
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
window.document.title = 'Поиск';
|
window.document.title = 'Поиск';
|
||||||
|
yaMetrika.pushHit(route.path, {
|
||||||
|
title: 'Поиск',
|
||||||
|
});
|
||||||
|
yaMetrika.reachGoal(YA_METRIKA_GOAL.VIEW_SEARCH);
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
Reference in New Issue
Block a user