feat(analytics): add OpenPanel tracking to storefront
Add comprehensive OpenPanel analytics tracking: - Install @openpanel/nextjs SDK - Add OpenPanelComponent to root layout for automatic page views - Create useAnalytics hook for tracking custom events - Track checkout funnel: started, shipping step, order completed - Track product views and add-to-cart events - Identify users on order completion - Add NEXT_PUBLIC_OPENPANEL_CLIENT_ID to environment
This commit is contained in:
@@ -10,6 +10,7 @@ import Footer from "@/components/layout/Footer";
|
||||
import { useSaleorCheckoutStore } from "@/stores/saleorCheckoutStore";
|
||||
import { formatPrice } from "@/lib/saleor";
|
||||
import { saleorClient } from "@/lib/saleor/client";
|
||||
import { useAnalytics } from "@/lib/analytics";
|
||||
import {
|
||||
CHECKOUT_SHIPPING_ADDRESS_UPDATE,
|
||||
CHECKOUT_BILLING_ADDRESS_UPDATE,
|
||||
@@ -96,6 +97,7 @@ export default function CheckoutPage() {
|
||||
const locale = useLocale();
|
||||
const router = useRouter();
|
||||
const { checkout, refreshCheckout, getLines, getTotal } = useSaleorCheckoutStore();
|
||||
const { trackCheckoutStarted, trackCheckoutStep, trackOrderCompleted, identifyUser } = useAnalytics();
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [orderComplete, setOrderComplete] = useState(false);
|
||||
@@ -138,6 +140,25 @@ export default function CheckoutPage() {
|
||||
}
|
||||
}, [checkout, refreshCheckout]);
|
||||
|
||||
// Track checkout started when page loads
|
||||
useEffect(() => {
|
||||
if (checkout) {
|
||||
const lines = getLines();
|
||||
const total = getTotal();
|
||||
trackCheckoutStarted({
|
||||
total,
|
||||
currency: "RSD",
|
||||
item_count: lines.reduce((sum, line) => sum + line.quantity, 0),
|
||||
items: lines.map(line => ({
|
||||
id: line.variant.id,
|
||||
name: line.variant.product.name,
|
||||
quantity: line.quantity,
|
||||
price: line.variant.pricing?.price?.gross?.amount || 0,
|
||||
})),
|
||||
});
|
||||
}
|
||||
}, [checkout]);
|
||||
|
||||
// Scroll to top when order is complete
|
||||
useEffect(() => {
|
||||
if (orderComplete) {
|
||||
@@ -257,6 +278,27 @@ export default function CheckoutPage() {
|
||||
if (order) {
|
||||
setOrderNumber(order.number);
|
||||
setOrderComplete(true);
|
||||
|
||||
// Track order completion
|
||||
const lines = getLines();
|
||||
const total = getTotal();
|
||||
trackOrderCompleted({
|
||||
order_id: checkout.id,
|
||||
order_number: order.number,
|
||||
total,
|
||||
currency: "RSD",
|
||||
item_count: lines.reduce((sum, line) => sum + line.quantity, 0),
|
||||
shipping_cost: shippingMethods.find(m => m.id === selectedShippingMethod)?.price.amount,
|
||||
customer_email: shippingAddress.email,
|
||||
});
|
||||
|
||||
// Identify the user
|
||||
identifyUser({
|
||||
profileId: shippingAddress.email,
|
||||
email: shippingAddress.email,
|
||||
firstName: shippingAddress.firstName,
|
||||
lastName: shippingAddress.lastName,
|
||||
});
|
||||
} else {
|
||||
throw new Error(t("errorCreatingOrder"));
|
||||
}
|
||||
@@ -330,6 +372,11 @@ export default function CheckoutPage() {
|
||||
setShippingMethods(availableMethods);
|
||||
setShowShippingMethods(true);
|
||||
|
||||
// Track shipping step
|
||||
trackCheckoutStep("shipping_method_selection", {
|
||||
available_methods_count: availableMethods.length,
|
||||
});
|
||||
|
||||
// Don't complete yet - show shipping method selection
|
||||
console.log("Phase 1 complete. Waiting for shipping method selection...");
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import { Metadata } from "next";
|
||||
import { NextIntlClientProvider } from "next-intl";
|
||||
import { getMessages, setRequestLocale } from "next-intl/server";
|
||||
import { SUPPORTED_LOCALES, DEFAULT_LOCALE, isValidLocale } from "@/lib/i18n/locales";
|
||||
import { OpenPanelComponent } from "@openpanel/nextjs";
|
||||
|
||||
const baseUrl = process.env.NEXT_PUBLIC_SITE_URL || "https://dev.manoonoils.com";
|
||||
|
||||
@@ -44,8 +45,15 @@ export default async function LocaleLayout({
|
||||
const messages = await getMessages();
|
||||
|
||||
return (
|
||||
<NextIntlClientProvider messages={messages}>
|
||||
{children}
|
||||
</NextIntlClientProvider>
|
||||
<>
|
||||
<OpenPanelComponent
|
||||
clientId={process.env.NEXT_PUBLIC_OPENPANEL_CLIENT_ID || ""}
|
||||
trackScreenViews={true}
|
||||
trackOutgoingLinks={true}
|
||||
/>
|
||||
<NextIntlClientProvider messages={messages}>
|
||||
{children}
|
||||
</NextIntlClientProvider>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user