Fix/STAY-135 & STAY-127 * fix: make quantity and delivery separate steps in mobile * fix: update design for delivery step in ancillary flow * fix: add error state for missing time * fix: only allow points or cash payment for ancillaries * fix: break out stepper to design system * fix: update design of select quantity step in add ancillaries flow * fix: add error states for quantity * fix: handle insufficient points case * fix: update stepper to include optional disabledMessage tooltip * fix: handle validations * fix: change name to camel case Approved-by: Bianca Widstam Approved-by: Chuma Mcphoy (We Ahead)
176 lines
4.9 KiB
TypeScript
176 lines
4.9 KiB
TypeScript
import { useIntl } from "react-intl"
|
||
|
||
import { formatPrice } from "@scandic-hotels/common/utils/numberFormatting"
|
||
import { Divider } from "@scandic-hotels/design-system/Divider"
|
||
import Image from "@scandic-hotels/design-system/Image"
|
||
import { Typography } from "@scandic-hotels/design-system/Typography"
|
||
|
||
import {
|
||
AncillaryStepEnum,
|
||
useAddAncillaryStore,
|
||
} from "@/stores/my-stay/add-ancillary-flow"
|
||
|
||
import styles from "./description.module.css"
|
||
|
||
export default function Description() {
|
||
const intl = useIntl()
|
||
const { selectedAncillary, isBreakfast, currentStep } = useAddAncillaryStore(
|
||
(state) => ({
|
||
selectedAncillary: state.selectedAncillary,
|
||
isBreakfast: state.isBreakfast,
|
||
currentStep: state.currentStep,
|
||
})
|
||
)
|
||
|
||
if (!selectedAncillary || currentStep === AncillaryStepEnum.confirmation) {
|
||
return null
|
||
}
|
||
|
||
return (
|
||
<div className={styles.contentContainer}>
|
||
<Image
|
||
src={selectedAncillary.imageUrl}
|
||
width={400}
|
||
height={200}
|
||
alt={selectedAncillary.title}
|
||
className={styles.image}
|
||
/>
|
||
<div className={styles.price}>
|
||
{isBreakfast ? (
|
||
<BreakfastPriceList />
|
||
) : (
|
||
<Typography variant="Body/Paragraph/mdBold">
|
||
<p>
|
||
{formatPrice(
|
||
intl,
|
||
selectedAncillary.price.total,
|
||
selectedAncillary.price.currency
|
||
)}
|
||
</p>
|
||
</Typography>
|
||
)}
|
||
|
||
{selectedAncillary.points && (
|
||
<Typography variant="Body/Paragraph/mdBold">
|
||
<p>
|
||
{intl.formatMessage(
|
||
{
|
||
id: "common.orNumberOfPoints",
|
||
defaultMessage:
|
||
"or {points, plural, one {# point} other {# points}}",
|
||
},
|
||
{
|
||
points: selectedAncillary.points,
|
||
}
|
||
)}
|
||
</p>
|
||
</Typography>
|
||
)}
|
||
{selectedAncillary.requiresQuantity && (
|
||
<Typography variant="Body/Paragraph/mdRegular">
|
||
<p>
|
||
{intl.formatMessage(
|
||
{
|
||
id: "addAncillaryFlowModal.perUnit",
|
||
defaultMessage: "/per {unit}",
|
||
},
|
||
{ unit: selectedAncillary.unitName }
|
||
)}
|
||
</p>
|
||
</Typography>
|
||
)}
|
||
</div>
|
||
<div className={styles.description}>
|
||
{selectedAncillary.description && (
|
||
<Typography variant="Body/Paragraph/mdRegular">
|
||
<p
|
||
dangerouslySetInnerHTML={{
|
||
__html: selectedAncillary.description,
|
||
}}
|
||
/>
|
||
</Typography>
|
||
)}
|
||
</div>
|
||
</div>
|
||
)
|
||
}
|
||
|
||
function BreakfastPriceList() {
|
||
const intl = useIntl()
|
||
const breakfastData = useAddAncillaryStore((state) => state.breakfastData)
|
||
|
||
if (!breakfastData) {
|
||
return intl.formatMessage({
|
||
id: "ancillaries.unableToDisplayBreakfastPrices",
|
||
defaultMessage: "Unable to display breakfast prices.",
|
||
})
|
||
}
|
||
|
||
return (
|
||
<div>
|
||
<div className={styles.breakfastPriceList}>
|
||
<Typography variant="Body/Paragraph/mdBold">
|
||
<span>
|
||
{intl.formatMessage(
|
||
{
|
||
id: "addAncillaryFlowModal.pricePerNightPerAdult",
|
||
defaultMessage: "{price}/night per adult",
|
||
},
|
||
{
|
||
price: formatPrice(
|
||
intl,
|
||
breakfastData.priceAdult,
|
||
breakfastData.currency
|
||
),
|
||
}
|
||
)}
|
||
</span>
|
||
</Typography>
|
||
|
||
{breakfastData.nrOfPayingChildren > 0 && (
|
||
<>
|
||
<div className={styles.divider}>
|
||
<Divider variant="vertical" color="Border/Divider/Subtle" />
|
||
</div>
|
||
|
||
<Typography variant="Body/Paragraph/mdBold">
|
||
<span>
|
||
{intl.formatMessage(
|
||
{
|
||
id: "addAncillaryFlowModal.pricePerNightPerKids",
|
||
defaultMessage: "{price}/night for kids (ages 4–12)",
|
||
},
|
||
{
|
||
price: formatPrice(
|
||
intl,
|
||
breakfastData.priceChild,
|
||
breakfastData.currency
|
||
),
|
||
}
|
||
)}
|
||
</span>
|
||
</Typography>
|
||
</>
|
||
)}
|
||
|
||
{breakfastData.nrOfFreeChildren > 0 && (
|
||
<>
|
||
<div className={styles.divider}>
|
||
<Divider variant="vertical" color="Border/Divider/Subtle" />
|
||
</div>
|
||
|
||
<Typography variant="Body/Paragraph/mdBold">
|
||
<span>
|
||
{intl.formatMessage({
|
||
id: "addAncillaryFlowModal.freeBreakfastForKids",
|
||
defaultMessage: "Free for kids (under 4)",
|
||
})}
|
||
</span>
|
||
</Typography>
|
||
</>
|
||
)}
|
||
</div>
|
||
</div>
|
||
)
|
||
}
|