@roomstay/frontend 2.6.98 → 2.6.99

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 (39) hide show
  1. package/dist/328.bundle.js +1 -1
  2. package/dist/370.bundle.js +1 -1
  3. package/dist/978.bundle.js +1 -1
  4. package/dist/main.bundle.js +1 -1
  5. package/dist/src/api/AvailabilityAPI.d.ts +3 -2
  6. package/dist/src/api/AvailabilityAPI.js +9 -5
  7. package/dist/src/api/AvailabilityAPI.js.map +1 -1
  8. package/dist/src/components/steps/addons/AddonCard.js +114 -9
  9. package/dist/src/components/steps/addons/AddonCard.js.map +1 -1
  10. package/dist/src/components/steps/room/RoomList.js.map +1 -1
  11. package/dist/src/components/steps/room/RoomModal.js +1 -1
  12. package/dist/src/components/steps/room/RoomModal.js.map +1 -1
  13. package/dist/src/components/steps/room/TabGroupedRooms/TabGroupedRooms.js.map +1 -1
  14. package/dist/src/components/summary/BEMobileSummaryModal.js +4 -3
  15. package/dist/src/components/summary/BEMobileSummaryModal.js.map +1 -1
  16. package/dist/src/components/summary/BESummary.js +4 -3
  17. package/dist/src/components/summary/BESummary.js.map +1 -1
  18. package/dist/src/components/summary/BESummaryAddonRow.d.ts +3 -0
  19. package/dist/src/components/summary/BESummaryAddonRow.js +16 -4
  20. package/dist/src/components/summary/BESummaryAddonRow.js.map +1 -1
  21. package/dist/src/contexts/BasketContext/BasketContextType.d.ts +3 -1
  22. package/dist/src/contexts/BasketContext/BasketContextType.js.map +1 -1
  23. package/dist/src/contexts/BasketContext/BasketContextWrapper.js +38 -4
  24. package/dist/src/contexts/BasketContext/BasketContextWrapper.js.map +1 -1
  25. package/dist/src/contexts/CompanyContext/CompanyContextWrapper.js +1 -1
  26. package/dist/src/contexts/CompanyContext/CompanyContextWrapper.js.map +1 -1
  27. package/dist/src/contexts/FullPageEngineContext/FullPageEngineContextWrapper.js +6 -5
  28. package/dist/src/contexts/FullPageEngineContext/FullPageEngineContextWrapper.js.map +1 -1
  29. package/dist/src/engines/InlineRoomMiniEngine/InlineRoomMiniEngineElement.js +5 -3
  30. package/dist/src/engines/InlineRoomMiniEngine/InlineRoomMiniEngineElement.js.map +1 -1
  31. package/dist/src/models/BasketAddonRow.d.ts +27 -0
  32. package/dist/src/models/BasketAddonRow.js +50 -0
  33. package/dist/src/models/BasketAddonRow.js.map +1 -1
  34. package/dist/src/utils/AddonDiscountCalculator.d.ts +79 -0
  35. package/dist/src/utils/AddonDiscountCalculator.js +142 -0
  36. package/dist/src/utils/AddonDiscountCalculator.js.map +1 -0
  37. package/dist/test.bundle.js +1 -1
  38. package/dist/vendors.bundle.js +1 -1
  39. package/package.json +2 -2
