Files
web/apps/scandic-web/components/HotelReservation/MyStay/Receipt/Specification/index.tsx
Anton Gunnarsson 002d093af4 Merged in feat/sw-2863-move-contentstack-router-to-trpc-package (pull request #2389)
feat(SW-2863): Move contentstack router to trpc package

* Add exports to packages and lint rule to prevent relative imports

* Add env to trpc package

* Add eslint to trpc package

* Apply lint rules

* Use direct imports from trpc package

* Add lint-staged config to trpc

* Move lang enum to common

* Restructure trpc package folder structure

* WIP first step

* update internal imports in trpc

* Fix most errors in scandic-web

Just 100 left...

* Move Props type out of trpc

* Fix CategorizedFilters types

* Move more schemas in hotel router

* Fix deps

* fix getNonContentstackUrls

* Fix import error

* Fix entry error handling

* Fix generateMetadata metrics

* Fix alertType enum

* Fix duplicated types

* lint:fix

* Merge branch 'master' into feat/sw-2863-move-contentstack-router-to-trpc-package

* Fix broken imports

* Merge branch 'master' into feat/sw-2863-move-contentstack-router-to-trpc-package


Approved-by: Linus Flood
2025-06-26 07:53:01 +00:00

243 lines
8.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { CurrencyEnum } from "@scandic-hotels/common/constants/currency"
import { Divider } from "@scandic-hotels/design-system/Divider"
import { Typography } from "@scandic-hotels/design-system/Typography"
import { RoomPackageCodeEnum } from "@scandic-hotels/trpc/enums/roomFilter"
import { calculateVat } from "@/components/HotelReservation/utils"
import { getIntl } from "@/i18n"
import styles from "./specification.module.css"
import type { SpecificationProps } from "@/types/components/hotelReservation/myStay/receipt"
export default async function Specification({
ancillaryPackages,
booking,
currency,
}: SpecificationProps) {
const intl = await getIntl()
const breakfastPackages = booking.packages.filter(
(p) => p.type === "Breakfast"
)
const breakfastTotalPriceInMoney = breakfastPackages
.filter((p) => p.currency !== CurrencyEnum.POINTS)
.reduce((acc, curr) => acc + curr.totalPrice, 0)
const breakfastTotalPriceInPoints = breakfastPackages
.filter((p) => p.currency === CurrencyEnum.POINTS)
.reduce((acc, curr) => acc + curr.totalPrice, 0)
const breakfastCount = breakfastPackages.reduce(
(acc, curr) => acc + curr.unit,
0
)
const displayBreakfastPrice =
breakfastCount > 0 && !booking.rateDefinition.breakfastIncluded
const breakfastMoneyString =
breakfastTotalPriceInMoney > 0
? `${breakfastTotalPriceInMoney} ${currency}`
: ""
const breakfastPointsString =
breakfastTotalPriceInPoints > 0
? `${breakfastTotalPriceInPoints} ${intl.formatMessage({
defaultMessage: "Points",
})}`
: ""
const breakfastPlusString =
breakfastMoneyString && breakfastPointsString ? " + " : ""
const petRoomPackage = booking.packages.find(
(p) => p.code === RoomPackageCodeEnum.PET_ROOM
)
const { vatAmount, priceExclVat } = calculateVat(
booking.roomPrice,
booking.vatPercentage
)
return (
<div className={styles.container}>
<div>
{/****** The room ********/}
<Typography variant="Body/Supporting text (caption)/smBold">
<span>
{intl.formatMessage({
defaultMessage: "Accommodation",
})}
{!booking.rateDefinition.mustBeGuaranteed && (
<>
{" - "}
{intl.formatMessage({
defaultMessage: "Room is prepaid",
})}
</>
)}
</span>
</Typography>
<dl className={styles.dl}>
<Typography variant="Body/Supporting text (caption)/smRegular">
<dt>
{intl.formatMessage({
defaultMessage: "Price including VAT",
})}
</dt>
</Typography>
<Typography variant="Body/Supporting text (caption)/smRegular">
<dd>
{!booking.rateDefinition.mustBeGuaranteed
? // eslint-disable-next-line formatjs/no-literal-string-in-jsx
`(${intl.formatMessage({
defaultMessage: "PREPAID",
})}) `
: null}
{/* eslint-disable-next-line formatjs/no-literal-string-in-jsx */}
{`${booking.roomPrice} ${currency}`}
</dd>
</Typography>
{petRoomPackage && (
<>
<Typography variant="Body/Supporting text (caption)/smRegular">
<dt>
{intl.formatMessage({
defaultMessage: "Pet room charge including VAT",
})}
</dt>
</Typography>
<Typography variant="Body/Supporting text (caption)/smRegular">
{/* eslint-disable-next-line formatjs/no-literal-string-in-jsx */}
<dd>{`${petRoomPackage.totalPrice} ${petRoomPackage.currency}`}</dd>
</Typography>
</>
)}
<Typography variant="Body/Supporting text (caption)/smRegular">
<dt className={styles.tertiary}>
{intl.formatMessage({
defaultMessage: "Price excluding VAT",
})}
</dt>
</Typography>
<Typography variant="Body/Supporting text (caption)/smRegular">
<dd className={styles.tertiary}>
{/* eslint-disable-next-line formatjs/no-literal-string-in-jsx */}
{`${priceExclVat.toFixed(2)} ${currency}`}
</dd>
</Typography>
<Typography variant="Body/Supporting text (caption)/smRegular">
<dt className={styles.tertiary}>
{intl.formatMessage({
defaultMessage: "VAT",
})}
{/* eslint-disable-next-line formatjs/no-literal-string-in-jsx */}
{` ${booking.vatPercentage} %`}
</dt>
</Typography>
<Typography variant="Body/Supporting text (caption)/smRegular">
<dd className={styles.tertiary}>
{/* eslint-disable-next-line formatjs/no-literal-string-in-jsx */}
{`${vatAmount.toFixed(2)} ${currency}`}
</dd>
</Typography>
</dl>
</div>
{/****** Ancillaries ********/}
{booking.ancillaries.map((ancillary) => (
<>
<Divider />
<div>
<div className={styles.quantifyingHeader}>
<Typography variant="Body/Supporting text (caption)/smBold">
<span>
{ancillaryPackages?.flatMap((a) =>
a.ancillaryContent.filter(
(aa) =>
aa.id === ancillary.code ||
aa.loyaltyCode === ancillary.code
)
)[0]?.title ??
intl.formatMessage({
defaultMessage: "Unknown item",
})}
</span>
</Typography>
<Typography variant="Body/Supporting text (caption)/smBold">
<span>
{/* eslint-disable-next-line formatjs/no-literal-string-in-jsx */}
{`× ${ancillary.unit}`}
</span>
</Typography>
</div>
<dl className={styles.dl}>
<Typography variant="Body/Supporting text (caption)/smRegular">
<dt>
{ancillary.currency !== CurrencyEnum.POINTS
? intl.formatMessage({
defaultMessage: "Price including VAT",
})
: intl.formatMessage({
defaultMessage: "Price",
})}
</dt>
</Typography>
<Typography variant="Body/Supporting text (caption)/smRegular">
<dd>
{/* eslint-disable-next-line formatjs/no-literal-string-in-jsx */}
{`${ancillary.totalPrice} ${ancillary.currency}`}
</dd>
</Typography>
</dl>
</div>
</>
))}
{/****** Breakfast ********/}
{displayBreakfastPrice && (
<>
<Divider />
<div>
<div className={styles.quantifyingHeader}>
<Typography variant="Body/Supporting text (caption)/smBold">
<span>
{intl.formatMessage({
defaultMessage: "Breakfast",
})}
</span>
</Typography>
<Typography variant="Body/Supporting text (caption)/smBold">
{/* eslint-disable-next-line formatjs/no-literal-string-in-jsx */}
<span>{`× ${breakfastCount}`}</span>
</Typography>
</div>
<dl className={styles.dl}>
<Typography variant="Body/Supporting text (caption)/smRegular">
<dt>
{breakfastTotalPriceInMoney > 0
? intl.formatMessage({
defaultMessage: "Price including VAT",
})
: intl.formatMessage({
defaultMessage: "Price",
})}
</dt>
</Typography>
<Typography variant="Body/Supporting text (caption)/smRegular">
<dd>
{/* eslint-disable-next-line formatjs/no-literal-string-in-jsx */}
{`${breakfastMoneyString}${breakfastPlusString}${breakfastPointsString}`}
</dd>
</Typography>
</dl>
</div>
</>
)}
</div>
)
}