Merged in feat/trackingsdk-client (pull request #1420)

feat: trackingsdk as client component

* feat: trackingsdk as client component

* Cleanup

* Merge changes from feat/trackingsdk-client

* revert yarn.lock

* Added lcpTime and wait with tracking until we have the values


Approved-by: Joakim Jäderberg
This commit is contained in:
Linus Flood
2025-02-27 07:22:58 +00:00
parent d995dcf0aa
commit 0c498d82ca
23 changed files with 90 additions and 103 deletions

View File

@@ -42,9 +42,7 @@ export default async function MyPages({}: PageArgs<
<p>{intl.formatMessage({ id: "No content published" })}</p>
)}
</main>
<Suspense fallback={null}>
<TrackingSDK pageData={tracking} />
</Suspense>
<TrackingSDK pageData={tracking} />
</>
)
}

View File

@@ -19,9 +19,7 @@ export default async function ProfilePage({}: PageArgs<LangParams>) {
return (
<>
<Profile />
<Suspense fallback={null}>
<TrackingSDK pageData={accountPage.tracking} />
</Suspense>
<TrackingSDK pageData={accountPage.tracking} />
</>
)
}

View File

@@ -30,9 +30,7 @@ export default function HotelReservationPage({ params }: PageArgs<LangParams>) {
return (
<div className={styles.page}>
New booking flow! Please report errors/issues in Slack.
<Suspense fallback={null}>
<TrackingSDK pageData={pageTrackingData} />
</Suspense>
<TrackingSDK pageData={pageTrackingData} />
</div>
)
}

View File

@@ -14,7 +14,6 @@ import Header from "@/components/Header"
import StorageCleaner from "@/components/HotelReservation/EnterDetails/StorageCleaner"
import SitewideAlert from "@/components/SitewideAlert"
import { ToastHandler } from "@/components/TempDesignSystem/Toasts"
import { preloadUserTracking } from "@/components/TrackingSDK"
import AdobeSDKScript from "@/components/TrackingSDK/AdobeSDKScript"
import GTMScript from "@/components/TrackingSDK/GTMScript"
import RouterTracking from "@/components/TrackingSDK/RouterTracking"
@@ -31,7 +30,6 @@ export default async function RootLayout({
bookingwidget: React.ReactNode
}
>) {
preloadUserTracking()
const { defaultLocale, locale, messages } = await getIntl()
return (

View File

@@ -11,7 +11,6 @@ import TokenRefresher from "@/components/Auth/TokenRefresher"
import CookieBotConsent from "@/components/CookieBot"
import StorageCleaner from "@/components/HotelReservation/EnterDetails/StorageCleaner"
import { ToastHandler } from "@/components/TempDesignSystem/Toasts"
import { preloadUserTracking } from "@/components/TrackingSDK"
import AdobeSDKScript from "@/components/TrackingSDK/AdobeSDKScript"
import GTMScript from "@/components/TrackingSDK/GTMScript"
import RouterTracking from "@/components/TrackingSDK/RouterTracking"
@@ -28,7 +27,6 @@ export default async function RootLayout({
return null
}
preloadUserTracking()
const { defaultLocale, locale, messages } = await getIntl()
return (

View File

@@ -90,9 +90,7 @@ export default async function DestinationCityPage({
/>
</HotelDataContainer>
</Suspense>
<Suspense fallback={null}>
<TrackingSDK pageData={tracking} />
</Suspense>
<TrackingSDK pageData={tracking} />
</>
)
}

View File

@@ -76,9 +76,7 @@ export default async function DestinationCountryPage() {
</aside>
</div>
<CountryMap country={destination_settings.country} />
<Suspense fallback={null}>
<TrackingSDK pageData={tracking} />
</Suspense>
<TrackingSDK pageData={tracking} />
</>
)
}

View File

@@ -40,9 +40,7 @@ export default async function DestinationOverviewPage() {
<HotelsSection destinations={destinationsData} />
</aside>
)}
<Suspense fallback={null}>
<TrackingSDK pageData={tracking} />
</Suspense>
<TrackingSDK pageData={tracking} />
</>
)
}