@@ -0,0 +1,142 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AddonDiscountCalculator = void 0;
4
+ const core_1 = require("@roomstay/core");
5
+ /**
6
+ * For `flatQuantity` and `flatQuantityPerStay` promo codes, discount allocation is SEQUENTIAL:
7
+ * The discount is consumed in this order:
8
+ * 1. Room rates
9
+ * 2. First addon added to basket
10
+ * 3. Second addon added to basket
11
+ * 4. Etc
12
+ *
13
+ * For `flatPercent` types, order doesn't matter as each addon gets its own percentage discount.
14
+ */
15
+ class AddonDiscountCalculator {
16
+ /**
17
+ * Calculate total price of all addons with promo code discounts applied.
18
+ * Discounts are applied sequentially for flatQuantityPerStay promo codes.
19
+ *
20
+ * @param addonRows - Array of basket addon rows (ORDER MATTERS for flatQuantityPerStay!)
21
+ * @param roomRows - Array of basket room rows
22
+ * @param promoCodeDetails - Promo code details from basket context
23
+ * @returns Total price of all addons after discounts
24
+ */
25
+ static calculateTotal(addonRows, roomRows, promoCodeDetails) {
26
+ if (!(promoCodeDetails === null || promoCodeDetails === void 0 ? void 0 : promoCodeDetails.applyToAddons)) {
27
+ return addonRows.reduce((total, row) => total + row.getTotalPrice(), 0);
28
+ }
29
+ const individualDiscounts = this.calculateIndividualDiscounts(addonRows, roomRows, promoCodeDetails);
30
+ return individualDiscounts.reduce((total, { addon, discount }) => {
31
+ return total + addon.getTotalPrice() - discount;
32
+ }, 0);
33
+ }
34
+ /**
35
+ * Calculate discount for each addon individually with remaining discount tracking.
36
+ * Useful for displaying discounts in the UI where you need to show each addon's
37
+ * discount separately.
38
+ *
39
+ * @param addonRows - Array of basket addon rows (ORDER MATTERS!)
40
+ * @param roomRows - Array of basket room rows
41
+ * @param promoCodeDetails - Promo code details
42
+ * @returns Array of objects with addon, its discount, and remaining discount available
43
+ */
44
+ static calculateIndividualDiscounts(addonRows, roomRows, promoCodeDetails) {
45
+ if (!(promoCodeDetails === null || promoCodeDetails === void 0 ? void 0 : promoCodeDetails.applyToAddons)) {
46
+ return addonRows.map((addon) => ({ addon, discount: 0, remainingDiscount: 0 }));
47
+ }
48
+ const { totalDiscountAvailable, runningDiscount: initialRunningDiscount } = this.getDiscountInfo(roomRows, promoCodeDetails);
49
+ let runningDiscount = initialRunningDiscount;
50
+ const results = [];
51
+ for (const addon of addonRows) {
52
+ const remainingFixedDiscount = this.getRemainingFixedDiscount(promoCodeDetails, totalDiscountAvailable, runningDiscount);
53
+ const discount = addon.getDiscountAmount(promoCodeDetails, remainingFixedDiscount);
54
+ results.push({ addon, discount, remainingDiscount: remainingFixedDiscount });
55
+ runningDiscount += discount;
56
+ }
57
+ return results;
58
+ }
59
+ /**
60
+ * Calculate discount for an addon being previewed (not yet in basket).
61
+ * This is used in the addon selection UI to show what discount would apply.
62
+ *
63
+ * For flatQuantityPerStay promos, this calculates the remaining discount after
64
+ * accounting for rooms and OTHER addons already in the basket.
65
+ *
66
+ * @param basePrice - Base price of the addon (before discount, before fees)
67
+ * @param addonRows - Array of basket addon rows (addons already in basket)
68
+ * @param roomRows - Array of basket room rows
69
+ * @param promoCodeDetails - Promo code details
70
+ * @returns Discount amount that would apply to this addon
71
+ */
72
+ static calculatePreviewDiscount(basePrice, addonRows, roomRows, promoCodeDetails) {
73
+ if (!(promoCodeDetails === null || promoCodeDetails === void 0 ? void 0 : promoCodeDetails.applyToAddons) || basePrice <= 0) {
74
+ return 0;
75
+ }
76
+ switch (promoCodeDetails.discountType) {
77
+ case core_1.EPromoCodeDiscountType.FlatPercent:
78
+ return Math.round(basePrice * (promoCodeDetails.discountAmount / 100));
79
+ case core_1.EPromoCodeDiscountType.FlatQuantity:
80
+ case core_1.EPromoCodeDiscountType.FlatQuantityPerStay: {
81
+ const { totalDiscountAvailable, runningDiscount: initialRunningDiscount } = this.getDiscountInfo(roomRows, promoCodeDetails);
82
+ // Calculate discount consumed by other addons in basket
83
+ let runningDiscount = initialRunningDiscount;
84
+ addonRows.forEach((row) => {
85
+ const addonRemainingDiscount = this.getRemainingFixedDiscount(promoCodeDetails, totalDiscountAvailable, runningDiscount);
86
+ const addonDiscount = row.getDiscountAmount(promoCodeDetails, addonRemainingDiscount);
87
+ runningDiscount += addonDiscount;
88
+ });
89
+ // Calculate remaining discount for this preview addon
90
+ const remainingDiscount = this.getRemainingFixedDiscount(promoCodeDetails, totalDiscountAvailable, runningDiscount);
91
+ return Math.min(remainingDiscount, basePrice);
92
+ }
93
+ default:
94
+ return 0;
95
+ }
96
+ }
97
+ /**
98
+ * Get discount info including total available and initial running discount from rooms.
99
+ *
100
+ * @param roomRows - Array of basket room rows
101
+ * @param promoCodeDetails - Promo code details
102
+ * @returns Object with totalDiscountAvailable and runningDiscount (from rooms)
103
+ */
104
+ static getDiscountInfo(roomRows, promoCodeDetails) {
105
+ const nights = roomRows.length > 0 ? roomRows[0].getNumberOfDays() : 0;
106
+ const roomDiscount = this.calculateRoomDiscount(roomRows);
107
+ const totalDiscountAvailable = promoCodeDetails.discountType === core_1.EPromoCodeDiscountType.FlatQuantity ? promoCodeDetails.discountAmount * nights : promoCodeDetails.discountAmount;
108
+ return {
109
+ totalDiscountAvailable,
110
+ runningDiscount: roomDiscount,
111
+ };
112
+ }
113
+ /**
114
+ * Calculate remaining fixed discount available.
115
+ *
116
+ * @param promoCodeDetails - Promo code details
117
+ * @param totalDiscountAvailable - Total discount available
118
+ * @param runningDiscount - Discount already consumed
119
+ * @returns Remaining discount available (0 for FlatPercent)
120
+ */
121
+ static getRemainingFixedDiscount(promoCodeDetails, totalDiscountAvailable, runningDiscount) {
122
+ if (promoCodeDetails.discountType === core_1.EPromoCodeDiscountType.FlatQuantity || promoCodeDetails.discountType === core_1.EPromoCodeDiscountType.FlatQuantityPerStay) {
123
+ return Math.max(0, totalDiscountAvailable - runningDiscount);
124
+ }
125
+ return 0;
126
+ }
127
+ /**
128
+ * Calculate total discount already consumed by room rates.
129
+ * This is used as the starting point for addon discount calculations.
130
+ *
131
+ * @param roomRows - Array of basket room rows
132
+ * @returns Total discount amount consumed by all rooms
133
+ */
134
+ static calculateRoomDiscount(roomRows) {
135
+ return roomRows.reduce((total, row) => {
136
+ const rate = row.getRate();
137
+ return total + ((rate === null || rate === void 0 ? void 0 : rate.getTotalDiscount()) || 0);
138
+ }, 0);
139
+ }
140
+ }
141
+ exports.AddonDiscountCalculator = AddonDiscountCalculator;
142
+ //# sourceMappingURL=AddonDiscountCalculator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AddonDiscountCalculator.js","sourceRoot":"/","sources":["src/utils/AddonDiscountCalculator.ts"],"names":[],"mappings":"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","sourcesContent":["import { EPromoCodeDiscountType, IPromoCodeDetails } from '@roomstay/core';\n\nimport BasketAddonRow from '@/models/BasketAddonRow';\nimport BasketRow from '@/models/BasketRow';\n\n/**\n * For `flatQuantity` and `flatQuantityPerStay` promo codes, discount allocation is SEQUENTIAL:\n * The discount is consumed in this order:\n * 1. Room rates\n * 2. First addon added to basket\n * 3. Second addon added to basket\n * 4. Etc\n *\n * For `flatPercent` types, order doesn't matter as each addon gets its own percentage discount.\n */\nexport class AddonDiscountCalculator {\n /**\n * Calculate total price of all addons with promo code discounts applied.\n * Discounts are applied sequentially for flatQuantityPerStay promo codes.\n *\n * @param addonRows - Array of basket addon rows (ORDER MATTERS for flatQuantityPerStay!)\n * @param roomRows - Array of basket room rows\n * @param promoCodeDetails - Promo code details from basket context\n * @returns Total price of all addons after discounts\n */\n static calculateTotal(addonRows: BasketAddonRow[], roomRows: BasketRow[], promoCodeDetails: IPromoCodeDetails | null): number {\n if (!promoCodeDetails?.applyToAddons) {\n return addonRows.reduce((total, row) => total + row.getTotalPrice(), 0);\n }\n\n const individualDiscounts = this.calculateIndividualDiscounts(addonRows, roomRows, promoCodeDetails);\n\n return individualDiscounts.reduce((total, { addon, discount }) => {\n return total + addon.getTotalPrice() - discount;\n }, 0);\n }\n\n /**\n * Calculate discount for each addon individually with remaining discount tracking.\n * Useful for displaying discounts in the UI where you need to show each addon's\n * discount separately.\n *\n * @param addonRows - Array of basket addon rows (ORDER MATTERS!)\n * @param roomRows - Array of basket room rows\n * @param promoCodeDetails - Promo code details\n * @returns Array of objects with addon, its discount, and remaining discount available\n */\n static calculateIndividualDiscounts(\n addonRows: BasketAddonRow[],\n roomRows: BasketRow[],\n promoCodeDetails: IPromoCodeDetails | null\n ): Array<{ addon: BasketAddonRow; discount: number; remainingDiscount: number }> {\n if (!promoCodeDetails?.applyToAddons) {\n return addonRows.map((addon) => ({ addon, discount: 0, remainingDiscount: 0 }));\n }\n\n const { totalDiscountAvailable, runningDiscount: initialRunningDiscount } = this.getDiscountInfo(roomRows, promoCodeDetails);\n\n let runningDiscount = initialRunningDiscount;\n const results: Array<{ addon: BasketAddonRow; discount: number; remainingDiscount: number }> = [];\n\n for (const addon of addonRows) {\n const remainingFixedDiscount = this.getRemainingFixedDiscount(promoCodeDetails, totalDiscountAvailable, runningDiscount);\n const discount = addon.getDiscountAmount(promoCodeDetails, remainingFixedDiscount);\n\n results.push({ addon, discount, remainingDiscount: remainingFixedDiscount });\n runningDiscount += discount;\n }\n\n return results;\n }\n\n /**\n * Calculate discount for an addon being previewed (not yet in basket).\n * This is used in the addon selection UI to show what discount would apply.\n *\n * For flatQuantityPerStay promos, this calculates the remaining discount after\n * accounting for rooms and OTHER addons already in the basket.\n *\n * @param basePrice - Base price of the addon (before discount, before fees)\n * @param addonRows - Array of basket addon rows (addons already in basket)\n * @param roomRows - Array of basket room rows\n * @param promoCodeDetails - Promo code details\n * @returns Discount amount that would apply to this addon\n */\n static calculatePreviewDiscount(basePrice: number, addonRows: BasketAddonRow[], roomRows: BasketRow[], promoCodeDetails: IPromoCodeDetails | null): number {\n if (!promoCodeDetails?.applyToAddons || basePrice <= 0) {\n return 0;\n }\n\n switch (promoCodeDetails.discountType) {\n case EPromoCodeDiscountType.FlatPercent:\n return Math.round(basePrice * (promoCodeDetails.discountAmount / 100));\n\n case EPromoCodeDiscountType.FlatQuantity:\n case EPromoCodeDiscountType.FlatQuantityPerStay: {\n const { totalDiscountAvailable, runningDiscount: initialRunningDiscount } = this.getDiscountInfo(roomRows, promoCodeDetails);\n\n // Calculate discount consumed by other addons in basket\n let runningDiscount = initialRunningDiscount;\n addonRows.forEach((row) => {\n const addonRemainingDiscount = this.getRemainingFixedDiscount(promoCodeDetails, totalDiscountAvailable, runningDiscount);\n const addonDiscount = row.getDiscountAmount(promoCodeDetails, addonRemainingDiscount);\n runningDiscount += addonDiscount;\n });\n\n // Calculate remaining discount for this preview addon\n const remainingDiscount = this.getRemainingFixedDiscount(promoCodeDetails, totalDiscountAvailable, runningDiscount);\n return Math.min(remainingDiscount, basePrice);\n }\n\n default:\n return 0;\n }\n }\n\n /**\n * Get discount info including total available and initial running discount from rooms.\n *\n * @param roomRows - Array of basket room rows\n * @param promoCodeDetails - Promo code details\n * @returns Object with totalDiscountAvailable and runningDiscount (from rooms)\n */\n private static getDiscountInfo(roomRows: BasketRow[], promoCodeDetails: IPromoCodeDetails): { totalDiscountAvailable: number; runningDiscount: number } {\n const nights = roomRows.length > 0 ? roomRows[0].getNumberOfDays() : 0;\n const roomDiscount = this.calculateRoomDiscount(roomRows);\n\n const totalDiscountAvailable =\n promoCodeDetails.discountType === EPromoCodeDiscountType.FlatQuantity ? promoCodeDetails.discountAmount * nights : promoCodeDetails.discountAmount;\n\n return {\n totalDiscountAvailable,\n runningDiscount: roomDiscount,\n };\n }\n\n /**\n * Calculate remaining fixed discount available.\n *\n * @param promoCodeDetails - Promo code details\n * @param totalDiscountAvailable - Total discount available\n * @param runningDiscount - Discount already consumed\n * @returns Remaining discount available (0 for FlatPercent)\n */\n private static getRemainingFixedDiscount(promoCodeDetails: IPromoCodeDetails, totalDiscountAvailable: number, runningDiscount: number): number {\n if (promoCodeDetails.discountType === EPromoCodeDiscountType.FlatQuantity || promoCodeDetails.discountType === EPromoCodeDiscountType.FlatQuantityPerStay) {\n return Math.max(0, totalDiscountAvailable - runningDiscount);\n }\n return 0;\n }\n\n /**\n * Calculate total discount already consumed by room rates.\n * This is used as the starting point for addon discount calculations.\n *\n * @param roomRows - Array of basket room rows\n * @returns Total discount amount consumed by all rooms\n */\n private static calculateRoomDiscount(roomRows: BasketRow[]): number {\n return roomRows.reduce((total, row) => {\n const rate = row.getRate();\n return total + (rate?.getTotalDiscount() || 0);\n }, 0);\n }\n}\n"]}