feat(BOOK-212): Refactored LoginButton and added successful tracking functionality

Approved-by: Linus Flood
This commit is contained in:
Erik Tiekstra
2025-11-11 06:57:49 +00:00
parent 1b35618eb2
commit c93e2b6f0b
8 changed files with 91 additions and 71 deletions

View File

@@ -2,9 +2,8 @@
import { useIntl } from "react-intl" import { useIntl } from "react-intl"
import { login } from "@scandic-hotels/common/constants/routes/handleAuth"
import { useLazyPathname } from "@scandic-hotels/common/hooks/useLazyPathname" import { useLazyPathname } from "@scandic-hotels/common/hooks/useLazyPathname"
import ButtonLink from "@scandic-hotels/design-system/ButtonLink" import { LoginButton } from "@scandic-hotels/design-system/LoginButton"
import { trackEvent } from "@scandic-hotels/tracking/base" import { trackEvent } from "@scandic-hotels/tracking/base"
import useLang from "@/hooks/useLang" import useLang from "@/hooks/useLang"
@@ -12,13 +11,13 @@ import useLang from "@/hooks/useLang"
export default function PromoLoginButton() { export default function PromoLoginButton() {
const lang = useLang() const lang = useLang()
const intl = useIntl() const intl = useIntl()
const pathname = useLazyPathname() const loginPathname = useLazyPathname({ includeSearchParams: true })
const loginHref = pathname
? `${login[lang]}?redirectTo=${encodeURIComponent(pathname)}`
: login[lang]
return ( return (
<ButtonLink <LoginButton
lang={lang}
redirectTo={loginPathname}
loginPosition="promo-campaign"
onClick={() => onClick={() =>
trackEvent({ trackEvent({
event: "loginStart", event: "loginStart",
@@ -29,16 +28,14 @@ export default function PromoLoginButton() {
}, },
}) })
} }
href={loginHref}
variant="Primary" variant="Primary"
color="Inverted" color="Inverted"
size="Medium" size="Medium"
prefetch={false}
> >
{intl.formatMessage({ {intl.formatMessage({
id: "promoCampaign.logIn", id: "promoCampaign.logIn",
defaultMessage: "Log in", defaultMessage: "Log in",
})} })}
</ButtonLink> </LoginButton>
) )
} }

View File

@@ -7,7 +7,6 @@ import { MembershipLevelEnum } from "@scandic-hotels/common/constants/membership
import { useLazyPathname } from "@scandic-hotels/common/hooks/useLazyPathname" import { useLazyPathname } from "@scandic-hotels/common/hooks/useLazyPathname"
import { Avatar } from "@scandic-hotels/design-system/Avatar" import { Avatar } from "@scandic-hotels/design-system/Avatar"
import { LoginButton } from "@scandic-hotels/design-system/LoginButton" import { LoginButton } from "@scandic-hotels/design-system/LoginButton"
import { Typography } from "@scandic-hotels/design-system/Typography"
import { trpc } from "@scandic-hotels/trpc/client" import { trpc } from "@scandic-hotels/trpc/client"
import { isValidSession } from "@scandic-hotels/trpc/utils/session" import { isValidSession } from "@scandic-hotels/trpc/utils/session"
@@ -75,17 +74,18 @@ export default function MyPagesMenuWrapper() {
trackLoginClick("top menu") trackLoginClick("top menu")
}} }}
redirectTo={loginPathname} redirectTo={loginPathname}
trackingId="loginStartNewTopMenu" loginPosition="top-menu"
variant="Text"
typography="Body/Paragraph/mdBold"
wrapping={false}
> >
<Avatar /> <Avatar />
<Typography variant={"Body/Paragraph/mdBold"}> <span className={styles.loginText}>
<span className={styles.loginText}> {intl.formatMessage({
{intl.formatMessage({ id: "header.logInJoin",
id: "header.logInJoin", defaultMessage: "Log in/Join",
defaultMessage: "Log in/Join", })}
})} </span>
</span>
</Typography>
</LoginButton> </LoginButton>
)} )}
</> </>

