import type { AnalyticsEventProperties, AnalyticsService } from './analytics-service'; /** * Type definition for the Umami global object. * * This represents the `window.umami` object that the Umami script exposes. * The `track` function can accept either an event name or a URL. */ type UmamiGlobal = { track?: (eventOrUrl: string, props?: AnalyticsEventProperties) => void; }; /** * Configuration options for UmamiAnalyticsService. * * @property enabled - Whether analytics are enabled */ export type UmamiAnalyticsServiceOptions = { enabled: boolean; }; /** * Umami Analytics Service Implementation. * * This service implements the AnalyticsService interface for Umami analytics. * It provides type-safe event tracking and pageview tracking. * * @example * ```typescript * // Service creation (usually done by create-services.ts) * const service = new UmamiAnalyticsService({ enabled: true }); * * // Track events * service.track('button_click', { button_id: 'cta' }); * service.trackPageview('/products/123'); * ``` * * @example * ```typescript * // Using through the service layer (recommended) * import { getAppServices } from '@/lib/services/create-services'; * * const services = getAppServices(); * services.analytics.track('product_add_to_cart', { * product_id: '123', * price: 99.99, * }); * ``` */ export class UmamiAnalyticsService implements AnalyticsService { constructor(private readonly options: UmamiAnalyticsServiceOptions) {} /** * Track a custom event with optional properties. * * This method checks if analytics are enabled and if we're in a browser environment * before attempting to track the event. * * @param eventName - The name of the event to track * @param props - Optional event properties * * @example * ```typescript * service.track('product_add_to_cart', { * product_id: '123', * product_name: 'Cable', * price: 99.99, * quantity: 1, * }); * ``` */ track(eventName: string, props?: AnalyticsEventProperties) { if (!this.options.enabled) return; // Server-side tracking via proxy if (typeof window === 'undefined') { const websiteId = process.env.NEXT_PUBLIC_UMAMI_WEBSITE_ID; const umamiUrl = process.env.NEXT_PUBLIC_UMAMI_SCRIPT_URL?.replace('/script.js', '') || 'https://analytics.infra.mintel.me'; if (!websiteId) return; fetch(`${umamiUrl}/api/send`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'User-Agent': 'KLZ-Server' }, body: JSON.stringify({ type: 'event', payload: { website: websiteId, name: eventName, data: props } }), }).catch(() => {}); return; } const umami = (window as unknown as { umami?: UmamiGlobal }).umami; umami?.track?.(eventName, props); } /** * Track a pageview. * * This method checks if analytics are enabled and if we're in a browser environment * before attempting to track the pageview. * * Umami treats `track(url)` as a pageview override, so we can use the same * `track` function for both events and pageviews. * * @param url - The URL to track (defaults to current location) * * @example * ```typescript * // Track current page * service.trackPageview(); * * // Track custom URL * service.trackPageview('/products/123?category=cables'); * ``` */ trackPageview(url?: string) { if (!this.options.enabled) return; // Server-side tracking via proxy if (typeof window === 'undefined') { const websiteId = process.env.NEXT_PUBLIC_UMAMI_WEBSITE_ID; const umamiUrl = process.env.NEXT_PUBLIC_UMAMI_SCRIPT_URL?.replace('/script.js', '') || 'https://analytics.infra.mintel.me'; if (!websiteId || !url) return; fetch(`${umamiUrl}/api/send`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'User-Agent': 'KLZ-Server' }, body: JSON.stringify({ type: 'event', payload: { website: websiteId, url } }), }).catch(() => {}); return; } const umami = (window as unknown as { umami?: UmamiGlobal }).umami; // Umami treats `track(url)` as a pageview override. if (url) umami?.track?.(url); else umami?.track?.(window.location.pathname + window.location.search); } }