Files
web/packages/design-system/lib/components/HotelCard/HotelDialogCard/StandaloneHotelCardDialog/index.tsx
Joakim Jäderberg 80c3327419 Merged in fix/linting (pull request #2708)
Fix/linting

* fix import issues and add lint check no-extraneous-dependencies
* fix use type HotelType instead of string

Approved-by: Anton Gunnarsson
2025-08-27 09:22:37 +00:00

264 lines
8.4 KiB
TypeScript

'use client'
import { useState } from 'react'
import { useIntl } from 'react-intl'
import { selectRate } from '@scandic-hotels/common/constants/routes/hotelReservation'
import Body from '../../../Body'
import Caption from '../../../Caption'
import Footnote from '../../../Footnote'
import { IconButton } from '../../../IconButton'
import { MaterialIcon } from '../../../Icons/MaterialIcon'
import Link from '../../../Link'
import { OldDSButton as Button } from '../../../OldDSButton'
import Subtitle from '../../../Subtitle'
import { Typography } from '../../../Typography'
import { NoPriceAvailableCard } from '../../NoPriceAvailableCard'
import { HotelCardDialogImage } from '../../HotelCardDialogImage'
import styles from './standaloneHotelCardDialog.module.css'
import { Lang } from '@scandic-hotels/common/constants/language'
import { HotelPin } from '../../../Map/types'
import { FacilityToIcon } from '../../../FacilityToIcon'
import { HotelPointsRow } from '../../HotelPointsRow'
interface StandaloneHotelCardProps {
data: HotelPin
lang: Lang
isUserLoggedIn: boolean
handleClose: () => void
onClick?: () => void
}
export function StandaloneHotelCardDialog({
data,
lang,
handleClose,
isUserLoggedIn,
onClick,
}: StandaloneHotelCardProps) {
const intl = useIntl()
const [imageError, setImageError] = useState(false)
const {
name,
chequePrice,
publicPrice,
memberPrice,
redemptionPrice,
voucherPrice,
currency,
amenities,
image,
ratings,
operaId,
hasEnoughPoints,
} = data
const notEnoughPointsLabel = intl.formatMessage({
defaultMessage: 'Not enough points',
})
const shouldShowNotEnoughPoints = redemptionPrice && !hasEnoughPoints
return (
<div className={styles.container}>
<IconButton
theme="Black"
style="Muted"
className={styles.closeButton}
onPress={handleClose}
aria-label={intl.formatMessage({
defaultMessage: 'Close',
})}
>
<MaterialIcon icon="close" size={22} color="CurrentColor" />
</IconButton>
<HotelCardDialogImage
firstImage={image?.url}
altText={image?.alt}
rating={{ tripAdvisor: ratings?.tripAdvisor ?? null }}
imageError={imageError}
setImageError={setImageError}
position="left"
/>
<div className={styles.content}>
<div className={styles.name}>
<Body textTransform="bold">{name}</Body>
</div>
<div className={styles.facilities}>
{amenities.slice(0, 3).map((facility) => {
const Icon = (
<FacilityToIcon id={facility.id} size={16} color="Icon/Default" />
)
return (
<div className={styles.facilitiesItem} key={facility.id}>
{Icon}
<Footnote color="uiTextMediumContrast">
{facility.name}
</Footnote>
</div>
)
})}
</div>
<div className={styles.pricesContainer}>
{publicPrice ||
memberPrice ||
redemptionPrice ||
voucherPrice ||
chequePrice ? (
<>
<div className={styles.priceCard}>
{redemptionPrice ? (
<Caption>
{intl.formatMessage({
defaultMessage: 'Available rates',
})}
</Caption>
) : (
<Caption type="bold">
{intl.formatMessage({
defaultMessage: 'From',
})}
</Caption>
)}
{chequePrice && (
<Subtitle type="two">
{intl.formatMessage(
{
defaultMessage: '{price} {currency}',
},
{
price: chequePrice.numberOfCheques,
currency: 'CC',
}
)}
{chequePrice.additionalPricePerStay > 0
? // eslint-disable-next-line formatjs/no-literal-string-in-jsx
' + ' +
intl.formatMessage(
{
defaultMessage: '{price} {currency}',
},
{
price: chequePrice.additionalPricePerStay,
currency: chequePrice.currency,
}
)
: null}
<Body asChild>
{/* eslint-disable-next-line formatjs/no-literal-string-in-jsx */}
<span>
/
{intl.formatMessage({
defaultMessage: 'night',
})}
</span>
</Body>
</Subtitle>
)}
{voucherPrice && (
<Subtitle type="two">
{intl.formatMessage(
{
defaultMessage: '{price} {currency}',
},
{
price: voucherPrice,
currency,
}
)}
<Body asChild>
{/* eslint-disable-next-line formatjs/no-literal-string-in-jsx */}
<span>
/
{intl.formatMessage({
defaultMessage: 'night',
})}
</span>
</Body>
</Subtitle>
)}
{publicPrice && !isUserLoggedIn && (
<Subtitle type="two">
{intl.formatMessage(
{
defaultMessage: '{price} {currency}',
},
{
price: publicPrice,
currency,
}
)}
<Body asChild>
{/* eslint-disable-next-line formatjs/no-literal-string-in-jsx */}
<span>
/
{intl.formatMessage({
defaultMessage: 'night',
})}
</span>
</Body>
</Subtitle>
)}
{memberPrice && (
<Subtitle type="two" color="red">
{intl.formatMessage(
{
defaultMessage: '{price} {currency}',
},
{
price: memberPrice,
currency,
}
)}
<Body asChild color="red">
{/* eslint-disable-next-line formatjs/no-literal-string-in-jsx */}
<span>
/
{intl.formatMessage({
defaultMessage: 'night',
})}
</span>
</Body>
</Subtitle>
)}
{redemptionPrice && (
<HotelPointsRow pointsPerStay={redemptionPrice} />
)}
</div>
{shouldShowNotEnoughPoints ? (
<div className={styles.notEnoughPointsButton}>
<Typography variant="Body/Paragraph/mdBold">
<span>{notEnoughPointsLabel}</span>
</Typography>
</div>
) : (
<Button
asChild
theme="base"
size="small"
className={styles.button}
onClick={onClick}
>
<Link
href={`${selectRate(lang)}?hotel=${operaId}`}
color="none"
keepSearchParams
>
{intl.formatMessage({
defaultMessage: 'See rooms',
})}
</Link>
</Button>
)}
</>
) : (
<NoPriceAvailableCard />
)}
</div>
</div>
</div>
)
}