From b7c78a53b5a1b9bce6534a228d6989adbac51a60 Mon Sep 17 00:00:00 2001 From: Simon Emanuelsson Date: Tue, 13 May 2025 11:30:49 +0200 Subject: [PATCH] feat: add support for bought children breakfast package --- .../PriceDetails/index.tsx | 4 +- .../AddAncillaryFlowModal/index.tsx | 28 ++++--- .../MyStay/Ancillaries/index.tsx | 49 +++++------ .../Rooms/SingleRoom/Details/Breakfast.tsx | 14 ++-- .../MyStay/utils/mapRoomDetails.ts | 41 ++++++--- .../PriceDetailsTable/Breakfast.tsx | 74 ++++++++++++++-- .../PriceDetailsTable/index.tsx | 2 + .../utils/convertToChildType.ts | 84 ++++++++++++++++--- apps/scandic-web/types/stores/my-stay.ts | 1 + 9 files changed, 229 insertions(+), 68 deletions(-) diff --git a/apps/scandic-web/components/HotelReservation/MyStay/Ancillaries/AddAncillaryFlow/AddAncillaryFlowModal/PriceDetails/index.tsx b/apps/scandic-web/components/HotelReservation/MyStay/Ancillaries/AddAncillaryFlow/AddAncillaryFlowModal/PriceDetails/index.tsx index cc87b8e2f..c03696851 100644 --- a/apps/scandic-web/components/HotelReservation/MyStay/Ancillaries/AddAncillaryFlow/AddAncillaryFlowModal/PriceDetails/index.tsx +++ b/apps/scandic-web/components/HotelReservation/MyStay/Ancillaries/AddAncillaryFlow/AddAncillaryFlowModal/PriceDetails/index.tsx @@ -77,7 +77,7 @@ export default function PriceDetails({ title: `${selectedAncillary.title} / ${intl.formatMessage({ defaultMessage: "Adult", })}`, - totalPrice: breakfastData.priceAdult * breakfastData.nrOfAdults, + totalPrice: breakfastData.priceAdult, currency: breakfastData.currency, quantityWithCard: breakfastData.nrOfAdults * breakfastData.nrOfNights, }, @@ -88,7 +88,7 @@ export default function PriceDetails({ title: `${selectedAncillary.title} / ${intl.formatMessage({ defaultMessage: "Children", })} 4-12`, - totalPrice: breakfastData.priceChild * breakfastData.nrOfPayingChildren, + totalPrice: breakfastData.priceChild, currency: breakfastData.currency, quantityWithCard: breakfastData.nrOfPayingChildren * breakfastData.nrOfNights, diff --git a/apps/scandic-web/components/HotelReservation/MyStay/Ancillaries/AddAncillaryFlow/AddAncillaryFlowModal/index.tsx b/apps/scandic-web/components/HotelReservation/MyStay/Ancillaries/AddAncillaryFlow/AddAncillaryFlowModal/index.tsx index 9ab835a76..4f4f797fd 100644 --- a/apps/scandic-web/components/HotelReservation/MyStay/Ancillaries/AddAncillaryFlow/AddAncillaryFlowModal/index.tsx +++ b/apps/scandic-web/components/HotelReservation/MyStay/Ancillaries/AddAncillaryFlow/AddAncillaryFlowModal/index.tsx @@ -511,20 +511,28 @@ function calculateBreakfastData( return null } - const [nrOfPayingChildren, nrOfFreeChildren] = childrenAges.reduce( - (acc, curr) => (curr >= 4 ? [acc[0] + 1, acc[1]] : [acc[0], acc[1] + 1]), - [0, 0] + const { nrOfPayingChildren, nrOfFreeChildren } = childrenAges.reduce( + (total, childAge) => { + if (childAge >= 4) { + total.nrOfPayingChildren = total.nrOfPayingChildren + 1 + } else { + total.nrOfFreeChildren = total.nrOfFreeChildren + 1 + } + return total + }, + { nrOfPayingChildren: 0, nrOfFreeChildren: 0 } ) - const priceAdult = packages?.find( + const adultPackage = packages?.find( (p) => p.code === BreakfastPackageEnum.ANCILLARY_REGULAR_BREAKFAST - )?.localPrice.price - const priceChild = packages?.find( + ) + const childPackage = 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 + ) + const priceAdult = adultPackage?.localPrice.price + const priceChild = childPackage?.localPrice.price + const currency = + adultPackage?.localPrice.currency ?? childPackage?.localPrice.currency if ( typeof priceAdult !== "number" || diff --git a/apps/scandic-web/components/HotelReservation/MyStay/Ancillaries/index.tsx b/apps/scandic-web/components/HotelReservation/MyStay/Ancillaries/index.tsx index dce402a32..5e1c6b5d9 100644 --- a/apps/scandic-web/components/HotelReservation/MyStay/Ancillaries/index.tsx +++ b/apps/scandic-web/components/HotelReservation/MyStay/Ancillaries/index.tsx @@ -107,33 +107,34 @@ export function Ancillaries({ return undefined } - const breakfastPackage = packages?.find( + const breakfastPackageAdults = packages?.find( (p) => p.code === BreakfastPackageEnum.ANCILLARY_REGULAR_BREAKFAST ) - const breakfastAncillary: SelectedAncillary | undefined = breakfastPackage - ? { - description: intl.formatMessage({ - defaultMessage: "Buffet", - }), - id: breakfastPackage.code, - title: intl.formatMessage({ - defaultMessage: "Breakfast", - }), - price: { - currency: breakfastPackage.localPrice.currency, - total: breakfastPackage.localPrice.totalPrice, - }, - // TODO: Change this to the correct URL, whatever that is - imageUrl: - "https://images.scandichotels.com/publishedmedia/inyre69evkpzgtygjnvp/Breakfast_-_Scandic_Sweden_-_Free_to_use.jpg", - requiresDeliveryTime: false, - loyaltyCode: undefined, - points: undefined, - hotelId: Number(booking.hotelId), - categoryName: "Food", - } - : undefined + const breakfastAncillary: SelectedAncillary | undefined = + breakfastPackageAdults + ? { + description: intl.formatMessage({ + defaultMessage: "Buffet", + }), + id: breakfastPackageAdults.code, + title: intl.formatMessage({ + defaultMessage: "Breakfast", + }), + price: { + currency: breakfastPackageAdults.localPrice.currency, + total: breakfastPackageAdults.localPrice.totalPrice, + }, + // TODO: Change this to the correct URL, whatever that is + imageUrl: + "https://images.scandichotels.com/publishedmedia/inyre69evkpzgtygjnvp/Breakfast_-_Scandic_Sweden_-_Free_to_use.jpg", + requiresDeliveryTime: false, + loyaltyCode: undefined, + points: undefined, + hotelId: Number(booking.hotelId), + categoryName: "Food", + } + : undefined return breakfastAncillary }, [ diff --git a/apps/scandic-web/components/HotelReservation/MyStay/Rooms/SingleRoom/Details/Breakfast.tsx b/apps/scandic-web/components/HotelReservation/MyStay/Rooms/SingleRoom/Details/Breakfast.tsx index ca34e8e2d..3e05fcb42 100644 --- a/apps/scandic-web/components/HotelReservation/MyStay/Rooms/SingleRoom/Details/Breakfast.tsx +++ b/apps/scandic-web/components/HotelReservation/MyStay/Rooms/SingleRoom/Details/Breakfast.tsx @@ -10,10 +10,13 @@ import Row from "./Row" export default function Breakfast() { const intl = useIntl() - const { breakfast, rateDefinition } = useMyStayStore((state) => ({ - breakfast: state.bookedRoom.breakfast, - rateDefinition: state.bookedRoom.rateDefinition, - })) + const { breakfast, breakfastChildren, rateDefinition } = useMyStayStore( + (state) => ({ + breakfast: state.bookedRoom.breakfast, + breakfastChildren: state.bookedRoom.breakfastChildren, + rateDefinition: state.bookedRoom.rateDefinition, + }) + ) let breakfastPrice = intl.formatMessage({ defaultMessage: "No breakfast", @@ -23,9 +26,10 @@ export default function Breakfast() { defaultMessage: "Included", }) } else if (breakfast) { + const childPrice = breakfastChildren?.localPrice.totalPrice || 0 breakfastPrice = formatPrice( intl, - breakfast.localPrice.totalPrice, + breakfast.localPrice.totalPrice + childPrice, breakfast.localPrice.currency ) } diff --git a/apps/scandic-web/components/HotelReservation/MyStay/utils/mapRoomDetails.ts b/apps/scandic-web/components/HotelReservation/MyStay/utils/mapRoomDetails.ts index 377f3f7be..bfed0cf4c 100644 --- a/apps/scandic-web/components/HotelReservation/MyStay/utils/mapRoomDetails.ts +++ b/apps/scandic-web/components/HotelReservation/MyStay/utils/mapRoomDetails.ts @@ -31,30 +31,50 @@ export function mapRoomDetails({ .startOf("day") .diff(dt(booking.checkInDate).startOf("day"), "days") - const validBreakfastPackages: string[] = [ + const validBreakfastPackagesAdults: string[] = [ BreakfastPackageEnum.REGULAR_BREAKFAST, - BreakfastPackageEnum.ANCILLARY_CHILD_PAYING_BREAKFAST, BreakfastPackageEnum.ANCILLARY_REGULAR_BREAKFAST, ] - const breakfastPackage = booking.packages.find((pkg) => - validBreakfastPackages.includes(pkg.code) + const breakfastPackageAdults = booking.packages.find((pkg) => + validBreakfastPackagesAdults.includes(pkg.code) ) // We don't get `requestedPrice` in packages const breakfast: Omit | null = - breakfastPackage + breakfastPackageAdults ? { - code: breakfastPackage.code, - description: breakfastPackage.description, + code: breakfastPackageAdults.code, + description: breakfastPackageAdults.description, localPrice: { - currency: breakfastPackage.currency, - price: breakfastPackage.unitPrice, - totalPrice: breakfastPackage.totalPrice, + currency: breakfastPackageAdults.currency, + price: breakfastPackageAdults.unitPrice, + totalPrice: breakfastPackageAdults.totalPrice, }, packageType: PackageTypeEnum.BreakfastAdult, } : null + const validBreakfastPackagesChildren: string[] = [ + BreakfastPackageEnum.ANCILLARY_CHILD_PAYING_BREAKFAST, + ] + const breakfastPackageChildren = booking.packages.find((pkg) => + validBreakfastPackagesChildren.includes(pkg.code) + ) + // We don't get `requestedPrice` in packages + const breakfastChildren: Omit | null = + breakfastPackageChildren + ? { + code: breakfastPackageChildren.code, + description: breakfastPackageChildren.description, + localPrice: { + currency: breakfastPackageChildren.currency, + price: breakfastPackageChildren.unitPrice, + totalPrice: breakfastPackageChildren.totalPrice, + }, + packageType: PackageTypeEnum.BreakfastChildren, + } + : null + const isCancelled = booking.reservationStatus === BookingStatusEnum.Cancelled const childrenAsString = formatChildBedPreferences({ @@ -120,6 +140,7 @@ export function mapRoomDetails({ }, bookingCode: booking.bookingCode, breakfast, + breakfastChildren, canChangeDate: booking.canChangeDate, cancellationNumber: booking.cancellationNumber, checkInDate: booking.checkInDate, diff --git a/apps/scandic-web/components/HotelReservation/PriceDetailsModal/PriceDetailsTable/Breakfast.tsx b/apps/scandic-web/components/HotelReservation/PriceDetailsModal/PriceDetailsTable/Breakfast.tsx index 2f4cff893..af72c0293 100644 --- a/apps/scandic-web/components/HotelReservation/PriceDetailsModal/PriceDetailsTable/Breakfast.tsx +++ b/apps/scandic-web/components/HotelReservation/PriceDetailsModal/PriceDetailsTable/Breakfast.tsx @@ -13,6 +13,7 @@ import type { Child } from "@/types/components/hotelReservation/selectRate/selec interface BreakfastProps { adults: number breakfast: Omit | false | undefined | null + breakfastChildren: Omit | null | undefined breakfastIncluded: boolean childrenInRoom: Child[] | undefined currency: string @@ -22,8 +23,9 @@ interface BreakfastProps { export default function Breakfast({ adults, breakfast, + breakfastChildren, breakfastIncluded, - childrenInRoom, + childrenInRoom = [], currency, nights, }: BreakfastProps) { @@ -70,17 +72,56 @@ export default function Breakfast({ return null } + const adultPricePerNight = breakfast.localPrice.price * adults const breakfastAdultsPricePerNight = formatPrice( intl, - breakfast.localPrice.price * adults, + adultPricePerNight, breakfast.localPrice.currency ) - const breakfastAdultsTotalPrice = formatPrice( + + const { payingChildren, freeChildren } = childrenInRoom.reduce( + (total, child) => { + if (child.age >= 4) { + total.payingChildren = total.payingChildren + 1 + } else { + total.freeChildren = total.freeChildren + 1 + } + return total + }, + { payingChildren: 0, freeChildren: 0 } + ) + + const childrenPrice = breakfastChildren?.localPrice.price || 0 + const childrenPricePerNight = childrenPrice * payingChildren + + const childCurrency = + breakfastChildren?.localPrice.currency ?? breakfast.localPrice.currency + + const breakfastChildrenPricePerNight = formatPrice( intl, - breakfast.localPrice.price * adults * nights, + childrenPricePerNight, + childCurrency + ) + + const totalAdultsPrice = adultPricePerNight * nights + const totalChildrenPrice = childrenPricePerNight * nights + const breakfastTotalPrice = formatPrice( + intl, + totalAdultsPrice + totalChildrenPrice, breakfast.localPrice.currency ) + const freeChildrenMsg = intl.formatMessage( + { + defaultMessage: + "Breakfast ({totalChildren, plural, one {# child} other {# children}}) x {totalBreakfasts}", + }, + { + totalChildren: freeChildren, + totalBreakfasts: nights, + } + ) + return ( - {childrenInRoom?.length ? ( + {breakfastChildren ? ( + + ) : null} + {breakfastChildren && freeChildren ? ( + + ) : null} + {childrenInRoom?.length && !breakfastChildren ? ( ) diff --git a/apps/scandic-web/components/HotelReservation/PriceDetailsModal/PriceDetailsTable/index.tsx b/apps/scandic-web/components/HotelReservation/PriceDetailsModal/PriceDetailsTable/index.tsx index daeb64db3..d1b5526d9 100644 --- a/apps/scandic-web/components/HotelReservation/PriceDetailsModal/PriceDetailsTable/index.tsx +++ b/apps/scandic-web/components/HotelReservation/PriceDetailsModal/PriceDetailsTable/index.tsx @@ -43,6 +43,7 @@ export interface Room { adults: number bedType: BedTypeSchema | undefined breakfast: Omit | false | undefined | null + breakfastChildren?: Omit | null breakfastIncluded: boolean childrenInRoom: Child[] | undefined packages: Packages | null @@ -182,6 +183,7 @@ export default function PriceDetailsTable({ = { - Crib: ChildBedMapEnum.IN_CRIB, - ExtraBed: ChildBedMapEnum.IN_EXTRA_BED, - ParentsBed: ChildBedMapEnum.IN_ADULTS_BED, - Unknown: ChildBedMapEnum.UNKNOWN, -} - export function convertToChildType( childrenAges: number[], childBedPreferences: BookingConfirmation["booking"]["childBedPreferences"] ): Child[] { - return childBedPreferences.map((preference, index) => ({ - age: childrenAges[index], - bed: bedTypeToMapEnum[preference.bedType] ?? ChildBedMapEnum.UNKNOWN, - })) + const lookup = childBedPreferences.reduce( + (preferences, preference) => { + preferences[preference.bedType] = preference.quantity + return preferences + }, + { + Crib: 0, + ExtraBed: 0, + ParentsBed: 0, + Unknown: 0, + } + ) + + const adultsAndOrCribKids = childrenAges.filter((childAge) => childAge <= 2) + const adultsAndOrExtraBedKids = childrenAges.filter( + (childAge) => childAge >= 3 && childAge <= 5 + ) + const extraBedKids = childrenAges.filter((childAge) => childAge >= 6) + + const cribKids = adultsAndOrCribKids.map((age) => { + if (lookup.Crib) { + lookup.Crib = lookup.Crib - 1 + return { + age, + bed: ChildBedMapEnum.IN_CRIB, + } + } + + lookup.ParentsBed = lookup.ParentsBed - 1 + return { + age, + bed: ChildBedMapEnum.IN_ADULTS_BED, + } + }) + + const adultOrExtraKids = adultsAndOrExtraBedKids.map((age) => { + if (lookup.ParentsBed) { + lookup.ParentsBed = lookup.ParentsBed - 1 + return { + age, + bed: ChildBedMapEnum.IN_ADULTS_BED, + } + } + + if (lookup.ExtraBed) { + lookup.ExtraBed = lookup.ExtraBed - 1 + return { + age, + bed: ChildBedMapEnum.IN_EXTRA_BED, + } + } + + return { + age, + bed: ChildBedMapEnum.UNKNOWN, + } + }) + + const extraKids = extraBedKids.map((age) => { + if (lookup.ExtraBed) { + lookup.ExtraBed = lookup.ExtraBed - 1 + return { + age, + bed: ChildBedMapEnum.IN_EXTRA_BED, + } + } + return { + age, + bed: ChildBedMapEnum.UNKNOWN, + } + }) + + return [...cribKids, ...adultOrExtraKids, ...extraKids] } diff --git a/apps/scandic-web/types/stores/my-stay.ts b/apps/scandic-web/types/stores/my-stay.ts index 1f527a85e..742f04cd6 100644 --- a/apps/scandic-web/types/stores/my-stay.ts +++ b/apps/scandic-web/types/stores/my-stay.ts @@ -43,6 +43,7 @@ export type Room = Pick< > & { bedType: BedTypeSchema breakfast: Omit | null + breakfastChildren: Omit | null childrenAsString: string childrenInRoom: Child[] isCancelled: boolean