View File

@@ -50,9 +50,7 @@ export default async function LoyaltyPage() {
) : null}
</MaxWidth>
</section>
<Suspense fallback={null}>
<TrackingSDK pageData={tracking} />
</Suspense>
<TrackingSDK pageData={tracking} />
</>
)
}

View File

@@ -59,9 +59,7 @@ export default async function StartPage() {
)
})}
</main>
<Suspense fallback={null}>
<TrackingSDK pageData={content.tracking} />
</Suspense>
<TrackingSDK pageData={content.tracking} />
</div>
)
}

View File

@@ -78,9 +78,7 @@ export default function StaticPage({
) : null}
</div>
</section>
<Suspense fallback={null}>
<TrackingSDK pageData={tracking} />
</Suspense>
<TrackingSDK pageData={tracking} />
</>
)
}

View File

@@ -112,7 +112,7 @@ export default function ReceiptRoom({
</div>
<div className={styles.entry}>
<Body>{intl.formatMessage({ id: "Breakfast buffet" })}</Body>
{booking.rateDefinition.breakfastIncluded ?? breakfastPkgIncluded ? (
{(booking.rateDefinition.breakfastIncluded ?? breakfastPkgIncluded) ? (
<Body color="red">{intl.formatMessage({ id: "Included" })}</Body>
) : null}
{breakfastPkgSelected ? (

View File

@@ -53,9 +53,9 @@
var(--Spacing-x2);
}
.img {
min-width: 306px;
}
.img {
min-width: 306px;
}
.roomDetails {
grid-template-columns: 1fr 1fr;
}

View File

@@ -159,13 +159,11 @@ export default async function BookingConfirmation({
</SidePanel>
</aside>
</Confirmation>
<Suspense fallback={null}>
<TrackingSDK
pageData={initialPageTrackingData}
hotelInfo={initialHotelsTrackingData}
paymentInfo={paymentInfo}
/>
</Suspense>
<TrackingSDK
pageData={initialPageTrackingData}
hotelInfo={initialHotelsTrackingData}
paymentInfo={paymentInfo}
/>
</>
)
}

View File

@@ -155,12 +155,7 @@ export async function SelectHotelMapContainer({
cityCoordinates={cityCoordinates}
bookingCode={bookingCode ?? ""}
/>
<Suspense fallback={null}>
<TrackingSDK
pageData={pageTrackingData}
hotelInfo={hotelsTrackingData}
/>
</Suspense>
<TrackingSDK pageData={pageTrackingData} hotelInfo={hotelsTrackingData} />
</>
)
}

View File

@@ -49,44 +49,65 @@ export default function RouterTransition({
useEffect(() => {
if (!hasRun && !hasRunInitial.current) {
const handleLoad = () => {
const perfObserver = new PerformanceObserver((observedEntries) => {
const entry = observedEntries.getEntriesByType("navigation")[0]
if (entry) {
const trackingData = {
...pageData,
sessionId,
pathName,
pageLoadTime: entry.duration / 1000,
}
const pageObject = createSDKPageObject(trackingData)
trackPageView({
event: "pageView",
pageInfo: pageObject,
userInfo: userData,
hotelInfo: hotelInfo,
paymentInfo,
})
perfObserver.disconnect()
}
})
perfObserver.observe({
type: "navigation",
buffered: true,
})
}
// If the page is already loaded, execute immediately
if (document.readyState === "complete") {
handleLoad()
} else {
// Otherwise wait for the load event
window.addEventListener("load", handleLoad)
return () => window.removeEventListener("load", handleLoad)
}
hasRunInitial.current = true
setHasRun()
const getPageLoadTime = () => {
return new Promise<number>((resolve) => {
const observer = new PerformanceObserver((entries) => {
const navEntry = entries.getEntriesByType("navigation")[0]
if (navEntry) {
observer.disconnect()
resolve(navEntry.duration / 1000)
}
})
observer.observe({ type: "navigation", buffered: true })
})
}
const getLCPTime = () => {
return new Promise<number>((resolve) => {
const observer = new PerformanceObserver((entries) => {
const lastEntry = entries.getEntries().pop()
if (lastEntry) {
observer.disconnect()
resolve(lastEntry.startTime / 1000)
}
})
observer.observe({ type: "largest-contentful-paint", buffered: true })
})
}
const trackPerformance = async () => {
const [pageLoadTime, lcpTime] = await Promise.all([
getPageLoadTime(),
getLCPTime(),
])
const trackingData = {
...pageData,
sessionId,
pathName,
pageLoadTime,
lcpTime,
}
const pageObject = createSDKPageObject(trackingData)
trackPageView({
event: "pageView",
pageInfo: pageObject,
userInfo: userData,
hotelInfo,
paymentInfo,
})
}
if (document.readyState === "complete") {
trackPerformance()
} else {
window.addEventListener("load", trackPerformance)
return () => window.removeEventListener("load", trackPerformance)
}
}
}, [
pathName,

View File

@@ -1,6 +1,7 @@
import { getUserTracking } from "@/lib/trpc/memoizedRequests"
"use client"
import RouterTransition from "@/components/TrackingSDK/RouterTransition"
import { trpc } from "@/lib/trpc/client"
import type {
TrackingSDKHotelInfo,
@@ -8,11 +9,7 @@ import type {
TrackingSDKPaymentInfo,
} from "@/types/components/tracking"
export const preloadUserTracking = () => {
void getUserTracking()
}
export default async function TrackingSDK({
export default function TrackingSDK({
pageData,
hotelInfo,
paymentInfo,
@@ -21,7 +18,12 @@ export default async function TrackingSDK({
hotelInfo?: TrackingSDKHotelInfo
paymentInfo?: TrackingSDKPaymentInfo
}) {
const userTrackingData = await getUserTracking()
const { data: userTrackingData, isPending } =
trpc.user.userTrackingInfo.useQuery()
if (isPending || !userTrackingData) {
return null
}
return (
<RouterTransition

View File

@@ -33,9 +33,7 @@ export default async function AccountPage() {
<Blocks content={accountPage.content} />
</MaxWidth>
<Suspense fallback={null}>
<TrackingSDK pageData={tracking} />
</Suspense>
<TrackingSDK pageData={tracking} />
</>
)
}

View File

@@ -29,9 +29,7 @@ export default async function AboutScandicFriends() {
</MaxWidth>
</section>
<Suspense fallback={null}>
<TrackingSDK pageData={tracking} />
</Suspense>
<TrackingSDK pageData={tracking} />
</>
)
}

View File

@@ -64,10 +64,6 @@ export const getMembershipCards = cache(
}
)
export const getUserTracking = cache(async function getMemoizedUserTracking() {
return serverClient().user.tracking()
})
export const getHotelsByCSFilter = cache(async function getMemoizedHotels(
input: GetHotelsByCSFilterInput
) {

View File

@@ -357,7 +357,7 @@ export const userQueryRouter = router({
const membershipLevel = getMembership(verifiedData.data.memberships)
return membershipLevel
}),
tracking: safeProtectedProcedure.query(async function ({ ctx }) {
userTrackingInfo: safeProtectedProcedure.query(async function ({ ctx }) {
const notLoggedInUserTrackingData: TrackingSDKUserData = {
loginStatus: "Non-logged in",
}

View File

@@ -16,4 +16,4 @@ export interface ConfirmationProps extends BookingConfirmation {
export interface BookingConfirmationAlertsProps {
booking: BookingConfirmationSchema
}
}

View File

@@ -26,6 +26,7 @@ export type TrackingSDKPageData = {
domain?: string
siteSections: string
pageLoadTime?: number // Page load time in seconds
lcpTime?: number // Largest contentful paint time in seconds
sessionId?: string | null
}