"use client"; import { useEffect, useState, type ReactNode } from "react"; import { FacebookPixels, GoogleAnalytics, YandexMetrika, PageViewTracker } from "@/components/analytics"; import { getPixels } from "@/entities/session/actions"; import { getSourceByPathname } from "@/shared/utils/source"; interface PixelsProviderProps { children: ReactNode; googleAnalyticsId?: string; yandexMetrikaId?: string; } /** * Pixels Provider Component * * Loads tracking scripts for Facebook Pixels, Google Analytics, and Yandex Metrika. * * IMPORTANT: This component should be placed in a layout (not in components that re-render on navigation) * to avoid duplicate API requests. Currently used in app/[funnelId]/layout.tsx * * Facebook Pixels are loaded from backend API (cached in localStorage). * Google Analytics and Yandex Metrika IDs come from funnel configuration. * * Flow: * 1. Check localStorage for cached FB pixels * 2. If not cached, request from backend (errors are handled gracefully) * 3. Save to localStorage if pixels received * 4. Render GA and YM if IDs provided in funnel config */ export function PixelsProvider({ children, googleAnalyticsId, yandexMetrikaId }: PixelsProviderProps) { const [pixels, setPixels] = useState([]); const [isLoading, setIsLoading] = useState(true); useEffect(() => { const loadPixels = async () => { try { // Check localStorage first const cachedPixels = localStorage.getItem("fb_pixels"); if (cachedPixels) { const parsed = JSON.parse(cachedPixels); setPixels(parsed); setIsLoading(false); return; } // Load from backend const locale = "en"; // TODO: Get from context or config const source = getSourceByPathname(); const domain = window.location.hostname; const response = await getPixels({ domain, source, locale, }); const pixelIds = response?.data?.fb || []; // Save to localStorage only if we got pixels if (pixelIds.length > 0) { localStorage.setItem("fb_pixels", JSON.stringify(pixelIds)); } setPixels(pixelIds); } catch (error) { // Silently handle errors - pixels are optional console.warn("Facebook pixels not available:", error instanceof Error ? error.message : error); setPixels([]); } finally { setIsLoading(false); } }; loadPixels(); }, []); return ( <> {!isLoading && } {children} ); }