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:
Unchained
2026-03-25 18:48:13 +02:00
parent bf628f873f
commit 17367024c2
6 changed files with 352 additions and 4 deletions

View File

@@ -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...");
}