chore(BOOK-701): replace subtitle with typography * chore(BOOK-701): replace subtitle with typography * chore(BOOK-701): align center * chore(BOOK-701): change token * chore(BOOK-701): change text color * fix(BOOK-704): revert pricechange dialog changes * chore(BOOK-701): remove subtitle from package.json Approved-by: Matilda Landström
297 lines
11 KiB
TypeScript
297 lines
11 KiB
TypeScript
import { Fragment } from "react"
|
|
import { useIntl } from "react-intl"
|
|
|
|
import { formatPrice } from "@scandic-hotels/common/utils/numberFormatting"
|
|
import Accordion from "@scandic-hotels/design-system/Accordion"
|
|
import AccordionItem from "@scandic-hotels/design-system/Accordion/AccordionItem"
|
|
import { Divider } from "@scandic-hotels/design-system/Divider"
|
|
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
|
|
import { Typography } from "@scandic-hotels/design-system/Typography"
|
|
import { BreakfastPackageEnum } from "@scandic-hotels/trpc/enums/breakfast"
|
|
|
|
import { getBreakfastPackagesFromAncillaryFlow } from "../../utils/hasBreakfastPackage"
|
|
import RemoveButton from "./RemoveButton"
|
|
|
|
import styles from "./addedAncillaries.module.css"
|
|
|
|
import type { PackageSchema } from "@scandic-hotels/trpc/types/bookingConfirmation"
|
|
|
|
import type { AddedAncillariesProps } from "@/types/components/myPages/myStay/ancillaries"
|
|
import type { Room } from "@/types/stores/my-stay"
|
|
|
|
export function AddedAncillaries({
|
|
ancillaries,
|
|
booking,
|
|
}: AddedAncillariesProps) {
|
|
const intl = useIntl()
|
|
|
|
const addedBreakfastPackages = getBreakfastPackagesFromAncillaryFlow(
|
|
booking.originalPackages
|
|
)
|
|
|
|
const addedAncillaries = getAddedAncillaries(booking, addedBreakfastPackages)
|
|
|
|
if (addedAncillaries.length === 0) {
|
|
return null
|
|
}
|
|
|
|
return (
|
|
<div className={styles.container}>
|
|
<div className={styles.header}>
|
|
<Typography variant="Title/Subtitle/lg">
|
|
<p>
|
|
{intl.formatMessage({
|
|
defaultMessage: "My extras",
|
|
id: "myStay.addedAncillaries.title",
|
|
})}
|
|
</p>
|
|
</Typography>
|
|
|
|
{booking.ancillary?.deliveryTime && (
|
|
<Typography variant="Body/Paragraph/mdBold">
|
|
<div className={styles.deliveryTime}>
|
|
<p>
|
|
{intl.formatMessage({
|
|
id: "ancillaries.deliveredAt",
|
|
defaultMessage: "Delivered at:",
|
|
})}
|
|
</p>
|
|
|
|
<p>{booking.ancillary?.deliveryTime}</p>
|
|
</div>
|
|
</Typography>
|
|
)}
|
|
</div>
|
|
|
|
{addedAncillaries.map((ancillary) => {
|
|
const ancillaryTitle = `${
|
|
ancillary.code === BreakfastPackageEnum.ANCILLARY_REGULAR_BREAKFAST
|
|
? intl.formatMessage({
|
|
id: "common.breakfast",
|
|
defaultMessage: "Breakfast",
|
|
})
|
|
: (ancillaries?.find(
|
|
(a) =>
|
|
a.id === ancillary.code || a.loyaltyCode === ancillary.code
|
|
)?.title ?? "")
|
|
} X${ancillary.totalUnit}`
|
|
|
|
return (
|
|
<Fragment key={ancillary.code}>
|
|
<Accordion className={styles.ancillaryMobile}>
|
|
<AccordionItem
|
|
title={ancillaryTitle}
|
|
icon={
|
|
<MaterialIcon
|
|
icon="check_circle"
|
|
color="Icon/Feedback/Success"
|
|
/>
|
|
}
|
|
>
|
|
<div>
|
|
{ancillary.comment && (
|
|
<>
|
|
<div className={styles.commentMobile}>
|
|
<Typography variant="Body/Paragraph/mdBold">
|
|
<p>
|
|
{intl.formatMessage({
|
|
id: "common.otherRequests",
|
|
defaultMessage: "Other requests",
|
|
})}
|
|
{/* eslint-disable-next-line formatjs/no-literal-string-in-jsx */}
|
|
{":"}
|
|
</p>
|
|
</Typography>
|
|
<Typography
|
|
variant="Body/Paragraph/mdBold"
|
|
className={styles.ancillaryComment}
|
|
>
|
|
<p>{ancillary.comment}</p>
|
|
</Typography>
|
|
</div>
|
|
</>
|
|
)}
|
|
<div className={styles.paymentMobileWrapper}>
|
|
<div className={styles.paymentMobile}>
|
|
<Typography variant="Body/Paragraph/mdRegular">
|
|
<p>
|
|
{intl.formatMessage({
|
|
id: "common.total",
|
|
defaultMessage: "Total",
|
|
})}
|
|
</p>
|
|
</Typography>
|
|
<Typography variant="Body/Paragraph/mdBold">
|
|
<p>
|
|
{ancillary.currency.toLowerCase() === "points"
|
|
? intl.formatMessage(
|
|
{
|
|
id: "common.numberOfPoints",
|
|
defaultMessage:
|
|
"{points, plural, one {# point} other {# points}}",
|
|
},
|
|
{
|
|
points: ancillary.totalPrice,
|
|
}
|
|
)
|
|
: formatPrice(
|
|
intl,
|
|
ancillary.totalPrice,
|
|
ancillary.currency
|
|
)}
|
|
</p>
|
|
</Typography>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div className={styles.footerMobile}>
|
|
{booking.confirmationNumber && ancillary.code ? (
|
|
<div className={styles.actions}>
|
|
<RemoveButton
|
|
refId={booking.refId}
|
|
codes={
|
|
ancillary.code ===
|
|
BreakfastPackageEnum.ANCILLARY_REGULAR_BREAKFAST
|
|
? addedBreakfastPackages!.map((p) => p.code)
|
|
: [ancillary.code]
|
|
}
|
|
title={ancillaryTitle}
|
|
booking={booking}
|
|
ancillary={ancillary}
|
|
addedBreakfastPackages={addedBreakfastPackages}
|
|
/>
|
|
</div>
|
|
) : null}
|
|
</div>
|
|
</AccordionItem>
|
|
</Accordion>
|
|
<div className={styles.ancillaryDesktop}>
|
|
<div className={styles.specification}>
|
|
<div className={styles.name}>
|
|
<MaterialIcon
|
|
icon="check_circle"
|
|
color="Icon/Feedback/Success"
|
|
/>
|
|
<Typography variant="Body/Paragraph/mdBold">
|
|
<p>{ancillaryTitle}</p>
|
|
</Typography>
|
|
</div>
|
|
<div className={styles.payment}>
|
|
<Typography variant="Body/Paragraph/mdRegular">
|
|
<p>
|
|
{intl.formatMessage({
|
|
id: "common.total",
|
|
defaultMessage: "Total",
|
|
})}
|
|
</p>
|
|
</Typography>
|
|
<Typography variant="Body/Paragraph/mdBold">
|
|
<p>
|
|
{ancillary.currency.toLowerCase() === "points"
|
|
? intl.formatMessage(
|
|
{
|
|
id: "common.numberOfPoints",
|
|
defaultMessage:
|
|
"{points, plural, one {# point} other {# points}}",
|
|
},
|
|
{
|
|
points: ancillary.totalPrice,
|
|
}
|
|
)
|
|
: formatPrice(
|
|
intl,
|
|
ancillary.totalPrice,
|
|
ancillary.currency
|
|
)}
|
|
</p>
|
|
</Typography>
|
|
</div>
|
|
</div>
|
|
|
|
<Divider />
|
|
|
|
<div className={styles.footer}>
|
|
<div className={styles.comment}>
|
|
{ancillary.comment && (
|
|
<>
|
|
<Typography variant="Body/Paragraph/mdRegular">
|
|
<p>
|
|
{intl.formatMessage({
|
|
id: "common.otherRequests",
|
|
defaultMessage: "Other requests",
|
|
})}
|
|
{/* eslint-disable-next-line formatjs/no-literal-string-in-jsx */}
|
|
{":"}
|
|
</p>
|
|
</Typography>
|
|
<Typography variant="Body/Paragraph/mdRegular">
|
|
<p>{ancillary.comment}</p>
|
|
</Typography>
|
|
</>
|
|
)}
|
|
</div>
|
|
{booking.confirmationNumber &&
|
|
ancillary.code &&
|
|
booking.canModifyAncillaries ? (
|
|
<div className={styles.actions}>
|
|
<RemoveButton
|
|
refId={booking.refId}
|
|
codes={
|
|
ancillary.code ===
|
|
BreakfastPackageEnum.ANCILLARY_REGULAR_BREAKFAST
|
|
? addedBreakfastPackages!.map((p) => p.code)
|
|
: [ancillary.code]
|
|
}
|
|
title={ancillaryTitle}
|
|
booking={booking}
|
|
ancillary={ancillary}
|
|
addedBreakfastPackages={addedBreakfastPackages}
|
|
/>
|
|
</div>
|
|
) : null}
|
|
</div>
|
|
</div>
|
|
</Fragment>
|
|
)
|
|
})}
|
|
</div>
|
|
)
|
|
}
|
|
|
|
/**
|
|
* All ancillaries that are added to the booking
|
|
*
|
|
* Adding a special ancillary for breakfast, calculated from
|
|
* all breakfast packages that has been added as ancillaries,
|
|
* not in the booking flow.
|
|
*/
|
|
function getAddedAncillaries(
|
|
booking: Room,
|
|
addedBreakfastPackages: PackageSchema[] | undefined
|
|
) {
|
|
if (!addedBreakfastPackages?.length) {
|
|
return booking.ancillaries
|
|
}
|
|
|
|
const combinedBreakfastPackageAsAncillary: PackageSchema = {
|
|
code: BreakfastPackageEnum.ANCILLARY_REGULAR_BREAKFAST,
|
|
unitPrice: 0,
|
|
currency: addedBreakfastPackages[0].currency,
|
|
type: addedBreakfastPackages[0].type,
|
|
description: addedBreakfastPackages[0].description,
|
|
comment: addedBreakfastPackages[0].comment,
|
|
totalPrice: addedBreakfastPackages.reduce(
|
|
(acc, curr) => acc + curr.totalPrice,
|
|
0
|
|
),
|
|
unit: addedBreakfastPackages.reduce((acc, curr) => acc + curr.unit, 0),
|
|
totalUnit: addedBreakfastPackages.reduce(
|
|
(acc, curr) => acc + curr.totalUnit,
|
|
0
|
|
),
|
|
}
|
|
|
|
return [combinedBreakfastPackageAsAncillary, ...booking.ancillaries]
|
|
}
|