fix(BOOK-704): add tag name to campaign banner tracking * fix(BOOK-704): add tag name to campaign banner tracking * fix(BOOK-704): handleclose tag name Approved-by: Erik Tiekstra Approved-by: Matilda Landström
157 lines
4.3 KiB
TypeScript
157 lines
4.3 KiB
TypeScript
"use client"
|
|
|
|
import NextLink from "next/link"
|
|
import { usePathname } from "next/navigation"
|
|
import { useCallback, useEffect, useRef, useState } from "react"
|
|
import { useIntl } from "react-intl"
|
|
import { useMediaQuery } from "usehooks-ts"
|
|
|
|
import { debounce } from "@scandic-hotels/common/utils/debounce"
|
|
import { IconButton } from "@scandic-hotels/design-system/IconButton"
|
|
import { trackClick } from "@scandic-hotels/tracking/base"
|
|
import { trpc } from "@scandic-hotels/trpc/client"
|
|
|
|
import useLang from "@/hooks/useLang"
|
|
|
|
import { DesktopCampaignBanner } from "./Desktop"
|
|
import { MobileCampaignBanner } from "./Mobile"
|
|
import { shouldShowCampaignBanner } from "./utils"
|
|
|
|
import styles from "./campaignBanner.module.css"
|
|
|
|
import type { CampaignBannerProps } from "@/components/CampaignBanner/types"
|
|
|
|
export default function CampaignBanner() {
|
|
const lang = useLang()
|
|
const intl = useIntl()
|
|
const pathname = usePathname()
|
|
const campaignBannerRef = useRef<HTMLDivElement>(null)
|
|
const isMobile = useMediaQuery("(max-width: 767px)")
|
|
const [closedPaths, setClosedPaths] = useState<Set<string>>(new Set())
|
|
const [
|
|
{ data: siteConfig, isLoading: siteConfigLoading },
|
|
{ data: campaignBanner, isLoading: campaignBannerLoading },
|
|
] = trpc.useQueries((t) => [
|
|
t.contentstack.base.siteConfig({ lang }, { refetchInterval: 60_000 }),
|
|
t.contentstack.base.sitewideCampaignBanner.get(
|
|
{ lang },
|
|
{ refetchInterval: 360_000 }
|
|
),
|
|
])
|
|
const isOnSamePage = pathname === campaignBanner?.link?.url
|
|
const sitewideAlertType = siteConfig?.sitewideAlert?.type || null
|
|
const shouldShowBanner = shouldShowCampaignBanner(
|
|
pathname,
|
|
lang,
|
|
closedPaths,
|
|
sitewideAlertType
|
|
)
|
|
const isVisible =
|
|
!siteConfigLoading &&
|
|
!campaignBannerLoading &&
|
|
!!campaignBanner &&
|
|
shouldShowBanner
|
|
|
|
const updateHeightRefCallback = useCallback((node: HTMLDivElement | null) => {
|
|
if (node) {
|
|
const debouncedUpdate = debounce(([entry]) => {
|
|
const height = entry.contentRect.height
|
|
|
|
document.documentElement.style.setProperty(
|
|
"--campaign-banner-height",
|
|
`${height}px`
|
|
)
|
|
}, 100)
|
|
|
|
const observer = new ResizeObserver(debouncedUpdate)
|
|
observer.observe(node)
|
|
|
|
return () => {
|
|
if (node) {
|
|
observer.unobserve(node)
|
|
}
|
|
observer.disconnect()
|
|
}
|
|
}
|
|
}, [])
|
|
|
|
useEffect(() => {
|
|
if (!isVisible) {
|
|
document.documentElement.style.removeProperty("--campaign-banner-height")
|
|
}
|
|
}, [isVisible])
|
|
|
|
if (!isVisible) {
|
|
return null
|
|
}
|
|
|
|
function handleClose() {
|
|
trackClick(`${campaignBanner?.tag} close`)
|
|
setClosedPaths((prev) => new Set(prev).add(pathname))
|
|
}
|
|
|
|
return (
|
|
<div
|
|
className={styles.campaignBanner}
|
|
ref={(node) => {
|
|
campaignBannerRef.current = node
|
|
return updateHeightRefCallback(node)
|
|
}}
|
|
>
|
|
<div className={styles.content}>
|
|
<InnerContent
|
|
link={isOnSamePage ? null : campaignBanner.link}
|
|
tag={campaignBanner.tag}
|
|
>
|
|
{isMobile ? (
|
|
<MobileCampaignBanner
|
|
tag={campaignBanner.tag}
|
|
text={campaignBanner.text}
|
|
link={isOnSamePage ? null : campaignBanner.link}
|
|
bookingCode={campaignBanner.booking_code}
|
|
/>
|
|
) : (
|
|
<DesktopCampaignBanner
|
|
tag={campaignBanner.tag}
|
|
text={campaignBanner.text}
|
|
link={isOnSamePage ? null : campaignBanner.link}
|
|
bookingCode={campaignBanner.booking_code}
|
|
/>
|
|
)}
|
|
</InnerContent>
|
|
<IconButton
|
|
className={styles.closeButton}
|
|
variant="Muted"
|
|
onPress={handleClose}
|
|
aria-label={intl.formatMessage({
|
|
id: "campaignBanner.dismissBanner",
|
|
defaultMessage: "Dismiss banner",
|
|
})}
|
|
iconName="close"
|
|
/>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
function InnerContent({
|
|
link,
|
|
children,
|
|
tag,
|
|
}: React.PropsWithChildren<{
|
|
link: CampaignBannerProps["link"]
|
|
tag: string
|
|
}>) {
|
|
return link ? (
|
|
<NextLink
|
|
href={link.url}
|
|
className={styles.innerContent}
|
|
onClick={() => trackClick(`${tag} campaign banner`)}
|
|
>
|
|
{children}
|
|
</NextLink>
|
|
) : (
|
|
<div className={styles.innerContent}>{children}</div>
|
|
)
|
|
}
|