All checks were successful
Build & Deploy KLZ Cables / build-and-deploy (push) Successful in 3m30s
151 lines
4.9 KiB
TypeScript
151 lines
4.9 KiB
TypeScript
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 { getServerAppServices } = require('../create-services.server');
|
|
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;
|
|
|
|
const logger = getServerAppServices().logger.child({ component: 'analytics' });
|
|
logger.info('Sending analytics event', { eventName, props });
|
|
|
|
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((error) => {
|
|
logger.error('Failed to send analytics event', { eventName, props, error });
|
|
});
|
|
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 { getServerAppServices } = require('../create-services.server');
|
|
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;
|
|
|
|
const logger = getServerAppServices().logger.child({ component: 'analytics' });
|
|
logger.info('Sending analytics pageview', { url });
|
|
|
|
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((error) => {
|
|
logger.error('Failed to send analytics pageview', { url, error });
|
|
});
|
|
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);
|
|
}
|
|
}
|