@roomstay/frontend 2.6.113 → 2.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/dist/177.bundle.js +1 -1
  2. package/dist/279.bundle.js +1 -1
  3. package/dist/288.bundle.js +1 -1
  4. package/dist/370.bundle.js +1 -1
  5. package/dist/422.bundle.js +1 -1
  6. package/dist/449.bundle.js +1 -1
  7. package/dist/537.bundle.js +1 -1
  8. package/dist/836.bundle.js +1 -1
  9. package/dist/873.bundle.js +1 -1
  10. package/dist/972.bundle.js +1 -1
  11. package/dist/978.bundle.js +1 -1
  12. package/dist/main.bundle.js +1 -1
  13. package/dist/src/components/generic/BookingWizard/BookingWizardContext.d.ts +2 -13
  14. package/dist/src/components/generic/BookingWizard/BookingWizardContext.js.map +1 -1
  15. package/dist/src/components/steps/confirmation/StepConfirmationForm.js +3 -2
  16. package/dist/src/components/steps/confirmation/StepConfirmationForm.js.map +1 -1
  17. package/dist/src/components/steps/room/AvailableUpgradesModal.js +20 -11
  18. package/dist/src/components/steps/room/AvailableUpgradesModal.js.map +1 -1
  19. package/dist/src/components/steps/room/roomFilter/RoomFilterRoomType.js +14 -3
  20. package/dist/src/components/steps/room/roomFilter/RoomFilterRoomType.js.map +1 -1
  21. package/dist/src/contexts/BasketContext/BasketContextWrapper.js +42 -22
  22. package/dist/src/contexts/BasketContext/BasketContextWrapper.js.map +1 -1
  23. package/dist/src/contexts/FullPageEngineContext/FullPageEngineContextWrapper.js +19 -8
  24. package/dist/src/contexts/FullPageEngineContext/FullPageEngineContextWrapper.js.map +1 -1
  25. package/dist/src/contexts/HotelOverridesContext.js +7 -3
  26. package/dist/src/contexts/HotelOverridesContext.js.map +1 -1
  27. package/dist/src/engines/BaseEngine.d.ts +17 -0
  28. package/dist/src/engines/BaseEngine.js.map +1 -1
  29. package/dist/src/engines/BookingWizardEngine/BookingWizardEngine.d.ts +1 -5
  30. package/dist/src/engines/BookingWizardEngine/BookingWizardEngine.js.map +1 -1
  31. package/dist/src/index.d.ts +1 -0
  32. package/dist/src/index.js +3 -1
  33. package/dist/src/index.js.map +1 -1
  34. package/dist/src/models/Api/HotelOverrideDTO.d.ts +1 -0
  35. package/dist/src/models/Api/HotelOverrideDTO.js.map +1 -1
  36. package/dist/src/models/Room/Room.d.ts +2 -0
  37. package/dist/src/models/Room/Room.js.map +1 -1
  38. package/dist/src/pages/steps/StepDate/StepDateComponent.js +10 -4
  39. package/dist/src/pages/steps/StepDate/StepDateComponent.js.map +1 -1
  40. package/dist/src/providers/FeatureProvider.js +2 -0
  41. package/dist/src/providers/FeatureProvider.js.map +1 -1
  42. package/dist/src/providers/feature/StepDateNextButtonAboveDatepickerFeature.d.ts +6 -0
  43. package/dist/src/providers/feature/StepDateNextButtonAboveDatepickerFeature.js +10 -0
  44. package/dist/src/providers/feature/StepDateNextButtonAboveDatepickerFeature.js.map +1 -0
  45. package/dist/src/util/SyncBasketAddonRowQuantities.d.ts +11 -0
  46. package/dist/src/util/SyncBasketAddonRowQuantities.js +44 -0
  47. package/dist/src/util/SyncBasketAddonRowQuantities.js.map +1 -0
  48. package/dist/test.bundle.js +1 -1
  49. package/dist/vendors.bundle.js +1 -1
  50. package/package.json +2 -2
@@ -1,23 +1,12 @@
1
1
  import type { Placement } from '@popperjs/core';
2
- import dayjs from 'dayjs';
2
+ import { BookingEngineOnBooking } from '../../../engines/BaseEngine';
3
3
  import type ColorProfile from '../../../models/Client/Hotel/ColorProfile';
4
4
  import { Hotel } from '../../../models/Client/Hotel/Hotel';
5
5
  import { EBookingWizardSection, EBookingWizardTheme } from './BookingWizard';
6
6
  export type BookingWizardLayout = 'vertical' | 'horizontal' | 'button' | 'horizontal-condensed' | 'booking-summary';
7
7
  export type BookingWizardOverlayType = 'popup' | 'inline' | 'bottom-sheet' | 'top-sheet';
8
8
  export type BookingWizardTheme = EBookingWizardTheme;
