diff --git a/components/TempDesignSystem/Link/index.tsx b/components/TempDesignSystem/Link/index.tsx index 1a2e46415..8ef6452da 100644 --- a/components/TempDesignSystem/Link/index.tsx +++ b/components/TempDesignSystem/Link/index.tsx @@ -1,9 +1,11 @@ "use client" import NextLink from "next/link" -import { usePathname } from "next/navigation" -import { useCallback, useEffect } from "react" +import { usePathname, useRouter } from "next/navigation" +import { startTransition, useCallback, useEffect } from "react" -import { trackClick } from "@/utils/tracking" +import useRouterTransitionStore from "@/stores/router-transition" + +import { trackClick, trackPageViewStart } from "@/utils/tracking" import { linkVariants } from "./variants" @@ -21,6 +23,7 @@ export default function Link({ prefetch, variant, trackingId, + onClick, ...props }: LinkProps) { const currentPageSlug = usePathname() @@ -39,28 +42,36 @@ export default function Link({ variant, }) + const router = useRouter() + + const startRouterTransition = useRouterTransitionStore( + (state) => state.startRouterTransition + ) + const trackClickById = useCallback(() => { if (trackingId) { trackClick(trackingId) } }, [trackingId]) - useEffect(() => { - if (trackingId) { - const linkComponent = document.getElementById(trackingId) - - linkComponent?.addEventListener("click", trackClickById) - return () => { - linkComponent?.removeEventListener("click", trackClickById) - } - } - }, [trackClickById, trackingId]) - return ( { + trackPageViewStart() + startTransition(() => { + startRouterTransition() + if (trackingId) { + trackClickById() + } + if (onClick) { + onClick(e) + } + router.push(href) + }) + }} href={href} id={trackingId} {...props} diff --git a/components/TrackingSDK/Client.tsx b/components/TrackingSDK/Client.tsx index 95d42884c..5add36f8a 100644 --- a/components/TrackingSDK/Client.tsx +++ b/components/TrackingSDK/Client.tsx @@ -1,7 +1,7 @@ "use client" import { usePathname } from "next/navigation" -import { useCallback, useEffect } from "react" +import { useCallback, useEffect, useState } from "react" import { webviews } from "@/constants/routes/webviews" @@ -16,20 +16,17 @@ function createSDKPageObject(trackingData: TrackingSDKData) { const { host: domain } = window.location const page_obj = { - event: "pageView", - pageInfo: { - pageType: trackingData.pageType, - pageName: joinedSegments, - pageId: trackingData.pageId, - channel: trackingData.channel, - siteSection: joinedSegments, - domain, - siteversion: "new-web", - domainlanguage: trackingData.lang ? trackingData.lang : lang, - createDate: trackingData.createdDate, - publishDate: trackingData.publishedDate, - // sessionid: "", // base on what? - }, + pageType: trackingData.pageType, + pageName: joinedSegments, + pageId: trackingData.pageId, + channel: trackingData.channel, + siteSection: joinedSegments, + domain, + siteversion: "new-web", + domainlanguage: trackingData.lang ? trackingData.lang : lang, + createDate: trackingData.createdDate, + publishDate: trackingData.publishedDate, + // sessionid: "", // base on what? } return page_obj } @@ -37,6 +34,7 @@ function createSDKPageObject(trackingData: TrackingSDKData) { export default function TrackingSDK({ pageData, userData }: TrackingSDKProps) { const pathName = usePathname() const isWebview = webviews.includes(pathName) + const [initPerformanceTracking, setInitPerformanceTracking] = useState(true) const CookiebotCallbackOnAccept = useCallback(() => { const cookie = window._satellite.cookie.get("CookieConsent") @@ -62,11 +60,55 @@ export default function TrackingSDK({ pageData, userData }: TrackingSDKProps) { } } + useEffect(() => { + if (initPerformanceTracking) { + const perfObserver = new PerformanceObserver((observedEntries) => { + const entry = observedEntries.getEntriesByType("navigation")[0] + + if (entry && window.adobeDataLayer) { + const trackingData = { ...pageData, pathName } + const pageObject = createSDKPageObject(trackingData) + + const { loadEventEnd, startTime, duration } = + entry as PerformanceNavigationTiming + + window.adobeDataLayer.push({ + event: "pageViewEnd", + pageInfo: pageObject, + userInfo: userData, + timing: { + duration, + loadEventEnd, + startTime, + }, + }) + } + }) + + perfObserver.observe({ + type: "navigation", + buffered: true, + }) + + setInitPerformanceTracking(false) + + // Cleanup function to disconnect the observer + return () => { + perfObserver.disconnect() + } + } + }, [pathName, pageData, userData, initPerformanceTracking]) + useEffect(() => { if (window.adobeDataLayer) { const trackingData = { ...pageData, pathName } const pageObject = createSDKPageObject(trackingData) - window.adobeDataLayer.push({ ...pageObject, userInfo: userData }) + + window.adobeDataLayer.push({ + event: "pageView", + pageInfo: pageObject, + userInfo: userData, + }) } }, [pathName, pageData, userData]) diff --git a/components/TrackingSDK/RouterTransition.tsx b/components/TrackingSDK/RouterTransition.tsx new file mode 100644 index 000000000..1b5de9346 --- /dev/null +++ b/components/TrackingSDK/RouterTransition.tsx @@ -0,0 +1,35 @@ +"use client" + +import { startTransition, useEffect, useOptimistic } from "react" + +import useRouterTransitionStore from "@/stores/router-transition" + +export default function RouterTransition() { + const [loading, setLoading] = useOptimistic(false) + const { isTransitioning, stopRouterTransition } = useRouterTransitionStore() + + useEffect(() => { + if (!isTransitioning) { + return + } + + if (isTransitioning) { + startTransition(() => { + setLoading(true) + }) + } + + if (!loading && isTransitioning) { + stopRouterTransition() + + // Send event to adobe that navigation transition is completed + if (window.adobeDataLayer) { + window.adobeDataLayer.push({ + event: "pageViewEnd", + }) + } + } + }, [isTransitioning, setLoading, loading, stopRouterTransition]) + + return null +} diff --git a/components/TrackingSDK/index.tsx b/components/TrackingSDK/index.tsx index 761169953..e725630e3 100644 --- a/components/TrackingSDK/index.tsx +++ b/components/TrackingSDK/index.tsx @@ -1,5 +1,7 @@ import { serverClient } from "@/lib/trpc/server" +import RouterTransition from "@/components/TrackingSDK/RouterTransition" + import TrackingSDKClient from "./Client" import { TrackingSDKPageData } from "@/types/components/tracking" @@ -15,5 +17,10 @@ export default async function TrackingSDK({ }) { const userTrackingData = await serverClient().user.tracking() - return + return ( + <> + + + + ) } diff --git a/stores/router-transition.ts b/stores/router-transition.ts new file mode 100644 index 000000000..0469868f3 --- /dev/null +++ b/stores/router-transition.ts @@ -0,0 +1,17 @@ +"use client" + +import { create } from "zustand" + +interface RouterTransitionState { + isTransitioning: boolean + startRouterTransition: () => void + stopRouterTransition: () => void +} + +const useRouterTransitionStore = create((set) => ({ + isTransitioning: false, + startRouterTransition: () => set(() => ({ isTransitioning: true })), + stopRouterTransition: () => set(() => ({ isTransitioning: false })), +})) + +export default useRouterTransitionStore