Merged in feat/LOY-196-dtmc-modal (pull request #1982)

feat(LOY-196): add modal to dtmc button containing card

Approved-by: Chuma Mcphoy (We Ahead)
This commit is contained in:
Christian Andolf
2025-05-07 09:07:44 +00:00
5 changed files with 219 additions and 26 deletions

View File

@@ -1,5 +1,6 @@
"use client"
import { cx } from "class-variance-authority"
import { motion } from "framer-motion"
import { type PropsWithChildren, useEffect, useState } from "react"
import {
@@ -40,6 +41,7 @@ function InnerModal({
subtitle,
withActions,
hideHeader,
className,
}: PropsWithChildren<InnerModalProps>) {
const intl = useIntl()
@@ -67,7 +69,7 @@ function InnerModal({
<MotionOverlay
animate={animation}
className={styles.overlay}
initial={"hidden"}
initial="hidden"
isDismissable
isExiting={animation === AnimationStateEnum.hidden}
onAnimationComplete={modalStateHandler}
@@ -76,10 +78,10 @@ function InnerModal({
onOpenChange={onOpenChange}
>
<MotionModal
className={styles.modal}
className={cx(styles.modal, className)}
variants={slideInOut}
animate={animation}
initial={"hidden"}
initial="hidden"
>
<Dialog
className={styles.dialog}
@@ -135,6 +137,7 @@ export default function Modal({
children,
withActions = false,
hideHeader = false,
className = "",
}: PropsWithChildren<ModalProps>) {
const [animation, setAnimation] = useState<AnimationState>(
AnimationStateEnum.visible
@@ -163,6 +166,7 @@ export default function Modal({
subtitle={subtitle}
withActions={withActions}
hideHeader={hideHeader}
className={className}
>
{children}
</InnerModal>
@@ -185,6 +189,7 @@ export default function Modal({
title={title}
subtitle={subtitle}
withActions={withActions}
className={className}
>
{children}
</InnerModal>

View File

@@ -14,6 +14,7 @@ export type ModalProps = {
subtitle?: string
withActions?: boolean
hideHeader?: boolean
className?: string
} & (
| { trigger: JSX.Element; isOpen?: never; onToggle?: never }
| {

View File

@@ -0,0 +1,116 @@
"use client"
import { Button } from "react-aria-components"
import { useIntl } from "react-intl"
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
import { Typography } from "@scandic-hotels/design-system/Typography"
import Modal from "@/components/Modal"
import styles from "./digitalTeamMemberCard.module.css"
import type { User } from "@/types/user"
interface DigitalTeamMemberCardClientProps {
user: User
}
export default function DigitalTeamMemberCardClient({
user,
}: DigitalTeamMemberCardClientProps) {
const intl = useIntl()
return (
<Modal
trigger={
<Button className={styles.button}>
<Typography variant="Body/Paragraph/mdBold">
<span className={styles.text}>
{/* @ts-expect-error Icon is supported in font, just not in React Material Symbols package */}
<MaterialIcon icon="id_card" size={24} color="CurrentColor" />
{intl.formatMessage({
defaultMessage: "Show Team Member Card",
})}
</span>
</Typography>
</Button>
}
className={styles.modal}
>
<Typography variant="Title/xs">
<h2 className={styles.title}>
{intl.formatMessage({ defaultMessage: "Scandic Family" })}
</h2>
</Typography>
<div className={styles.card}>
<div className={styles.content}>
<div className={styles.top}>
<Typography variant="Tag/sm">
<span>
{intl.formatMessage({ defaultMessage: "Team Member" })}
</span>
</Typography>
<Typography variant="Tag/sm">
{/* TODO: Should display country of employment */}
{/* eslint-disable-next-line formatjs/no-literal-string-in-jsx */}
<span>SWE</span>
</Typography>
</div>
<div className={styles.middle}>
<div className={styles.employeeNumber}>
<Typography variant="Title/sm">
{/* TODO: Should display employee number */}
{/* eslint-disable-next-line formatjs/no-literal-string-in-jsx */}
<div>123 456</div>
</Typography>
<svg
width="42"
height="42"
fill="none"
xmlns="http://www.w3.org/2000/svg"
className={styles.icon}
>
<path
d="M3.5 35V12.288C3.5 8.933 3.5 7 4.268 7c.912 0 1.569 1.156 3.17 3.978l11.374 20.044C20.413 33.844 21.041 35 21.982 35c.768 0 .768-1.933.768-5.288V7M28 22.75h10.5M29.016 8.016c1.355-1.355 7.113-1.355 8.468 0 1.355 1.355 1.355 7.113 0 8.468-1.355 1.355-7.114 1.355-8.468 0-1.355-1.355-1.355-7.113 0-8.468Z"
strokeWidth="2.625"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
</div>
<Typography variant="Title/md">
<div>
{user.firstName} {user.lastName}
</div>
</Typography>
</div>
<div className={styles.bottom}>
<Typography variant="Tag/sm">
<span>
{/* TODO: Should display department of employment */}
{/* eslint-disable formatjs/no-literal-string-in-jsx */}
Haymarket by Scandic
{/* eslint-enable */}
</span>
</Typography>
<Typography variant="Tag/sm">
{/* TODO: Should display current state of employment */}
{/* eslint-disable-next-line formatjs/no-literal-string-in-jsx */}
<span>Employee</span>
</Typography>
</div>
</div>
</div>
<Typography variant="Body/Paragraph/mdRegular">
<div className={styles.footer}>
{intl.formatMessage({
defaultMessage:
"Book discounted stays for yourself, family and friends!",
})}
</div>
</Typography>
</Modal>
)
}

View File

@@ -1,4 +1,4 @@
.card {
.button {
padding: var(--Space-x2) var(--Space-x15);
border-radius: var(--Corner-radius-md);
border: 0;
@@ -28,3 +28,90 @@
align-items: center;
gap: var(--Space-x1);
}
.title {
color: var(--Text-Accent-Primary);
}
.card {
position: relative;
overflow: hidden;
border-radius: var(--Corner-radius-lg);
box-shadow: 0 2px 1px rgb(255 255 255 / 11%) inset;
padding: var(--Space-x2);
height: 400px;
width: 327px;
max-width: 100%;
color: var(--Text-Brand-OnPrimary-3-Accent);
background-color: var(--Surface-Brand-Primary-1-OnSurface-Default);
&::before {
content: "";
position: absolute;
z-index: 2;
bottom: 158px;
left: 132px;
width: 360px;
height: 360px;
opacity: 0.3;
background: #e9aba3;
box-shadow: 192px 192px 192px;
border-radius: 9999px;
filter: blur(96px);
}
&::after {
content: "";
position: absolute;
z-index: 1;
top: 60px;
left: -210px;
width: 2465px;
height: 459px;
background-image: url("/_static/img/scandic-logotype.svg");
background-repeat: no-repeat;
opacity: 0.2;
}
}
.content {
position: relative;
z-index: 3;
height: 100%;
display: flex;
flex-direction: column;
justify-content: space-between;
}
.employeeNumber {
display: flex;
justify-content: space-between;
align-items: center;
color: var(--Base-Text-Inverted);
}
.icon {
stroke: var(--Text-Brand-OnPrimary-3-Accent);
}
.top,
.bottom {
display: flex;
justify-content: space-between;
}
.middle {
display: flex;
flex-direction: column;
gap: 50px;
}
.footer {
text-align: center;
}
@media screen and (min-width: 768px) {
.modal {
max-width: 375px;
}
}

View File

@@ -1,11 +1,6 @@
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
import { Typography } from "@scandic-hotels/design-system/Typography"
import { env } from "@/env/server"
import { getIntl } from "@/i18n"
import styles from "./digitalTeamMemberCard.module.css"
import DigitalTeamMemberCardClient from "./Client"
import type { User } from "@/types/user"
@@ -13,25 +8,14 @@ interface DigitalTeamMemberCardProps {
user: User
}
export default async function DigitalTeamMemberCard(
// TODO: Make a check whether user is eligible for benefits or not
_props: DigitalTeamMemberCardProps
) {
export default async function DigitalTeamMemberCard({
user,
}: DigitalTeamMemberCardProps) {
if (!env.ENABLE_DTMC) {
return null
}
const intl = await getIntl()
// TODO: Make a check whether user is eligible for benefits or not
return (
<button className={styles.card} type="button">
<Typography variant="Body/Paragraph/mdBold">
<span className={styles.text}>
{/* @ts-expect-error Icon is supported in font, just not in React Material Symbols package */}
<MaterialIcon icon="id_card" size={24} color="CurrentColor" />
{intl.formatMessage({ defaultMessage: "Show Team Member Card" })}
</span>
</Typography>
</button>
)
return <DigitalTeamMemberCardClient user={user} />
}