/** * **Composable для аналитики Barneo Search Widget** * * Предоставляет удобный интерфейс для отправки аналитических данных в систему Barneo. * Позволяет отслеживать пользовательское поведение и взаимодействия с поисковым интерфейсом. * * ## Основные возможности: * - Объединение неавторизованного и авторизованного пользователя * - Отслеживание поисковых запросов * - Отслеживание использования функций поиска (фильтры, подсказки, история) * - Отслеживание применения поисковых подсказок * - Отслеживание конверсии товаров (клики, корзина, заказы) * - Отслеживание конверсии категорий * - Быстрые методы для удобного отслеживания * - Автоматическая интеграция с BarneoConfigManager * * ## Пример использования: * ```typescript * import { useAnalytics } from 'barneo-search-widget-lib' * * const analytics = useAnalytics() * * // Отслеживание событий * await analytics.trackQueryUse('сковорода') * await analytics.quickTrack.productClick('product-123', { * position: 1, * query: 'сковорода', * source: 'search' * }) * ``` * * @module analytics-composable */ import { ref, computed, readonly, inject } from "vue"; import { AnalyticsService } from "../services/AnalyticsService"; import { SearchFunction, ProductConversionSource, ProductConversionType, CategoryConversionSource, } from "../types/enums"; import type { AuthorizeCustomerRequest } from "../types/requests"; import type { BarneoConfigManager } from "../../../config/BarneoConfigManager"; /** * **Composable для работы с аналитикой** * * Основная функция для отслеживания пользовательского поведения в поисковом интерфейсе. * Автоматически интегрируется с BarneoConfigManager для получения конфигурации API. * * ## Возвращаемые значения: * * ### Состояние: * - `isLoading` - реактивное состояние загрузки (только для чтения) * - `error` - реактивное состояние ошибки (только для чтения) * - `isInitialized` - вычисляемое свойство, true если сервис инициализирован * - `getConfigInfo` - вычисляемое свойство с информацией о конфигурации * * ### Методы инициализации: * - `initializeAnalytics()` - инициализация с кастомной конфигурацией * * ### Основные методы отслеживания: * - `authorizeCustomer()` - объединение пользователей * - `mergeCustomers()` - удобный метод для объединения пользователей * - `trackQueryUse()` - отслеживание поискового запроса * - `trackFunctionUse()` - отслеживание использования функций * - `trackHintUse()` - отслеживание применения подсказки * - `trackProductConversion()` - отслеживание конверсии товара * - `trackCategoryConversion()` - отслеживание конверсии категории * * ### Быстрые методы (quickTrack): * - `productClick()` - быстрое отслеживание клика по товару * - `productBasket()` - быстрое отслеживание добавления в корзину * - `productOrder()` - быстрое отслеживание заказа * - `categoryClick()` - быстрое отслеживание клика по категории * - `filterUse()` - быстрое отслеживание использования фильтров * - `hintUse()` - быстрое отслеживание использования подсказок * - `historyUse()` - быстрое отслеживание использования истории * * ### Утилиты: * - `clearError()` - очистка ошибки * * @returns Объект с состоянием и методами для работы с аналитикой * * @example * ```typescript * const analytics = useAnalytics() * * // Проверка инициализации * console.log('Инициализирована:', analytics.isInitialized.value) * console.log('Конфигурация:', analytics.getConfigInfo.value) * * // Отслеживание событий * await analytics.trackQueryUse('сковорода') * await analytics.quickTrack.productClick('product-123', { * position: 1, * query: 'сковорода', * source: 'search' * }) * * // Проверка состояния * console.log('Загрузка:', analytics.isLoading.value) * console.log('Ошибка:', analytics.error.value) * ``` */ export function useAnalytics() { const isLoading = ref(false); const error = ref(null); const analyticsService = ref(null); // Получаем менеджер конфигурации из Vue контекста или напрямую const configManager = inject("barneoConfigManager") || (typeof window !== "undefined" ? (window as any).__barneoConfigManager : null); // Автоматически инициализируем аналитику с конфигурацией из менеджера if (configManager && !analyticsService.value) { try { const apiConfig = configManager.getApiConfig(); analyticsService.value = new AnalyticsService({ baseUrl: apiConfig.baseUrl, token: apiConfig.token, customerId: apiConfig.customerId, locationId: apiConfig.locationId, apiVersion: apiConfig.apiVersion, }); } catch (err) { console.warn("Failed to initialize analytics with config manager:", err); } } /** * Инициализировать сервис аналитики с кастомной конфигурацией * * Позволяет инициализировать сервис аналитики с пользовательскими настройками * вместо автоматической конфигурации из BarneoConfigManager. * * @param config - Конфигурация для инициализации сервиса * @param config.baseUrl - Базовый URL API * @param config.token - API токен для авторизации * @param config.customerId - ID клиента * @param config.locationId - ID локации * @param config.apiVersion - Версия API (опционально) * * @example * ```typescript * // Инициализация с кастомной конфигурацией * analytics.initializeAnalytics({ * baseUrl: 'https://api.search.ensi.cloud', * token: 'your-token', * customerId: '4', * locationId: '1', * apiVersion: 'v1' * }) * * // Проверка инициализации * console.log('Инициализирована:', analytics.isInitialized.value) * ``` */ const initializeAnalytics = (config: { baseUrl: string; token: string; customerId: string; locationId: string; apiVersion?: string; }) => { analyticsService.value = new AnalyticsService({ baseUrl: config.baseUrl, token: config.token, customerId: config.customerId, locationId: config.locationId, apiVersion: config.apiVersion, // Может быть undefined }); }; /** * Выполнить запрос с обработкой ошибок * * Внутренний метод для выполнения API запросов с единообразной обработкой * ошибок и состояний загрузки. * * @param requestFn - Функция для выполнения запроса * @returns Результат запроса или null в случае ошибки * * @internal */ const executeRequest = async ( requestFn: () => Promise ): Promise => { if (!analyticsService.value) { error.value = "Сервис аналитики не инициализирован"; return null; } isLoading.value = true; error.value = null; try { const result = await requestFn(); return result; } catch (err) { error.value = err instanceof Error ? err.message : "Неизвестная ошибка"; console.error("Analytics error:", err); return null; } finally { isLoading.value = false; } }; /** * Объединение неавторизованного и авторизованного пользователя * * Объединяет данные неавторизованного пользователя с авторизованным, * что позволяет сохранить историю взаимодействий при авторизации. * * @param request - Запрос на объединение пользователей * @param request.guest_customer_id - ID неавторизованного пользователя * @param request.authorized_customer_id - ID авторизованного пользователя * * @example * ```typescript * // Объединение пользователей * await analytics.authorizeCustomer({ * guest_customer_id: 'guest-123', * authorized_customer_id: 'auth-456' * }) * ``` */ const authorizeCustomer = async (request: AuthorizeCustomerRequest) => { return executeRequest(() => analyticsService.value!.authorizeCustomer(request) ); }; /** * Удобный метод для объединения пользователей * * Упрощенный метод для объединения неавторизованного и авторизованного пользователя. * Внутренне вызывает `authorizeCustomer` с правильной структурой запроса. * * @param guestId - ID неавторизованного пользователя * @param authorizedId - ID авторизованного пользователя * * @example * ```typescript * // Удобное объединение пользователей * await analytics.mergeCustomers('guest-123', 'auth-456') * ``` */ const mergeCustomers = async (guestId: string, authorizedId: string) => { return authorizeCustomer({ guest_customer_id: guestId, authorized_customer_id: authorizedId, }); }; /** * Отслеживание поискового запроса клиента * * Отправляет данные о поисковом запросе пользователя для анализа * поискового поведения и улучшения качества поиска. * * @param query - Поисковый запрос пользователя * * @example * ```typescript * // Отслеживание поискового запроса * await analytics.trackQueryUse('сковорода') * await analytics.trackQueryUse('чайник электрический') * ``` */ const trackQueryUse = async (query: string) => { return executeRequest(() => analyticsService.value!.trackQueryUse(query)); }; /** * Отслеживание использования функции поиска * * Отправляет данные об использовании различных функций поиска * (фильтры, подсказки, история) для анализа пользовательского поведения. * * @param functionName - Название функции поиска (filter, hint, history) * * @example * ```typescript * // Отслеживание использования функций * await analytics.trackFunctionUse('filter') // фильтры * await analytics.trackFunctionUse('hint') // подсказки * await analytics.trackFunctionUse('history') // история * ``` */ const trackFunctionUse = async (functionName: SearchFunction) => { return executeRequest(() => analyticsService.value!.trackFunctionUse(functionName) ); }; /** * Отслеживание применения поисковой подсказки * * Отправляет данные о том, какую подсказку выбрал пользователь * из предложенных вариантов автодополнения. * * @param query - Исходный поисковый запрос пользователя * @param hint - Выбранная подсказка/автодополнение * * @example * ```typescript * // Отслеживание применения подсказки * await analytics.trackHintUse('сков', 'сковорода') * await analytics.trackHintUse('чай', 'чайник электрический') * ``` */ const trackHintUse = async (query: string, hint: string) => { return executeRequest(() => analyticsService.value!.trackHintUse(query, hint) ); }; /** * Отслеживание конверсии товара * * Отправляет данные о различных типах конверсии товара (клик, добавление в корзину, заказ) * для анализа эффективности поиска и рекомендаций. * * @param productId - ID товара * @param conversion - Тип конверсии (click, basket, order) * @param options - Дополнительные параметры * @param options.source - Источник конверсии (search, category, recommendation) * @param options.position - Позиция товара в результатах поиска * @param options.query - Поисковый запрос, который привел к конверсии * * @example * ```typescript * // Отслеживание клика по товару * await analytics.trackProductConversion('product-123', 'click', { * position: 1, * query: 'сковорода', * source: 'search' * }) * * // Отслеживание добавления в корзину * await analytics.trackProductConversion('product-123', 'basket', { * position: 2, * query: 'сковорода', * source: 'search' * }) * ``` */ const trackProductConversion = async ( productId: string, conversion: ProductConversionType, options?: { source?: ProductConversionSource; position?: number; query?: string; } ) => { return executeRequest(() => analyticsService.value!.trackProductConversion( productId, conversion, options ) ); }; /** * Отслеживание конверсии категории * * Отправляет данные о клике по категории для анализа * эффективности навигации и категоризации. * * @param categoryId - ID категории * @param options - Дополнительные параметры * @param options.query - Поисковый запрос, который привел к клику по категории * @param options.source - Источник конверсии (search, navigation) * * @example * ```typescript * // Отслеживание клика по категории * await analytics.trackCategoryConversion('category-456', { * query: 'посуда', * source: 'search' * }) * ``` */ const trackCategoryConversion = async ( categoryId: string, options?: { query?: string; source?: CategoryConversionSource; } ) => { return executeRequest(() => analyticsService.value!.trackCategoryConversion(categoryId, options) ); }; /** * Удобные методы для быстрого отслеживания * * Объект с упрощенными методами для частых операций отслеживания. * Все методы внутренне используют основные методы отслеживания. */ const quickTrack = { /** * Быстрое отслеживание клика по товару * * Упрощенный метод для отслеживания клика по товару. * Внутренне вызывает `trackProductConversion` с типом 'click'. * * @param productId - ID товара * @param options - Дополнительные параметры * @param options.position - Позиция товара в результатах поиска * @param options.query - Поисковый запрос, который привел к клику * @param options.source - Источник клика (search, category, recommendation) * * @example * ```typescript * // Быстрое отслеживание клика * await analytics.quickTrack.productClick('product-123', { * position: 1, * query: 'сковорода', * source: 'search' * }) * ``` */ productClick: ( productId: string, options?: { position?: number; query?: string; source?: ProductConversionSource; } ) => { return trackProductConversion( productId, ProductConversionType.CLICK, options ); }, /** * Быстрое отслеживание добавления товара в корзину * * Упрощенный метод для отслеживания добавления товара в корзину. * Внутренне вызывает `trackProductConversion` с типом 'basket'. * * @param productId - ID товара * @param options - Дополнительные параметры * @param options.position - Позиция товара в результатах поиска * @param options.query - Поисковый запрос, который привел к добавлению в корзину * @param options.source - Источник добавления в корзину (search, category, recommendation) * * @example * ```typescript * // Быстрое отслеживание добавления в корзину * await analytics.quickTrack.productBasket('product-123', { * position: 2, * query: 'сковорода', * source: 'search' * }) * ``` */ productBasket: ( productId: string, options?: { position?: number; query?: string; source?: ProductConversionSource; } ) => { return trackProductConversion( productId, ProductConversionType.BASKET, options ); }, /** * Быстрое отслеживание заказа товара * * Упрощенный метод для отслеживания заказа товара. * Внутренне вызывает `trackProductConversion` с типом 'order'. * * @param productId - ID товара * @param options - Дополнительные параметры * @param options.position - Позиция товара в результатах поиска * @param options.query - Поисковый запрос, который привел к заказу * @param options.source - Источник заказа (search, category, recommendation) * * @example * ```typescript * // Быстрое отслеживание заказа * await analytics.quickTrack.productOrder('product-123', { * position: 3, * query: 'сковорода', * source: 'search' * }) * ``` */ productOrder: ( productId: string, options?: { position?: number; query?: string; source?: ProductConversionSource; } ) => { return trackProductConversion( productId, ProductConversionType.ORDER, options ); }, /** * Быстрое отслеживание клика по категории * * Упрощенный метод для отслеживания клика по категории. * Внутренне вызывает `trackCategoryConversion`. * * @param categoryId - ID категории * @param options - Дополнительные параметры * @param options.query - Поисковый запрос, который привел к клику по категории * @param options.source - Источник клика (search, navigation) * * @example * ```typescript * // Быстрое отслеживание клика по категории * await analytics.quickTrack.categoryClick('category-456', { * query: 'посуда', * source: 'search' * }) * ``` */ categoryClick: ( categoryId: string, options?: { query?: string; source?: CategoryConversionSource } ) => { return trackCategoryConversion(categoryId, options); }, /** * Быстрое отслеживание использования фильтров * * Упрощенный метод для отслеживания использования фильтров. * Внутренне вызывает `trackFunctionUse` с функцией 'filter'. * * @example * ```typescript * // Быстрое отслеживание использования фильтров * await analytics.quickTrack.filterUse() * ``` */ filterUse: () => { return trackFunctionUse(SearchFunction.FILTER); }, /** * Быстрое отслеживание использования автоподсказок * * Упрощенный метод для отслеживания использования автоподсказок. * Внутренне вызывает `trackFunctionUse` с функцией 'hint'. * * @example * ```typescript * // Быстрое отслеживание использования автоподсказок * await analytics.quickTrack.hintUse() * ``` */ hintUse: () => { return trackFunctionUse(SearchFunction.HINT); }, /** * Быстрое отслеживание использования истории * * Упрощенный метод для отслеживания использования истории поиска. * Внутренне вызывает `trackFunctionUse` с функцией 'history'. * * @example * ```typescript * // Быстрое отслеживание использования истории * await analytics.quickTrack.historyUse() * ``` */ historyUse: () => { return trackFunctionUse(SearchFunction.HISTORY); }, }; /** * Получить информацию о конфигурации * * Вычисляемое свойство, возвращающее информацию о текущей конфигурации * сервиса аналитики (baseUrl, apiVersion, customerId, locationId). * * @returns Информация о конфигурации или null если сервис не инициализирован * * @example * ```typescript * // Получение информации о конфигурации * const config = analytics.getConfigInfo.value * console.log('Конфигурация:', config) * // { baseUrl: 'https://api.search.ensi.cloud', apiVersion: 'v1', ... } * ``` */ const getConfigInfo = computed(() => { return analyticsService.value?.getConfigInfo() || null; }); /** * Проверить, инициализирован ли сервис * * Вычисляемое свойство, возвращающее true если сервис аналитики * успешно инициализирован и готов к использованию. * * @returns true если сервис инициализирован, false в противном случае * * @example * ```typescript * // Проверка инициализации * if (analytics.isInitialized.value) { * console.log('Сервис аналитики готов к использованию') * await analytics.trackQueryUse('тестовый запрос') * } else { * console.log('Сервис аналитики не инициализирован') * } * ``` */ const isInitialized = computed(() => { return analyticsService.value !== null; }); return { // Состояние /** Реактивное состояние загрузки (только для чтения) */ isLoading: readonly(isLoading), /** Реактивное состояние ошибки (только для чтения) */ error: readonly(error), /** Вычисляемое свойство: true если сервис инициализирован */ isInitialized, /** Вычисляемое свойство: информация о конфигурации */ getConfigInfo, // Методы инициализации /** Инициализировать сервис аналитики с кастомной конфигурацией */ initializeAnalytics, // Основные методы отслеживания /** Объединение неавторизованного и авторизованного пользователя */ authorizeCustomer, /** Удобный метод для объединения пользователей */ mergeCustomers, /** Отслеживание поискового запроса клиента */ trackQueryUse, /** Отслеживание использования функции поиска */ trackFunctionUse, /** Отслеживание применения поисковой подсказки */ trackHintUse, /** Отслеживание конверсии товара */ trackProductConversion, /** Отслеживание конверсии категории */ trackCategoryConversion, // Быстрые методы /** Объект с упрощенными методами для частых операций отслеживания */ quickTrack, // Утилиты /** Очистить ошибку */ clearError: () => { error.value = null; }, }; }