9
- export interface BookingWizardOnBooking {
10
- id: string;
11
- checkIn: dayjs.Dayjs;
12
- checkout: dayjs.Dayjs;
13
- promoCode?: string;
14
- guests: {
15
- adults: number;
16
- children: number;
17
- infants: number;
18
- room: number;
19
- };
20
- }
9
+ export type BookingWizardOnBooking = BookingEngineOnBooking;
21
10
  export interface BookingWizardContextType extends Pick<Hotel, 'startsWeekOnDay'> {
22
11
  layout: BookingWizardLayout;
23
12
  type: BookingWizardOverlayType;
@@ -1 +1 @@
1
- {"version":3,"file":"BookingWizardContext.js","sourceRoot":"/","sources":["src/components/generic/BookingWizard/BookingWizardContext.ts"],"names":[],"mappings":";;;AAEA,iCAAkD;AA0DrC,QAAA,oBAAoB,GAAG,IAAA,qBAAa,EAA2B,EAA8B,CAAC,CAAC;AAErG,MAAM,gBAAgB,GAAG,GAAG,EAAE,CAAC,IAAA,kBAAU,EAAC,4BAAoB,CAAC,CAAC;AAA1D,QAAA,gBAAgB,oBAA0C","sourcesContent":["import type { Placement } from '@popperjs/core';\nimport dayjs from 'dayjs';\nimport { createContext, useContext } from 'react';\n\nimport type ColorProfile from '@/models/Client/Hotel/ColorProfile';\nimport { Hotel } from '@/models/Client/Hotel/Hotel';\n\nimport { EBookingWizardSection, EBookingWizardTheme } from './BookingWizard';\n\nexport type BookingWizardLayout = 'vertical' | 'horizontal' | 'button' | 'horizontal-condensed' | 'booking-summary';\nexport type BookingWizardOverlayType = 'popup' | 'inline' | 'bottom-sheet' | 'top-sheet';\nexport type BookingWizardTheme = EBookingWizardTheme;\n\nexport interface BookingWizardOnBooking {\n id: string;\n checkIn: dayjs.Dayjs;\n checkout: dayjs.Dayjs;\n promoCode?: string;\n guests: {\n adults: number;\n children: number;\n infants: number;\n room: number;\n };\n}\n\nexport interface BookingWizardContextType extends Pick<Hotel, 'startsWeekOnDay'> {\n layout: BookingWizardLayout;\n type: BookingWizardOverlayType;\n theme: BookingWizardTheme;\n className?: string;\n style?: any;\n color?: ColorProfile;\n isMobile?: boolean;\n isSmallContainer?: boolean;\n\n onSubmit?: (result: BookingWizardOnBooking) => void;\n\n overlay?: {\n offset?: [number, number];\n placement?: Placement;\n fallbackPlacements?: Placement;\n dateOffset?: [number, number];\n };\n isImagesTheme: boolean;\n isImagesThemeMobile: boolean;\n activeSection: EBookingWizardSection | null; // Currently active section in the wizard\n onNextHandler: (currentSection: EBookingWizardSection) => void; // Called when proceeding to the next section\n onPrevHandler: (currentSection: EBookingWizardSection) => void; // Called when returning to the previous section\n stateSections: Record<\n EBookingWizardSection,\n {\n opening: boolean; // Indicates if the section is currently expanding/animating\n isFirst: boolean; // Marks the first enabled/available section in the flow\n hidden: boolean; // Whether the section is excluded (unsupported/disabled)\n inactive: boolean; // Whether the section is currently inactive (not rendered/visible)\n }\n >;\n}\n\nexport const BookingWizardContext = createContext<BookingWizardContextType>({} as BookingWizardContextType);\n\nexport const useBookingWizard = () => useContext(BookingWizardContext);\n"]}
1
+ {"version":3,"file":"BookingWizardContext.js","sourceRoot":"/","sources":["src/components/generic/BookingWizard/BookingWizardContext.ts"],"names":[],"mappings":";;;AACA,iCAAkD;AAgDrC,QAAA,oBAAoB,GAAG,IAAA,qBAAa,EAA2B,EAA8B,CAAC,CAAC;AAErG,MAAM,gBAAgB,GAAG,GAAG,EAAE,CAAC,IAAA,kBAAU,EAAC,4BAAoB,CAAC,CAAC;AAA1D,QAAA,gBAAgB,oBAA0C","sourcesContent":["import type { Placement } from '@popperjs/core';\nimport { createContext, useContext } from 'react';\n\nimport { BookingEngineOnBooking } from '@/engines/BaseEngine';\nimport type ColorProfile from '@/models/Client/Hotel/ColorProfile';\nimport { Hotel } from '@/models/Client/Hotel/Hotel';\n\nimport { EBookingWizardSection, EBookingWizardTheme } from './BookingWizard';\n\nexport type BookingWizardLayout = 'vertical' | 'horizontal' | 'button' | 'horizontal-condensed' | 'booking-summary';\nexport type BookingWizardOverlayType = 'popup' | 'inline' | 'bottom-sheet' | 'top-sheet';\nexport type BookingWizardTheme = EBookingWizardTheme;\n\nexport type BookingWizardOnBooking = BookingEngineOnBooking;\n\nexport interface BookingWizardContextType extends Pick<Hotel, 'startsWeekOnDay'> {\n layout: BookingWizardLayout;\n type: BookingWizardOverlayType;\n theme: BookingWizardTheme;\n className?: string;\n style?: any;\n color?: ColorProfile;\n isMobile?: boolean;\n isSmallContainer?: boolean;\n\n onSubmit?: (result: BookingWizardOnBooking) => void;\n\n overlay?: {\n offset?: [number, number];\n placement?: Placement;\n fallbackPlacements?: Placement;\n dateOffset?: [number, number];\n };\n isImagesTheme: boolean;\n isImagesThemeMobile: boolean;\n activeSection: EBookingWizardSection | null; // Currently active section in the wizard\n onNextHandler: (currentSection: EBookingWizardSection) => void; // Called when proceeding to the next section\n onPrevHandler: (currentSection: EBookingWizardSection) => void; // Called when returning to the previous section\n stateSections: Record<\n EBookingWizardSection,\n {\n opening: boolean; // Indicates if the section is currently expanding/animating\n isFirst: boolean; // Marks the first enabled/available section in the flow\n hidden: boolean; // Whether the section is excluded (unsupported/disabled)\n inactive: boolean; // Whether the section is currently inactive (not rendered/visible)\n }\n >;\n}\n\nexport const BookingWizardContext = createContext<BookingWizardContextType>({} as BookingWizardContextType);\n\nexport const useBookingWizard = () => useContext(BookingWizardContext);\n"]}
@@ -80,6 +80,7 @@ const usePaymentHelper_1 = require("../../../util/usePaymentHelper");
80
80
  function StepConfirmationForm() {
81
81
  var _a;
82
82
  const { t } = (0, react_i18next_1.useTranslation)();
83
+ const { showPromocodeFieldInCheckout } = (0, react_1.useContext)(contexts_1.HotelOverridesContext);
83
84
  const formContext = (0, react_hook_form_1.useFormContext)();
84
85
  const { isInPaymentMode, isBackFromPlanpay, bookingSource } = (0, ConfirmationStepContext_1.useConfirmationStep)();
85
86
  const basketContext = (0, contexts_1.useBasket)();
@@ -130,8 +131,8 @@ function StepConfirmationForm() {
130
131
  react_1.default.createElement("div", { className: "u-marg-bottom--heavy" },
131
132
  react_1.default.createElement("div", { className: "u-marg-bottom" },
132
133
  react_1.default.createElement(Headline_1.default, { bold: true }, t(Translation_1.Translation.Navigation.Menu.PaymentInformation))),
133
- react_1.default.createElement("div", { className: "u-marg-bottom rs-promotional-code-wrapper" },
134
- react_1.default.createElement(PromotionalCodeInput_1.PromotionalCodeInput, null)),
134
+ showPromocodeFieldInCheckout && (react_1.default.createElement("div", { className: "u-marg-bottom rs-promotional-code-wrapper" },
135
+ react_1.default.createElement(PromotionalCodeInput_1.PromotionalCodeInput, null))),
135
136
  isFullyCoveredByGiftCard ? (react_1.default.createElement(Alert_1.default, { type: Alert_1.AlertType.Success },
136
137
  react_1.default.createElement("div", null,
137
138
  react_1.default.createElement(Text_1.default, { bold: true }, "No payment required"),
@@ -1 +1 @@
1
- {"version":3,"file":"StepConfirmationForm.js","sourceRoot":"/","sources":["src/components/steps/confirmation/StepConfirmationForm.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useBasket } from '@frontend/contexts';\nimport { useConfirmationStep } from 'contexts/ConfirmationStepContext/ConfirmationStepContext';\nimport { useHistoryConsistentPush } from 'hooks/HistoryConsistentPush';\nimport React, { useEffect } from 'react';\nimport { useFormContext } from 'react-hook-form';\nimport { useTranslation } from 'react-i18next';\nimport { Translation } from 'translations/Translation';\n\nimport AutoAutoHeight from '@/animations/AutoAutoHeight';\nimport Alert, { AlertType } from '@/components/generic/Alert';\nimport SSLSecureBadge from '@/components/generic/badging/SSLSecureBadge';\nimport BEButton from '@/components/generic/BEButton';\nimport Headline from '@/components/generic/Headline';\nimport Icon, { IconType } from '@/components/generic/Icon/Icon';\nimport LineBreak from '@/components/generic/LineBreak';\nimport LargeLoader from '@/components/generic/loader/LargeLoader';\nimport { PromotionalCodeInput } from '@/components/generic/PromotionalCodeInput';\nimport ScrollToTop from '@/components/generic/ScrollToTop';\nimport Text, { TextType } from '@/components/generic/Text';\nimport TextBox from '@/components/generic/TextBox';\nimport GiftCardRedemption from '@/components/steps/confirmation/GiftCardRedemption';\nimport PaymentInformation from '@/components/steps/confirmation/PaymentInformation';\nimport RoomContactDetails from '@/components/steps/confirmation/RoomContactDetails';\nimport StepConfirmationAcknowledgement from '@/components/steps/confirmation/StepConfirmationAcknowledgement';\nimport { StepConfirmationCommentsComponent } from '@/components/steps/confirmation/StepConfirmationCommentsComponent';\nimport StepConfirmationPoliciesSection from '@/components/steps/confirmation/StepConfirmationPoliciesSection';\nimport StepConfirmationPolicyBlock from '@/components/steps/confirmation/StepConfirmationPolicyBlock';\nimport { ConfirmationFormValues } from '@/models/Confirmation';\nimport ConfirmationVerifyFeature from '@/providers/feature/ConfirmationVerifyFeature';\nimport ShowIATANumberOnCheckoutFeature from '@/providers/feature/ShowIATANumberOnCheckoutFeature';\nimport { Color } from '@/util/Color';\nimport getStepRoom from '@/util/GetStepRoom';\nimport { usePaymentHelper } from '@/util/usePaymentHelper';\n\nexport default function StepConfirmationForm() {\n const { t } = useTranslation();\n\n const formContext = useFormContext<ConfirmationFormValues>();\n const { isInPaymentMode, isBackFromPlanpay, bookingSource } = useConfirmationStep();\n const basketContext = useBasket();\n\n const historyConsistentPush = useHistoryConsistentPush();\n\n const { exclusivePayment } = usePaymentHelper();\n\n const returnToPickRooms = async () => {\n historyConsistentPush(getStepRoom().getStepUrl());\n };\n\n // Check if gift card fully covers the booking\n const giftCardAmount = Math.abs(formContext.watch('giftCardAmount') || 0);\n const totalBookingAmount = basketContext.getTotalPrice() || 0;\n const isFullyCoveredByGiftCard = giftCardAmount > 0 && giftCardAmount >= totalBookingAmount;\n\n // Ensure paymentMethod is set when gift card covers everything\n useEffect(() => {\n if (isFullyCoveredByGiftCard && !formContext.getValues('paymentMethod')) {\n formContext.setValue('paymentMethod', 'VGS');\n }\n }, [isFullyCoveredByGiftCard]);\n\n const prePaymentInfo = (\n <>\n <div className=\"u-marg-bottom--heavy\">\n <div className=\"u-marg-bottom--light\">\n <Headline bold>{t(Translation.Step.Confirmation.GuestDetails)}</Headline>\n </div>\n <Text type={TextType.Small} color={Color.Success}>\n <strong>{t(Translation.Step.Confirmation.AlmostDone, { required: '*' })}</strong>\n </Text>\n </div>\n <RoomContactDetails />\n {bookingSource === 'inline' && basketContext.canAddAnotherRoom ? (\n <Alert type={AlertType.Accent2} heavyPadding>\n <div className=\"u-flex u-w-100 flex-wrap flex-xl-nowrap\">\n <div>\n <Text color={Color.Accent} bold>\n {t(Translation.Step.Confirmation.WantMoreRooms)}\n </Text>\n <Text color={Color.Navy}>{t(Translation.Step.Confirmation.YouCanAddAndEditRooms)}</Text>\n </div>\n <div className=\"d-flex align-items-center u-marg-top@xl-\">\n <BEButton filled primary icon={IconType.Add} iconPosition=\"left\" onClick={returnToPickRooms}>\n {t(Translation.Step.Confirmation.AddMoreRooms)}\n </BEButton>\n </div>\n </div>\n </Alert>\n ) : (\n <></>\n )}\n {ShowIATANumberOnCheckoutFeature.isActive() && (\n <>\n <div className=\"u-marg-bottom--heavy\">\n <div className=\"u-marg-bottom\">\n <Headline bold>{t(Translation.Step.Confirmation.TravelAgent)}</Headline>\n </div>\n\n <div className=\"u-marg-bottom--light\">\n <Text type={TextType.Small}>\n <strong>{t(Translation.Step.Confirmation.Inputs.IATANumber)}</strong>\n </Text>\n </div>\n <div className=\"u-marg-bottom--light\">\n <Text type={TextType.Small}>{t(Translation.Step.Confirmation.Inputs.IATANumberGuide)}</Text>\n </div>\n <TextBox placeholder={t(Translation.Step.Confirmation.Inputs.IATANumber)} wide {...formContext.register('IATANumber')} />\n </div>\n <LineBreak />\n </>\n )}\n <StepConfirmationCommentsComponent />\n <LineBreak />\n <GiftCardRedemption />\n {!exclusivePayment && (\n <>\n <div className=\"u-marg-bottom--heavy\">\n <div className=\"u-marg-bottom\">\n <Headline bold>{t(Translation.Navigation.Menu.PaymentInformation)}</Headline>\n </div>\n <div className=\"u-marg-bottom rs-promotional-code-wrapper\">\n <PromotionalCodeInput />\n </div>\n {isFullyCoveredByGiftCard ? (\n <Alert type={AlertType.Success}>\n <div>\n <Text bold>No payment required</Text>\n <Text>Your gift card covers the full booking amount. No additional payment is required.</Text>\n </div>\n </Alert>\n ) : (\n <>\n <PaymentInformation />\n <div className=\"u-marg-top u-flex align-items-center\">\n <div className=\"u-flex align-items-center justify-content-start\">\n <Icon icon={IconType.Lock} color={Color.Success} />\n <div className=\"u-marg-left--light\">\n <Text type={TextType.Small} color={Color.Success}>\n <strong>{t(Translation.Step.Confirmation.Secured)}</strong>\n </Text>\n </div>\n </div>\n <SSLSecureBadge />\n </div>\n </>\n )}\n </div>\n <LineBreak />\n </>\n )}\n <StepConfirmationPoliciesSection />\n <div className=\"u-pad-top--light u-marg-bottom\">\n <StepConfirmationPolicyBlock />\n </div>\n {ConfirmationVerifyFeature.isActive() && (\n <>\n <LineBreak />\n <div>\n <StepConfirmationAcknowledgement />\n </div>\n </>\n )}\n </>\n );\n\n return (\n <form>\n {isBackFromPlanpay ? (\n <LargeLoader />\n ) : (\n <>\n <AutoAutoHeight open={!exclusivePayment || !isInPaymentMode}>{prePaymentInfo}</AutoAutoHeight>\n {exclusivePayment && (\n <AutoAutoHeight open={isInPaymentMode}>\n <ScrollToTop />\n {exclusivePayment.renderPaymentInput?.()}\n </AutoAutoHeight>\n )}\n </>\n )}\n </form>\n );\n}\n"]}
1
+ {"version":3,"file":"StepConfirmationForm.js","sourceRoot":"/","sources":["src/components/steps/confirmation/StepConfirmationForm.tsx"],"names":[],"mappings":"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","sourcesContent":["import { HotelOverridesContext, useBasket } from '@frontend/contexts';\nimport { useConfirmationStep } from 'contexts/ConfirmationStepContext/ConfirmationStepContext';\nimport { useHistoryConsistentPush } from 'hooks/HistoryConsistentPush';\nimport React, { useContext, useEffect } from 'react';\nimport { useFormContext } from 'react-hook-form';\nimport { useTranslation } from 'react-i18next';\nimport { Translation } from 'translations/Translation';\n\nimport AutoAutoHeight from '@/animations/AutoAutoHeight';\nimport Alert, { AlertType } from '@/components/generic/Alert';\nimport SSLSecureBadge from '@/components/generic/badging/SSLSecureBadge';\nimport BEButton from '@/components/generic/BEButton';\nimport Headline from '@/components/generic/Headline';\nimport Icon, { IconType } from '@/components/generic/Icon/Icon';\nimport LineBreak from '@/components/generic/LineBreak';\nimport LargeLoader from '@/components/generic/loader/LargeLoader';\nimport { PromotionalCodeInput } from '@/components/generic/PromotionalCodeInput';\nimport ScrollToTop from '@/components/generic/ScrollToTop';\nimport Text, { TextType } from '@/components/generic/Text';\nimport TextBox from '@/components/generic/TextBox';\nimport GiftCardRedemption from '@/components/steps/confirmation/GiftCardRedemption';\nimport PaymentInformation from '@/components/steps/confirmation/PaymentInformation';\nimport RoomContactDetails from '@/components/steps/confirmation/RoomContactDetails';\nimport StepConfirmationAcknowledgement from '@/components/steps/confirmation/StepConfirmationAcknowledgement';\nimport { StepConfirmationCommentsComponent } from '@/components/steps/confirmation/StepConfirmationCommentsComponent';\nimport StepConfirmationPoliciesSection from '@/components/steps/confirmation/StepConfirmationPoliciesSection';\nimport StepConfirmationPolicyBlock from '@/components/steps/confirmation/StepConfirmationPolicyBlock';\nimport { ConfirmationFormValues } from '@/models/Confirmation';\nimport ConfirmationVerifyFeature from '@/providers/feature/ConfirmationVerifyFeature';\nimport ShowIATANumberOnCheckoutFeature from '@/providers/feature/ShowIATANumberOnCheckoutFeature';\nimport { Color } from '@/util/Color';\nimport getStepRoom from '@/util/GetStepRoom';\nimport { usePaymentHelper } from '@/util/usePaymentHelper';\n\nexport default function StepConfirmationForm() {\n const { t } = useTranslation();\n const { showPromocodeFieldInCheckout } = useContext(HotelOverridesContext);\n const formContext = useFormContext<ConfirmationFormValues>();\n const { isInPaymentMode, isBackFromPlanpay, bookingSource } = useConfirmationStep();\n const basketContext = useBasket();\n const historyConsistentPush = useHistoryConsistentPush();\n const { exclusivePayment } = usePaymentHelper();\n\n const returnToPickRooms = async () => {\n historyConsistentPush(getStepRoom().getStepUrl());\n };\n\n // Check if gift card fully covers the booking\n const giftCardAmount = Math.abs(formContext.watch('giftCardAmount') || 0);\n const totalBookingAmount = basketContext.getTotalPrice() || 0;\n const isFullyCoveredByGiftCard = giftCardAmount > 0 && giftCardAmount >= totalBookingAmount;\n\n // Ensure paymentMethod is set when gift card covers everything\n useEffect(() => {\n if (isFullyCoveredByGiftCard && !formContext.getValues('paymentMethod')) {\n formContext.setValue('paymentMethod', 'VGS');\n }\n }, [isFullyCoveredByGiftCard]);\n\n const prePaymentInfo = (\n <>\n <div className=\"u-marg-bottom--heavy\">\n <div className=\"u-marg-bottom--light\">\n <Headline bold>{t(Translation.Step.Confirmation.GuestDetails)}</Headline>\n </div>\n <Text type={TextType.Small} color={Color.Success}>\n <strong>{t(Translation.Step.Confirmation.AlmostDone, { required: '*' })}</strong>\n </Text>\n </div>\n <RoomContactDetails />\n {bookingSource === 'inline' && basketContext.canAddAnotherRoom ? (\n <Alert type={AlertType.Accent2} heavyPadding>\n <div className=\"u-flex u-w-100 flex-wrap flex-xl-nowrap\">\n <div>\n <Text color={Color.Accent} bold>\n {t(Translation.Step.Confirmation.WantMoreRooms)}\n </Text>\n <Text color={Color.Navy}>{t(Translation.Step.Confirmation.YouCanAddAndEditRooms)}</Text>\n </div>\n <div className=\"d-flex align-items-center u-marg-top@xl-\">\n <BEButton filled primary icon={IconType.Add} iconPosition=\"left\" onClick={returnToPickRooms}>\n {t(Translation.Step.Confirmation.AddMoreRooms)}\n </BEButton>\n </div>\n </div>\n </Alert>\n ) : (\n <></>\n )}\n {ShowIATANumberOnCheckoutFeature.isActive() && (\n <>\n <div className=\"u-marg-bottom--heavy\">\n <div className=\"u-marg-bottom\">\n <Headline bold>{t(Translation.Step.Confirmation.TravelAgent)}</Headline>\n </div>\n\n <div className=\"u-marg-bottom--light\">\n <Text type={TextType.Small}>\n <strong>{t(Translation.Step.Confirmation.Inputs.IATANumber)}</strong>\n </Text>\n </div>\n <div className=\"u-marg-bottom--light\">\n <Text type={TextType.Small}>{t(Translation.Step.Confirmation.Inputs.IATANumberGuide)}</Text>\n </div>\n <TextBox placeholder={t(Translation.Step.Confirmation.Inputs.IATANumber)} wide {...formContext.register('IATANumber')} />\n </div>\n <LineBreak />\n </>\n )}\n <StepConfirmationCommentsComponent />\n <LineBreak />\n <GiftCardRedemption />\n {!exclusivePayment && (\n <>\n <div className=\"u-marg-bottom--heavy\">\n <div className=\"u-marg-bottom\">\n <Headline bold>{t(Translation.Navigation.Menu.PaymentInformation)}</Headline>\n </div>\n {showPromocodeFieldInCheckout && (\n <div className=\"u-marg-bottom rs-promotional-code-wrapper\">\n <PromotionalCodeInput />\n </div>\n )}\n {isFullyCoveredByGiftCard ? (\n <Alert type={AlertType.Success}>\n <div>\n <Text bold>No payment required</Text>\n <Text>Your gift card covers the full booking amount. No additional payment is required.</Text>\n </div>\n </Alert>\n ) : (\n <>\n <PaymentInformation />\n <div className=\"u-marg-top u-flex align-items-center\">\n <div className=\"u-flex align-items-center justify-content-start\">\n <Icon icon={IconType.Lock} color={Color.Success} />\n <div className=\"u-marg-left--light\">\n <Text type={TextType.Small} color={Color.Success}>\n <strong>{t(Translation.Step.Confirmation.Secured)}</strong>\n </Text>\n </div>\n </div>\n <SSLSecureBadge />\n </div>\n </>\n )}\n </div>\n <LineBreak />\n </>\n )}\n <StepConfirmationPoliciesSection />\n <div className=\"u-pad-top--light u-marg-bottom\">\n <StepConfirmationPolicyBlock />\n </div>\n {ConfirmationVerifyFeature.isActive() && (\n <>\n <LineBreak />\n <div>\n <StepConfirmationAcknowledgement />\n </div>\n </>\n )}\n </>\n );\n\n return (\n <form>\n {isBackFromPlanpay ? (\n <LargeLoader />\n ) : (\n <>\n <AutoAutoHeight open={!exclusivePayment || !isInPaymentMode}>{prePaymentInfo}</AutoAutoHeight>\n {exclusivePayment && (\n <AutoAutoHeight open={isInPaymentMode}>\n <ScrollToTop />\n {exclusivePayment.renderPaymentInput?.()}\n </AutoAutoHeight>\n )}\n </>\n )}\n </form>\n );\n}\n"]}
@@ -77,6 +77,22 @@ const AvailableUpgradesModal = () => {
77
77
  const selectedRate = row === null || row === void 0 ? void 0 : row.getRate();
78
78
  const [roomUpgrades, setRoomUpgrades] = (0, react_1.useState)([]);
79
79
  const [rateUpgrades, setRateUpgrades] = (0, react_1.useState)([]);
80
+ // Determines whether a rate is eligible to be displayed in the Upsell modal.
81
+ // A rate is eligible if:
82
+ // - It has valid room data and a room code
83
+ // - The room is not marked as `doNotUpsell`
84
+ // - If `upsellRoomCodes` is empty then all rooms are allowed
85
+ // - If `upsellRoomCodes` is defined then only rooms with matching codes are allowed
86
+ const isEligibleUpsellRate = (rate) => {
87
+ var _a, _b;
88
+ const roomData = (_a = rate.getRoom) === null || _a === void 0 ? void 0 : _a.call(rate); // Room data of the rate being evaluated (used to check if this room can be upsold)
89
+ const roomCode = roomData === null || roomData === void 0 ? void 0 : roomData.code;
90
+ if (!room || !roomData || !roomCode || !!(roomData === null || roomData === void 0 ? void 0 : roomData.doNotUpsell))
91
+ return false;
92
+ // Important: defines which room codes are allowed for upsell based on the current basket room
93
+ const upsellCodes = (_b = room.upsellRoomCodes) !== null && _b !== void 0 ? _b : [];
94
+ return upsellCodes.length === 0 || upsellCodes.includes(roomCode);
95
+ };
80
96
  (0, react_1.useEffect)(() => {
81
97
  let cancelled = false;
82
98
  on(events_1.AddToCartEvent, (event) => __awaiter(void 0, void 0, void 0, function* () {
@@ -96,14 +112,7 @@ const AvailableUpgradesModal = () => {
96
112
  var _a;
97
113
  if (cancelled)
98
114
  return;
99
- const upsellEligibleRates = (_a = availableRooms === null || availableRooms === void 0 ? void 0 : availableRooms.flatMap((room) => {
100
- var _a, _b, _c;
101
- return (_c = (_b = (_a = room.getRates) === null || _a === void 0 ? void 0 : _a.call(room)) === null || _b === void 0 ? void 0 : _b.filter((rate) => {
102
- var _a;
103
- const roomData = (_a = rate.getRoom) === null || _a === void 0 ? void 0 : _a.call(rate);
104
- return roomData && !roomData.doNotUpsell;
105
- })) !== null && _c !== void 0 ? _c : [];
106
- })) !== null && _a !== void 0 ? _a : [];
115
+ const upsellEligibleRates = (_a = availableRooms === null || availableRooms === void 0 ? void 0 : availableRooms.flatMap((room) => { var _a, _b, _c; return (_c = (_b = (_a = room.getRates) === null || _a === void 0 ? void 0 : _a.call(room)) === null || _b === void 0 ? void 0 : _b.filter(isEligibleUpsellRate)) !== null && _c !== void 0 ? _c : []; })) !== null && _a !== void 0 ? _a : [];
107
116
  const roomUpsellsEnabled = (hotel === null || hotel === void 0 ? void 0 : hotel.enableRoomUpsells) || RoomUpsellFeature_1.default.isActive();
108
117
  const rateUpsellsEnabled = hotel === null || hotel === void 0 ? void 0 : hotel.enableRateUpsells;
109
118
  // Check for minimum stay opportunities
@@ -220,8 +229,8 @@ const AvailableUpgradesModal = () => {
220
229
  };
221
230
  const roomUpsellsEnabled = (hotel === null || hotel === void 0 ? void 0 : hotel.enableRoomUpsells) || RoomUpsellFeature_1.default.isActive();
222
231
  const rateUpsellsEnabled = hotel === null || hotel === void 0 ? void 0 : hotel.enableRateUpsells;
223
- const upsellsEnabled = roomUpsellsEnabled || rateUpsellsEnabled;
224
- const allUpgrades = [...rateUpgrades, ...roomUpgrades].slice(0, 3);
232
+ const allowedUpgrades = [...rateUpgrades, ...roomUpgrades].slice(0, 3);
233
+ const upsellsEnabled = (roomUpsellsEnabled || rateUpsellsEnabled) && allowedUpgrades.length > 0;
225
234
  return upsellsEnabled && row ? (react_1.default.createElement(SimpleModal_1.default, { open: isOpen, onClose: cancelUpsell, title: t(Translation_1.Translation.Step.Room.AvailableUpgrades) },
226
235
  react_1.default.createElement("div", { className: "upgrades-room-modal" },
227
236
  react_1.default.createElement("div", { className: "current-room-block container" },
@@ -249,7 +258,7 @@ const AvailableUpgradesModal = () => {
249
258
  react_1.default.createElement("div", { className: "u-pad-top u-marg-bottom" },
250
259
  react_1.default.createElement("div", { className: "u-marg-bottom--heavy" },
251
260
  react_1.default.createElement(Headline_1.default, { bold: true }, t(Translation_1.Translation.Step.Room.CheckOutOtherOptions))),
252
- react_1.default.createElement("div", { className: "row no-gutters" }, allUpgrades.map((rate) => {
261
+ react_1.default.createElement("div", { className: "row no-gutters" }, allowedUpgrades.map((rate) => {
253
262
  const isRateUpgrade = rate.getRoom().code === (selectedRate === null || selectedRate === void 0 ? void 0 : selectedRate.getRoom().code);
254
263
  const key = `room-${rate.getRoom().code}-${rate.code}`;
255
264
  return (react_1.default.createElement("div", { className: "col-md-6 col-xl-4 u-marg-bottom u-pad-left--light u-pad-right--light", key: key }, selectedRate &&
@@ -1 +1 @@
1
- {"version":3,"file":"AvailableUpgradesModal.js","sourceRoot":"/","sources":["src/components/steps/room/AvailableUpgradesModal.tsx"],"names":[],"mappings":"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","sourcesContent":["import { BasketContext } from '@frontend/contexts';\nimport { AddToCartEvent } from '@frontend/events';\nimport { useCurrentHotel, useEvent } from '@frontend/hooks';\nimport React, { FC, useContext, useEffect, useState } from 'react';\nimport { useTranslation } from 'react-i18next';\nimport { Translation } from 'translations/Translation';\n\nimport API from '@/api/BookingAPI';\nimport BEButton from '@/components/generic/BEButton';\nimport Currency from '@/components/generic/Currency';\nimport Headline from '@/components/generic/Headline';\nimport SimpleModal from '@/components/generic/modal/SimpleModal';\nimport { TaxInclusionNotice } from '@/components/generic/TaxInclusionNotice/TaxInclusionNotice';\nimport Text, { TextType } from '@/components/generic/Text';\nimport ImageGallerySlider from '@/components/steps/room/ImageGallerySlider';\nimport LargeRateCard from '@/components/steps/room/LargeRateCard';\nimport LargeRoomCard from '@/components/steps/room/LargeRoomCard';\nimport { useSignedInMember } from '@/hooks/useSignedInMember';\nimport BasketRow from '@/models/BasketRow';\nimport { RoomRate } from '@/models/Room/RoomRate';\nimport RoomUpsellFeature from '@/providers/feature/RoomUpsellFeature';\nimport { Color } from '@/util/Color';\nimport DataLayer from '@/util/DataLayer';\n\nconst AvailableUpgradesModal: FC = () => {\n const { t } = useTranslation();\n const { hotel } = useCurrentHotel();\n const { memberNumber } = useSignedInMember();\n const [isOpen, setIsOpen] = useState(false);\n const [row, setRow] = useState<BasketRow | null>(null);\n\n const basketContext = useContext(BasketContext);\n\n const { on } = useEvent();\n\n const room = row?.getRoom();\n const selectedRate = row?.getRate();\n\n const [roomUpgrades, setRoomUpgrades] = useState<RoomRate[]>([]);\n const [rateUpgrades, setRateUpgrades] = useState<RoomRate[]>([]);\n\n useEffect(() => {\n let cancelled = false;\n\n on(AddToCartEvent, async (event: AddToCartEvent): Promise<void | boolean> => {\n if (cancelled) return;\n\n setRow(event.basketRow);\n return true;\n });\n\n return () => {\n cancelled = true;\n };\n }, []);\n\n useEffect(() => {\n let cancelled = false;\n\n if (row && selectedRate) {\n API.Availability.fetchAvailabilityData(row, hotel?.hotelID as string, memberNumber, true).then(async (availableRooms) => {\n if (cancelled) return;\n\n const upsellEligibleRates: RoomRate[] =\n availableRooms?.flatMap(\n (room) =>\n room.getRates?.()?.filter((rate) => {\n const roomData = rate.getRoom?.();\n return roomData && !roomData.doNotUpsell;\n }) ?? []\n ) ?? [];\n\n const roomUpsellsEnabled = hotel?.enableRoomUpsells || RoomUpsellFeature.isActive();\n const rateUpsellsEnabled = hotel?.enableRateUpsells;\n\n // Check for minimum stay opportunities\n const hasMinStayOpportunities = API.Availability.hasMinStayUpsellOpportunities(row, hotel?.hotelID as string, memberNumber);\n const minStayUpsells: RoomRate[] = [];\n\n if (rateUpsellsEnabled && hasMinStayOpportunities) {\n // Fetch extended availability for minimum stay upsells\n const extendedRooms = await API.Availability.fetchMinimumStayExtensions(row, hotel?.hotelID as string, memberNumber);\n\n if (extendedRooms && !cancelled) {\n extendedRooms.forEach((room) => {\n room.getRates().forEach((rate) => {\n // We want to find rates where the user can extend the stay in their current room, for a lower price per night.\n const wasAvailableForOriginalDates = upsellEligibleRates.find((r) => r.code === rate.code);\n const isCheaperPerNight = rate.getAveragePrice() < selectedRate.getAveragePrice();\n const requiresLongerStay = rate.minNightStayOverride && rate.minNightStayOverride > row.getNumberOfDays();\n const isSameRoom = rate.getRoom().code === selectedRate.getRoom().code;\n\n if (!wasAvailableForOriginalDates && isSameRoom && rate.isUpsellRate && isCheaperPerNight && requiresLongerStay) {\n minStayUpsells.push(rate);\n }\n });\n });\n }\n }\n\n if (cancelled) return;\n\n // Find rate upgrades (same room, different rate)\n const rateUpgradeRoomRates = rateUpsellsEnabled\n ? upsellEligibleRates\n .filter((rate) => {\n const isSameRoom = rate.getRoom().code === selectedRate.getRoom().code;\n const isDifferentRate = rate.code !== selectedRate.code;\n const isUpsellRate = rate.isUpsellRate === true;\n const isMoreExpensive = rate.getAveragePrice() > selectedRate.getAveragePrice();\n\n // Don't show rates with lower minimum stay requirements as upsells, because we might show a minimum\n // stay upsell when the user has already chosen a rate with a longer minimum stay requirement.\n // But, still show upsell rates that don't have a minimum night stay requirement.\n const hasLowerMinStay = rate.minNightStayOverride && rate.minNightStayOverride < row.getNumberOfDays();\n\n return isSameRoom && isDifferentRate && isUpsellRate && isMoreExpensive && !hasLowerMinStay;\n })\n .sort((a, b) => a.getAveragePrice() - b.getAveragePrice())\n .slice(0, 3)\n : [];\n\n // Combine regular rate upgrades with minimum stay upsells\n const allRateUpgrades = [...rateUpgradeRoomRates, ...minStayUpsells]\n .sort((a, b) => {\n // Prioritize minimum stay deals (rates with minNightStayOverride)\n const aIsMinStay = !!(a.minNightStayOverride && a.minNightStayOverride > row.getNumberOfDays());\n const bIsMinStay = !!(b.minNightStayOverride && b.minNightStayOverride > row.getNumberOfDays());\n if (aIsMinStay && !bIsMinStay) return -1;\n if (!aIsMinStay && bIsMinStay) return 1;\n return a.getAveragePrice() - b.getAveragePrice();\n })\n .slice(0, 3);\n\n // Find room upgrades (different room, same rate, higher price)\n const roomUpgradeRoomRates = roomUpsellsEnabled\n ? upsellEligibleRates\n .filter((rate) => {\n const isDifferentRoom = rate.getRoom().code !== selectedRate.getRoom().code;\n const isSameRate = rate.name === selectedRate.name;\n const isMoreExpensive = rate.getAveragePrice() > selectedRate.getAveragePrice();\n\n return isDifferentRoom && isSameRate && isMoreExpensive;\n })\n .sort((a, b) => a.getAveragePrice() - b.getAveragePrice())\n .slice(0, 3)\n : [];\n\n setRoomUpgrades(roomUpgradeRoomRates);\n setRateUpgrades(allRateUpgrades);\n\n const hasUpgrades = roomUpgradeRoomRates.length > 0 || allRateUpgrades.length > 0;\n if (!hasUpgrades && selectedRate) {\n // Proceed as if user clicked the nav.\n confirmRateOnClick();\n } else {\n setIsOpen(hasUpgrades);\n }\n });\n }\n\n return () => {\n cancelled = true;\n };\n }, [row, memberNumber]);\n\n const closeModal = () => {\n setIsOpen(false);\n setRow(null);\n };\n\n const cancelUpsell = () => {\n if (row) {\n DataLayer.instance.sendUpsellCancelled({ from: row.getStartDate(), to: row.getEndDate() }, row.getRate() as RoomRate);\n }\n\n closeModal();\n };\n\n const confirmRateOnClick = (rate?: RoomRate) => {\n if (row) {\n if (rate) {\n DataLayer.instance.sendUpsellConfirmed({ from: row.getStartDate(), to: row.getEndDate() }, row.getRate() as RoomRate, rate);\n\n const isUpsellActive = hotel?.enableRoomUpsells || RoomUpsellFeature.isActive() || hotel?.enableRateUpsells;\n\n // Check if this is a minimum stay upsell that requires extending the checkout\n if (rate.minNightStayOverride && rate.minNightStayOverride > row.getNumberOfDays()) {\n const newStartDate = row.getStartDate();\n const newEndDate = newStartDate.add(rate.minNightStayOverride, 'days');\n\n row.setStartDate(newStartDate);\n row.setEndDate(newEndDate);\n }\n\n row.setRate(rate, isUpsellActive);\n basketContext.updateBasketRow(row);\n closeModal();\n } else {\n cancelUpsell();\n }\n }\n };\n\n const roomUpsellsEnabled = hotel?.enableRoomUpsells || RoomUpsellFeature.isActive();\n const rateUpsellsEnabled = hotel?.enableRateUpsells;\n const upsellsEnabled = roomUpsellsEnabled || rateUpsellsEnabled;\n\n const allUpgrades = [...rateUpgrades, ...roomUpgrades].slice(0, 3);\n\n return upsellsEnabled && row ? (\n <SimpleModal open={isOpen} onClose={cancelUpsell} title={t(Translation.Step.Room.AvailableUpgrades)}>\n <div className=\"upgrades-room-modal\">\n <div className=\"current-room-block container\">\n <div className=\"u-flex u-flex-flex-start row\">\n <div className=\"image-wrapper col-md-3 col-12\">\n <ImageGallerySlider images={room?.getImages() || []} />\n </div>\n <div className=\"u-flex info-block col-md-7 col-sm-9\">\n <div>\n <Text color={Color.DarkGrey} bold>\n {t(Translation.Step.Room.CurrentSelectedRoom)}\n </Text>\n <Headline bold>{room?.name}</Headline>\n <Text type={TextType.Small} color={Color.DarkGrey}>\n {selectedRate?.name}\n </Text>\n </div>\n <div>\n <div className=\"room-details--content-price flex-row\">\n <Headline bold>\n <Currency hideDecimals>{selectedRate?.getAveragePrice()}</Currency>\n </Headline>\n <Text color={Color.DarkGrey} inline>\n /{t(Translation.Misc.Night)}\n </Text>\n </div>\n <div className=\"u-marg-bottom\">\n <Text type={TextType.Small} color={Color.Grey} className=\"u-nowrap\">\n <TaxInclusionNotice rate={selectedRate} />\n </Text>\n </div>\n </div>\n </div>\n <div className=\"u-flex u-flex-align-center col-md-2 col-sm-3\">\n <BEButton wide filled primary onClick={() => confirmRateOnClick()}>\n {t(Translation.Step.Room.Confirm)}\n </BEButton>\n </div>\n </div>\n </div>\n <div className=\"container\" style={{ backgroundColor: Color.Snow }}>\n <div className=\"u-pad-top u-marg-bottom\">\n <div className=\"u-marg-bottom--heavy\">\n <Headline bold>{t(Translation.Step.Room.CheckOutOtherOptions)}</Headline>\n </div>\n\n {/* Combined Upgrades - Rate upgrades first, then Room upgrades */}\n <div className=\"row no-gutters\">\n {allUpgrades.map((rate) => {\n const isRateUpgrade = rate.getRoom().code === selectedRate?.getRoom().code;\n const key = `room-${rate.getRoom().code}-${rate.code}`;\n\n return (\n <div className=\"col-md-6 col-xl-4 u-marg-bottom u-pad-left--light u-pad-right--light\" key={key}>\n {selectedRate &&\n (isRateUpgrade ? (\n <LargeRateCard\n selectedRate={selectedRate}\n rate={rate}\n onSelectRate={() => confirmRateOnClick(rate)}\n startDate={row.getStartDate()}\n />\n ) : (\n <LargeRoomCard selectedRate={selectedRate} rate={rate} onSelectRate={() => confirmRateOnClick(rate)} />\n ))}\n </div>\n );\n })}\n </div>\n </div>\n </div>\n </div>\n </SimpleModal>\n ) : null;\n};\n\nexport default AvailableUpgradesModal;\n"]}
1
+ {"version":3,"file":"AvailableUpgradesModal.js","sourceRoot":"/","sources":["src/components/steps/room/AvailableUpgradesModal.tsx"],"names":[],"mappings":"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","sourcesContent":["import { BasketContext } from '@frontend/contexts';\nimport { AddToCartEvent } from '@frontend/events';\nimport { useCurrentHotel, useEvent } from '@frontend/hooks';\nimport React, { FC, useContext, useEffect, useState } from 'react';\nimport { useTranslation } from 'react-i18next';\nimport { Translation } from 'translations/Translation';\n\nimport API from '@/api/BookingAPI';\nimport BEButton from '@/components/generic/BEButton';\nimport Currency from '@/components/generic/Currency';\nimport Headline from '@/components/generic/Headline';\nimport SimpleModal from '@/components/generic/modal/SimpleModal';\nimport { TaxInclusionNotice } from '@/components/generic/TaxInclusionNotice/TaxInclusionNotice';\nimport Text, { TextType } from '@/components/generic/Text';\nimport ImageGallerySlider from '@/components/steps/room/ImageGallerySlider';\nimport LargeRateCard from '@/components/steps/room/LargeRateCard';\nimport LargeRoomCard from '@/components/steps/room/LargeRoomCard';\nimport { useSignedInMember } from '@/hooks/useSignedInMember';\nimport BasketRow from '@/models/BasketRow';\nimport { RoomRate } from '@/models/Room/RoomRate';\nimport RoomUpsellFeature from '@/providers/feature/RoomUpsellFeature';\nimport { Color } from '@/util/Color';\nimport DataLayer from '@/util/DataLayer';\n\nconst AvailableUpgradesModal: FC = () => {\n const { t } = useTranslation();\n const { hotel } = useCurrentHotel();\n const { memberNumber } = useSignedInMember();\n const [isOpen, setIsOpen] = useState(false);\n const [row, setRow] = useState<BasketRow | null>(null);\n\n const basketContext = useContext(BasketContext);\n\n const { on } = useEvent();\n\n const room = row?.getRoom();\n const selectedRate = row?.getRate();\n\n const [roomUpgrades, setRoomUpgrades] = useState<RoomRate[]>([]);\n const [rateUpgrades, setRateUpgrades] = useState<RoomRate[]>([]);\n\n // Determines whether a rate is eligible to be displayed in the Upsell modal.\n // A rate is eligible if:\n // - It has valid room data and a room code\n // - The room is not marked as `doNotUpsell`\n // - If `upsellRoomCodes` is empty then all rooms are allowed\n // - If `upsellRoomCodes` is defined then only rooms with matching codes are allowed\n const isEligibleUpsellRate = (rate: RoomRate): boolean => {\n const roomData = rate.getRoom?.(); // Room data of the rate being evaluated (used to check if this room can be upsold)\n const roomCode = roomData?.code;\n if (!room || !roomData || !roomCode || !!roomData?.doNotUpsell) return false;\n // Important: defines which room codes are allowed for upsell based on the current basket room\n const upsellCodes = room.upsellRoomCodes ?? [];\n return upsellCodes.length === 0 || upsellCodes.includes(roomCode);\n };\n\n useEffect(() => {\n let cancelled = false;\n\n on(AddToCartEvent, async (event: AddToCartEvent): Promise<void | boolean> => {\n if (cancelled) return;\n\n setRow(event.basketRow);\n return true;\n });\n\n return () => {\n cancelled = true;\n };\n }, []);\n\n useEffect(() => {\n let cancelled = false;\n\n if (row && selectedRate) {\n API.Availability.fetchAvailabilityData(row, hotel?.hotelID as string, memberNumber, true).then(async (availableRooms) => {\n if (cancelled) return;\n const upsellEligibleRates: RoomRate[] = availableRooms?.flatMap((room) => room.getRates?.()?.filter(isEligibleUpsellRate) ?? []) ?? [];\n const roomUpsellsEnabled = hotel?.enableRoomUpsells || RoomUpsellFeature.isActive();\n const rateUpsellsEnabled = hotel?.enableRateUpsells;\n\n // Check for minimum stay opportunities\n const hasMinStayOpportunities = API.Availability.hasMinStayUpsellOpportunities(row, hotel?.hotelID as string, memberNumber);\n const minStayUpsells: RoomRate[] = [];\n\n if (rateUpsellsEnabled && hasMinStayOpportunities) {\n // Fetch extended availability for minimum stay upsells\n const extendedRooms = await API.Availability.fetchMinimumStayExtensions(row, hotel?.hotelID as string, memberNumber);\n\n if (extendedRooms && !cancelled) {\n extendedRooms.forEach((room) => {\n room.getRates().forEach((rate) => {\n // We want to find rates where the user can extend the stay in their current room, for a lower price per night.\n const wasAvailableForOriginalDates = upsellEligibleRates.find((r) => r.code === rate.code);\n const isCheaperPerNight = rate.getAveragePrice() < selectedRate.getAveragePrice();\n const requiresLongerStay = rate.minNightStayOverride && rate.minNightStayOverride > row.getNumberOfDays();\n const isSameRoom = rate.getRoom().code === selectedRate.getRoom().code;\n\n if (!wasAvailableForOriginalDates && isSameRoom && rate.isUpsellRate && isCheaperPerNight && requiresLongerStay) {\n minStayUpsells.push(rate);\n }\n });\n });\n }\n }\n\n if (cancelled) return;\n\n // Find rate upgrades (same room, different rate)\n const rateUpgradeRoomRates = rateUpsellsEnabled\n ? upsellEligibleRates\n .filter((rate) => {\n const isSameRoom = rate.getRoom().code === selectedRate.getRoom().code;\n const isDifferentRate = rate.code !== selectedRate.code;\n const isUpsellRate = rate.isUpsellRate === true;\n const isMoreExpensive = rate.getAveragePrice() > selectedRate.getAveragePrice();\n\n // Don't show rates with lower minimum stay requirements as upsells, because we might show a minimum\n // stay upsell when the user has already chosen a rate with a longer minimum stay requirement.\n // But, still show upsell rates that don't have a minimum night stay requirement.\n const hasLowerMinStay = rate.minNightStayOverride && rate.minNightStayOverride < row.getNumberOfDays();\n\n return isSameRoom && isDifferentRate && isUpsellRate && isMoreExpensive && !hasLowerMinStay;\n })\n .sort((a, b) => a.getAveragePrice() - b.getAveragePrice())\n .slice(0, 3)\n : [];\n\n // Combine regular rate upgrades with minimum stay upsells\n const allRateUpgrades = [...rateUpgradeRoomRates, ...minStayUpsells]\n .sort((a, b) => {\n // Prioritize minimum stay deals (rates with minNightStayOverride)\n const aIsMinStay = !!(a.minNightStayOverride && a.minNightStayOverride > row.getNumberOfDays());\n const bIsMinStay = !!(b.minNightStayOverride && b.minNightStayOverride > row.getNumberOfDays());\n if (aIsMinStay && !bIsMinStay) return -1;\n if (!aIsMinStay && bIsMinStay) return 1;\n return a.getAveragePrice() - b.getAveragePrice();\n })\n .slice(0, 3);\n\n // Find room upgrades (different room, same rate, higher price)\n const roomUpgradeRoomRates = roomUpsellsEnabled\n ? upsellEligibleRates\n .filter((rate) => {\n const isDifferentRoom = rate.getRoom().code !== selectedRate.getRoom().code;\n const isSameRate = rate.name === selectedRate.name;\n const isMoreExpensive = rate.getAveragePrice() > selectedRate.getAveragePrice();\n\n return isDifferentRoom && isSameRate && isMoreExpensive;\n })\n .sort((a, b) => a.getAveragePrice() - b.getAveragePrice())\n .slice(0, 3)\n : [];\n\n setRoomUpgrades(roomUpgradeRoomRates);\n setRateUpgrades(allRateUpgrades);\n\n const hasUpgrades = roomUpgradeRoomRates.length > 0 || allRateUpgrades.length > 0;\n if (!hasUpgrades && selectedRate) {\n // Proceed as if user clicked the nav.\n confirmRateOnClick();\n } else {\n setIsOpen(hasUpgrades);\n }\n });\n }\n\n return () => {\n cancelled = true;\n };\n }, [row, memberNumber]);\n\n const closeModal = () => {\n setIsOpen(false);\n setRow(null);\n };\n\n const cancelUpsell = () => {\n if (row) {\n DataLayer.instance.sendUpsellCancelled({ from: row.getStartDate(), to: row.getEndDate() }, row.getRate() as RoomRate);\n }\n\n closeModal();\n };\n\n const confirmRateOnClick = (rate?: RoomRate) => {\n if (row) {\n if (rate) {\n DataLayer.instance.sendUpsellConfirmed({ from: row.getStartDate(), to: row.getEndDate() }, row.getRate() as RoomRate, rate);\n\n const isUpsellActive = hotel?.enableRoomUpsells || RoomUpsellFeature.isActive() || hotel?.enableRateUpsells;\n\n // Check if this is a minimum stay upsell that requires extending the checkout\n if (rate.minNightStayOverride && rate.minNightStayOverride > row.getNumberOfDays()) {\n const newStartDate = row.getStartDate();\n const newEndDate = newStartDate.add(rate.minNightStayOverride, 'days');\n\n row.setStartDate(newStartDate);\n row.setEndDate(newEndDate);\n }\n\n row.setRate(rate, isUpsellActive);\n basketContext.updateBasketRow(row);\n closeModal();\n } else {\n cancelUpsell();\n }\n }\n };\n\n const roomUpsellsEnabled = hotel?.enableRoomUpsells || RoomUpsellFeature.isActive();\n const rateUpsellsEnabled = hotel?.enableRateUpsells;\n const allowedUpgrades = [...rateUpgrades, ...roomUpgrades].slice(0, 3);\n const upsellsEnabled = (roomUpsellsEnabled || rateUpsellsEnabled) && allowedUpgrades.length > 0;\n\n return upsellsEnabled && row ? (\n <SimpleModal open={isOpen} onClose={cancelUpsell} title={t(Translation.Step.Room.AvailableUpgrades)}>\n <div className=\"upgrades-room-modal\">\n <div className=\"current-room-block container\">\n <div className=\"u-flex u-flex-flex-start row\">\n <div className=\"image-wrapper col-md-3 col-12\">\n <ImageGallerySlider images={room?.getImages() || []} />\n </div>\n <div className=\"u-flex info-block col-md-7 col-sm-9\">\n <div>\n <Text color={Color.DarkGrey} bold>\n {t(Translation.Step.Room.CurrentSelectedRoom)}\n </Text>\n <Headline bold>{room?.name}</Headline>\n <Text type={TextType.Small} color={Color.DarkGrey}>\n {selectedRate?.name}\n </Text>\n </div>\n <div>\n <div className=\"room-details--content-price flex-row\">\n <Headline bold>\n <Currency hideDecimals>{selectedRate?.getAveragePrice()}</Currency>\n </Headline>\n <Text color={Color.DarkGrey} inline>\n /{t(Translation.Misc.Night)}\n </Text>\n </div>\n <div className=\"u-marg-bottom\">\n <Text type={TextType.Small} color={Color.Grey} className=\"u-nowrap\">\n <TaxInclusionNotice rate={selectedRate} />\n </Text>\n </div>\n </div>\n </div>\n <div className=\"u-flex u-flex-align-center col-md-2 col-sm-3\">\n <BEButton wide filled primary onClick={() => confirmRateOnClick()}>\n {t(Translation.Step.Room.Confirm)}\n </BEButton>\n </div>\n </div>\n </div>\n <div className=\"container\" style={{ backgroundColor: Color.Snow }}>\n <div className=\"u-pad-top u-marg-bottom\">\n <div className=\"u-marg-bottom--heavy\">\n <Headline bold>{t(Translation.Step.Room.CheckOutOtherOptions)}</Headline>\n </div>\n\n {/* Combined Upgrades - Rate upgrades first, then Room upgrades */}\n <div className=\"row no-gutters\">\n {allowedUpgrades.map((rate) => {\n const isRateUpgrade = rate.getRoom().code === selectedRate?.getRoom().code;\n const key = `room-${rate.getRoom().code}-${rate.code}`;\n\n return (\n <div className=\"col-md-6 col-xl-4 u-marg-bottom u-pad-left--light u-pad-right--light\" key={key}>\n {selectedRate &&\n (isRateUpgrade ? (\n <LargeRateCard\n selectedRate={selectedRate}\n rate={rate}\n onSelectRate={() => confirmRateOnClick(rate)}\n startDate={row.getStartDate()}\n />\n ) : (\n <LargeRoomCard selectedRate={selectedRate} rate={rate} onSelectRate={() => confirmRateOnClick(rate)} />\n ))}\n </div>\n );\n })}\n </div>\n </div>\n </div>\n </div>\n </SimpleModal>\n ) : null;\n};\n\nexport default AvailableUpgradesModal;\n"]}
@@ -37,6 +37,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
37
37
  };
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
39
  exports.default = RoomFilterRoomType;
40
+ const contexts_1 = require("../../../../contexts/index.js");
40
41
  const react_1 = __importStar(require("react"));
41
42
  const react_i18next_1 = require("react-i18next");
42
43
  const Translation_1 = require("translations/Translation");
@@ -45,24 +46,34 @@ const Checkbox_1 = __importDefault(require("../../../generic/Checkbox/Checkbox")
45
46
  const SmallSpinner_1 = __importDefault(require("../../../generic/loader/SmallSpinner"));
46
47
  const Text_1 = __importStar(require("../../../generic/Text"));
47
48
  const RoomTypeFilter_1 = __importDefault(require("../../../../models/Room/Filters/RoomTypeFilter"));
49
+ const RoomSortProvider_1 = __importDefault(require("../../../../providers/RoomSortProvider"));
48
50
  function RoomFilterRoomType(props) {
49
51
  var _a;
50
52
  const { t } = (0, react_i18next_1.useTranslation)();
53
+ const beContext = (0, react_1.useContext)(contexts_1.BookingEngineContext);
51
54
  const [selectedRooms, setSelectedRooms] = (0, react_1.useState)({});
52
55
  const [resetCounter, setResetCounter] = (0, react_1.useState)(0);
53
56
  const obj = (0, react_1.useRef)(null);
57
+ const sortBy = beContext.currentRoomSort;
58
+ // Sort rooms using the same logic as the room list so the filter order matches the displayed rooms.
59
+ const sortedRoomList = (0, react_1.useMemo)(() => {
60
+ var _a;
61
+ const sorted = [...((_a = props.roomList) !== null && _a !== void 0 ? _a : [])];
62
+ sorted.sort(RoomSortProvider_1.default.getSortByFunction(sortBy));
63
+ return sorted;
64
+ }, [props.roomList, sortBy]);
54
65
  (0, react_1.useEffect)(() => {
55
- if (props.roomList) {
66
+ if (sortedRoomList) {
56
67
  const roomTypes = {};
57
68
  const visibleRoomsFilter = props.filter ? props.filter.getVisibleRooms() : null;
58
- props.roomList.forEach((room) => {
69
+ sortedRoomList.forEach((room) => {
59
70
  roomTypes[room.name] = {
60
71
  status: visibleRoomsFilter ? visibleRoomsFilter.includes(room.name) || visibleRoomsFilter.includes(room.code) : true,
61
72
  };
62
73
  });
63
74
  setSelectedRooms(roomTypes);
64
75
  }
65
- }, [props.roomList, resetCounter]);
76
+ }, [sortedRoomList, resetCounter]);
66
77
  const allRooms = Object.keys(selectedRooms);
67
78
  const rooms = allRooms.filter((name) => selectedRooms[name].status);
68
79
  (0, react_1.useEffect)(() => {
@@ -1 +1 @@
1
- {"version":3,"file":"RoomFilterRoomType.js","sourceRoot":"/","sources":["src/components/steps/room/roomFilter/RoomFilterRoomType.tsx"],"names":[],"mappings":"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","sourcesContent":["import React, { useEffect, useRef, useState } from 'react';\nimport { useTranslation } from 'react-i18next';\nimport { Translation } from 'translations/Translation';\n\nimport BEButton from '@/components/generic/BEButton';\nimport Checkbox from '@/components/generic/Checkbox/Checkbox';\nimport SmallSpinner from '@/components/generic/loader/SmallSpinner';\nimport Text, { TextType } from '@/components/generic/Text';\nimport RoomTypeFilter from '@/models/Room/Filters/RoomTypeFilter';\nimport { Room } from '@/models/Room/Room';\n\ninterface RoomFilterRoomTypeProps {\n roomList: Room[];\n filter: RoomTypeFilter;\n onChange: (filter: RoomTypeFilter | null) => void;\n}\n\ninterface RoomFilterRoomTypeStatus {\n [name: string]: {\n status: boolean;\n };\n}\n\nexport default function RoomFilterRoomType(props: RoomFilterRoomTypeProps) {\n const { t } = useTranslation();\n const [selectedRooms, setSelectedRooms] = useState<{ [name: string]: { status: boolean } }>({});\n const [resetCounter, setResetCounter] = useState(0);\n const obj = useRef<HTMLDivElement | null>(null);\n\n useEffect(() => {\n if (props.roomList) {\n const roomTypes: RoomFilterRoomTypeStatus = {};\n const visibleRoomsFilter = props.filter ? props.filter.getVisibleRooms() : null;\n\n props.roomList.forEach((room) => {\n roomTypes[room.name as string] = {\n status: visibleRoomsFilter ? visibleRoomsFilter.includes(room.name as string) || visibleRoomsFilter.includes(room.code as string) : true,\n };\n });\n\n setSelectedRooms(roomTypes);\n }\n }, [props.roomList, resetCounter]);\n\n const allRooms = Object.keys(selectedRooms);\n const rooms = allRooms.filter((name) => selectedRooms[name].status);\n useEffect(() => {\n if (!obj?.current || !props.roomList?.length) return;\n if (rooms.length === allRooms.length) {\n props.onChange(null);\n } else {\n props.onChange(new RoomTypeFilter(rooms));\n }\n }, [selectedRooms]);\n const clearOnClickHandler = () => {\n props.onChange(null);\n setResetCounter((old) => old + 1);\n };\n return (\n <>\n <div className=\"u-flex u-marg-bottom u-marg-top--heavy\" ref={obj}>\n <Text inline type={TextType.Small}>\n <strong>{t(Translation.Step.Room.Filter.RoomType)}</strong>\n </Text>\n <BEButton size=\"tiny\" isText disabled={!props.filter} onClick={clearOnClickHandler}>\n {t(Translation.Misc.Clear)}\n </BEButton>\n </div>\n <div>\n {props.roomList?.length > 0 ? (\n selectedRooms &&\n Object.keys(selectedRooms).map((selectedRoomKey) => {\n const onChangeCheckboxHandler = (value: React.ChangeEvent<HTMLInputElement>) => {\n const oldRooms = { ...selectedRooms };\n oldRooms[selectedRoomKey].status = value.target.checked;\n setSelectedRooms(oldRooms);\n };\n return (\n <div className=\"u-marg-bottom--light\" key={selectedRoomKey}>\n <Checkbox label={selectedRoomKey} checked={selectedRooms[selectedRoomKey].status} onChange={onChangeCheckboxHandler} />\n </div>\n );\n })\n ) : (\n <div className=\"u-marg-top-bottom u-flex justify-content-center\">\n <SmallSpinner />\n </div>\n )}\n </div>\n </>\n );\n}\n"]}
1
+ {"version":3,"file":"RoomFilterRoomType.js","sourceRoot":"/","sources":["src/components/steps/room/roomFilter/RoomFilterRoomType.tsx"],"names":[],"mappings":"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","sourcesContent":["import { BookingEngineContext } from '@frontend/contexts';\nimport React, { useContext, useEffect, useMemo, useRef, useState } from 'react';\nimport { useTranslation } from 'react-i18next';\nimport { Translation } from 'translations/Translation';\n\nimport BEButton from '@/components/generic/BEButton';\nimport Checkbox from '@/components/generic/Checkbox/Checkbox';\nimport SmallSpinner from '@/components/generic/loader/SmallSpinner';\nimport Text, { TextType } from '@/components/generic/Text';\nimport RoomTypeFilter from '@/models/Room/Filters/RoomTypeFilter';\nimport { Room } from '@/models/Room/Room';\nimport RoomSortProvider from '@/providers/RoomSortProvider';\n\ninterface RoomFilterRoomTypeProps {\n roomList: Room[];\n filter: RoomTypeFilter;\n onChange: (filter: RoomTypeFilter | null) => void;\n}\n\ninterface RoomFilterRoomTypeStatus {\n [name: string]: {\n status: boolean;\n };\n}\n\nexport default function RoomFilterRoomType(props: RoomFilterRoomTypeProps) {\n const { t } = useTranslation();\n const beContext = useContext(BookingEngineContext);\n const [selectedRooms, setSelectedRooms] = useState<{ [name: string]: { status: boolean } }>({});\n const [resetCounter, setResetCounter] = useState(0);\n const obj = useRef<HTMLDivElement | null>(null);\n\n const sortBy = beContext.currentRoomSort;\n\n // Sort rooms using the same logic as the room list so the filter order matches the displayed rooms.\n const sortedRoomList = useMemo(() => {\n const sorted = [...(props.roomList ?? [])];\n sorted.sort(RoomSortProvider.getSortByFunction(sortBy));\n return sorted;\n }, [props.roomList, sortBy]);\n\n useEffect(() => {\n if (sortedRoomList) {\n const roomTypes: RoomFilterRoomTypeStatus = {};\n const visibleRoomsFilter = props.filter ? props.filter.getVisibleRooms() : null;\n\n sortedRoomList.forEach((room) => {\n roomTypes[room.name as string] = {\n status: visibleRoomsFilter ? visibleRoomsFilter.includes(room.name as string) || visibleRoomsFilter.includes(room.code as string) : true,\n };\n });\n\n setSelectedRooms(roomTypes);\n }\n }, [sortedRoomList, resetCounter]);\n\n const allRooms = Object.keys(selectedRooms);\n const rooms = allRooms.filter((name) => selectedRooms[name].status);\n useEffect(() => {\n if (!obj?.current || !props.roomList?.length) return;\n if (rooms.length === allRooms.length) {\n props.onChange(null);\n } else {\n props.onChange(new RoomTypeFilter(rooms));\n }\n }, [selectedRooms]);\n const clearOnClickHandler = () => {\n props.onChange(null);\n setResetCounter((old) => old + 1);\n };\n return (\n <>\n <div className=\"u-flex u-marg-bottom u-marg-top--heavy\" ref={obj}>\n <Text inline type={TextType.Small}>\n <strong>{t(Translation.Step.Room.Filter.RoomType)}</strong>\n </Text>\n <BEButton size=\"tiny\" isText disabled={!props.filter} onClick={clearOnClickHandler}>\n {t(Translation.Misc.Clear)}\n </BEButton>\n </div>\n <div>\n {props.roomList?.length > 0 ? (\n selectedRooms &&\n Object.keys(selectedRooms).map((selectedRoomKey) => {\n const onChangeCheckboxHandler = (value: React.ChangeEvent<HTMLInputElement>) => {\n const oldRooms = { ...selectedRooms };\n oldRooms[selectedRoomKey].status = value.target.checked;\n setSelectedRooms(oldRooms);\n };\n return (\n <div className=\"u-marg-bottom--light\" key={selectedRoomKey}>\n <Checkbox label={selectedRoomKey} checked={selectedRooms[selectedRoomKey].status} onChange={onChangeCheckboxHandler} />\n </div>\n );\n })\n ) : (\n <div className=\"u-marg-top-bottom u-flex justify-content-center\">\n <SmallSpinner />\n </div>\n )}\n </div>\n </>\n );\n}\n"]}
@@ -82,6 +82,7 @@ const Debounce_1 = require("../../util/Debounce");
82
82
  const GetStepRoom_1 = __importDefault(require("../../util/GetStepRoom"));
83
83
  const StepManager_1 = __importDefault(require("../../util/StepManager"));
84
84
  const StringHelper_1 = __importDefault(require("../../util/StringHelper"));
85
+ const SyncBasketAddonRowQuantities_1 = require("../../util/SyncBasketAddonRowQuantities");
85
86
  const AddonDiscountCalculator_1 = require("../../utils/AddonDiscountCalculator");
86
87
  function BasketContextWrapper(props) {
87
88
  var _a, _b, _c, _d;
@@ -222,6 +223,28 @@ function BasketContextWrapper(props) {
222
223
  onlyBasketRow.setEndDate(endDate);
223
224
  workingContext.updateBasketRow(onlyBasketRow, false);
224
225
  }
226
+ // Handle addon rows when dates change
227
+ if (basketAddonRows.length > 0) {
228
+ const newAddonRows = basketAddonRows.filter((addonRow) => {
229
+ const addon = addonRow.getAddon();
230
+ if (!addon)
231
+ return false;
232
+ // Check if addon has any available date within [startDate, endDate)
233
+ const hasAvailabilityInRange = Object.keys(addon.availability).some((date) => {
234
+ const addonDate = (0, dayjs_1.default)(date, 'YYYY-MM-DD');
235
+ return !addonDate.isBefore(startDate, 'day') && addonDate.isBefore(endDate, 'day');
236
+ });
237
+ if (!hasAvailabilityInRange) {
238
+ return false; // Remove addon — not available in new date range
239
+ }
240
+ // Update addon dates so per-night pricing uses the correct night count
241
+ addon.checkInDate = startDate;
242
+ addon.checkOutDate = endDate;
243
+ addon.nights = endDate.diff(startDate, 'day');
244
+ return true;
245
+ });
246
+ setBasketAddonRows([...newAddonRows]);
247
+ }
225
248
  }, 500), [startDate, endDate]);
226
249
  (0, react_1.useEffect)(() => {
227
250
  debounceUseEffect();
@@ -255,6 +278,22 @@ function BasketContextWrapper(props) {
255
278
  (0, react_1.useEffect)(() => {
256
279
  updateBasketFeeRows();
257
280
  }, [currentBasketRows, basketAddonRows]);
281
+ (0, react_1.useEffect)(() => {
282
+ if (basketAddonRows.length === 0)
283
+ return;
284
+ const basketRow = currentBasketRows === null || currentBasketRows === void 0 ? void 0 : currentBasketRows[0];
285
+ if (!basketRow)
286
+ return;
287
+ let changed = false;
288
+ basketAddonRows.forEach((addonRow) => {
289
+ if ((0, SyncBasketAddonRowQuantities_1.syncBasketAddonRowQuantities)(addonRow, basketRow)) {
290
+ changed = true;
291
+ }
292
+ });
293
+ if (changed) {
294
+ setBasketAddonRows([...basketAddonRows]);
295
+ }
296
+ }, [currentBasketRows]);
258
297
  const checkAndApplyMemberOnlyPromo = (row, skipUpdate = false) => {
259
298
  if (row && (hotel === null || hotel === void 0 ? void 0 : hotel.memberOnlyPromoCode) && signedInUser && !row.getPromoCode()) {
260
299
  if (isExternalMemberHotel) {
@@ -1063,28 +1102,9 @@ function BasketContextWrapper(props) {
1063
1102
  addonRow.setChildQuantity((_c = quantities.child) !== null && _c !== void 0 ? _c : 0);
1064
1103
  addonRow.setSelectedDate(selectedDate);
1065
1104
  addonRow.setSelectedTime(addon.availability[selectedDate].selectorTimes ? selectedTime : undefined);
1066
- // TODO: Quick hack to fix Synxis Basic Addon quantities.
1067
- if (addon.pricingType === core_1.EAddonPricingType.PER_ADULT_OCCUPANCY_PER_NIGHT) {
1068
- // TODO: Because all addons only apply to first room, we can select the 0th from the list.
1069
- const basketRow = currentBasketRows === null || currentBasketRows === void 0 ? void 0 : currentBasketRows[0];
1070
- if (basketRow) {
1071
- addonRow.setAdultQuantity(basketRow.getAdults());
1072
- }
1073
- }
1074
- if (addon.pricingType === core_1.EAddonPricingType.PER_CHILD_OCCUPANCY_PER_NIGHT) {
1075
- // TODO: Because all addons only apply to first room, we can select the 0th from the list.
1076
- const basketRow = currentBasketRows === null || currentBasketRows === void 0 ? void 0 : currentBasketRows[0];
1077
- if (basketRow) {
1078
- addonRow.setChildQuantity(basketRow.getChildren());
1079
- }
1080
- }
1081
- // TODO: More hackyness to handle RMS Per Person Fee requirements
1082
- if (addon.pricingType === core_1.EAddonPricingType.PER_ALL_PERSONS || addon.pricingType === core_1.EAddonPricingType.PER_ALL_PERSONS_PER_NIGHT) {
1083
- const basketRow = currentBasketRows === null || currentBasketRows === void 0 ? void 0 : currentBasketRows[0];
1084
- if (basketRow) {
1085
- addonRow.setAdultQuantity(basketRow.getAdults());
1086
- addonRow.setChildQuantity(basketRow.getChildren());
1087
- }
1105
+ const basketRow = currentBasketRows === null || currentBasketRows === void 0 ? void 0 : currentBasketRows[0];
1106
+ if (basketRow) {
1107
+ (0, SyncBasketAddonRowQuantities_1.syncBasketAddonRowQuantities)(addonRow, basketRow);
1088
1108
  }
1089
1109
  setBasketAddonRows([...basketAddonRows]);
1090
1110
  },