Merged in feat/SW-2407-mystay-tc-to-modal (pull request #2082)

Feat/SW-2407 mystay tc to modal

* feat: SW-2407 Moved terms in modal view for my stay

* feat: SW-2407 Updated modal to show terms for reward nights and corporate cheque stay

* feat: SW-2407 Optimised code

* feat: SW-2407 Optimised code


Approved-by: Niclas Edenvin
This commit is contained in:
Hrishikesh Vaipurkar
2025-05-27 07:00:02 +00:00
parent db93cc7d0e
commit e2c1b066be
8 changed files with 194 additions and 48 deletions

View File

@@ -1,15 +1,19 @@
"use client" "use client"
import { useIntl } from "react-intl" import { useIntl } from "react-intl"
import { IconButton } from "@scandic-hotels/design-system/IconButton"
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon" import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
import { Typography } from "@scandic-hotels/design-system/Typography" import { Typography } from "@scandic-hotels/design-system/Typography"
import { CancellationRuleEnum } from "@/constants/booking"
import { dt } from "@/lib/dt" import { dt } from "@/lib/dt"
import { IconForFeatureCode } from "@/components/HotelReservation/utils" import { IconForFeatureCode } from "@/components/HotelReservation/utils"
import Image from "@/components/Image" import Image from "@/components/Image"
import Modal from "@/components/Modal"
import Divider from "@/components/TempDesignSystem/Divider" import Divider from "@/components/TempDesignSystem/Divider"
import IconChip from "@/components/TempDesignSystem/IconChip" import IconChip from "@/components/TempDesignSystem/IconChip"
import useRateTitles from "@/hooks/booking/useRateTitles"
import useLang from "@/hooks/useLang" import useLang from "@/hooks/useLang"
import { formatPrice } from "@/utils/numberFormatting" import { formatPrice } from "@/utils/numberFormatting"
@@ -20,6 +24,7 @@ import RoomDetailsSidePeek from "./RoomDetailsSidePeek"
import styles from "./room.module.css" import styles from "./room.module.css"
import { RoomPackageCodeEnum } from "@/types/components/hotelReservation/selectRate/roomFilter" import { RoomPackageCodeEnum } from "@/types/components/hotelReservation/selectRate/roomFilter"
import { RateEnum } from "@/types/enums/rate"
import type { Room } from "@/types/stores/my-stay" import type { Room } from "@/types/stores/my-stay"
import type { SafeUser } from "@/types/user" import type { SafeUser } from "@/types/user"
@@ -93,6 +98,19 @@ export default function Room({ booking, roomNr, user }: RoomProps) {
) )
} }
const rateTitles = useRateTitles()
let rateTerm: { paymentTerm: string; title: string }
switch (rateDefinition.cancellationRule) {
case CancellationRuleEnum.CancellableBefore6PM:
rateTerm = rateTitles[RateEnum.flex]
break
case CancellationRuleEnum.Changeable:
rateTerm = rateTitles[RateEnum.change]
break
default:
rateTerm = rateTitles[RateEnum.save]
}
return ( return (
<article className={styles.multiRoom}> <article className={styles.multiRoom}>
<Typography variant="Title/smRegular"> <Typography variant="Title/smRegular">
@@ -218,7 +236,7 @@ export default function Room({ booking, roomNr, user }: RoomProps) {
</p> </p>
</Typography> </Typography>
</div> </div>
{rateDefinition.cancellationText ? ( {
<div className={styles.row}> <div className={styles.row}>
<Typography variant="Body/Paragraph/mdBold"> <Typography variant="Body/Paragraph/mdBold">
<p> <p>
@@ -227,11 +245,45 @@ export default function Room({ booking, roomNr, user }: RoomProps) {
})} })}
</p> </p>
</Typography> </Typography>
<div className={styles.termsLabel}>
<Typography variant="Body/Paragraph/mdRegular"> <Typography variant="Body/Paragraph/mdRegular">
<p>{rateDefinition.cancellationText}</p> <span>{rateTerm.title}</span>
</Typography> </Typography>
<Modal
title={rateTerm.title}
subtitle={rateTerm.paymentTerm}
trigger={
<IconButton
theme="Black"
style="Muted"
className={styles.termsInfoIcon}
>
<MaterialIcon
icon="info"
color="Icon/Default"
size={20}
/>
</IconButton>
}
>
<div className={styles.terms}>
{rateDefinition.generalTerms.map((term) => (
<Typography key={term} variant="Body/Paragraph/mdRegular">
<p className={styles.term}>
<MaterialIcon
icon="check"
color="Icon/Feedback/Success"
size={20}
/>
{term}
</p>
</Typography>
))}
</div> </div>
) : null} </Modal>
</div>
</div>
}
{hasModifiableRate(rateDefinition.cancellationRule) && ( {hasModifiableRate(rateDefinition.cancellationRule) && (
<div className={styles.row}> <div className={styles.row}>
<Typography variant="Body/Paragraph/mdBold"> <Typography variant="Body/Paragraph/mdBold">

View File

@@ -1,8 +1,8 @@
.multiRoom { .multiRoom {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: var(--Spacing-x2); gap: var(--Space-x2);
padding: 0 var(--Spacing-x2); padding: 0 var(--Space-x2);
} }
.cancelled { .cancelled {
@@ -16,12 +16,12 @@
.multiRoomCard { .multiRoomCard {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: var(--Spacing-x2); gap: var(--Space-x2);
background-color: var(--Base-Background-Primary-Normal); background-color: var(--Base-Background-Primary-Normal);
border-radius: var(--Corner-radius-lg); border-radius: var(--Corner-radius-lg);
border: 1px solid var(--Base-Border-Subtle); border: 1px solid var(--Base-Border-Subtle);
overflow: hidden; overflow: hidden;
padding-bottom: var(--Spacing-x3); padding-bottom: var(--Space-x3);
position: relative; position: relative;
} }
@@ -38,14 +38,14 @@
.roomHeader { .roomHeader {
display: flex; display: flex;
align-items: center; align-items: center;
gap: var(--Spacing-x-one-and-half); gap: var(--Space-x15);
} }
.chip { .chip {
background-color: var(--Scandic-Peach-30); background-color: var(--Scandic-Peach-30);
color: var(--Scandic-Red-100); color: var(--Scandic-Red-100);
border-radius: var(--Corner-radius-sm); border-radius: var(--Corner-radius-sm);
padding: var(--Spacing-x-half) var(--Spacing-x1); padding: var(--Space-x05) var(--Space-x1);
height: fit-content; height: fit-content;
} }
@@ -55,13 +55,13 @@
.reference { .reference {
display: flex; display: flex;
gap: var(--Spacing-x-half); gap: var(--Space-x05);
} }
.details { .details {
display: flex; display: flex;
padding: var(--Spacing-x-one-and-half) var(--Spacing-x2) 0; padding: var(--Space-x15) var(--Space-x2) 0;
gap: var(--Spacing-x2); gap: var(--Space-x2);
flex-direction: column; flex-direction: column;
} }
@@ -77,16 +77,37 @@
left: 10px; left: 10px;
display: flex; display: flex;
flex-direction: row; flex-direction: row;
gap: var(--Spacing-x1); gap: var(--Space-x1);
z-index: 100; z-index: 100;
} }
.package { .package {
background-color: var(--Main-Grey-White); background-color: var(--Main-Grey-White);
padding: var(--Spacing-x-half) var(--Spacing-x1); padding: var(--Space-x05) var(--Space-x1);
border-radius: var(--Corner-radius-sm); border-radius: var(--Corner-radius-sm);
} }
.termsLabel {
display: flex;
align-items: center;
}
.termsInfoIcon {
margin: -10px -10px -10px -5px;
}
.terms {
display: flex;
flex-direction: column;
gap: var(--Space-x1);
margin-top: var(--Space-x2);
}
.term {
display: flex;
gap: var(--Space-x1);
}
@media (min-width: 768px) { @media (min-width: 768px) {
.multiRoom { .multiRoom {
padding: 0; padding: 0;

View File

@@ -13,7 +13,12 @@ interface RowProps {
title: string title: string
} }
export default function Row({ icon, text, title }: RowProps) { export default function Row({
icon,
text,
title,
children,
}: React.PropsWithChildren<RowProps>) {
return ( return (
<div className={styles.row}> <div className={styles.row}>
<span className={styles.title}> <span className={styles.title}>
@@ -26,6 +31,7 @@ export default function Row({ icon, text, title }: RowProps) {
<Typography variant="Body/Paragraph/mdRegular"> <Typography variant="Body/Paragraph/mdRegular">
<p>{text}</p> <p>{text}</p>
</Typography> </Typography>
{children}
</div> </div>
</div> </div>
) )

View File

@@ -1,7 +1,7 @@
.row { .row {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
padding: var(--Spacing-x-one-and-half) 0; padding: var(--Space-x15) 0;
} }
.row:last-child { .row:last-child {
@@ -11,7 +11,7 @@
.title { .title {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
gap: var(--Spacing-x1); gap: var(--Space-x1);
} }
.title svg { .title svg {
@@ -20,7 +20,9 @@
} }
.content { .content {
padding-left: var(--Spacing-x4); display: flex;
align-items: center;
padding-left: var(--Space-x4);
} }
@media (min-width: 768px) { @media (min-width: 768px) {

View File

@@ -1,27 +0,0 @@
import { useIntl } from "react-intl"
import { useMyStayStore } from "@/stores/my-stay"
import Row from "./Row"
export default function Terms() {
const intl = useIntl()
const cancellationText = useMyStayStore(
(state) => state.bookedRoom.rateDefinition.cancellationText
)
if (!cancellationText) {
return null
}
return (
<Row
icon="contract"
text={cancellationText}
title={intl.formatMessage({
defaultMessage: "Terms",
})}
/>
)
}

View File

@@ -0,0 +1,76 @@
import { useIntl } from "react-intl"
import { IconButton } from "@scandic-hotels/design-system/IconButton"
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
import { Typography } from "@scandic-hotels/design-system/Typography"
import { CancellationRuleEnum } from "@/constants/booking"
import { useMyStayStore } from "@/stores/my-stay"
import Modal from "@/components/Modal"
import useRateTitles from "@/hooks/booking/useRateTitles"
import Row from "../Row"
import styles from "./terms.module.css"
import { RateEnum } from "@/types/enums/rate"
export default function Terms() {
const intl = useIntl()
const rateTitles = useRateTitles()
const { generalTerms, cancellationRule } = useMyStayStore((state) => ({
generalTerms: state.bookedRoom.rateDefinition.generalTerms,
cancellationRule: state.bookedRoom.rateDefinition.cancellationRule,
}))
let rateTerm: { paymentTerm: string; title: string }
switch (cancellationRule) {
case CancellationRuleEnum.CancellableBefore6PM:
rateTerm = rateTitles[RateEnum.flex]
break
case CancellationRuleEnum.Changeable:
rateTerm = rateTitles[RateEnum.change]
break
default:
rateTerm = rateTitles[RateEnum.save]
}
return (
<>
<Row
icon="contract"
text={rateTerm.title}
title={intl.formatMessage({
defaultMessage: "Terms",
})}
>
<Modal
title={rateTerm.title}
subtitle={rateTerm.paymentTerm}
trigger={
<IconButton theme="Black" style="Muted" className={styles.button}>
<MaterialIcon icon="info" color="Icon/Default" size={20} />
</IconButton>
}
>
<div className={styles.terms}>
{generalTerms.map((term) => (
<Typography key={term} variant="Body/Paragraph/mdRegular">
<p className={styles.term}>
<MaterialIcon
icon="check"
color="Icon/Feedback/Success"
size={20}
/>
{term}
</p>
</Typography>
))}
</div>
</Modal>
</Row>
</>
)
}

View File

@@ -0,0 +1,16 @@
.terms {
display: flex;
flex-direction: column;
gap: var(--Space-x1);
margin-top: var(--Space-x3);
}
.term {
display: flex;
align-items: center;
gap: var(--Space-x1);
}
.button {
margin: -10px -5px;
}

View File

@@ -1,9 +1,9 @@
import Terms from "./Terms/Terms"
import BedPreference from "./BedPreference" import BedPreference from "./BedPreference"
import Breakfast from "./Breakfast" import Breakfast from "./Breakfast"
import Guests from "./Guests" import Guests from "./Guests"
import ModifyBy from "./ModifyBy" import ModifyBy from "./ModifyBy"
import Packages from "./Packages" import Packages from "./Packages"
import Terms from "./Terms"
import styles from "./details.module.css" import styles from "./details.module.css"