View File

@@ -1,7 +1,5 @@
.loginLink { .loginLink:hover {
display: flex; text-decoration: none !important; /* Special case for the login link inside the header */
align-items: center;
gap: var(--Spacing-x1);
} }
@media screen and (max-width: 767px) { @media screen and (max-width: 767px) {

View File

@@ -6,6 +6,7 @@ import { startTransition, useEffect } from "react"
import { isSameBookingWidgetParams } from "@scandic-hotels/booking-flow/utils/isSameBooking" import { isSameBookingWidgetParams } from "@scandic-hotels/booking-flow/utils/isSameBooking"
import useRouterTransitionStore from "@scandic-hotels/common/stores/router-transition" import useRouterTransitionStore from "@scandic-hotels/common/stores/router-transition"
import useTrackingStore from "@scandic-hotels/common/stores/tracking" import useTrackingStore from "@scandic-hotels/common/stores/tracking"
import { trackEvent } from "@scandic-hotels/tracking/base"
import useLang from "@/hooks/useLang" import useLang from "@/hooks/useLang"
import { trackPageViewStart } from "@/utils/tracking" import { trackPageViewStart } from "@/utils/tracking"
@@ -57,5 +58,26 @@ export default function RouteChange() {
startRouterTransition, startRouterTransition,
]) ])
// Track login success if loginPosition param is present. The LoginButton component has a
// loginPosition prop that adds this param to the URL upon successful login.
useEffect(() => {
const loginPosition = searchParams.get("loginPosition")
if (loginPosition) {
const position = `${loginPosition}, ${pathName}`
trackEvent({
event: "loginSuccess",
login: {
position,
},
})
const params = new URLSearchParams(searchParams)
params.delete("loginPosition")
const search = params.toString()
const newUrl = search ? `${pathName}?${search}` : pathName
window.history.replaceState(null, "", newUrl)
}
}, [pathName, searchParams])
return null return null
} }

View File

@@ -5,7 +5,6 @@ import { useIntl } from "react-intl"
import { useLazyPathname } from "@scandic-hotels/common/hooks/useLazyPathname" import { useLazyPathname } from "@scandic-hotels/common/hooks/useLazyPathname"
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon" import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
import { LoginButton } from "@scandic-hotels/design-system/LoginButton" import { LoginButton } from "@scandic-hotels/design-system/LoginButton"
import { Typography } from "@scandic-hotels/design-system/Typography"
import useLang from "@/hooks/useLang" import useLang from "@/hooks/useLang"
import { trackLoginClick } from "@/utils/tracking" import { trackLoginClick } from "@/utils/tracking"
@@ -22,17 +21,18 @@ export function LoyaltyLoginButton() {
trackLoginClick("join scandic friends sidebar") trackLoginClick("join scandic friends sidebar")
}} }}
redirectTo={loginPathname} redirectTo={loginPathname}
trackingId="loginJoinLoyalty" loginPosition="scandic-friends-sidebar"
variant="Text"
typography="Body/Supporting text (caption)/smBold"
wrapping={false}
> >
<MaterialIcon icon="arrow_forward" color="CurrentColor" size={20} /> <MaterialIcon icon="arrow_forward" color="CurrentColor" size={20} />
<Typography variant={"Body/Paragraph/mdRegular"}> <span>
<span> {intl.formatMessage({
{intl.formatMessage({ id: "loyalty.loginButton",
id: "loyalty.loginButton", defaultMessage: "Log in here",
defaultMessage: "Log in here", })}
})} </span>
</span>
</Typography>
</LoginButton> </LoginButton>
) )
} }

View File

