Files
web/apps/partner-sas/components/Menu/UserMenu/index.tsx
Joakim Jäderberg aafad9781f Merged in feat/lokalise-rebuild (pull request #2993)
Feat/lokalise rebuild

* chore(lokalise): update translation ids

* chore(lokalise): easier to switch between projects

* chore(lokalise): update translation ids

* .

* .

* .

* .

* .

* .

* chore(lokalise): update translation ids

* chore(lokalise): update translation ids

* .

* .

* .

* chore(lokalise): update translation ids

* chore(lokalise): update translation ids

* .

* .

* chore(lokalise): update translation ids

* chore(lokalise): update translation ids

* chore(lokalise): new translations

* merge

* switch to errors for missing id's

* merge

* sync translations


Approved-by: Linus Flood
2025-10-22 11:00:03 +00:00

217 lines
6.7 KiB
TypeScript

"use client"
import { useSession } from "next-auth/react"
import { useEffect, useState } from "react"
import {
Dialog,
DialogTrigger,
Modal,
ModalOverlay,
Popover,
} from "react-aria-components"
import { useIntl } from "react-intl"
import { Avatar } from "@scandic-hotels/design-system/Avatar"
import { Button } from "@scandic-hotels/design-system/Button"
import { Divider } from "@scandic-hotels/design-system/Divider"
import SkeletonShimmer from "@scandic-hotels/design-system/SkeletonShimmer"
import { Typography } from "@scandic-hotels/design-system/Typography"
import { trpc } from "@scandic-hotels/trpc/client"
import useLang from "@/hooks/useLang"
import { getInitials } from "../utils"
import styles from "./user-menu.module.css"
export function UserMenu({ isMobile = false }: { isMobile?: boolean }) {
const intl = useIntl()
const lang = useLang()
const session = useSession()
const [loginLink, setLoginLink] = useState(`/${lang}/login`)
const {
data: profileData,
isLoading,
isSuccess,
isError,
} = trpc.partner.sas.getEuroBonusProfile.useQuery(undefined, {
enabled: session.status === "authenticated",
})
useEffect(() => {
setLoginLink(`/${lang}/login?redirectTo=${window?.location.href}`)
}, [lang, setLoginLink])
const firstName = profileData?.firstName
const lastName = profileData?.lastName
return (
<div className={styles.userMenu}>
{(session.status === "loading" || isLoading) &&
(isMobile ? (
<SkeletonShimmer width={"4ch"} height={"4ch"} />
) : (
<SkeletonShimmer width={"12ch"} height={"1ch"} />
))}
{(session.status === "unauthenticated" || isError) && (
<a href={loginLink} className={styles.loginLink}>
<Avatar className={styles.avatar} />
{isMobile ? null : (
<Typography
variant={
isMobile
? "Body/Paragraph/mdRegular"
: "Body/Supporting text (caption)/smRegular"
}
>
<span>
{intl.formatMessage({
id: "partnerSas.menu.login",
defaultMessage: "Log in",
})}
</span>
</Typography>
)}
</a>
)}
{session.status === "authenticated" && isSuccess && profileData && (
<div className={styles.loggedInMenu}>
<DialogTrigger>
<Button className={styles.userName} variant={"Text"}>
<Avatar
className={styles.avatar}
initials={getInitials(
firstName ?? session.data?.user?.name ?? "",
lastName ?? session.data?.user?.name ?? ""
)}
/>
{isMobile ? null : (
<Typography variant="Body/Supporting text (caption)/smBold">
<span>
{firstName ?? session.data?.user?.email}
{/* eslint-disable-next-line formatjs/no-literal-string-in-jsx */}
{` ${lastName}`}
</span>
</Typography>
)}
</Button>
{isMobile ? (
<ModalOverlay isDismissable>
<Modal className={styles.modal}>
<Dialog className={styles.dialog}>
{({ close }) => (
<>
<Button
variant={"Text"}
type="button"
className={styles.closeModalBtn}
aria-label={intl.formatMessage({
id: "header.closeMenu",
defaultMessage: "Close menu",
})}
onPress={close}
>
<span className={styles.bar} />
</Button>
<UserMenuContent
firstName={firstName}
lastName={lastName}
points={profileData.points.total}
isMobile={true}
/>
</>
)}
</Dialog>
</Modal>
</ModalOverlay>
) : (
<Popover className={styles.userDetailsPopover} offset={20}>
<Dialog className={styles.userDetailsContainer}>
<UserMenuContent
firstName={firstName}
lastName={lastName}
points={profileData.points.total}
/>
</Dialog>
</Popover>
)}
</DialogTrigger>
</div>
)}
</div>
)
}
function UserMenuContent({
firstName,
lastName,
points,
isMobile,
}: {
firstName?: string
lastName?: string
points?: number
isMobile?: boolean
}) {
const intl = useIntl()
const lang = useLang()
return (
<>
<div>
{isMobile && (
<Typography variant={"Title/Subtitle/md"}>
<h3>
{intl.formatMessage(
{
id: "partnerSas.mobileMenu.greeting",
defaultMessage: `Hi {fName} {lName}!`,
},
{ fName: firstName, lName: lastName }
)}
</h3>
</Typography>
)}
<p className={styles.pointsDetails}>
<Typography variant="Title/Overline/sm">
<span>
{intl.formatMessage({
id: "partnerSas.menu.ebPointsTitle",
defaultMessage: "EB Points",
})}
</span>
</Typography>
<Typography variant="Title/Overline/sm">
{/* eslint-disable-next-line formatjs/no-literal-string-in-jsx */}
<span>{"·"}</span>
</Typography>
<Typography variant="Body/Paragraph/mdRegular">
<span>
{intl.formatMessage(
{
id: "partnerSas.menu.ebPoints",
defaultMessage: "{points} points",
},
{
points: points,
}
)}
</span>
</Typography>
</p>
</div>
<Divider className={styles.menuDivider} />
<Typography variant={"Link/md"} className={styles.logoutLink}>
{/* Link triggers rsc which doesn't reload complete page and shows logged in even after logout */}
<a href={`/${lang}/logout`}>
{intl.formatMessage({
id: "common.logOut",
defaultMessage: "Log out",
})}
</a>
</Typography>
</>
)
}