Files
manoon-headless/src/lib/analytics-server.ts
Unchained d4039c6e3b
Some checks failed
Build and Deploy / build (push) Has been cancelled
feat(analytics): complete Rybbit tracking integration
- Add Rybbit server-side tracking to analytics-server.ts for order completion and revenue
- Add trackNewsletterSignup to analytics.ts and wire up NewsletterSection
- Add cart tracking to CartDrawer (cart view, remove from cart)
- All ecommerce events now track to both OpenPanel and Rybbit
2026-03-31 05:53:53 +02:00

151 lines
4.1 KiB
TypeScript

"use server";
import { OpenPanel } from "@openpanel/nextjs";
// Server-side OpenPanel instance
const op = new OpenPanel({
clientId: process.env.NEXT_PUBLIC_OPENPANEL_CLIENT_ID || "",
clientSecret: process.env.OPENPANEL_CLIENT_SECRET || "",
apiUrl: process.env.OPENPANEL_API_URL || "https://op.nodecrew.me/api",
});
// Rybbit server-side tracking
const RYBBIT_HOST = process.env.NEXT_PUBLIC_RYBBIT_HOST || "https://rybbit.nodecrew.me";
const RYBBIT_API_KEY = process.env.RYBBIT_API_KEY;
const RYBBIT_SITE_ID = process.env.NEXT_PUBLIC_RYBBIT_SITE_ID || "1";
export interface ServerOrderData {
orderId: string;
orderNumber: string;
total: number;
currency: string;
itemCount: number;
customerEmail?: string;
paymentMethod?: string;
shippingCost?: number;
couponCode?: string;
}
export interface ServerEventData {
event: string;
properties?: Record<string, any>;
}
async function trackRybbitServer(eventName: string, properties?: Record<string, any>) {
try {
const headers: Record<string, string> = {
"Content-Type": "application/json",
};
if (RYBBIT_API_KEY) {
headers["Authorization"] = `Bearer ${RYBBIT_API_KEY}`;
}
const response = await fetch(`${RYBBIT_HOST}/api/track`, {
method: "POST",
headers,
body: JSON.stringify({
site_id: RYBBIT_SITE_ID,
type: "custom_event",
event_name: eventName,
properties: JSON.stringify(properties || {}),
}),
});
if (!response.ok) {
console.warn("[Rybbit Server] Track failed:", await response.text());
}
} catch (error) {
console.warn("[Rybbit Server] Track error:", error);
}
}
/**
* Server-side analytics tracking
* Called from API routes or Server Components
*/
export async function trackOrderCompletedServer(data: ServerOrderData) {
try {
console.log("[Server Analytics] Tracking order:", data.orderNumber, "Total:", data.total);
// Track order event with OpenPanel
await op.track("order_completed", {
order_id: data.orderId,
order_number: data.orderNumber,
total: data.total,
currency: data.currency,
item_count: data.itemCount,
customer_email: data.customerEmail,
payment_method: data.paymentMethod,
shipping_cost: data.shippingCost,
coupon_code: data.couponCode,
source: "server",
});
// Track revenue with OpenPanel
await op.revenue(data.total, {
currency: data.currency,
transaction_id: data.orderNumber,
order_id: data.orderId,
source: "server",
});
// Track conversion/revenue with Rybbit
await trackRybbitServer("order_completed", {
order_id: data.orderId,
order_number: data.orderNumber,
total: data.total,
currency: data.currency,
item_count: data.itemCount,
customer_email: data.customerEmail,
payment_method: data.paymentMethod,
shipping_cost: data.shippingCost,
coupon_code: data.couponCode,
revenue: data.total,
source: "server",
});
console.log("[Server Analytics] Order tracked successfully");
return { success: true };
} catch (error) {
console.error("[Server Analytics] Failed to track order:", error);
// Don't throw - analytics shouldn't break the app
return { success: false, error: String(error) };
}
}
/**
* Track any server-side event
*/
export async function trackServerEvent(data: ServerEventData) {
try {
await op.track(data.event, {
...data.properties,
source: "server",
});
// Also track to Rybbit
await trackRybbitServer(data.event, data.properties);
return { success: true };
} catch (error) {
console.error("[Server Analytics] Event tracking failed:", error);
return { success: false, error: String(error) };
}
}
/**
* Identify user server-side
*/
export async function identifyUserServer(profileId: string, properties?: Record<string, any>) {
try {
await op.identify({
profileId,
...properties,
});
return { success: true };
} catch (error) {
console.error("[Server Analytics] Identify failed:", error);
return { success: false, error: String(error) };
}
}