@@ -17,6 +17,7 @@ article.wrapper .preamble {
.loginContainer { .loginContainer {
display: grid; display: grid;
gap: var(--Spacing-x2); gap: var(--Spacing-x2);
justify-items: start;
} }
.button { .button {

View File

@@ -7,7 +7,6 @@ import { formatPrice } from "@scandic-hotels/common/utils/numberFormatting"
import Footnote from "@scandic-hotels/design-system/Footnote" import Footnote from "@scandic-hotels/design-system/Footnote"
import Checkbox from "@scandic-hotels/design-system/Form/Checkbox" import Checkbox from "@scandic-hotels/design-system/Form/Checkbox"
import { LoginButton } from "@scandic-hotels/design-system/LoginButton" import { LoginButton } from "@scandic-hotels/design-system/LoginButton"
import { OldDSButton as Button } from "@scandic-hotels/design-system/OldDSButton"
import Link from "@scandic-hotels/design-system/OldDSLink" import Link from "@scandic-hotels/design-system/OldDSLink"
import { Typography } from "@scandic-hotels/design-system/Typography" import { Typography } from "@scandic-hotels/design-system/Typography"
import { trackLoginClick } from "@scandic-hotels/tracking/navigation" import { trackLoginClick } from "@scandic-hotels/tracking/navigation"
@@ -74,23 +73,23 @@ export function JoinScandicFriendsCard({ name = "join" }: Props) {
</Typography> </Typography>
</Checkbox> </Checkbox>
<Button size="small" color="Primary" asChild> <LoginButton
<LoginButton color="Primary"
lang={lang} variant="Tertiary"
className={styles.login} size="Small"
color="white" lang={lang}
trackingId="join-scandic-friends-enter-details" className={styles.login}
onClick={() => { loginPosition="enter-details"
trackLoginClick("enter details") onClick={() => {
}} trackLoginClick("enter details")
redirectTo={loginPathname} }}
> redirectTo={loginPathname}
{intl.formatMessage({ >
id: "enterDetails.joinScandicFriendsCard.loginButtonText", {intl.formatMessage({
defaultMessage: "Log in", id: "enterDetails.joinScandicFriendsCard.loginButtonText",
})} defaultMessage: "Log in",
</LoginButton> })}
</Button> </LoginButton>
<div className={styles.terms}> <div className={styles.terms}>
<Footnote color="uiTextPlaceholder"> <Footnote color="uiTextPlaceholder">

View File

@@ -1,31 +1,34 @@
'use client' 'use client'
import { login } from '@scandic-hotels/common/constants/routes/handleAuth' import { login } from '@scandic-hotels/common/constants/routes/handleAuth'
import Link, { type LinkProps } from '../OldDSLink'
import type { Lang } from '@scandic-hotels/common/constants/language' import type { Lang } from '@scandic-hotels/common/constants/language'
import type { PropsWithChildren } from 'react' import ButtonLink, { ButtonLinkProps } from '../ButtonLink'
interface LoginButtonProps
extends React.PropsWithChildren<Omit<ButtonLinkProps, 'href'>> {
lang: Lang
redirectTo: string | null
loginPosition: string
}
export function LoginButton({ export function LoginButton({
lang, lang,
redirectTo, redirectTo,
trackingId, loginPosition,
children,
...props ...props
}: PropsWithChildren< }: LoginButtonProps) {
{ let href = login[lang]
lang: Lang
redirectTo: string | null
trackingId: string
} & Omit<LinkProps, 'href'>
>) {
const href = redirectTo
? `${login[lang]}?redirectTo=${encodeURIComponent(redirectTo)}`
: login[lang]
return ( if (redirectTo) {
<Link id={trackingId} href={href} prefetch={false} {...props}> const [pathname, existingQuery] = redirectTo.split('?')
{children} const searchParams = new URLSearchParams(existingQuery)
</Link>
) searchParams.set('loginPosition', loginPosition)
const redirectUrl = `${pathname}?${searchParams.toString()}`
href = `${href}?redirectTo=${encodeURIComponent(redirectUrl)}`
}
return <ButtonLink href={href} prefetch={false} {...props} />
} }