Merged in feat/sw-1681-add-breakfast (pull request #1635)
Feat/sw-1681 add breakfast This implements the add breakfast flow Approved-by: Pontus Dreij
This commit is contained in:
@@ -9,22 +9,24 @@ import PriceRow from "./PriceRow"
|
||||
|
||||
import styles from "./priceSummary.module.css"
|
||||
|
||||
import type { Ancillary } from "@/types/components/myPages/myStay/ancillaries"
|
||||
|
||||
interface PriceSummaryProps {
|
||||
totalPrice: number | null
|
||||
totalPoints: number | null
|
||||
quantityWithPoints: number
|
||||
quantityWithCard: number
|
||||
selectedAncillary: NonNullable<Ancillary["ancillaryContent"][number]>
|
||||
|
||||
items: {
|
||||
title: string
|
||||
totalPrice: number
|
||||
currency: string
|
||||
points?: number
|
||||
quantityWithCard?: number
|
||||
quantityWithPoints?: number
|
||||
}[]
|
||||
}
|
||||
|
||||
export default function PriceSummary({
|
||||
totalPrice,
|
||||
totalPoints,
|
||||
quantityWithPoints,
|
||||
quantityWithCard,
|
||||
selectedAncillary,
|
||||
items,
|
||||
}: PriceSummaryProps) {
|
||||
const intl = useIntl()
|
||||
const hasTotalPoints = typeof totalPoints === "number"
|
||||
@@ -37,27 +39,28 @@ export default function PriceSummary({
|
||||
</Typography>
|
||||
<Divider color="subtle" />
|
||||
|
||||
{hasTotalPrice && (
|
||||
<PriceRow
|
||||
title={selectedAncillary.title}
|
||||
quantity={quantityWithCard}
|
||||
label={intl.formatMessage({ id: "Price including VAT" })}
|
||||
value={formatPrice(
|
||||
intl,
|
||||
selectedAncillary.price.totalPrice,
|
||||
selectedAncillary.price.currency
|
||||
{items.map((item) => (
|
||||
<>
|
||||
{item.quantityWithCard && (
|
||||
<PriceRow
|
||||
title={item.title}
|
||||
quantity={item.quantityWithCard}
|
||||
label={intl.formatMessage({ id: "Price including VAT" })}
|
||||
value={formatPrice(intl, item.totalPrice, item.currency)}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
{hasTotalPoints && (
|
||||
<PriceRow
|
||||
title={selectedAncillary.title}
|
||||
quantity={quantityWithPoints}
|
||||
label={intl.formatMessage({ id: "Points" })}
|
||||
value={`${selectedAncillary.points} ${intl.formatMessage({ id: "points" })}`}
|
||||
/>
|
||||
)}
|
||||
<Divider color="subtle" />
|
||||
{item.quantityWithPoints && (
|
||||
<PriceRow
|
||||
title={item.title}
|
||||
quantity={item.quantityWithPoints}
|
||||
label={intl.formatMessage({ id: "Points" })}
|
||||
value={`${item.points} ${intl.formatMessage({ id: "points" })}`}
|
||||
/>
|
||||
)}
|
||||
<Divider color="subtle" />
|
||||
</>
|
||||
))}
|
||||
|
||||
<div className={styles.column}>
|
||||
<Typography variant="Body/Paragraph/mdBold">
|
||||
<p>
|
||||
@@ -71,11 +74,7 @@ export default function PriceSummary({
|
||||
<Typography variant="Body/Paragraph/mdBold">
|
||||
<p>
|
||||
{hasTotalPrice &&
|
||||
formatPrice(
|
||||
intl,
|
||||
totalPrice,
|
||||
selectedAncillary.price.currency
|
||||
)}
|
||||
formatPrice(intl, totalPrice, items[0]?.currency)}
|
||||
{hasTotalPoints && hasTotalPrice && " + "}
|
||||
{hasTotalPoints &&
|
||||
`${totalPoints} ${intl.formatMessage({ id: "points" })}`}
|
||||
|
||||
@@ -5,6 +5,7 @@ import { Typography } from "@scandic-hotels/design-system/Typography"
|
||||
|
||||
import {
|
||||
AncillaryStepEnum,
|
||||
type BreakfastData,
|
||||
useAddAncillaryStore,
|
||||
} from "@/stores/my-stay/add-ancillary-flow"
|
||||
|
||||
@@ -15,6 +16,8 @@ import PriceSummary from "./PriceSummary"
|
||||
|
||||
import styles from "./priceDetails.module.css"
|
||||
|
||||
import type { SelectedAncillary } from "@/types/components/myPages/myStay/ancillaries"
|
||||
|
||||
interface PriceDetailsProps {
|
||||
isPriceDetailsOpen: boolean
|
||||
}
|
||||
@@ -24,18 +27,28 @@ export default function PriceDetails({
|
||||
}: PriceDetailsProps) {
|
||||
const intl = useIntl()
|
||||
|
||||
const { currentStep, selectedAncillary } = useAddAncillaryStore((state) => ({
|
||||
currentStep: state.currentStep,
|
||||
selectedAncillary: state.selectedAncillary,
|
||||
}))
|
||||
const { currentStep, selectedAncillary, isBreakfast, breakfastData } =
|
||||
useAddAncillaryStore((state) => ({
|
||||
currentStep: state.currentStep,
|
||||
selectedAncillary: state.selectedAncillary,
|
||||
isBreakfast: state.isBreakfast,
|
||||
breakfastData: state.breakfastData,
|
||||
}))
|
||||
const quantityWithPoints = useWatch({ name: "quantityWithPoints" })
|
||||
const quantityWithCard = useWatch({ name: "quantityWithCard" })
|
||||
|
||||
if (!selectedAncillary || currentStep !== AncillaryStepEnum.confirmation) {
|
||||
return null
|
||||
}
|
||||
const totalPrice =
|
||||
quantityWithCard && selectedAncillary
|
||||
|
||||
if (isBreakfast && !breakfastData) {
|
||||
return null
|
||||
}
|
||||
|
||||
const totalPrice = isBreakfast
|
||||
? breakfastData!.priceAdult * breakfastData!.nrOfAdults +
|
||||
breakfastData!.priceChild * breakfastData!.nrOfPayingChildren
|
||||
: quantityWithCard && selectedAncillary
|
||||
? selectedAncillary.price.totalPrice * quantityWithCard
|
||||
: null
|
||||
|
||||
@@ -47,6 +60,57 @@ export default function PriceDetails({
|
||||
const hasTotalPoints = typeof totalPoints === "number"
|
||||
const hasTotalPrice = typeof totalPrice === "number"
|
||||
|
||||
function getBreakfastItems(
|
||||
selectedAncillary: SelectedAncillary,
|
||||
breakfastData: BreakfastData | null
|
||||
) {
|
||||
if (!breakfastData) {
|
||||
return []
|
||||
}
|
||||
|
||||
const items = [
|
||||
{
|
||||
title: `${selectedAncillary.title} / ${intl.formatMessage({ id: "Adult" })}`,
|
||||
totalPrice: breakfastData.priceAdult,
|
||||
currency: breakfastData.currency,
|
||||
quantityWithCard: breakfastData.nrOfAdults,
|
||||
},
|
||||
]
|
||||
|
||||
if (breakfastData.nrOfPayingChildren > 0) {
|
||||
items.push({
|
||||
title: `${selectedAncillary.title} / ${intl.formatMessage({ id: "Children" })} 4-12`,
|
||||
totalPrice: breakfastData.priceChild,
|
||||
currency: breakfastData.currency,
|
||||
quantityWithCard: breakfastData.nrOfPayingChildren,
|
||||
})
|
||||
}
|
||||
|
||||
if (breakfastData.nrOfFreeChildren > 0) {
|
||||
items.push({
|
||||
title: `${selectedAncillary.title} / ${intl.formatMessage({ id: "Children under {age}" }, { age: 4 })}`,
|
||||
totalPrice: 0,
|
||||
currency: breakfastData.currency,
|
||||
quantityWithCard: breakfastData.nrOfFreeChildren,
|
||||
})
|
||||
}
|
||||
|
||||
return items
|
||||
}
|
||||
|
||||
const items = isBreakfast
|
||||
? getBreakfastItems(selectedAncillary, breakfastData)
|
||||
: [
|
||||
{
|
||||
title: selectedAncillary.title,
|
||||
totalPrice: selectedAncillary.price.totalPrice,
|
||||
currency: selectedAncillary.price.currency,
|
||||
points: selectedAncillary.points,
|
||||
quantityWithCard,
|
||||
quantityWithPoints,
|
||||
},
|
||||
]
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={styles.totalPrice}>
|
||||
@@ -94,9 +158,7 @@ export default function PriceDetails({
|
||||
<PriceSummary
|
||||
totalPrice={totalPrice}
|
||||
totalPoints={totalPoints}
|
||||
quantityWithCard={quantityWithCard ?? 0}
|
||||
quantityWithPoints={quantityWithPoints ?? 0}
|
||||
selectedAncillary={selectedAncillary}
|
||||
items={items}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { useEffect } from "react"
|
||||
import { useFormContext } from "react-hook-form"
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
@@ -6,23 +7,30 @@ import { Typography } from "@scandic-hotels/design-system/Typography"
|
||||
|
||||
import { useAddAncillaryStore } from "@/stores/my-stay/add-ancillary-flow"
|
||||
|
||||
import Alert from "@/components/TempDesignSystem/Alert"
|
||||
import ErrorMessage from "@/components/TempDesignSystem/Form/ErrorMessage"
|
||||
import Select from "@/components/TempDesignSystem/Form/Select"
|
||||
import Body from "@/components/TempDesignSystem/Text/Body"
|
||||
|
||||
import styles from "./selectQuantityStep.module.css"
|
||||
|
||||
import type { SelectQuantityStepProps } from "@/types/components/myPages/myStay/ancillaries"
|
||||
import { BreakfastPackageEnum } from "@/types/enums/breakfast"
|
||||
import { AlertTypeEnum } from "@/types/enums/alert"
|
||||
|
||||
export default function SelectQuantityStep({ user }: SelectQuantityStepProps) {
|
||||
const intl = useIntl()
|
||||
const selectedAncillary = useAddAncillaryStore(
|
||||
(state) => state.selectedAncillary
|
||||
)
|
||||
const { isBreakfast, selectedAncillary } = useAddAncillaryStore((state) => ({
|
||||
isBreakfast: state.isBreakfast,
|
||||
selectedAncillary: state.selectedAncillary,
|
||||
}))
|
||||
const {
|
||||
formState: { errors },
|
||||
} = useFormContext()
|
||||
|
||||
if (isBreakfast) {
|
||||
return <BreakfastInfo />
|
||||
}
|
||||
|
||||
const cardQuantityOptions = Array.from({ length: 7 }, (_, i) => ({
|
||||
label: `${i}`,
|
||||
value: i,
|
||||
@@ -47,13 +55,6 @@ export default function SelectQuantityStep({ user }: SelectQuantityStepProps) {
|
||||
? intl.formatMessage({ id: "Insufficient points" })
|
||||
: intl.formatMessage({ id: "Select quantity" })
|
||||
|
||||
// TODO: Remove this when add breakfast is implemented
|
||||
if (
|
||||
selectedAncillary?.id === BreakfastPackageEnum.ANCILLARY_REGULAR_BREAKFAST
|
||||
) {
|
||||
return "Breakfast TBI"
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={styles.selectContainer}>
|
||||
{selectedAncillary?.points && user && (
|
||||
@@ -96,3 +97,77 @@ export default function SelectQuantityStep({ user }: SelectQuantityStepProps) {
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function BreakfastInfo() {
|
||||
const intl = useIntl()
|
||||
const breakfastData = useAddAncillaryStore((state) => state.breakfastData)
|
||||
|
||||
const { setValue } = useFormContext()
|
||||
|
||||
setValue("quantityWithCard", 1)
|
||||
|
||||
if (!breakfastData) {
|
||||
return intl.formatMessage({
|
||||
id: "Can not show breakfast prices.",
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={styles.breakfastContainer}>
|
||||
<Alert
|
||||
type={AlertTypeEnum.Info}
|
||||
text={intl.formatMessage({
|
||||
id: "Breakfast can only be added for the entire duration of the stay
and for all guests.",
|
||||
})}
|
||||
/>
|
||||
{(breakfastData.nrOfPayingChildren > 0 ||
|
||||
breakfastData.nrOfFreeChildren > 0) && (
|
||||
<dl className={styles.breakfastPrices}>
|
||||
<div className={styles.breakfastPriceBox}>
|
||||
<MaterialIcon icon="check_circle" className={styles.icon} />
|
||||
<div>
|
||||
<dt>
|
||||
{`${breakfastData.nrOfAdults} X ${intl.formatMessage({ id: "Adults" })}`}
|
||||
</dt>
|
||||
<dd>
|
||||
<Body>
|
||||
{`${breakfastData.priceAdult} ${breakfastData.currency}`}
|
||||
</Body>
|
||||
</dd>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{breakfastData.nrOfPayingChildren > 0 && (
|
||||
<div className={styles.breakfastPriceBox}>
|
||||
<MaterialIcon icon="check_circle" className={styles.icon} />
|
||||
<div>
|
||||
<dt>
|
||||
{`${breakfastData.nrOfPayingChildren} X ${intl.formatMessage({ id: "ages" })} 4-12`}
|
||||
</dt>
|
||||
<dd>
|
||||
<Body>
|
||||
{`${breakfastData.priceChild} ${breakfastData.currency}`}
|
||||
</Body>
|
||||
</dd>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{breakfastData.nrOfFreeChildren > 0 && (
|
||||
<div className={`${styles.breakfastPriceBox} ${styles.free}`}>
|
||||
<MaterialIcon icon="check_circle" className={styles.icon} />
|
||||
<div>
|
||||
<dt>
|
||||
{`${breakfastData.nrOfFreeChildren} X ${intl.formatMessage({ id: "under" })} 4`}
|
||||
</dt>
|
||||
<dd>
|
||||
<Body>{intl.formatMessage({ id: "Free" })}</Body>
|
||||
</dd>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</dl>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -28,3 +28,52 @@
|
||||
gap: var(--Space-x15);
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.breakfastContainer {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--Space-x2);
|
||||
}
|
||||
|
||||
.breakfastPrices {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--Spacing-x1);
|
||||
align-self: stretch;
|
||||
}
|
||||
|
||||
.breakfastPriceBox {
|
||||
display: flex;
|
||||
padding: var(--Space-15);
|
||||
flex: 1 0 0;
|
||||
border-radius: var(--Corner-radius-md);
|
||||
border: 1px solid var(--Base-Border-Subtle);
|
||||
background: var(--Surface-Feedback-Information);
|
||||
|
||||
align-items: center;
|
||||
gap: var(--Spacing-x1);
|
||||
align-self: stretch;
|
||||
}
|
||||
|
||||
.free {
|
||||
background: var(--Surface-Feedback-Succes);
|
||||
}
|
||||
|
||||
.breakfastPriceBox dt {
|
||||
color: var(--Text-Secondary);
|
||||
|
||||
font-family: var(--Tag-Font-family);
|
||||
font-size: var(--Tag-Size);
|
||||
font-style: normal;
|
||||
font-weight: var(--Tag-Font-weight);
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.icon {
|
||||
display: none;
|
||||
}
|
||||
@media screen and (min-width: 768px) {
|
||||
.icon {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,3 +97,24 @@
|
||||
width: 833px;
|
||||
}
|
||||
}
|
||||
|
||||
.breakfastPriceList {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.divider {
|
||||
display: none;
|
||||
height: var(--Space-x4);
|
||||
}
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
.breakfastPriceList {
|
||||
flex-direction: row;
|
||||
gap: var(--Space-x2);
|
||||
}
|
||||
|
||||
.divider {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import { env } from "@/env/client"
|
||||
import { trpc } from "@/lib/trpc/client"
|
||||
import {
|
||||
AncillaryStepEnum,
|
||||
type BreakfastData,
|
||||
useAddAncillaryStore,
|
||||
} from "@/stores/my-stay/add-ancillary-flow"
|
||||
|
||||
@@ -39,22 +40,34 @@ import Steps from "./Steps"
|
||||
|
||||
import styles from "./addAncillaryFlowModal.module.css"
|
||||
|
||||
import type { AddAncillaryFlowModalProps } from "@/types/components/myPages/myStay/ancillaries"
|
||||
import type {
|
||||
AddAncillaryFlowModalProps,
|
||||
Packages,
|
||||
} from "@/types/components/myPages/myStay/ancillaries"
|
||||
import { BreakfastPackageEnum } from "@/types/enums/breakfast"
|
||||
|
||||
export default function AddAncillaryFlowModal({
|
||||
booking,
|
||||
packages,
|
||||
user,
|
||||
savedCreditCards,
|
||||
refId,
|
||||
}: AddAncillaryFlowModalProps) {
|
||||
const { currentStep, selectedAncillary, closeModal } = useAddAncillaryStore(
|
||||
(state) => ({
|
||||
currentStep: state.currentStep,
|
||||
selectedAncillary: state.selectedAncillary,
|
||||
closeModal: state.closeModal,
|
||||
})
|
||||
)
|
||||
const {
|
||||
currentStep,
|
||||
selectedAncillary,
|
||||
closeModal,
|
||||
breakfastData,
|
||||
setBreakfastData,
|
||||
isBreakfast,
|
||||
} = useAddAncillaryStore((state) => ({
|
||||
currentStep: state.currentStep,
|
||||
selectedAncillary: state.selectedAncillary,
|
||||
closeModal: state.closeModal,
|
||||
breakfastData: state.breakfastData,
|
||||
setBreakfastData: state.setBreakfastData,
|
||||
isBreakfast: state.isBreakfast,
|
||||
}))
|
||||
const intl = useIntl()
|
||||
const lang = useLang()
|
||||
const router = useRouter()
|
||||
@@ -139,17 +152,44 @@ export default function AddAncillaryFlowModal({
|
||||
selectedAncillary,
|
||||
})
|
||||
if (booking.guaranteeInfo) {
|
||||
const packages = []
|
||||
const packagesToAdd = []
|
||||
if (selectedAncillary?.id && data.quantityWithCard) {
|
||||
packages.push({
|
||||
code: selectedAncillary.id,
|
||||
quantity: data.quantityWithCard,
|
||||
comment: data.optionalText || undefined,
|
||||
})
|
||||
if (!isBreakfast) {
|
||||
packagesToAdd.push({
|
||||
code: selectedAncillary.id,
|
||||
quantity: data.quantityWithCard,
|
||||
comment: data.optionalText || undefined,
|
||||
})
|
||||
} else {
|
||||
if (!breakfastData) {
|
||||
toast.error(
|
||||
intl.formatMessage({
|
||||
id: "Something went wrong!",
|
||||
})
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
packagesToAdd.push({
|
||||
code: BreakfastPackageEnum.ANCILLARY_REGULAR_BREAKFAST,
|
||||
quantity: breakfastData.nrOfAdults,
|
||||
comment: data.optionalText || undefined,
|
||||
})
|
||||
packagesToAdd.push({
|
||||
code: BreakfastPackageEnum.ANCILLARY_CHILD_PAYING_BREAKFAST,
|
||||
quantity: breakfastData.nrOfPayingChildren,
|
||||
comment: data.optionalText || undefined,
|
||||
})
|
||||
packagesToAdd.push({
|
||||
code: BreakfastPackageEnum.FREE_CHILD_BREAKFAST,
|
||||
quantity: breakfastData.nrOfFreeChildren,
|
||||
comment: data.optionalText || undefined,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if (selectedAncillary?.loyaltyCode && data.quantityWithPoints) {
|
||||
packages.push({
|
||||
packagesToAdd.push({
|
||||
code: selectedAncillary.loyaltyCode,
|
||||
quantity: data.quantityWithPoints,
|
||||
comment: data.optionalText || undefined,
|
||||
@@ -162,7 +202,7 @@ export default function AddAncillaryFlowModal({
|
||||
ancillaryDeliveryTime: selectedAncillary?.requiresDeliveryTime
|
||||
? data.deliveryTime
|
||||
: undefined,
|
||||
packages,
|
||||
packages: packagesToAdd,
|
||||
language: lang,
|
||||
})
|
||||
} else {
|
||||
@@ -212,9 +252,26 @@ export default function AddAncillaryFlowModal({
|
||||
}
|
||||
}, [searchParams, pathname, formMethods, router])
|
||||
|
||||
useEffect(() => {
|
||||
setBreakfastData(
|
||||
calculateBreakfastData(
|
||||
isBreakfast,
|
||||
packages,
|
||||
booking.adults,
|
||||
booking.childrenAges
|
||||
)
|
||||
)
|
||||
}, [
|
||||
booking.adults,
|
||||
booking.childrenAges,
|
||||
isBreakfast,
|
||||
packages,
|
||||
setBreakfastData,
|
||||
])
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className={styles.loading}>
|
||||
<div>
|
||||
<LoadingSpinner />
|
||||
</div>
|
||||
)
|
||||
@@ -251,10 +308,14 @@ export default function AddAncillaryFlowModal({
|
||||
<div className={styles.price}>
|
||||
<Typography variant="Body/Paragraph/mdBold">
|
||||
<p>
|
||||
{formatPrice(
|
||||
intl,
|
||||
selectedAncillary.price.totalPrice,
|
||||
selectedAncillary.price.currency
|
||||
{isBreakfast ? (
|
||||
<BreakfastPriceList />
|
||||
) : (
|
||||
formatPrice(
|
||||
intl,
|
||||
selectedAncillary.price.totalPrice,
|
||||
selectedAncillary.price.currency
|
||||
)
|
||||
)}
|
||||
</p>
|
||||
</Typography>
|
||||
@@ -292,10 +353,7 @@ export default function AddAncillaryFlowModal({
|
||||
<Steps user={user} savedCreditCards={savedCreditCards} />
|
||||
</div>
|
||||
</form>
|
||||
{/* TODO: Remove the berakfast check when add breakfast is implemented */}
|
||||
{currentStep === AncillaryStepEnum.selectAncillary ||
|
||||
selectedAncillary?.id ===
|
||||
BreakfastPackageEnum.ANCILLARY_REGULAR_BREAKFAST ? null : (
|
||||
{currentStep === AncillaryStepEnum.selectAncillary ? null : (
|
||||
<div
|
||||
className={
|
||||
currentStep === AncillaryStepEnum.confirmation
|
||||
@@ -316,3 +374,96 @@ export default function AddAncillaryFlowModal({
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
|
||||
function BreakfastPriceList() {
|
||||
const intl = useIntl()
|
||||
const breakfastData = useAddAncillaryStore((state) => state.breakfastData)
|
||||
|
||||
if (!breakfastData) {
|
||||
return intl.formatMessage({
|
||||
id: "Can not show breakfast prices.",
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className={styles.breakfastPriceList}>
|
||||
<Typography variant="Body/Paragraph/mdBold">
|
||||
<span>{`${breakfastData.priceAdult} ${breakfastData.currency} / ${intl.formatMessage({ id: "Adult" })}`}</span>
|
||||
</Typography>
|
||||
|
||||
{breakfastData.nrOfPayingChildren > 0 && (
|
||||
<>
|
||||
<div className={styles.divider}>
|
||||
<Divider variant="vertical" color="baseSurfaceSubtleNormal" />
|
||||
</div>
|
||||
|
||||
<Typography variant="Body/Paragraph/mdBold">
|
||||
<span>{`${breakfastData.priceChild} ${breakfastData.currency} / ${intl.formatMessage({ id: "Years" })} 4-12`}</span>
|
||||
</Typography>
|
||||
</>
|
||||
)}
|
||||
|
||||
{breakfastData.nrOfFreeChildren > 0 && (
|
||||
<>
|
||||
<div className={styles.divider}>
|
||||
<Divider variant="vertical" color="baseSurfaceSubtleNormal" />
|
||||
</div>
|
||||
|
||||
<Typography variant="Body/Paragraph/mdBold">
|
||||
<span>{`${intl.formatMessage({ id: "Free" })} / ${intl.formatMessage({ id: "Under {age} years" }, { age: 4 })}`}</span>
|
||||
</Typography>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* This function calculates some breakfast data in the store.
|
||||
* It is used in various places in the add flow, but only needs
|
||||
* to be calculated once.
|
||||
*/
|
||||
function calculateBreakfastData(
|
||||
isBreakfast: boolean,
|
||||
packages: Packages | null,
|
||||
nrOfAdults: number,
|
||||
childrenAges: number[]
|
||||
): BreakfastData | null {
|
||||
if (!isBreakfast) {
|
||||
return null
|
||||
}
|
||||
|
||||
const [nrOfPayingChildren, nrOfFreeChildren] = childrenAges.reduce(
|
||||
(acc, curr) => (curr >= 4 ? [acc[0] + 1, acc[1]] : [acc[0], acc[1] + 1]),
|
||||
[0, 0]
|
||||
)
|
||||
|
||||
const priceAdult = packages?.find(
|
||||
(p) => p.code === BreakfastPackageEnum.ANCILLARY_REGULAR_BREAKFAST
|
||||
)?.localPrice.price
|
||||
const priceChild = packages?.find(
|
||||
(p) => p.code === BreakfastPackageEnum.ANCILLARY_CHILD_PAYING_BREAKFAST
|
||||
)?.localPrice.price
|
||||
const currency = packages?.find(
|
||||
(p) => p.code === BreakfastPackageEnum.ANCILLARY_REGULAR_BREAKFAST
|
||||
)?.localPrice.currency
|
||||
|
||||
if (
|
||||
typeof priceAdult !== "number" ||
|
||||
typeof priceChild !== "number" ||
|
||||
typeof currency !== "string"
|
||||
) {
|
||||
return null
|
||||
} else {
|
||||
return {
|
||||
nrOfAdults,
|
||||
nrOfPayingChildren,
|
||||
nrOfFreeChildren,
|
||||
priceAdult,
|
||||
priceChild,
|
||||
currency,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -197,6 +197,7 @@ export function Ancillaries({
|
||||
<AddAncillaryFlowModal
|
||||
user={user}
|
||||
booking={booking}
|
||||
packages={packages}
|
||||
refId={refId}
|
||||
savedCreditCards={savedCreditCards}
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user