diff --git a/apps/scandic-web/.env.local.example b/apps/scandic-web/.env.local.example
index c1426f4e2..f16417451 100644
--- a/apps/scandic-web/.env.local.example
+++ b/apps/scandic-web/.env.local.example
@@ -54,6 +54,7 @@ GOOGLE_DYNAMIC_MAP_ID=""
ENABLE_SURPRISES="true"
ENABLE_DTMC="true"
+ENABLE_NEW_OVERVIEW_SECTION="true"
SHOW_SITE_WIDE_ALERT="false"
diff --git a/apps/scandic-web/components/Blocks/DynamicContent/Overview/UserBaseInfo/index.tsx b/apps/scandic-web/components/Blocks/DynamicContent/Overview/UserBaseInfo/index.tsx
new file mode 100644
index 000000000..1a1ae93cf
--- /dev/null
+++ b/apps/scandic-web/components/Blocks/DynamicContent/Overview/UserBaseInfo/index.tsx
@@ -0,0 +1,58 @@
+import { Avatar } from "@scandic-hotels/design-system/Avatar"
+import { Typography } from "@scandic-hotels/design-system/Typography"
+
+import CopyMembershipIdButton from "@/components/MyPages/CopyMembershipIdButton"
+import { getIntl } from "@/i18n"
+import { getInitials } from "@/utils/user"
+
+import styles from "./userBaseInfo.module.css"
+
+import type { User } from "@scandic-hotels/trpc/types/user"
+
+interface UserBaseInfoProps {
+ user: User
+}
+
+export default async function UserBaseInfo({ user }: UserBaseInfoProps) {
+ const intl = await getIntl()
+ const initials = getInitials(user.firstName, user.lastName)
+
+ return (
+
+
+
+
+
+ {user.firstName} {user.lastName}
+
+
+
+
+
+ {intl.formatMessage({
+ defaultMessage: "Membership ID:",
+ })}
+
+
+ {user.membership?.membershipNumber ? (
+
+ ) : (
+
+
+ {intl.formatMessage({
+ defaultMessage: "N/A",
+ })}
+
+
+ )}
+
+
+
+ )
+}
diff --git a/apps/scandic-web/components/Blocks/DynamicContent/Overview/UserBaseInfo/userBaseInfo.module.css b/apps/scandic-web/components/Blocks/DynamicContent/Overview/UserBaseInfo/userBaseInfo.module.css
new file mode 100644
index 000000000..230ef4051
--- /dev/null
+++ b/apps/scandic-web/components/Blocks/DynamicContent/Overview/UserBaseInfo/userBaseInfo.module.css
@@ -0,0 +1,18 @@
+.container {
+ display: flex;
+ align-items: center;
+ gap: var(--Space-x15);
+}
+
+.fullName {
+ color: var(--Text-Heading);
+ text-transform: capitalize;
+}
+
+.membershipInfo {
+ display: flex;
+ flex-wrap: wrap;
+ align-items: center;
+ gap: var(--Space-x05);
+ color: var(--Scandic-Red-100);
+}
diff --git a/apps/scandic-web/components/Blocks/DynamicContent/Overview/index.tsx b/apps/scandic-web/components/Blocks/DynamicContent/Overview/index.tsx
index 631f5c6e8..18682829b 100644
--- a/apps/scandic-web/components/Blocks/DynamicContent/Overview/index.tsx
+++ b/apps/scandic-web/components/Blocks/DynamicContent/Overview/index.tsx
@@ -16,6 +16,7 @@ import Hero from "./Friend/Hero"
import MembershipNumber from "./Friend/MembershipNumber"
import Friend from "./Friend"
import Stats from "./Stats"
+import UserBaseInfo from "./UserBaseInfo"
import styles from "./overview.module.css"
@@ -57,6 +58,8 @@ export default async function Overview({
>
+ {env.ENABLE_NEW_OVERVIEW_SECTION ? : null}
+ {/*TODO: Replace Hero Section Cards with New ones. */}
diff --git a/apps/scandic-web/components/Header/MainMenu/Avatar/avatar.module.css b/apps/scandic-web/components/Header/MainMenu/Avatar/avatar.module.css
deleted file mode 100644
index 577b720d2..000000000
--- a/apps/scandic-web/components/Header/MainMenu/Avatar/avatar.module.css
+++ /dev/null
@@ -1,14 +0,0 @@
-.avatar {
- display: flex;
- justify-content: center;
- align-items: center;
- overflow: hidden;
- border-radius: var(--Corner-radius-rounded);
- width: 2rem;
- height: 2rem;
- background-color: var(--UI-Grey-40);
-}
-
-.initials {
- background-color: var(--Base-Icon-Low-contrast);
-}
diff --git a/apps/scandic-web/components/Header/MainMenu/Avatar/index.tsx b/apps/scandic-web/components/Header/MainMenu/Avatar/index.tsx
deleted file mode 100644
index db3cf3a98..000000000
--- a/apps/scandic-web/components/Header/MainMenu/Avatar/index.tsx
+++ /dev/null
@@ -1,28 +0,0 @@
-import Footnote from "@scandic-hotels/design-system/Footnote"
-import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
-import Image from "@scandic-hotels/design-system/Image"
-
-import styles from "./avatar.module.css"
-
-import type { AvatarProps } from "@/types/components/header/avatar"
-
-export default function Avatar({ image, initials }: AvatarProps) {
- let classNames = [styles.avatar]
- let element =
- if (image) {
- classNames.push(styles.image)
- element =
- } else if (initials) {
- classNames.push(styles.initials)
- element = (
-
- {initials}
-
- )
- }
- return (
-
- {element}
-
- )
-}
diff --git a/apps/scandic-web/components/Header/MainMenu/MyPagesMenu/index.tsx b/apps/scandic-web/components/Header/MainMenu/MyPagesMenu/index.tsx
index 49bf553c7..9537fe7c9 100644
--- a/apps/scandic-web/components/Header/MainMenu/MyPagesMenu/index.tsx
+++ b/apps/scandic-web/components/Header/MainMenu/MyPagesMenu/index.tsx
@@ -3,6 +3,7 @@
import { useRef } from "react"
import { useIntl } from "react-intl"
+import { Avatar } from "@scandic-hotels/design-system/Avatar"
import Body from "@scandic-hotels/design-system/Body"
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
import SkeletonShimmer from "@scandic-hotels/design-system/SkeletonShimmer"
@@ -13,7 +14,6 @@ import useClickOutside from "@/hooks/useClickOutside"
import { useHandleKeyUp } from "@/hooks/useHandleKeyUp"
import { getInitials } from "@/utils/user"
-import Avatar from "../Avatar"
import MainMenuButton from "../MainMenuButton"
import MyPagesMenuContent, { useMyPagesNavigation } from "../MyPagesMenuContent"
diff --git a/apps/scandic-web/components/Header/MainMenu/MyPagesMenuWrapper/index.tsx b/apps/scandic-web/components/Header/MainMenu/MyPagesMenuWrapper/index.tsx
index b0d0e8aa3..a70b1168f 100644
--- a/apps/scandic-web/components/Header/MainMenu/MyPagesMenuWrapper/index.tsx
+++ b/apps/scandic-web/components/Header/MainMenu/MyPagesMenuWrapper/index.tsx
@@ -4,13 +4,13 @@ import { useSession } from "next-auth/react"
import { useIntl } from "react-intl"
import { MembershipLevelEnum } from "@scandic-hotels/common/constants/membershipLevels"
+import { Avatar } from "@scandic-hotels/design-system/Avatar"
import { trpc } from "@scandic-hotels/trpc/client"
import LoginButton from "@/components/LoginButton"
import useLang from "@/hooks/useLang"
import { isValidClientSession } from "@/utils/clientSession"
-import Avatar from "../Avatar"
import MyPagesMenu, { MyPagesMenuSkeleton } from "../MyPagesMenu"
import MyPagesMobileMenu, {
MyPagesMobileMenuSkeleton,
diff --git a/apps/scandic-web/components/Header/MainMenu/MyPagesMobileMenu/index.tsx b/apps/scandic-web/components/Header/MainMenu/MyPagesMobileMenu/index.tsx
index d2ead5b65..ac9a7cb74 100644
--- a/apps/scandic-web/components/Header/MainMenu/MyPagesMobileMenu/index.tsx
+++ b/apps/scandic-web/components/Header/MainMenu/MyPagesMobileMenu/index.tsx
@@ -5,12 +5,13 @@ import { Dialog, Modal } from "react-aria-components"
import { useIntl } from "react-intl"
import { useMediaQuery } from "usehooks-ts"
+import { Avatar } from "@scandic-hotels/design-system/Avatar"
+
import useDropdownStore from "@/stores/main-menu"
import { useHandleKeyUp } from "@/hooks/useHandleKeyUp"
import { getInitials } from "@/utils/user"
-import Avatar from "../Avatar"
import MainMenuButton from "../MainMenuButton"
import MyPagesMenuContent from "../MyPagesMenuContent"
diff --git a/apps/scandic-web/components/MyPages/CopyMembershipIdButton/copyMembershipIdButton.module.css b/apps/scandic-web/components/MyPages/CopyMembershipIdButton/copyMembershipIdButton.module.css
new file mode 100644
index 000000000..b35ca57ad
--- /dev/null
+++ b/apps/scandic-web/components/MyPages/CopyMembershipIdButton/copyMembershipIdButton.module.css
@@ -0,0 +1,11 @@
+.container {
+ display: flex;
+ align-items: center;
+ gap: var(--Space-x1);
+ color: var(--Scandic-Red-100);
+}
+
+.copyButton {
+ min-width: auto;
+ padding: var(--Space-x05);
+}
diff --git a/apps/scandic-web/components/MyPages/CopyMembershipIdButton/index.tsx b/apps/scandic-web/components/MyPages/CopyMembershipIdButton/index.tsx
new file mode 100644
index 000000000..112e11673
--- /dev/null
+++ b/apps/scandic-web/components/MyPages/CopyMembershipIdButton/index.tsx
@@ -0,0 +1,56 @@
+"use client"
+
+import { useIntl } from "react-intl"
+
+import { Button } from "@scandic-hotels/design-system/Button"
+import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
+import { Typography } from "@scandic-hotels/design-system/Typography"
+
+import { toast } from "@/components/TempDesignSystem/Toasts"
+
+import styles from "./copyMembershipIdButton.module.css"
+
+interface CopyMembershipIdButtonProps {
+ membershipNumber: string
+}
+
+export default function CopyMembershipIdButton({
+ membershipNumber,
+}: CopyMembershipIdButtonProps) {
+ const intl = useIntl()
+
+ function handleCopy() {
+ try {
+ navigator.clipboard.writeText(membershipNumber)
+ toast.success(
+ intl.formatMessage({
+ defaultMessage: "Membership ID copied to clipboard",
+ })
+ )
+ } catch {
+ toast.error(
+ intl.formatMessage({
+ defaultMessage: "Failed to copy",
+ })
+ )
+ }
+ }
+
+ return (
+
+
+
+ {membershipNumber}
+
+
+
+
+ )
+}
diff --git a/apps/scandic-web/env/server.ts b/apps/scandic-web/env/server.ts
index da3c39ac8..545725c9f 100644
--- a/apps/scandic-web/env/server.ts
+++ b/apps/scandic-web/env/server.ts
@@ -160,6 +160,11 @@ export const env = createEnv({
.refine((s) => s === "1" || s === "0")
.transform((s) => s === "1")
.default("1"),
+ ENABLE_NEW_OVERVIEW_SECTION: z
+ .string()
+ .refine((s) => s === "true" || s === "false")
+ .transform((s) => s === "true")
+ .default("false"),
},
emptyStringAsUndefined: true,
runtimeEnv: {
@@ -243,6 +248,7 @@ export const env = createEnv({
DTMC_ENTRA_ID_SECRET: process.env.DTMC_ENTRA_ID_SECRET,
CAMPAIGN_PAGES_ENABLED: process.env.CAMPAIGN_PAGES_ENABLED,
WEBVIEW_SHOW_OVERVIEW: process.env.WEBVIEW_SHOW_OVERVIEW,
+ ENABLE_NEW_OVERVIEW_SECTION: process.env.ENABLE_NEW_OVERVIEW_SECTION,
},
})
diff --git a/packages/design-system/lib/components/Avatar/Avatar.stories.tsx b/packages/design-system/lib/components/Avatar/Avatar.stories.tsx
new file mode 100644
index 000000000..27172b478
--- /dev/null
+++ b/packages/design-system/lib/components/Avatar/Avatar.stories.tsx
@@ -0,0 +1,125 @@
+import type { Meta, StoryObj } from '@storybook/nextjs-vite'
+
+import { Avatar } from '.'
+import { config } from './variants'
+
+const meta: Meta = {
+ title: 'Components/Avatar',
+ component: Avatar,
+ parameters: {
+ layout: 'centered',
+ },
+ argTypes: {
+ size: {
+ control: { type: 'select' },
+ options: Object.keys(config.variants.size),
+ },
+ },
+}
+
+export default meta
+
+type Story = StoryObj
+
+export const WithImage: Story = {
+ args: {
+ src: `../../../public/img/profile-picture.png`,
+ alt: 'Profile photo',
+ size: 'md',
+ },
+}
+
+export const WithInitials: Story = {
+ args: {
+ initials: 'FR',
+ size: 'md',
+ },
+}
+
+export const Fallback: Story = {
+ args: {
+ size: 'md',
+ },
+}
+
+export const SmallSize: Story = {
+ render: () => (
+
+ ),
+}
+
+export const MediumSize: Story = {
+ render: () => (
+
+ ),
+}
+
+export const LargeSize: Story = {
+ render: () => (
+
+ ),
+}
+
+export const AllSizes: Story = {
+ render: () => (
+
+ ),
+}
diff --git a/packages/design-system/lib/components/Avatar/avatar.module.css b/packages/design-system/lib/components/Avatar/avatar.module.css
new file mode 100644
index 000000000..5d9677d69
--- /dev/null
+++ b/packages/design-system/lib/components/Avatar/avatar.module.css
@@ -0,0 +1,38 @@
+.avatar {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ overflow: hidden;
+ border-radius: var(--Corner-radius-rounded);
+ background-color: var(--Overlay-40);
+}
+
+.avatar:has(.initials) {
+ background-color: var(--Icon-Accent);
+}
+
+.avatar img {
+ object-fit: cover;
+ width: 100%;
+ height: 100%;
+}
+
+.size-sm {
+ width: 24px;
+ height: 24px;
+}
+
+.size-md {
+ width: 32px;
+ height: 32px;
+}
+
+.size-lg {
+ width: 55px;
+ height: 55px;
+}
+
+.initials {
+ color: white;
+ text-transform: uppercase;
+}
diff --git a/packages/design-system/lib/components/Avatar/index.tsx b/packages/design-system/lib/components/Avatar/index.tsx
new file mode 100644
index 000000000..b7edcb89d
--- /dev/null
+++ b/packages/design-system/lib/components/Avatar/index.tsx
@@ -0,0 +1,35 @@
+import { MaterialIcon } from '../Icons/MaterialIcon'
+import { Typography } from '../Typography'
+import Image from '../Image'
+
+import { variants } from './variants'
+import type { AvatarProps } from './types'
+
+export function Avatar({
+ src,
+ alt,
+ initials,
+ size = 'md',
+ className,
+}: AvatarProps) {
+ const classNames = variants({ size, className })
+ const pixelSize = size === 'sm' ? 24 : size === 'md' ? 32 : 55
+ const iconSize = size === 'sm' ? 16 : 24
+
+ return (
+
+ {src ? (
+
+ ) : initials ? (
+
+ {initials}
+
+ ) : (
+
+ )}
+
+ )
+}
diff --git a/packages/design-system/lib/components/Avatar/types.ts b/packages/design-system/lib/components/Avatar/types.ts
new file mode 100644
index 000000000..4a821a306
--- /dev/null
+++ b/packages/design-system/lib/components/Avatar/types.ts
@@ -0,0 +1,23 @@
+export interface AvatarProps {
+ /**
+ * The URL of the image to display
+ */
+ src?: string
+ /**
+ * Alt text for the image (for accessibility)
+ */
+ alt?: string
+ /**
+ * Initials to display when no image is provided
+ */
+ initials?: string | null
+ /**
+ * Size of the avatar
+ * @default 'md'
+ */
+ size?: 'sm' | 'md' | 'lg'
+ /**
+ * Additional CSS class names
+ */
+ className?: string
+}
diff --git a/packages/design-system/lib/components/Avatar/variants.ts b/packages/design-system/lib/components/Avatar/variants.ts
new file mode 100644
index 000000000..58888eb17
--- /dev/null
+++ b/packages/design-system/lib/components/Avatar/variants.ts
@@ -0,0 +1,20 @@
+import { cva } from 'class-variance-authority'
+
+import styles from './avatar.module.css'
+
+export const config = {
+ variants: {
+ size: {
+ sm: styles['size-sm'],
+ md: styles['size-md'],
+ lg: styles['size-lg'],
+ },
+ },
+ defaultVariants: {
+ size: 'md',
+ },
+} as const
+
+export const variants = Object.assign(cva(styles.avatar, config), {
+ initials: styles.initials,
+})
diff --git a/packages/design-system/package.json b/packages/design-system/package.json
index a4da7d289..cec3025f3 100644
--- a/packages/design-system/package.json
+++ b/packages/design-system/package.json
@@ -6,6 +6,7 @@
"exports": {
"./Accordion": "./lib/components/Accordion/index.tsx",
"./Accordion/AccordionItem": "./lib/components/Accordion/AccordionItem/index.tsx",
+ "./Avatar": "./lib/components/Avatar/index.tsx",
"./BackToTopButton": "./lib/components/BackToTopButton/index.tsx",
"./Body": "./lib/components/Body/index.tsx",
"./BookingCodeChip": "./lib/components/BookingCodeChip/index.tsx",
diff --git a/packages/design-system/public/img/profile-picture.png b/packages/design-system/public/img/profile-picture.png
new file mode 100644
index 000000000..6eff6bda2
Binary files /dev/null and b/packages/design-system/public/img/profile-picture.png differ