Files
manoon-headless/src/lib/analytics/core/AnalyticsTracker.ts
T
Unchained 66829aeffd
Build and Deploy / build (push) Has been cancelled
refactor(analytics): abstract analytics into provider pattern
- Add type-safe AnalyticsEvent union types
- Create AnalyticsProvider interface for pluggable analytics backends
- Implement OpenPanelProvider and RybbitProvider adapters
- Create AnalyticsTracker that fans out events to all providers
- Simplifies adding new analytics platforms in the future
2026-03-31 07:45:21 +02:00

113 lines
3.4 KiB
TypeScript

"use client";
import type { AnalyticsEvent, AnalyticsProvider, UserData } from "./types";
export class AnalyticsTracker {
private providers: AnalyticsProvider[] = [];
addProvider(provider: AnalyticsProvider): void {
this.providers.push(provider);
}
track(event: AnalyticsEvent): void {
for (const provider of this.providers) {
try {
provider.track(event);
} catch (e) {
console.error(`[Analytics] ${provider.name} tracking error:`, e);
}
}
}
identify(user: UserData): void {
for (const provider of this.providers) {
if (provider.identify) {
try {
provider.identify(user);
} catch (e) {
console.error(`[Analytics] ${provider.name} identify error:`, e);
}
}
}
}
async revenue(amount: number, currency: string, properties?: Record<string, unknown>): Promise<void> {
const promises: Promise<void>[] = [];
for (const provider of this.providers) {
if (provider.revenue) {
promises.push(
provider.revenue(amount, currency, properties).catch((e) => {
console.error(`[Analytics] ${provider.name} revenue error:`, e);
})
);
}
}
await Promise.all(promises);
}
productViewed(product: { id: string; name: string; price: number; currency: string; category?: string; variant?: string }) {
this.track({ type: "product_viewed", product });
}
addToCart(product: { id: string; name: string; price: number; currency: string; quantity: number; variant?: string }) {
this.track({ type: "add_to_cart", product });
}
removeFromCart(product: { id: string; name: string; quantity: number }) {
this.track({ type: "remove_from_cart", product });
}
cartViewed(cart: { total: number; currency: string; item_count: number }) {
this.track({ type: "cart_view", cart });
}
checkoutStarted(cart: { total: number; currency: string; item_count: number; items?: Array<{ id: string; name: string; quantity: number; price: number }> }) {
this.track({ type: "checkout_started", cart });
}
checkoutStep(step: string, data?: Record<string, unknown>) {
this.track({ type: "checkout_step", step, data });
}
orderCompleted(order: { order_id: string; order_number: string; total: number; currency: string; item_count: number; shipping_cost?: number; coupon_code?: string; customer_email?: string; payment_method?: string }) {
this.track({ type: "order_completed", order });
this.revenue(order.total, order.currency, {
transaction_id: order.order_number,
order_id: order.order_id,
});
}
searchPerformed(query: string, results_count: number) {
this.track({ type: "search", query, results_count });
}
externalLinkClicked(url: string, label?: string) {
this.track({ type: "external_link_click", url, label });
}
wishlistAdded(product: { id: string; name: string }) {
this.track({ type: "wishlist_add", product });
}
userLoggedIn(method: string) {
this.track({ type: "user_login", method });
}
userRegistered(method: string) {
this.track({ type: "user_register", method });
}
newsletterSignedUp(email: string, source: string) {
this.track({ type: "newsletter_signup", email, source });
}
}
let trackerInstance: AnalyticsTracker | null = null;
export function getTracker(): AnalyticsTracker {
if (!trackerInstance) {
trackerInstance = new AnalyticsTracker();
}
return trackerInstance;
}