Merged in feat/BOOK-479-scandic-go-hotels-rm-brf- (pull request #3143)
feat(BOOK-479): Updated breakfast UI for ScandicGo hotels Approved-by: Erik Tiekstra
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
"use client"
|
"use client"
|
||||||
import PriceDetailsModal from "@scandic-hotels/booking-flow/components/PriceDetailsModal"
|
import PriceDetailsModal from "@scandic-hotels/booking-flow/components/PriceDetailsModal"
|
||||||
import { dt } from "@scandic-hotels/common/dt"
|
import { dt } from "@scandic-hotels/common/dt"
|
||||||
|
import { HotelTypeEnum } from "@scandic-hotels/trpc/enums/hotelType"
|
||||||
|
|
||||||
import { useMyStayStore } from "@/stores/my-stay"
|
import { useMyStayStore } from "@/stores/my-stay"
|
||||||
|
|
||||||
@@ -9,17 +10,20 @@ import { calculateTotalPrice, mapToPrice } from "./mapToPrice"
|
|||||||
import styles from "./priceDetails.module.css"
|
import styles from "./priceDetails.module.css"
|
||||||
|
|
||||||
export default function PriceDetails() {
|
export default function PriceDetails() {
|
||||||
const { bookedRoom, rooms } = useMyStayStore((state) => ({
|
const { bookedRoom, rooms, hotelOffersBreakfast } = useMyStayStore(
|
||||||
bookedRoom: state.bookedRoom,
|
(state) => ({
|
||||||
rooms: state.rooms
|
bookedRoom: state.bookedRoom,
|
||||||
.filter((room) => !room.isCancelled)
|
rooms: state.rooms
|
||||||
.map((room) => ({
|
.filter((room) => !room.isCancelled)
|
||||||
...room,
|
.map((room) => ({
|
||||||
breakfastIncluded: room.rateDefinition.breakfastIncluded,
|
...room,
|
||||||
price: mapToPrice(room),
|
breakfastIncluded: room.rateDefinition.breakfastIncluded,
|
||||||
roomType: room.roomName,
|
price: mapToPrice(room),
|
||||||
})),
|
roomType: room.roomName,
|
||||||
}))
|
})),
|
||||||
|
hotelOffersBreakfast: state.hotel.hotelType !== HotelTypeEnum.ScandicGo,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
const bookingCode =
|
const bookingCode =
|
||||||
rooms.find((room) => room.bookingCode)?.bookingCode ?? undefined
|
rooms.find((room) => room.bookingCode)?.bookingCode ?? undefined
|
||||||
@@ -38,6 +42,7 @@ export default function PriceDetails() {
|
|||||||
vat={bookedRoom.vatPercentage}
|
vat={bookedRoom.vatPercentage}
|
||||||
defaultCurrency={bookedRoom.currencyCode}
|
defaultCurrency={bookedRoom.currencyCode}
|
||||||
isCampaignRate={bookedRoom.isCampaignRate}
|
isCampaignRate={bookedRoom.isCampaignRate}
|
||||||
|
hotelOffersBreakfast={hotelOffersBreakfast}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -9,15 +9,23 @@ import { mapToPrice } from "./mapToPrice"
|
|||||||
import type { Price } from "../../../types/price"
|
import type { Price } from "../../../types/price"
|
||||||
|
|
||||||
export default function PriceDetails() {
|
export default function PriceDetails() {
|
||||||
const { bookingCode, currency, fromDate, rooms, vat, toDate } =
|
const {
|
||||||
useBookingConfirmationStore((state) => ({
|
bookingCode,
|
||||||
bookingCode: state.bookingCode ?? undefined,
|
currency,
|
||||||
currency: state.currencyCode,
|
hotelOffersBreakfast,
|
||||||
fromDate: state.fromDate,
|
fromDate,
|
||||||
rooms: state.rooms,
|
rooms,
|
||||||
toDate: state.toDate,
|
vat,
|
||||||
vat: state.vat,
|
toDate,
|
||||||
}))
|
} = useBookingConfirmationStore((state) => ({
|
||||||
|
bookingCode: state.bookingCode ?? undefined,
|
||||||
|
currency: state.currencyCode,
|
||||||
|
hotelOffersBreakfast: state.hotelOffersBreakfast,
|
||||||
|
fromDate: state.fromDate,
|
||||||
|
rooms: state.rooms,
|
||||||
|
toDate: state.toDate,
|
||||||
|
vat: state.vat,
|
||||||
|
}))
|
||||||
|
|
||||||
if (!rooms[0]) {
|
if (!rooms[0]) {
|
||||||
return null
|
return null
|
||||||
@@ -135,6 +143,7 @@ export default function PriceDetails() {
|
|||||||
vat={vat}
|
vat={vat}
|
||||||
defaultCurrency={currency}
|
defaultCurrency={currency}
|
||||||
isCampaignRate={isCampaignRate}
|
isCampaignRate={isCampaignRate}
|
||||||
|
hotelOffersBreakfast={hotelOffersBreakfast}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import { ChildBedTypeEnum } from "@scandic-hotels/trpc/enums/childBedTypeEnum"
|
|||||||
|
|
||||||
import { useBookingConfirmationStore } from "../../../../stores/booking-confirmation"
|
import { useBookingConfirmationStore } from "../../../../stores/booking-confirmation"
|
||||||
import { getRoomFeatureDescription } from "../../../../utils/getRoomFeatureDescription"
|
import { getRoomFeatureDescription } from "../../../../utils/getRoomFeatureDescription"
|
||||||
import Breakfast from "./Breakfast"
|
import { SummaryBreakfast } from "../../../SummaryBreakfast"
|
||||||
import RoomSkeletonLoader from "./RoomSkeletonLoader"
|
import RoomSkeletonLoader from "./RoomSkeletonLoader"
|
||||||
|
|
||||||
import styles from "./room.module.css"
|
import styles from "./room.module.css"
|
||||||
@@ -36,11 +36,12 @@ export function ReceiptRoom({
|
|||||||
showBookingCodeChip = false,
|
showBookingCodeChip = false,
|
||||||
}: BookingConfirmationReceiptRoomProps) {
|
}: BookingConfirmationReceiptRoomProps) {
|
||||||
const intl = useIntl()
|
const intl = useIntl()
|
||||||
const { currencyCode, isVatCurrency, bookingCode } =
|
const { currencyCode, isVatCurrency, bookingCode, hotelOffersBreakfast } =
|
||||||
useBookingConfirmationStore((state) => ({
|
useBookingConfirmationStore((state) => ({
|
||||||
currencyCode: state.currencyCode,
|
currencyCode: state.currencyCode,
|
||||||
isVatCurrency: state.isVatCurrency,
|
isVatCurrency: state.isVatCurrency,
|
||||||
bookingCode: state.bookingCode,
|
bookingCode: state.bookingCode,
|
||||||
|
hotelOffersBreakfast: state.hotelOffersBreakfast,
|
||||||
}))
|
}))
|
||||||
|
|
||||||
if (!room) {
|
if (!room) {
|
||||||
@@ -80,6 +81,11 @@ export function ReceiptRoom({
|
|||||||
const showDiscounted =
|
const showDiscounted =
|
||||||
room.rateDefinition.isMemberRate || room.rateDefinition.isCampaignRate
|
room.rateDefinition.isMemberRate || room.rateDefinition.isCampaignRate
|
||||||
|
|
||||||
|
const breakfastPrice = room.breakfast
|
||||||
|
? room.breakfast?.totalPrice
|
||||||
|
: room.breakfast
|
||||||
|
const breakfastCurrency = room.breakfast ? room.breakfast?.currency : null
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className={styles.room}>
|
<div className={styles.room}>
|
||||||
@@ -275,10 +281,12 @@ export function ReceiptRoom({
|
|||||||
</div>
|
</div>
|
||||||
</Typography>
|
</Typography>
|
||||||
) : null}
|
) : null}
|
||||||
<Breakfast
|
<SummaryBreakfast
|
||||||
breakfast={room.breakfast}
|
breakfastPrice={breakfastPrice}
|
||||||
|
breakfastCurrency={breakfastCurrency}
|
||||||
breakfastIncluded={room.breakfastIncluded}
|
breakfastIncluded={room.breakfastIncluded}
|
||||||
guests={guests}
|
guests={guests}
|
||||||
|
hotelOffersBreakfast={hotelOffersBreakfast}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{showBookingCodeChip && (
|
{showBookingCodeChip && (
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { AlertTypeEnum } from "@scandic-hotels/common/constants/alert"
|
|||||||
import { dt } from "@scandic-hotels/common/dt"
|
import { dt } from "@scandic-hotels/common/dt"
|
||||||
import { Alert } from "@scandic-hotels/design-system/Alert"
|
import { Alert } from "@scandic-hotels/design-system/Alert"
|
||||||
import { Divider } from "@scandic-hotels/design-system/Divider"
|
import { Divider } from "@scandic-hotels/design-system/Divider"
|
||||||
|
import { HotelTypeEnum } from "@scandic-hotels/trpc/enums/hotelType"
|
||||||
|
|
||||||
import { BookingConfirmationProvider } from "../../providers/BookingConfirmationProvider"
|
import { BookingConfirmationProvider } from "../../providers/BookingConfirmationProvider"
|
||||||
import { getBookingConfirmation } from "../../trpc/memoizedRequests/getBookingConfirmation"
|
import { getBookingConfirmation } from "../../trpc/memoizedRequests/getBookingConfirmation"
|
||||||
@@ -51,6 +52,7 @@ export async function BookingConfirmation({
|
|||||||
bookingCode={booking.bookingCode}
|
bookingCode={booking.bookingCode}
|
||||||
currencyCode={booking.currencyCode}
|
currencyCode={booking.currencyCode}
|
||||||
fromDate={booking.checkInDate}
|
fromDate={booking.checkInDate}
|
||||||
|
hotelOffersBreakfast={hotel.hotelType !== HotelTypeEnum.ScandicGo}
|
||||||
toDate={booking.checkOutDate}
|
toDate={booking.checkOutDate}
|
||||||
roomCategories={roomCategories}
|
roomCategories={roomCategories}
|
||||||
rooms={[
|
rooms={[
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import { useEnterDetailsStore } from "../../../stores/enter-details"
|
|
||||||
import { SidePanel } from "../../SidePanel"
|
import { SidePanel } from "../../SidePanel"
|
||||||
import SummaryUI from "./UI"
|
import SummaryUI from "./UI"
|
||||||
|
|
||||||
@@ -9,30 +8,9 @@ type Props = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function DesktopSummary({ isUserLoggedIn }: Props) {
|
export default function DesktopSummary({ isUserLoggedIn }: Props) {
|
||||||
const toggleSummaryOpen = useEnterDetailsStore(
|
|
||||||
(state) => state.actions.toggleSummaryOpen
|
|
||||||
)
|
|
||||||
|
|
||||||
const { booking, rooms, totalPrice, vat, defaultCurrency } =
|
|
||||||
useEnterDetailsStore((state) => ({
|
|
||||||
booking: state.booking,
|
|
||||||
rooms: state.rooms,
|
|
||||||
totalPrice: state.totalPrice,
|
|
||||||
vat: state.vat,
|
|
||||||
defaultCurrency: state.defaultCurrency,
|
|
||||||
}))
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SidePanel variant="summary">
|
<SidePanel variant="summary">
|
||||||
<SummaryUI
|
<SummaryUI isUserLoggedIn={isUserLoggedIn} />
|
||||||
booking={booking}
|
|
||||||
rooms={rooms}
|
|
||||||
isUserLoggedIn={isUserLoggedIn}
|
|
||||||
totalPrice={totalPrice}
|
|
||||||
vat={vat}
|
|
||||||
toggleSummaryOpen={toggleSummaryOpen}
|
|
||||||
defaultCurrency={defaultCurrency}
|
|
||||||
/>
|
|
||||||
</SidePanel>
|
</SidePanel>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,22 +11,14 @@ type Props = {
|
|||||||
isUserLoggedIn: boolean
|
isUserLoggedIn: boolean
|
||||||
}
|
}
|
||||||
export default function MobileSummary({ isUserLoggedIn }: Props) {
|
export default function MobileSummary({ isUserLoggedIn }: Props) {
|
||||||
const { isSummaryOpen, toggleSummaryOpen } = useEnterDetailsStore(
|
const { rooms, isSummaryOpen, toggleSummaryOpen } = useEnterDetailsStore(
|
||||||
(state) => ({
|
(state) => ({
|
||||||
|
rooms: state.rooms,
|
||||||
isSummaryOpen: state.isSummaryOpen,
|
isSummaryOpen: state.isSummaryOpen,
|
||||||
toggleSummaryOpen: state.actions.toggleSummaryOpen,
|
toggleSummaryOpen: state.actions.toggleSummaryOpen,
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
const { booking, rooms, totalPrice, vat, defaultCurrency } =
|
|
||||||
useEnterDetailsStore((state) => ({
|
|
||||||
booking: state.booking,
|
|
||||||
rooms: state.rooms,
|
|
||||||
totalPrice: state.totalPrice,
|
|
||||||
vat: state.vat,
|
|
||||||
defaultCurrency: state.defaultCurrency,
|
|
||||||
}))
|
|
||||||
|
|
||||||
const showPromo =
|
const showPromo =
|
||||||
!isUserLoggedIn &&
|
!isUserLoggedIn &&
|
||||||
rooms.length === 1 &&
|
rooms.length === 1 &&
|
||||||
@@ -52,15 +44,7 @@ export default function MobileSummary({ isUserLoggedIn }: Props) {
|
|||||||
|
|
||||||
<SummaryBottomSheet isUserLoggedIn={isUserLoggedIn}>
|
<SummaryBottomSheet isUserLoggedIn={isUserLoggedIn}>
|
||||||
<div className={styles.wrapper}>
|
<div className={styles.wrapper}>
|
||||||
<SummaryUI
|
<SummaryUI isUserLoggedIn={isUserLoggedIn} />
|
||||||
booking={booking}
|
|
||||||
rooms={rooms}
|
|
||||||
isUserLoggedIn={isUserLoggedIn}
|
|
||||||
totalPrice={totalPrice}
|
|
||||||
vat={vat}
|
|
||||||
toggleSummaryOpen={toggleSummaryOpen}
|
|
||||||
defaultCurrency={defaultCurrency}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</SummaryBottomSheet>
|
</SummaryBottomSheet>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,13 +0,0 @@
|
|||||||
.entry {
|
|
||||||
display: flex;
|
|
||||||
gap: var(--Space-x05);
|
|
||||||
justify-content: space-between;
|
|
||||||
}
|
|
||||||
|
|
||||||
.textDefault {
|
|
||||||
color: var(--Text-Default);
|
|
||||||
}
|
|
||||||
|
|
||||||
.uiTextMediumContrast {
|
|
||||||
color: var(--UI-Text-Medium-contrast);
|
|
||||||
}
|
|
||||||
@@ -1,79 +0,0 @@
|
|||||||
"use client"
|
|
||||||
import { useIntl } from "react-intl"
|
|
||||||
|
|
||||||
import { formatPrice } from "@scandic-hotels/common/utils/numberFormatting"
|
|
||||||
import { Typography } from "@scandic-hotels/design-system/Typography"
|
|
||||||
|
|
||||||
import styles from "./breakfast.module.css"
|
|
||||||
|
|
||||||
import type { BreakfastPackage } from "@scandic-hotels/trpc/routers/hotels/schemas/packages"
|
|
||||||
|
|
||||||
interface BreakfastProps {
|
|
||||||
adults: number
|
|
||||||
breakfast: BreakfastPackage | false | undefined
|
|
||||||
breakfastIncluded: boolean
|
|
||||||
guests: string
|
|
||||||
nights: number
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function Breakfast({
|
|
||||||
adults,
|
|
||||||
breakfast,
|
|
||||||
breakfastIncluded,
|
|
||||||
guests,
|
|
||||||
nights,
|
|
||||||
}: BreakfastProps) {
|
|
||||||
const intl = useIntl()
|
|
||||||
|
|
||||||
const breakfastBuffet = intl.formatMessage({
|
|
||||||
id: "common.breakfastBuffet",
|
|
||||||
defaultMessage: "Breakfast buffet",
|
|
||||||
})
|
|
||||||
|
|
||||||
if (breakfastIncluded || breakfast) {
|
|
||||||
const price = breakfast
|
|
||||||
? formatPrice(
|
|
||||||
intl,
|
|
||||||
breakfast.localPrice.price * adults * nights,
|
|
||||||
breakfast.localPrice.currency
|
|
||||||
)
|
|
||||||
: intl.formatMessage({
|
|
||||||
id: "common.included",
|
|
||||||
defaultMessage: "Included",
|
|
||||||
})
|
|
||||||
return (
|
|
||||||
<div className={styles.entry}>
|
|
||||||
<div>
|
|
||||||
<Typography variant="Body/Paragraph/mdRegular">
|
|
||||||
<p className={styles.textDefault}>{breakfastBuffet}</p>
|
|
||||||
</Typography>
|
|
||||||
<Typography variant="Body/Supporting text (caption)/smRegular">
|
|
||||||
<p className={styles.uiTextMediumContrast}>{guests}</p>
|
|
||||||
</Typography>
|
|
||||||
</div>
|
|
||||||
<Typography variant="Body/Paragraph/mdRegular">
|
|
||||||
<p className={styles.textDefault}>{price}</p>
|
|
||||||
</Typography>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (breakfast === false) {
|
|
||||||
const noBreakfast = intl.formatMessage({
|
|
||||||
id: "common.noBreakfast",
|
|
||||||
defaultMessage: "No breakfast",
|
|
||||||
})
|
|
||||||
return (
|
|
||||||
<div className={styles.entry}>
|
|
||||||
<Typography variant="Body/Paragraph/mdRegular">
|
|
||||||
<p className={styles.textDefault}>{breakfastBuffet}</p>
|
|
||||||
</Typography>
|
|
||||||
<Typography variant="Body/Paragraph/mdRegular">
|
|
||||||
<p className={styles.textDefault}>{noBreakfast}</p>
|
|
||||||
</Typography>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
@@ -12,7 +12,7 @@ import { Typography } from "@scandic-hotels/design-system/Typography"
|
|||||||
import { ChildBedMapEnum } from "@scandic-hotels/trpc/enums/childBedMapEnum"
|
import { ChildBedMapEnum } from "@scandic-hotels/trpc/enums/childBedMapEnum"
|
||||||
|
|
||||||
import { getRoomFeatureDescription } from "../../../../../utils/getRoomFeatureDescription"
|
import { getRoomFeatureDescription } from "../../../../../utils/getRoomFeatureDescription"
|
||||||
import Breakfast from "./Breakfast"
|
import { SummaryBreakfast } from "../../../../SummaryBreakfast"
|
||||||
|
|
||||||
import styles from "./room.module.css"
|
import styles from "./room.module.css"
|
||||||
|
|
||||||
@@ -26,6 +26,7 @@ interface RoomProps {
|
|||||||
nightsCount: number
|
nightsCount: number
|
||||||
defaultCurrency: CurrencyEnum
|
defaultCurrency: CurrencyEnum
|
||||||
showBookingCodeChip?: boolean
|
showBookingCodeChip?: boolean
|
||||||
|
hotelOffersBreakfast?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function Room({
|
export default function Room({
|
||||||
@@ -36,6 +37,7 @@ export default function Room({
|
|||||||
nightsCount,
|
nightsCount,
|
||||||
defaultCurrency,
|
defaultCurrency,
|
||||||
showBookingCodeChip = false,
|
showBookingCodeChip = false,
|
||||||
|
hotelOffersBreakfast = true,
|
||||||
}: RoomProps) {
|
}: RoomProps) {
|
||||||
const intl = useIntl()
|
const intl = useIntl()
|
||||||
const adults = room.adults
|
const adults = room.adults
|
||||||
@@ -132,6 +134,13 @@ export default function Room({
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const breakfastPrice = room.breakfast
|
||||||
|
? room.breakfast?.localPrice.price * adults * nightsCount
|
||||||
|
: room.breakfast
|
||||||
|
const breakfastCurrency = room.breakfast
|
||||||
|
? room.breakfast?.localPrice.currency
|
||||||
|
: null
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className={styles.room} data-testid={`summary-room-${roomNumber}`}>
|
<div className={styles.room} data-testid={`summary-room-${roomNumber}`}>
|
||||||
@@ -340,12 +349,12 @@ export default function Room({
|
|||||||
</Typography>
|
</Typography>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
<Breakfast
|
<SummaryBreakfast
|
||||||
adults={room.adults}
|
|
||||||
breakfast={room.breakfast}
|
|
||||||
breakfastIncluded={room.breakfastIncluded}
|
breakfastIncluded={room.breakfastIncluded}
|
||||||
|
breakfastPrice={breakfastPrice}
|
||||||
|
breakfastCurrency={breakfastCurrency}
|
||||||
guests={guests}
|
guests={guests}
|
||||||
nights={nightsCount}
|
hotelOffersBreakfast={hotelOffersBreakfast}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{showBookingCodeChip && (
|
{showBookingCodeChip && (
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import Subtitle from "@scandic-hotels/design-system/Subtitle"
|
|||||||
import { Typography } from "@scandic-hotels/design-system/Typography"
|
import { Typography } from "@scandic-hotels/design-system/Typography"
|
||||||
|
|
||||||
import useLang from "../../../../hooks/useLang"
|
import useLang from "../../../../hooks/useLang"
|
||||||
|
import { useEnterDetailsStore } from "../../../../stores/enter-details"
|
||||||
import PriceDetailsModal from "../../../PriceDetailsModal"
|
import PriceDetailsModal from "../../../PriceDetailsModal"
|
||||||
import { isBookingCodeRate } from "../../../SelectRate/RoomsContainer/RateSummary/utils"
|
import { isBookingCodeRate } from "../../../SelectRate/RoomsContainer/RateSummary/utils"
|
||||||
import SignupPromoDesktop from "../../../SignupPromo/Desktop"
|
import SignupPromoDesktop from "../../../SignupPromo/Desktop"
|
||||||
@@ -25,33 +26,33 @@ import { getMemberPrice } from "./utils"
|
|||||||
|
|
||||||
import styles from "./ui.module.css"
|
import styles from "./ui.module.css"
|
||||||
|
|
||||||
import type { RoomState } from "../../../../stores/enter-details/types"
|
|
||||||
import type { Price } from "../../../../types/price"
|
|
||||||
import type { DetailsBooking } from "../../../../utils/url"
|
|
||||||
|
|
||||||
type EnterDetailsSummaryProps = {
|
|
||||||
booking: DetailsBooking
|
|
||||||
isUserLoggedIn: boolean
|
|
||||||
totalPrice: Price
|
|
||||||
vat: number
|
|
||||||
rooms: RoomState[]
|
|
||||||
toggleSummaryOpen: () => void
|
|
||||||
defaultCurrency: CurrencyEnum
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function SummaryUI({
|
export default function SummaryUI({
|
||||||
booking,
|
|
||||||
rooms,
|
|
||||||
totalPrice,
|
|
||||||
isUserLoggedIn,
|
isUserLoggedIn,
|
||||||
vat,
|
}: {
|
||||||
toggleSummaryOpen,
|
isUserLoggedIn: boolean
|
||||||
defaultCurrency,
|
}) {
|
||||||
}: EnterDetailsSummaryProps) {
|
|
||||||
const intl = useIntl()
|
const intl = useIntl()
|
||||||
const lang = useLang()
|
const lang = useLang()
|
||||||
const isDesktop = useMediaQuery("(min-width: 1367px)")
|
const isDesktop = useMediaQuery("(min-width: 1367px)")
|
||||||
|
|
||||||
|
const {
|
||||||
|
booking,
|
||||||
|
defaultCurrency,
|
||||||
|
hotelOffersBreakfast,
|
||||||
|
rooms,
|
||||||
|
totalPrice,
|
||||||
|
vat,
|
||||||
|
toggleSummaryOpen,
|
||||||
|
} = useEnterDetailsStore((state) => ({
|
||||||
|
booking: state.booking,
|
||||||
|
defaultCurrency: state.defaultCurrency,
|
||||||
|
hotelOffersBreakfast: state.hotelOffersBreakfast,
|
||||||
|
rooms: state.rooms,
|
||||||
|
totalPrice: state.totalPrice,
|
||||||
|
vat: state.vat,
|
||||||
|
toggleSummaryOpen: state.actions.toggleSummaryOpen,
|
||||||
|
}))
|
||||||
|
|
||||||
const nights = dt(booking.toDate).diff(booking.fromDate, "days")
|
const nights = dt(booking.toDate).diff(booking.fromDate, "days")
|
||||||
|
|
||||||
const nightsMsg = intl.formatMessage(
|
const nightsMsg = intl.formatMessage(
|
||||||
@@ -143,6 +144,7 @@ export default function SummaryUI({
|
|||||||
(room.roomRate.rateDefinition.isCampaignRate ||
|
(room.roomRate.rateDefinition.isCampaignRate ||
|
||||||
!!room.roomRate.bookingCode)
|
!!room.roomRate.bookingCode)
|
||||||
}
|
}
|
||||||
|
hotelOffersBreakfast={hotelOffersBreakfast}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
@@ -231,6 +233,7 @@ export default function SummaryUI({
|
|||||||
totalPrice={totalPrice}
|
totalPrice={totalPrice}
|
||||||
vat={vat}
|
vat={vat}
|
||||||
isCampaignRate={!!containsCampaignRate}
|
isCampaignRate={!!containsCampaignRate}
|
||||||
|
hotelOffersBreakfast={hotelOffersBreakfast}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ interface BreakfastProps {
|
|||||||
childrenInRoom: Child[] | undefined
|
childrenInRoom: Child[] | undefined
|
||||||
currency: string
|
currency: string
|
||||||
nights: number
|
nights: number
|
||||||
|
hotelOffersBreakfast?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function Breakfast({
|
export default function Breakfast({
|
||||||
@@ -28,6 +29,7 @@ export default function Breakfast({
|
|||||||
childrenInRoom = [],
|
childrenInRoom = [],
|
||||||
currency,
|
currency,
|
||||||
nights,
|
nights,
|
||||||
|
hotelOffersBreakfast = true,
|
||||||
}: BreakfastProps) {
|
}: BreakfastProps) {
|
||||||
const intl = useIntl()
|
const intl = useIntl()
|
||||||
|
|
||||||
@@ -194,7 +196,10 @@ export default function Breakfast({
|
|||||||
})
|
})
|
||||||
return (
|
return (
|
||||||
<Tbody>
|
<Tbody>
|
||||||
<BoldRow label={breakfastBuffet} value={noBreakfast} />
|
<BoldRow
|
||||||
|
label={hotelOffersBreakfast ? breakfastBuffet : undefined}
|
||||||
|
value={noBreakfast}
|
||||||
|
/>
|
||||||
</Tbody>
|
</Tbody>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { Typography } from "@scandic-hotels/design-system/Typography"
|
|||||||
import styles from "./row.module.css"
|
import styles from "./row.module.css"
|
||||||
|
|
||||||
interface RowProps {
|
interface RowProps {
|
||||||
label: string
|
label?: string
|
||||||
value: string
|
value: string
|
||||||
regularValue?: string
|
regularValue?: string
|
||||||
isDiscounted?: boolean
|
isDiscounted?: boolean
|
||||||
@@ -19,11 +19,13 @@ export default function BoldRow({
|
|||||||
}: RowProps) {
|
}: RowProps) {
|
||||||
return (
|
return (
|
||||||
<tr className={styles.row}>
|
<tr className={styles.row}>
|
||||||
<td>
|
{label ? (
|
||||||
<Typography variant="Body/Supporting text (caption)/smBold">
|
<td>
|
||||||
<span>{label}</span>
|
<Typography variant="Body/Supporting text (caption)/smBold">
|
||||||
</Typography>
|
<span>{label}</span>
|
||||||
</td>
|
</Typography>
|
||||||
|
</td>
|
||||||
|
) : null}
|
||||||
<td className={styles.price}>
|
<td className={styles.price}>
|
||||||
{isDiscounted && regularValue ? (
|
{isDiscounted && regularValue ? (
|
||||||
<Typography variant="Body/Supporting text (caption)/smRegular">
|
<Typography variant="Body/Supporting text (caption)/smRegular">
|
||||||
|
|||||||
@@ -66,6 +66,7 @@ export interface PriceDetailsTableProps {
|
|||||||
totalPrice: Price
|
totalPrice: Price
|
||||||
vat: number
|
vat: number
|
||||||
defaultCurrency: CurrencyEnum
|
defaultCurrency: CurrencyEnum
|
||||||
|
hotelOffersBreakfast?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function PriceDetailsTable({
|
export default function PriceDetailsTable({
|
||||||
@@ -77,6 +78,7 @@ export default function PriceDetailsTable({
|
|||||||
totalPrice,
|
totalPrice,
|
||||||
vat,
|
vat,
|
||||||
defaultCurrency,
|
defaultCurrency,
|
||||||
|
hotelOffersBreakfast,
|
||||||
}: PriceDetailsTableProps) {
|
}: PriceDetailsTableProps) {
|
||||||
const intl = useIntl()
|
const intl = useIntl()
|
||||||
const lang = useLang()
|
const lang = useLang()
|
||||||
@@ -217,6 +219,7 @@ export default function PriceDetailsTable({
|
|||||||
childrenInRoom={room.childrenInRoom}
|
childrenInRoom={room.childrenInRoom}
|
||||||
currency={currency}
|
currency={currency}
|
||||||
nights={nights}
|
nights={nights}
|
||||||
|
hotelOffersBreakfast={hotelOffersBreakfast}
|
||||||
/>
|
/>
|
||||||
{rooms.length !== 1 &&
|
{rooms.length !== 1 &&
|
||||||
(room.rateDefinition.isCampaignRate || !!bookingCode) && (
|
(room.rateDefinition.isCampaignRate || !!bookingCode) && (
|
||||||
|
|||||||
@@ -4,20 +4,22 @@ import { useIntl } from "react-intl"
|
|||||||
import { formatPrice } from "@scandic-hotels/common/utils/numberFormatting"
|
import { formatPrice } from "@scandic-hotels/common/utils/numberFormatting"
|
||||||
import { Typography } from "@scandic-hotels/design-system/Typography"
|
import { Typography } from "@scandic-hotels/design-system/Typography"
|
||||||
|
|
||||||
import styles from "./breakfast.module.css"
|
import styles from "./summaryBreakfast.module.css"
|
||||||
|
|
||||||
import type { PackageSchema } from "@scandic-hotels/trpc/types/bookingConfirmation"
|
|
||||||
|
|
||||||
interface BreakfastProps {
|
interface BreakfastProps {
|
||||||
breakfast: PackageSchema | false | undefined
|
breakfastPrice: number | false | undefined
|
||||||
|
breakfastCurrency?: string | null
|
||||||
breakfastIncluded: boolean
|
breakfastIncluded: boolean
|
||||||
guests: string
|
guests: string
|
||||||
|
hotelOffersBreakfast: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function Breakfast({
|
export function SummaryBreakfast({
|
||||||
breakfast,
|
breakfastPrice,
|
||||||
|
breakfastCurrency,
|
||||||
breakfastIncluded,
|
breakfastIncluded,
|
||||||
guests,
|
guests,
|
||||||
|
hotelOffersBreakfast = true,
|
||||||
}: BreakfastProps) {
|
}: BreakfastProps) {
|
||||||
const intl = useIntl()
|
const intl = useIntl()
|
||||||
|
|
||||||
@@ -26,13 +28,14 @@ export default function Breakfast({
|
|||||||
defaultMessage: "Breakfast buffet",
|
defaultMessage: "Breakfast buffet",
|
||||||
})
|
})
|
||||||
|
|
||||||
if (breakfastIncluded || breakfast) {
|
if (breakfastIncluded || (breakfastPrice && breakfastCurrency)) {
|
||||||
const price = breakfast
|
const price =
|
||||||
? formatPrice(intl, breakfast.totalPrice, breakfast.currency)
|
breakfastPrice && breakfastCurrency
|
||||||
: intl.formatMessage({
|
? formatPrice(intl, breakfastPrice, breakfastCurrency)
|
||||||
id: "common.included",
|
: intl.formatMessage({
|
||||||
defaultMessage: "Included",
|
id: "common.included",
|
||||||
})
|
defaultMessage: "Included",
|
||||||
|
})
|
||||||
return (
|
return (
|
||||||
<div className={styles.entry}>
|
<div className={styles.entry}>
|
||||||
<div>
|
<div>
|
||||||
@@ -50,20 +53,18 @@ export default function Breakfast({
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (breakfast === false) {
|
if (breakfastPrice === false) {
|
||||||
const noBreakfast = intl.formatMessage({
|
const noBreakfast = intl.formatMessage({
|
||||||
id: "common.noBreakfast",
|
id: "common.noBreakfast",
|
||||||
defaultMessage: "No breakfast",
|
defaultMessage: "No breakfast",
|
||||||
})
|
})
|
||||||
return (
|
return (
|
||||||
<div className={styles.entry}>
|
<Typography variant="Body/Paragraph/mdRegular">
|
||||||
<Typography variant="Body/Paragraph/mdRegular">
|
<div className={`${styles.entry} ${styles.textDefault}`}>
|
||||||
<p className={styles.textDefault}>{breakfastBuffet}</p>
|
{hotelOffersBreakfast ? <p>{breakfastBuffet}</p> : null}
|
||||||
</Typography>
|
<p>{noBreakfast}</p>
|
||||||
<Typography variant="Body/Paragraph/mdRegular">
|
</div>
|
||||||
<p className={styles.textDefault}>{noBreakfast}</p>
|
</Typography>
|
||||||
</Typography>
|
|
||||||
</div>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
.entry {
|
.entry {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
gap: var(--Space-x05);
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -36,6 +36,7 @@ export const EnterDetailsContext = createContext<EnterDetailsStore | null>(null)
|
|||||||
type DetailsProviderProps = React.PropsWithChildren & {
|
type DetailsProviderProps = React.PropsWithChildren & {
|
||||||
booking: DetailsBooking
|
booking: DetailsBooking
|
||||||
breakfastPackages: BreakfastPackages
|
breakfastPackages: BreakfastPackages
|
||||||
|
hotelOffersBreakfast: boolean
|
||||||
lang: Lang
|
lang: Lang
|
||||||
rooms: Room[]
|
rooms: Room[]
|
||||||
searchParamsStr: string
|
searchParamsStr: string
|
||||||
@@ -49,6 +50,7 @@ export default function EnterDetailsProvider({
|
|||||||
booking,
|
booking,
|
||||||
breakfastPackages,
|
breakfastPackages,
|
||||||
children,
|
children,
|
||||||
|
hotelOffersBreakfast,
|
||||||
lang,
|
lang,
|
||||||
rooms,
|
rooms,
|
||||||
searchParamsStr,
|
searchParamsStr,
|
||||||
@@ -67,6 +69,7 @@ export default function EnterDetailsProvider({
|
|||||||
if (!storeRef.current) {
|
if (!storeRef.current) {
|
||||||
const initialData: InitialState = {
|
const initialData: InitialState = {
|
||||||
booking,
|
booking,
|
||||||
|
hotelOffersBreakfast,
|
||||||
rooms: rooms
|
rooms: rooms
|
||||||
.filter((r) => r.bedTypes?.length) // TODO: how to handle room without bedtypes?
|
.filter((r) => r.bedTypes?.length) // TODO: how to handle room without bedtypes?
|
||||||
.map((room) => ({
|
.map((room) => ({
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { Suspense } from "react"
|
|||||||
|
|
||||||
import { FamilyAndFriendsCodes } from "@scandic-hotels/common/constants/familyAndFriends"
|
import { FamilyAndFriendsCodes } from "@scandic-hotels/common/constants/familyAndFriends"
|
||||||
import { SEARCH_TYPE_REDEMPTION } from "@scandic-hotels/trpc/constants/booking"
|
import { SEARCH_TYPE_REDEMPTION } from "@scandic-hotels/trpc/constants/booking"
|
||||||
|
import { HotelTypeEnum } from "@scandic-hotels/trpc/enums/hotelType"
|
||||||
|
|
||||||
import { BookingFlowConfig } from "../bookingFlowConfig/bookingFlowConfig"
|
import { BookingFlowConfig } from "../bookingFlowConfig/bookingFlowConfig"
|
||||||
import HotelHeader from "../components/EnterDetails/Header"
|
import HotelHeader from "../components/EnterDetails/Header"
|
||||||
@@ -137,6 +138,7 @@ export async function EnterDetailsPage({
|
|||||||
rooms={rooms}
|
rooms={rooms}
|
||||||
searchParamsStr={selectRoomParams.toString()}
|
searchParamsStr={selectRoomParams.toString()}
|
||||||
user={user}
|
user={user}
|
||||||
|
hotelOffersBreakfast={hotel.hotelType !== HotelTypeEnum.ScandicGo}
|
||||||
hotelName={hotel.name}
|
hotelName={hotel.name}
|
||||||
vat={hotel.vat}
|
vat={hotel.vat}
|
||||||
roomCategories={hotelData.roomCategories}
|
roomCategories={hotelData.roomCategories}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ export function BookingConfirmationProvider({
|
|||||||
children,
|
children,
|
||||||
currencyCode,
|
currencyCode,
|
||||||
fromDate,
|
fromDate,
|
||||||
|
hotelOffersBreakfast,
|
||||||
toDate,
|
toDate,
|
||||||
roomCategories,
|
roomCategories,
|
||||||
rooms,
|
rooms,
|
||||||
@@ -94,6 +95,7 @@ export function BookingConfirmationProvider({
|
|||||||
rooms,
|
rooms,
|
||||||
vat,
|
vat,
|
||||||
isVatCurrency,
|
isVatCurrency,
|
||||||
|
hotelOffersBreakfast,
|
||||||
formattedTotalCost,
|
formattedTotalCost,
|
||||||
totalBookingPrice,
|
totalBookingPrice,
|
||||||
totalBookingCheques,
|
totalBookingCheques,
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ export function createBookingConfirmationStore(initialState: InitialState) {
|
|||||||
roomCategories: initialState.roomCategories,
|
roomCategories: initialState.roomCategories,
|
||||||
vat: initialState.vat,
|
vat: initialState.vat,
|
||||||
formattedTotalCost: initialState.formattedTotalCost,
|
formattedTotalCost: initialState.formattedTotalCost,
|
||||||
|
hotelOffersBreakfast: initialState.hotelOffersBreakfast,
|
||||||
isVatCurrency: initialState.isVatCurrency,
|
isVatCurrency: initialState.isVatCurrency,
|
||||||
totalBookingPrice: initialState.totalBookingPrice,
|
totalBookingPrice: initialState.totalBookingPrice,
|
||||||
totalBookingCheques: initialState.totalBookingCheques,
|
totalBookingCheques: initialState.totalBookingCheques,
|
||||||
|
|||||||
@@ -107,6 +107,7 @@ export function createDetailsStore(
|
|||||||
canProceedToPayment: false,
|
canProceedToPayment: false,
|
||||||
isSubmitting: false,
|
isSubmitting: false,
|
||||||
isSummaryOpen: false,
|
isSummaryOpen: false,
|
||||||
|
hotelOffersBreakfast: initialState.hotelOffersBreakfast,
|
||||||
lastRoom: initialState.booking.rooms.length - 1,
|
lastRoom: initialState.booking.rooms.length - 1,
|
||||||
rooms: initialRooms.map((room, idx) => {
|
rooms: initialRooms.map((room, idx) => {
|
||||||
const steps: RoomState["steps"] = {
|
const steps: RoomState["steps"] = {
|
||||||
|
|||||||
@@ -85,6 +85,7 @@ export interface RoomState {
|
|||||||
|
|
||||||
export type InitialState = {
|
export type InitialState = {
|
||||||
booking: DetailsBooking
|
booking: DetailsBooking
|
||||||
|
hotelOffersBreakfast: boolean
|
||||||
rooms: InitialRoomData[]
|
rooms: InitialRoomData[]
|
||||||
vat: number
|
vat: number
|
||||||
hotelName: string
|
hotelName: string
|
||||||
@@ -106,6 +107,7 @@ export interface DetailsState {
|
|||||||
booking: DetailsBooking
|
booking: DetailsBooking
|
||||||
breakfastPackages: BreakfastPackages
|
breakfastPackages: BreakfastPackages
|
||||||
canProceedToPayment: boolean
|
canProceedToPayment: boolean
|
||||||
|
hotelOffersBreakfast: boolean
|
||||||
isSubmitting: boolean
|
isSubmitting: boolean
|
||||||
isSummaryOpen: boolean
|
isSummaryOpen: boolean
|
||||||
lastRoom: number
|
lastRoom: number
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ export interface BookingConfirmationProviderProps
|
|||||||
bookingCode: string | null
|
bookingCode: string | null
|
||||||
currencyCode: CurrencyEnum
|
currencyCode: CurrencyEnum
|
||||||
fromDate: string
|
fromDate: string
|
||||||
|
hotelOffersBreakfast: boolean
|
||||||
roomCategories: RoomCategories
|
roomCategories: RoomCategories
|
||||||
rooms: (Room | null)[]
|
rooms: (Room | null)[]
|
||||||
toDate: string
|
toDate: string
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ export interface InitialState {
|
|||||||
roomCategories: RoomCategories
|
roomCategories: RoomCategories
|
||||||
vat: number
|
vat: number
|
||||||
isVatCurrency: boolean
|
isVatCurrency: boolean
|
||||||
|
hotelOffersBreakfast: boolean
|
||||||
formattedTotalCost: string
|
formattedTotalCost: string
|
||||||
totalBookingPrice: number
|
totalBookingPrice: number
|
||||||
totalBookingCheques: number
|
totalBookingCheques: number
|
||||||
|
|||||||
Reference in New Issue
Block a user