Merged in fix/SW-2739-map-reward-night-not-enough-points (pull request #2435)

fix(SW-2739): remove tooltip and add correct CTA on map for reward nights

* fix(SW-2739): remove tooltip and add correct CTA on map for reward nights

* fix(SW-2739): fix pr comment


Approved-by: Arvid Norlin
This commit is contained in:
Bianca Widstam
2025-06-25 13:30:43 +00:00
parent 94fc5cabb3
commit 9e3d82b62c
7 changed files with 106 additions and 49 deletions

View File

@@ -22,7 +22,6 @@ import { FacilityToIcon } from "@/components/ContentType/HotelPage/data"
import ImageGallery from "@/components/ImageGallery" import ImageGallery from "@/components/ImageGallery"
import Link from "@/components/TempDesignSystem/Link" import Link from "@/components/TempDesignSystem/Link"
import Caption from "@/components/TempDesignSystem/Text/Caption" import Caption from "@/components/TempDesignSystem/Text/Caption"
import { Tooltip } from "@/components/TempDesignSystem/Tooltip"
import { mapApiImagesToGalleryImages } from "@/utils/imageGallery" import { mapApiImagesToGalleryImages } from "@/utils/imageGallery"
import { getSingleDecimal } from "@/utils/numberFormatting" import { getSingleDecimal } from "@/utils/numberFormatting"
@@ -245,17 +244,11 @@ function HotelCard({
</div> </div>
) : null} ) : null}
{isDisabled ? ( {isDisabled ? (
<Tooltip <div className={cx(styles.fakeButton, styles.disabled)}>
arrow="left" <Typography variant="Body/Paragraph/mdBold">
position="bottom" <span>{notEnoughPointsLabel}</span>
text={notEnoughPointsLabel} </Typography>
> </div>
<div className={cx(styles.fakeButton, styles.disabled)}>
<Typography variant="Body/Paragraph/mdBold">
<span>{notEnoughPointsLabel}</span>
</Typography>
</div>
</Tooltip>
) : ( ) : (
<div className={styles.fakeButton}> <div className={styles.fakeButton}>
<Typography variant="Body/Paragraph/mdBold"> <Typography variant="Body/Paragraph/mdBold">

View File

@@ -54,11 +54,15 @@ export default function ListingHotelCardDialog({
redemptionPrice, redemptionPrice,
chequePrice, chequePrice,
voucherPrice, voucherPrice,
hasEnoughPoints,
} = data } = data
const firstImage = images[0]?.imageSizes?.small const firstImage = images[0]?.imageSizes?.small
const altText = images[0]?.metaData?.altText const altText = images[0]?.metaData?.altText
const notEnoughPointsLabel = intl.formatMessage({
defaultMessage: "Not enough points",
})
return ( return (
<div className={styles.container}> <div className={styles.container}>
<IconButton <IconButton
@@ -212,17 +216,30 @@ export default function ListingHotelCardDialog({
)} )}
</div> </div>
</div> </div>
<Button asChild theme="base" size="small" className={styles.button}> {hasEnoughPoints ? (
<Link <Button
href={`${selectRate(lang)}?hotel=${operaId}`} asChild
color="none" theme="base"
keepSearchParams size="small"
className={styles.button}
> >
{intl.formatMessage({ <Link
defaultMessage: "See rooms", href={`${selectRate(lang)}?hotel=${operaId}`}
})} color="none"
</Link> keepSearchParams
</Button> >
{intl.formatMessage({
defaultMessage: "See rooms",
})}
</Link>
</Button>
) : (
<div className={styles.notEnoughPointsButton}>
<Typography variant="Body/Paragraph/mdBold">
<span>{notEnoughPointsLabel}</span>
</Typography>
</div>
)}
</div> </div>
) : ( ) : (
<NoPriceAvailableCard /> <NoPriceAvailableCard />

View File

@@ -74,3 +74,18 @@
top: 8px; top: 8px;
right: 8px; right: 8px;
} }
.notEnoughPointsButton {
border-radius: var(--Corner-radius-rounded);
border-width: 2px;
border-style: solid;
display: flex;
align-items: center;
justify-content: center;
gap: var(--Space-x05);
padding: 10px var(--Space-x2);
background-color: var(--Component-Button-Brand-Primary-Fill-Disabled);
border-color: var(--Component-Button-Brand-Primary-Border-Disabled);
color: var(--Component-Button-Brand-Primary-On-fill-Disabled);
}

View File

@@ -5,6 +5,7 @@ import { useIntl } from "react-intl"
import { IconButton } from "@scandic-hotels/design-system/IconButton" 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 { selectRate } from "@/constants/routes/hotelReservation" import { selectRate } from "@/constants/routes/hotelReservation"
@@ -53,10 +54,14 @@ export default function StandaloneHotelCardDialog({
images, images,
ratings, ratings,
operaId, operaId,
hasEnoughPoints,
} = data } = data
const firstImage = images[0]?.imageSizes?.small const firstImage = images[0]?.imageSizes?.small
const altText = images[0]?.metaData?.altText const altText = images[0]?.metaData?.altText
const notEnoughPointsLabel = intl.formatMessage({
defaultMessage: "Not enough points",
})
return ( return (
<div className={styles.container}> <div className={styles.container}>
@@ -224,33 +229,41 @@ export default function StandaloneHotelCardDialog({
<HotelPointsRow pointsPerStay={redemptionPrice} /> <HotelPointsRow pointsPerStay={redemptionPrice} />
)} )}
</div> </div>
<Button {hasEnoughPoints ? (
asChild <Button
theme="base" asChild
size="small" theme="base"
className={styles.button} size="small"
onClick={() => className={styles.button}
trackEvent({ onClick={() =>
event: "hotelClickMap", trackEvent({
map: { event: "hotelClickMap",
action: "hotel click - map", map: {
}, action: "hotel click - map",
hotelInfo: { },
hotelId: operaId, hotelInfo: {
}, hotelId: operaId,
}) },
} })
> }
<Link
href={`${selectRate(lang)}?hotel=${operaId}`}
color="none"
keepSearchParams
> >
{intl.formatMessage({ <Link
defaultMessage: "See rooms", href={`${selectRate(lang)}?hotel=${operaId}`}
})} color="none"
</Link> keepSearchParams
</Button> >
{intl.formatMessage({
defaultMessage: "See rooms",
})}
</Link>
</Button>
) : (
<div className={styles.notEnoughPointsButton}>
<Typography variant="Body/Paragraph/mdBold">
<span>{notEnoughPointsLabel}</span>
</Typography>
</div>
)}
</> </>
) : ( ) : (
<NoPriceAvailableCard /> <NoPriceAvailableCard />

View File

@@ -65,3 +65,18 @@
right: 8px; right: 8px;
z-index: 1; z-index: 1;
} }
.notEnoughPointsButton {
border-radius: var(--Corner-radius-rounded);
border-width: 2px;
border-style: solid;
display: flex;
align-items: center;
justify-content: center;
gap: var(--Space-x05);
padding: 10px var(--Space-x2);
background-color: var(--Component-Button-Brand-Primary-Fill-Disabled);
border-color: var(--Component-Button-Brand-Primary-Border-Disabled);
color: var(--Component-Button-Brand-Primary-On-fill-Disabled);
}

View File

@@ -51,6 +51,9 @@ export function getHotelPins(
ratings: hotel.ratings?.tripAdvisor.rating ?? null, ratings: hotel.ratings?.tripAdvisor.rating ?? null,
operaId: hotel.operaId, operaId: hotel.operaId,
facilityIds: hotel.detailedFacilities.map((facility) => facility.id), facilityIds: hotel.detailedFacilities.map((facility) => facility.id),
hasEnoughPoints: !!availability.productType?.redemptions?.some(
(r) => r.hasEnoughPoints
),
} }
}) })
} }

View File

@@ -46,6 +46,7 @@ export type HotelPin = {
ratings: number | null ratings: number | null
operaId: string operaId: string
facilityIds: number[] facilityIds: number[]
hasEnoughPoints: boolean
} }
export interface HotelListingMapContentProps { export interface HotelListingMapContentProps {