ng-miam 10.7.1 → 10.8.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 (54) hide show
  1. package/bundles/ng-miam.umd.js +370 -99
  2. package/bundles/ng-miam.umd.js.map +1 -1
  3. package/bundles/ng-miam.umd.min.js +1 -1
  4. package/bundles/ng-miam.umd.min.js.map +1 -1
  5. package/esm2015/lib/_components/product-card/product-card.component.js +11 -3
  6. package/esm2015/lib/_constants/error-toast-codes.js +8 -0
  7. package/esm2015/lib/_models/basket-actions/add-ingredient-from-replace-action.js +4 -2
  8. package/esm2015/lib/_services/analytics.service.js +5 -2
  9. package/esm2015/lib/_services/context-registry.service.js +2 -1
  10. package/esm2015/lib/_services/context.service.js +9 -2
  11. package/esm2015/lib/_services/new-recipe-details.service.js +32 -8
  12. package/esm2015/lib/_services/recipe-details-products.service.js +25 -5
  13. package/esm2015/lib/_services/recipe-details.service.js +3 -4
  14. package/esm2015/lib/_services/recipes.service.js +5 -1
  15. package/esm2015/lib/_services/replace-item.service.js +115 -60
  16. package/esm2015/lib/_services/store-out-of-stock.service.js +24 -5
  17. package/esm2015/lib/_types/builded/mealz-interface.js +1 -1
  18. package/esm2015/lib/_types/builded/mealz-internal-interface.js +1 -1
  19. package/esm2015/lib/_utils/attach-product-show-tracking.js +56 -0
  20. package/esm2015/lib/_utils/product-show-tracker.js +62 -0
  21. package/esm2015/lib/_web-components/basket-preview/basket-preview-product/basket-preview-product.component.js +3 -1
  22. package/esm2015/lib/environments/version.js +2 -2
  23. package/fesm2015/ng-miam.js +339 -86
  24. package/fesm2015/ng-miam.js.map +1 -1
  25. package/lib/_components/product-card/product-card.component.d.ts +1 -0
  26. package/lib/_components/product-card/product-card.component.d.ts.map +1 -1
  27. package/lib/_constants/error-toast-codes.d.ts +5 -0
  28. package/lib/_constants/error-toast-codes.d.ts.map +1 -0
  29. package/lib/_models/basket-actions/add-ingredient-from-replace-action.d.ts.map +1 -1
  30. package/lib/_services/analytics.service.d.ts.map +1 -1
  31. package/lib/_services/context-registry.service.d.ts +5 -0
  32. package/lib/_services/context-registry.service.d.ts.map +1 -1
  33. package/lib/_services/context.service.d.ts.map +1 -1
  34. package/lib/_services/new-recipe-details.service.d.ts +3 -1
  35. package/lib/_services/new-recipe-details.service.d.ts.map +1 -1
  36. package/lib/_services/recipe-details-products.service.d.ts +5 -1
  37. package/lib/_services/recipe-details-products.service.d.ts.map +1 -1
  38. package/lib/_services/recipe-details.service.d.ts.map +1 -1
  39. package/lib/_services/recipes.service.d.ts.map +1 -1
  40. package/lib/_services/replace-item.service.d.ts +26 -15
  41. package/lib/_services/replace-item.service.d.ts.map +1 -1
  42. package/lib/_services/store-out-of-stock.service.d.ts +7 -0
  43. package/lib/_services/store-out-of-stock.service.d.ts.map +1 -1
  44. package/lib/_types/builded/mealz-interface.d.ts +7 -0
  45. package/lib/_types/builded/mealz-interface.d.ts.map +1 -1
  46. package/lib/_types/builded/mealz-internal-interface.d.ts +9 -0
  47. package/lib/_types/builded/mealz-internal-interface.d.ts.map +1 -1
  48. package/lib/_utils/attach-product-show-tracking.d.ts +14 -0
  49. package/lib/_utils/attach-product-show-tracking.d.ts.map +1 -0
  50. package/lib/_utils/product-show-tracker.d.ts +25 -0
  51. package/lib/_utils/product-show-tracker.d.ts.map +1 -0
  52. package/lib/_web-components/basket-preview/basket-preview-product/basket-preview-product.component.d.ts.map +1 -1
  53. package/lib/environments/version.d.ts +1 -1
  54. package/package.json +1 -1
@@ -1647,7 +1647,7 @@
1647
1647
  analyticsEnabled: true // Only used in DEV mode
1648
1648
  };
1649
1649
 
1650
- var WINDOW_SINGLETON_KEY = '__mealzRecipeCardShowTracker__';
1650
+ var WINDOW_SINGLETON_KEY$1 = '__mealzRecipeCardShowTracker__';
1651
1651
  /**
1652
1652
  * Tracks recipe.show events and prevents duplicates (first show, or again after scroll).
1653
1653
  * Shared across SDK and Lit recipe cards via {@link WINDOW_SINGLETON_KEY} on window.
@@ -1672,7 +1672,7 @@
1672
1672
  var w = typeof window !== 'undefined'
1673
1673
  ? window
1674
1674
  : undefined;
1675
- var fromWindow = w === null || w === void 0 ? void 0 : w[WINDOW_SINGLETON_KEY];
1675
+ var fromWindow = w === null || w === void 0 ? void 0 : w[WINDOW_SINGLETON_KEY$1];
1676
1676
  if (fromWindow) {
1677
1677
  RecipeCardShowTracker.instance = fromWindow;
1678
1678
  return fromWindow;
@@ -1680,7 +1680,7 @@
1680
1680
  if (!RecipeCardShowTracker.instance) {
1681
1681
  RecipeCardShowTracker.instance = new RecipeCardShowTracker();
1682
1682
  if (w) {
1683
- w[WINDOW_SINGLETON_KEY] = RecipeCardShowTracker.instance;
1683
+ w[WINDOW_SINGLETON_KEY$1] = RecipeCardShowTracker.instance;
1684
1684
  }
1685
1685
  }
1686
1686
  return RecipeCardShowTracker.instance;
@@ -1740,7 +1740,7 @@
1740
1740
  this.hasScrolled = false;
1741
1741
  RecipeCardShowTracker.instance = null;
1742
1742
  if (typeof window !== 'undefined') {
1743
- delete window[WINDOW_SINGLETON_KEY];
1743
+ delete window[WINDOW_SINGLETON_KEY$1];
1744
1744
  }
1745
1745
  };
1746
1746
  RecipeCardShowTracker.prototype.setupScrollListener = function () {
@@ -1843,6 +1843,135 @@
1843
1843
  };
1844
1844
  }
1845
1845
 
1846
+ var WINDOW_SINGLETON_KEY = '__mealzProductShowTracker__';
1847
+ /**
1848
+ * Dedupes product.show per recipe visit (recipeId:itemId).
1849
+ * Cleared at the start of each openRecipeDetails visit and by ContextService.loadAndSetupRecipe
1850
+ * at the start of each recipe-to-basket visit.
1851
+ */
1852
+ var ProductShowTracker = /** @class */ (function () {
1853
+ function ProductShowTracker() {
1854
+ var _this = this;
1855
+ this.trackedKeys = new Set();
1856
+ this.handlePageUnload = function () {
1857
+ _this.destroy();
1858
+ };
1859
+ if (typeof window !== 'undefined') {
1860
+ window.addEventListener('beforeunload', this.handlePageUnload);
1861
+ }
1862
+ }
1863
+ ProductShowTracker.getInstance = function () {
1864
+ var windowRef = typeof window !== 'undefined' ? window : undefined;
1865
+ var fromWindow = windowRef === null || windowRef === void 0 ? void 0 : windowRef[WINDOW_SINGLETON_KEY];
1866
+ if (fromWindow) {
1867
+ ProductShowTracker.instance = fromWindow;
1868
+ return fromWindow;
1869
+ }
1870
+ if (!ProductShowTracker.instance) {
1871
+ ProductShowTracker.instance = new ProductShowTracker();
1872
+ if (windowRef) {
1873
+ windowRef[WINDOW_SINGLETON_KEY] = ProductShowTracker.instance;
1874
+ }
1875
+ }
1876
+ return ProductShowTracker.instance;
1877
+ };
1878
+ ProductShowTracker.prototype.trackProductShow = function (analyticsPath, props) {
1879
+ var _a, _b;
1880
+ var trackingKey = props.recipe_id + ":" + props.item_id;
1881
+ if (this.trackedKeys.has(trackingKey)) {
1882
+ return;
1883
+ }
1884
+ this.trackedKeys.add(trackingKey);
1885
+ (_b = (_a = window.mealzInternal) === null || _a === void 0 ? void 0 : _a.analytics) === null || _b === void 0 ? void 0 : _b.sendEvent('product.show', analyticsPath, props);
1886
+ };
1887
+ ProductShowTracker.prototype.clearForRecipe = function (recipeId) {
1888
+ var e_1, _c;
1889
+ var prefix = recipeId + ":";
1890
+ try {
1891
+ for (var _d = __values(this.trackedKeys), _e = _d.next(); !_e.done; _e = _d.next()) {
1892
+ var key = _e.value;
1893
+ if (key.startsWith(prefix)) {
1894
+ this.trackedKeys.delete(key);
1895
+ }
1896
+ }
1897
+ }
1898
+ catch (e_1_1) { e_1 = { error: e_1_1 }; }
1899
+ finally {
1900
+ try {
1901
+ if (_e && !_e.done && (_c = _d.return)) _c.call(_d);
1902
+ }
1903
+ finally { if (e_1) throw e_1.error; }
1904
+ }
1905
+ };
1906
+ ProductShowTracker.prototype.clearAll = function () {
1907
+ this.trackedKeys.clear();
1908
+ };
1909
+ ProductShowTracker.prototype.destroy = function () {
1910
+ if (typeof window !== 'undefined') {
1911
+ window.removeEventListener('beforeunload', this.handlePageUnload);
1912
+ delete window[WINDOW_SINGLETON_KEY];
1913
+ }
1914
+ this.trackedKeys.clear();
1915
+ ProductShowTracker.instance = null;
1916
+ };
1917
+ return ProductShowTracker;
1918
+ }());
1919
+ ProductShowTracker.instance = null;
1920
+
1921
+ var PRODUCT_SHOW_ANALYTICS_PATH_SUFFIX = '/shopping';
1922
+ var PRODUCT_SHOW_VISIBILITY_THRESHOLD = 0.8;
1923
+ var PRODUCT_SHOW_DEBOUNCE_MS = 1000;
1924
+ var SKIP_PRODUCT_SHOW_STATUSES = ['unavailable', 'out_of_stock', 'ignored', 'deleted', 'often_deleted'];
1925
+ function buildProductShowProps(product, recipeId) {
1926
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
1927
+ var basketEntry = product === null || product === void 0 ? void 0 : product.basketEntry;
1928
+ var selectedItem = basketEntry === null || basketEntry === void 0 ? void 0 : basketEntry.selectedItem;
1929
+ var item = (selectedItem === null || selectedItem === void 0 ? void 0 : selectedItem.id) != null ? selectedItem : product === null || product === void 0 ? void 0 : product.item;
1930
+ if ((item === null || item === void 0 ? void 0 : item.id) == null) {
1931
+ return null;
1932
+ }
1933
+ var itemAttributes = item.attributes || {};
1934
+ var entryName = (selectedItem === null || selectedItem === void 0 ? void 0 : selectedItem.id) != null
1935
+ ? ((_c = (_a = basketEntry.name) !== null && _a !== void 0 ? _a : (_b = basketEntry.attributes) === null || _b === void 0 ? void 0 : _b.name) !== null && _c !== void 0 ? _c : '')
1936
+ : ((_h = (_f = (_e = (_d = product.ingredient) === null || _d === void 0 ? void 0 : _d.attributes) === null || _e === void 0 ? void 0 : _e.name) !== null && _f !== void 0 ? _f : (_g = product.ingredient) === null || _g === void 0 ? void 0 : _g.name) !== null && _h !== void 0 ? _h : '');
1937
+ var productBasePrice = ((_j = product === null || product === void 0 ? void 0 : product.price) === null || _j === void 0 ? void 0 : _j.unit) != null ? String(product.price.unit) : '0.0';
1938
+ return {
1939
+ entry_name: entryName,
1940
+ item_id: String(item.id),
1941
+ ext_item_id: String((_k = itemAttributes['ext-id']) !== null && _k !== void 0 ? _k : ''),
1942
+ item_ean: String((_m = (_l = item.ean) !== null && _l !== void 0 ? _l : itemAttributes.ean) !== null && _m !== void 0 ? _m : ''),
1943
+ recipe_id: recipeId,
1944
+ product_base_price: productBasePrice
1945
+ };
1946
+ }
1947
+ function isProductShowEligible(status) {
1948
+ return !!status && !SKIP_PRODUCT_SHOW_STATUSES.includes(status);
1949
+ }
1950
+ function getProductShowStatus(product) {
1951
+ var _a, _b;
1952
+ return (_b = (_a = product.basketEntry) === null || _a === void 0 ? void 0 : _a.status) !== null && _b !== void 0 ? _b : product.status;
1953
+ }
1954
+ function attachProductShowTracking(options) {
1955
+ var element = options.element, product = options.product, recipeId = options.recipeId, _o = options.analyticsPath, analyticsPath = _o === void 0 ? "/recipes/" + recipeId + PRODUCT_SHOW_ANALYTICS_PATH_SUFFIX : _o;
1956
+ ProductShowTracker.getInstance();
1957
+ var listener = new ViewportListener(element, function () {
1958
+ if (!isProductShowEligible(getProductShowStatus(product))) {
1959
+ return;
1960
+ }
1961
+ var props = buildProductShowProps(product, recipeId);
1962
+ if (!props) {
1963
+ return;
1964
+ }
1965
+ ProductShowTracker.getInstance().trackProductShow(analyticsPath, props);
1966
+ }, {
1967
+ threshold: PRODUCT_SHOW_VISIBILITY_THRESHOLD,
1968
+ debounce: PRODUCT_SHOW_DEBOUNCE_MS
1969
+ });
1970
+ return {
1971
+ disconnect: function () { return listener.disconnect(); }
1972
+ };
1973
+ }
1974
+
1846
1975
  var Ingredient = /** @class */ (function (_super) {
1847
1976
  __extends(Ingredient, _super);
1848
1977
  function Ingredient() {
@@ -3646,7 +3775,7 @@
3646
3775
  EventJourney["EMPTY"] = "";
3647
3776
  })(EventJourney || (EventJourney = {}));
3648
3777
 
3649
- var VERSION = "10.7.1"; // TODO: replace by ##VERSION## and update it in the CI/CD
3778
+ var VERSION = "10.8.0"; // TODO: replace by ##VERSION## and update it in the CI/CD
3650
3779
 
3651
3780
  var ContextRegistryService = /** @class */ (function () {
3652
3781
  function ContextRegistryService() {
@@ -3655,6 +3784,7 @@
3655
3784
  this.guestsChanged = new i0.EventEmitter();
3656
3785
  this.recipePriceChanged = new i0.EventEmitter();
3657
3786
  this.addAllProductsWasClicked = new i0.EventEmitter();
3787
+ this.drawerErrorToast = new i0.EventEmitter();
3658
3788
  // CALLBACKS
3659
3789
  this.hookCallback = function (isLogged, isPosValid) {
3660
3790
  mealzWarn('[Mealz] hookCallback (used to force user login) not set');
@@ -3794,7 +3924,7 @@
3794
3924
  var detectedJourney = journey !== null && journey !== void 0 ? journey : detectJourney();
3795
3925
  var isBulk = actions.length > 1;
3796
3926
  actions.forEach(function (action) {
3797
- var _a, _b, _c, _d, _e, _f, _g, _h, _j;
3927
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
3798
3928
  try {
3799
3929
  var params = action.params;
3800
3930
  // Determine add_source: replacement if previousItem exists, otherwise unit or bulk based on actions count
@@ -3827,6 +3957,7 @@
3827
3957
  ext_item_id: (_j = item.attributes) === null || _j === void 0 ? void 0 : _j['ext-id'],
3828
3958
  item_ean: item.ean,
3829
3959
  product_quantity: quantity.toString(),
3960
+ product_base_price: ((_k = basketEntry === null || basketEntry === void 0 ? void 0 : basketEntry.price) !== null && _k !== void 0 ? _k : 0).toString(),
3830
3961
  recipe_id: recipeId,
3831
3962
  add_source: addSource
3832
3963
  }, detectedJourney);
@@ -3840,12 +3971,14 @@
3840
3971
  var _this = this;
3841
3972
  var detectedJourney = journey !== null && journey !== void 0 ? journey : detectJourney();
3842
3973
  actions.forEach(function (action) {
3974
+ var _a;
3843
3975
  _this.sendEvent('entry.deleted', action.params.eventTrace.originPath, {
3844
3976
  entry_name: action.params.basketEntry.name,
3845
3977
  item_id: action.params.basketEntry.selectedItem.id,
3846
3978
  ext_item_id: action.params.basketEntry.selectedItem.attributes['ext-id'],
3847
3979
  item_ean: action.params.basketEntry.selectedItem.ean,
3848
3980
  product_quantity: action.params.basketEntry.quantity.toString(),
3981
+ product_base_price: ((_a = action.params.basketEntry.price) !== null && _a !== void 0 ? _a : 0).toString(),
3849
3982
  recipe_id: action.params.recipeId
3850
3983
  }, detectedJourney);
3851
3984
  });
@@ -5902,6 +6035,9 @@
5902
6035
  RecipesService.prototype.openRecipeDetails = function (recipeId, guests, initialTabIndex, path, categoryId) {
5903
6036
  if (path === void 0) { path = ''; }
5904
6037
  if (categoryId === void 0) { categoryId = null; }
6038
+ if (recipeId) {
6039
+ ProductShowTracker.getInstance().clearForRecipe(String(recipeId));
6040
+ }
5905
6041
  this.display(recipeId, { previewAllowed: true, previewMode: false, guests: guests });
5906
6042
  this.recipeDetailsTabIndex$.next(initialTabIndex);
5907
6043
  this.analyticsService.sendEvent('recipe.display', path, { recipe_id: recipeId, category_id: categoryId });
@@ -9269,6 +9405,7 @@
9269
9405
  var StoreOutOfStockService = /** @class */ (function () {
9270
9406
  function StoreOutOfStockService() {
9271
9407
  this.outOfStockEntries = [];
9408
+ this.outOfStockPersisted$ = new rxjs.Subject();
9272
9409
  this.fetchCurrentOutOfStocks();
9273
9410
  }
9274
9411
  StoreOutOfStockService.prototype.fetchCurrentOutOfStocks = function () {
@@ -9287,13 +9424,17 @@
9287
9424
  var _a;
9288
9425
  if (entry.status === 'active') {
9289
9426
  this.removeEntryFromStorage(entry);
9290
- return;
9427
+ return false;
9291
9428
  }
9292
9429
  if ((_a = entry === null || entry === void 0 ? void 0 : entry.selectedItem) === null || _a === void 0 ? void 0 : _a.id) {
9293
- return this.outOfStockEntries.includes(entry.selectedItem.id.toString());
9430
+ return this.isItemOutOfStock(entry.selectedItem.id);
9294
9431
  }
9295
9432
  return false;
9296
9433
  };
9434
+ StoreOutOfStockService.prototype.isItemOutOfStock = function (itemId) {
9435
+ this.fetchCurrentOutOfStocks();
9436
+ return this.outOfStockEntries.includes(itemId);
9437
+ };
9297
9438
  StoreOutOfStockService.prototype.removeEntryFromStorage = function (entry) {
9298
9439
  var _a;
9299
9440
  if ((_a = entry === null || entry === void 0 ? void 0 : entry.selectedItem) === null || _a === void 0 ? void 0 : _a.id) {
@@ -9301,12 +9442,25 @@
9301
9442
  sessionStorage.setItem('_miam/outOfStockEntry', JSON.stringify(this.outOfStockEntries));
9302
9443
  }
9303
9444
  };
9445
+ /**
9446
+ * Marks an entry as out of stock in session storage.
9447
+ */
9448
+ StoreOutOfStockService.prototype.persistOutOfStock = function (entry) {
9449
+ var _a;
9450
+ if (!((_a = entry.selectedItem) === null || _a === void 0 ? void 0 : _a.id)) {
9451
+ return;
9452
+ }
9453
+ entry.status = 'out_of_stock';
9454
+ this.addEntryToStorage(entry);
9455
+ this.outOfStockPersisted$.next(entry);
9456
+ };
9304
9457
  StoreOutOfStockService.prototype.addEntryToStorage = function (entry) {
9305
9458
  var _a;
9306
9459
  if ((_a = entry.selectedItem) === null || _a === void 0 ? void 0 : _a.id) {
9307
9460
  this.fetchCurrentOutOfStocks();
9308
- if (!this.outOfStockEntries.includes(entry.selectedItem.id)) {
9309
- this.outOfStockEntries.push(entry.selectedItem.id);
9461
+ var itemId = entry.selectedItem.id.toString();
9462
+ if (!this.outOfStockEntries.includes(itemId)) {
9463
+ this.outOfStockEntries.push(itemId);
9310
9464
  }
9311
9465
  sessionStorage.setItem('_miam/outOfStockEntry', JSON.stringify(this.outOfStockEntries));
9312
9466
  }
@@ -9876,9 +10030,8 @@
9876
10030
  RecipeDetailsService.prototype.setProductOutOfStockInSession = function (products) {
9877
10031
  var _this = this;
9878
10032
  products.forEach(function (_c) {
9879
- var entry = _c.entry;
9880
- entry.status = 'out_of_stock';
9881
- _this.storeOutOfStockService.addEntryToStorage(entry);
10033
+ var ingredientId = _c.ingredientId, entry = _c.entry;
10034
+ _this.storeOutOfStockService.persistOutOfStock(entry);
9882
10035
  });
9883
10036
  };
9884
10037
  return RecipeDetailsService;
@@ -9940,7 +10093,9 @@
9940
10093
  }
9941
10094
  this.basketEntry.addRelationship(this.updateMealzParams.item, 'selected-item');
9942
10095
  this.basketEntriesService.selectItem(this.basketEntry.id, this.updateMealzParams.item.id, this.quantity)
9943
- .pipe(operators.switchMap(function () { return _this.basketsService.refreshCurrentBasket(); }), operators.take(1)).subscribe();
10096
+ .pipe(operators.switchMap(function () { return _this.basketsService.refreshCurrentBasket(); }), operators.take(1)).subscribe({
10097
+ error: function () { return _this.onError.next(); }
10098
+ });
9944
10099
  };
9945
10100
  AddIngredientFromReplaceAction.prototype.createActionForRetailer = function () {
9946
10101
  var _this = this;
@@ -9988,8 +10143,16 @@
9988
10143
  return AddIngredientAction;
9989
10144
  }(BasketAction));
9990
10145
 
10146
+ var UNAVAILABLE_PRODUCT_ADD_ERROR_CODE = 'unavailableProductAdd';
10147
+ var UNAVAILABLE_PRODUCTS_ADD_ERROR_CODE = 'unavailableProductsAdd';
10148
+ function unavailableProductAddErrorCode(failureCount) {
10149
+ return failureCount <= 1
10150
+ ? UNAVAILABLE_PRODUCT_ADD_ERROR_CODE
10151
+ : UNAVAILABLE_PRODUCTS_ADD_ERROR_CODE;
10152
+ }
10153
+
9991
10154
  var ReplaceItemService = /** @class */ (function () {
9992
- function ReplaceItemService(basketsService, basketEntriesService, itemsService, recipeDetailsService, basketSynchroService, ignoredBasketsService, recipesService, analyticsService) {
10155
+ function ReplaceItemService(basketsService, basketEntriesService, itemsService, recipeDetailsService, basketSynchroService, ignoredBasketsService, recipesService, analyticsService, storeOutOfStockService, contextRegistryService) {
9993
10156
  var _this = this;
9994
10157
  this.basketsService = basketsService;
9995
10158
  this.basketEntriesService = basketEntriesService;
@@ -9999,6 +10162,8 @@
9999
10162
  this.ignoredBasketsService = ignoredBasketsService;
10000
10163
  this.recipesService = recipesService;
10001
10164
  this.analyticsService = analyticsService;
10165
+ this.storeOutOfStockService = storeOutOfStockService;
10166
+ this.contextRegistryService = contextRegistryService;
10002
10167
  this.basketEntry = new BasketEntry();
10003
10168
  this.itemsWithPricesList$ = new rxjs.BehaviorSubject([]);
10004
10169
  this.fetchingItemLoading$ = new rxjs.BehaviorSubject(false);
@@ -10032,14 +10197,20 @@
10032
10197
  _this.recipe = recipeDisplay === null || recipeDisplay === void 0 ? void 0 : recipeDisplay.recipe;
10033
10198
  });
10034
10199
  this.replaceProductFromPreviewOpen$.pipe(operators.filter(function (isOpen) { return !!isOpen; }), operators.switchMap(function () { return _this.fetchItems(); })).subscribe();
10200
+ this.storeOutOfStockService.outOfStockPersisted$.subscribe(function () {
10201
+ _this.refreshItemsOutOfStockFlags();
10202
+ });
10035
10203
  }
10036
- ReplaceItemService.prototype.handleItemSelection = function (item, searchString, onComplete, eventTrace, timeOutAction) {
10204
+ ReplaceItemService.prototype.handleItemSelection = function (item, searchString, onComplete, eventTrace) {
10037
10205
  var _this = this;
10038
10206
  var fromRecipeDetails = !!this.recipe;
10039
- this.timeOutAction = timeOutAction;
10207
+ this.onComplete = onComplete;
10040
10208
  this.onSelectItem(item, searchString, eventTrace)
10041
10209
  .pipe(operators.take(1))
10042
10210
  .subscribe(function (action) {
10211
+ if (!action) {
10212
+ return;
10213
+ }
10043
10214
  action.onSuccess.pipe(operators.take(1)).subscribe(function () {
10044
10215
  _this.recipeDetailsService.hasReplaced = true;
10045
10216
  if (eventTrace && fromRecipeDetails && !_this.addProductMode) {
@@ -10047,15 +10218,17 @@
10047
10218
  }
10048
10219
  if (_this.addProductMode || !fromRecipeDetails) {
10049
10220
  _this.basketsService.itemWillBeAdded(item.id).subscribe(function (addItem) {
10050
- if (!addItem && _this.replaceItemLoading$.value) {
10221
+ if (!addItem) {
10222
+ _this.replaceItemLoading$.next(undefined);
10051
10223
  onComplete(item.id);
10052
10224
  }
10053
10225
  });
10054
10226
  }
10055
10227
  else {
10056
10228
  _this.basketsService.ingredientWillBeAdded(_this.ingredient.id).subscribe(function (addIngredient) {
10057
- if (!addIngredient && _this.replaceItemLoading$.value) {
10058
- onComplete(_this.basketEntry.id);
10229
+ if (!addIngredient) {
10230
+ _this.replaceItemLoading$.next(undefined);
10231
+ onComplete(item.id);
10059
10232
  }
10060
10233
  });
10061
10234
  }
@@ -10097,7 +10270,8 @@
10097
10270
  var priceObservables = items.map(function (item) { return _this.itemsService.getPriceForItem(item.id).pipe(operators.map(function (price) { return ({ item: item, price: price }); })); });
10098
10271
  return (priceObservables.length === 0 ? rxjs.of([]) : rxjs.forkJoin(priceObservables)).pipe(operators.map(function (itemsWithPrices) {
10099
10272
  _this.fetchingItemLoading$.next(false);
10100
- return _this.itemsWithPricesList$.next(itemsWithPrices);
10273
+ var itemsWithOutOfStockFlags = _this.withOutOfStockFlags(itemsWithPrices);
10274
+ return _this.itemsWithPricesList$.next(itemsWithOutOfStockFlags);
10101
10275
  }));
10102
10276
  }));
10103
10277
  };
@@ -10128,18 +10302,13 @@
10128
10302
  * Adds the action to the synchronization service's action list for processing.
10129
10303
  */
10130
10304
  ReplaceItemService.prototype.handleAddProductMode = function (item, eventTrace) {
10131
- var _this = this;
10132
10305
  this.replaceItemLoading$.next(item);
10133
10306
  var newBasketEntry = new BasketEntry();
10134
10307
  newBasketEntry.quantity = 1;
10135
10308
  newBasketEntry.quantityToAdd = 0;
10136
10309
  this.replaceRelationship(newBasketEntry, item);
10137
10310
  var addIngredientWithoutRecipe = new AddIngredientAction(newBasketEntry, { eventTrace: eventTrace, item: item }, this.basketsService, this.basketEntriesService);
10138
- rxjs.merge(addIngredientWithoutRecipe.onTimeout, addIngredientWithoutRecipe.onError)
10139
- .pipe(operators.take(1))
10140
- .subscribe(function () {
10141
- _this.rollbackReplace();
10142
- });
10311
+ this.listenToAddActionFailure(addIngredientWithoutRecipe, item, newBasketEntry);
10143
10312
  return rxjs.of(addIngredientWithoutRecipe);
10144
10313
  };
10145
10314
  /**
@@ -10164,43 +10333,55 @@
10164
10333
  var priceObservables = items.map(function (item) { return _this.itemsService.getPriceForItem(item.id).pipe(operators.map(function (price) { return ({ item: item, price: price }); })); });
10165
10334
  return (priceObservables.length === 0 ? rxjs.of([]) : rxjs.forkJoin(priceObservables));
10166
10335
  }), operators.map(function (itemsWithPrices) {
10167
- _this.itemsWithPricesList$.next(_this.moveSelectedItemToFront(itemsWithPrices));
10336
+ var sortedItems = _this.withOutOfStockFlags(_this.moveSelectedItemToFront(itemsWithPrices));
10337
+ _this.itemsWithPricesList$.next(sortedItems);
10168
10338
  _this.fetchingItemLoading$.next(false);
10169
- return itemsWithPrices;
10339
+ return sortedItems;
10170
10340
  }));
10171
10341
  };
10172
10342
  /**
10173
- * Replace the current basket entry with the given item
10343
+ * Replace the current basket entry with the given item.
10344
+ * Removes the previous item from the retailer cart first; the add action is only enqueued after remove succeeds.
10174
10345
  */
10175
10346
  ReplaceItemService.prototype.selectItem = function (item) {
10176
10347
  var _this = this;
10177
- var _a, _b;
10348
+ var _a;
10178
10349
  var deletedBasketEntry = deepCloneWithClass(this.basketEntry);
10179
10350
  deletedBasketEntry.quantity = 0;
10180
10351
  deletedBasketEntry.selectedItem.extId = this.previousItem.extId;
10181
- var removeIngredient = new RemoveIngredientFromReplaceAction(deletedBasketEntry, {}, this.basketsService);
10182
- rxjs.merge(removeIngredient.onTimeout, removeIngredient.onError).pipe(operators.take(1)).subscribe(function () {
10183
- _this.rollbackReplace();
10184
- });
10185
- this.basketSynchroService.addToActionList([removeIngredient]);
10186
- var guests = +((_a = this.recipe) === null || _a === void 0 ? void 0 : _a.modifiedGuests) || +((_b = this.recipe) === null || _b === void 0 ? void 0 : _b.guests) || 4;
10187
- return (this.ingredient && this.recipe ?
10188
- this.basketsService.basketEntryFromIngredient(this.ingredient.id, guests, true, item.id) :
10189
- rxjs.of(this.basketEntry)).pipe(operators.switchMap(function (newEntry) {
10190
- var newBasketEntry = deepCloneWithClass(_this.basketEntry);
10191
- newBasketEntry.quantityToAdd = newEntry.quantityToAdd;
10192
- _this.replaceRelationship(newBasketEntry, item);
10193
- var addIngredientFromReplace = new AddIngredientFromReplaceAction(newBasketEntry, {
10194
- item: item
10195
- }, _this.basketsService, _this.basketEntriesService);
10196
- rxjs.merge(addIngredientFromReplace.onTimeout, addIngredientFromReplace.onError).pipe(operators.take(1)).subscribe(function () {
10197
- _this.rollbackReplace();
10352
+ var removeIngredient = new RemoveIngredientFromReplaceAction(deletedBasketEntry, { ingredientId: (_a = this.ingredient) === null || _a === void 0 ? void 0 : _a.id }, this.basketsService);
10353
+ return new rxjs.Observable(function (observer) {
10354
+ rxjs.merge(removeIngredient.onError, removeIngredient.onTimeout).pipe(operators.take(1)).subscribe(function () {
10355
+ _this.clearReplaceLoadingState();
10356
+ observer.complete();
10198
10357
  });
10199
- addIngredientFromReplace.onSuccess.pipe(operators.take(1)).subscribe(function () {
10200
- _this.recipeDetailsService.updateIngredientFromBasketLoading = false;
10358
+ removeIngredient.onSuccess.pipe(operators.take(1), operators.switchMap(function () { return _this.createAddIngredientFromReplaceAction(item); })).subscribe({
10359
+ next: function (addIngredientFromReplace) {
10360
+ _this.listenToAddActionFailure(addIngredientFromReplace, item);
10361
+ addIngredientFromReplace.onSuccess.pipe(operators.take(1)).subscribe(function () {
10362
+ _this.recipeDetailsService.updateIngredientFromBasketLoading = false;
10363
+ });
10364
+ observer.next(addIngredientFromReplace);
10365
+ observer.complete();
10366
+ },
10367
+ error: function () {
10368
+ _this.confirmMealzRemoveAfterReplaceAddFailure();
10369
+ _this.clearReplaceLoadingState();
10370
+ observer.complete();
10371
+ }
10201
10372
  });
10202
- return rxjs.of(addIngredientFromReplace);
10203
- }));
10373
+ _this.basketSynchroService.addToActionList([removeIngredient]);
10374
+ });
10375
+ };
10376
+ ReplaceItemService.prototype.withOutOfStockFlags = function (items) {
10377
+ var _this = this;
10378
+ return items.map(function (itemWithPrice) { return (Object.assign(Object.assign({}, itemWithPrice), { isOutOfStock: _this.storeOutOfStockService.isItemOutOfStock(itemWithPrice.item.id) })); });
10379
+ };
10380
+ ReplaceItemService.prototype.refreshItemsOutOfStockFlags = function () {
10381
+ if (this.itemsWithPricesList$.value.length === 0) {
10382
+ return;
10383
+ }
10384
+ this.itemsWithPricesList$.next(this.withOutOfStockFlags(this.itemsWithPricesList$.value));
10204
10385
  };
10205
10386
  ReplaceItemService.prototype.changeReplaceModalState = function (state, basketEntryToReplace) {
10206
10387
  if (basketEntryToReplace) {
@@ -10226,18 +10407,62 @@
10226
10407
  }
10227
10408
  basketEntry.addRelationship(item, 'selected-item');
10228
10409
  };
10410
+ ReplaceItemService.prototype.createAddIngredientFromReplaceAction = function (item) {
10411
+ var _this = this;
10412
+ var _a, _b;
10413
+ var guests = +((_a = this.recipe) === null || _a === void 0 ? void 0 : _a.modifiedGuests) || +((_b = this.recipe) === null || _b === void 0 ? void 0 : _b.guests) || 4;
10414
+ var newEntry$ = this.ingredient && this.recipe ?
10415
+ this.basketsService.basketEntryFromIngredient(this.ingredient.id, guests, true, item.id) :
10416
+ rxjs.of(this.basketEntry);
10417
+ return newEntry$.pipe(operators.map(function (newEntry) {
10418
+ var newBasketEntry = deepCloneWithClass(_this.basketEntry);
10419
+ newBasketEntry.quantityToAdd = newEntry.quantityToAdd;
10420
+ _this.replaceRelationship(newBasketEntry, item);
10421
+ return new AddIngredientFromReplaceAction(newBasketEntry, { item: item }, _this.basketsService, _this.basketEntriesService);
10422
+ }));
10423
+ };
10424
+ ReplaceItemService.prototype.listenToAddActionFailure = function (addAction, attemptedItem, basketEntry) {
10425
+ var _this = this;
10426
+ if (basketEntry === void 0) { basketEntry = this.basketEntry; }
10427
+ var isReplaceAdd = addAction instanceof AddIngredientFromReplaceAction;
10428
+ addAction.onTimeout.pipe(operators.take(1)).subscribe(function () {
10429
+ _this.handleAddTimeout(attemptedItem, basketEntry, isReplaceAdd);
10430
+ });
10431
+ addAction.onError.pipe(operators.take(1)).subscribe(function () {
10432
+ _this.clearReplaceLoadingState();
10433
+ });
10434
+ };
10229
10435
  /**
10230
- * Rolls back the replacement process by resetting loading states and
10231
- * restoring the previous selected item in the basket entry.
10436
+ * After a failed replace-add, the retailer already removed the old product but Mealz was never updated
10437
+ * (remove-from-replace is retailer-only). reloadBasket would re-fetch Mealz and still show the old product
10438
+ * as active. syncMealzBasketWithRetailerCart is too broad and risky while other actions may be pending.
10439
+ * A targeted remove-recipe aligns Mealz with the retailer for this ingredient only.
10232
10440
  */
10233
- ReplaceItemService.prototype.rollbackReplace = function () {
10441
+ ReplaceItemService.prototype.confirmMealzRemoveAfterReplaceAddFailure = function () {
10442
+ var _a;
10443
+ if (!((_a = this.ingredient) === null || _a === void 0 ? void 0 : _a.id)) {
10444
+ return;
10445
+ }
10446
+ var basketEntryToRemove = deepCloneWithClass(this.basketEntry);
10447
+ this.basketsService.removeIngredientFromRecipe(basketEntryToRemove, this.ingredient.id, 0).pipe(operators.take(1)).subscribe();
10448
+ };
10449
+ ReplaceItemService.prototype.handleAddTimeout = function (attemptedItem, basketEntry, isReplaceAdd) {
10450
+ if (basketEntry === void 0) { basketEntry = this.basketEntry; }
10451
+ if (isReplaceAdd === void 0) { isReplaceAdd = false; }
10452
+ if (isReplaceAdd) {
10453
+ this.confirmMealzRemoveAfterReplaceAddFailure();
10454
+ }
10455
+ this.clearReplaceLoadingState();
10456
+ this.replaceRelationship(basketEntry, attemptedItem);
10457
+ this.storeOutOfStockService.persistOutOfStock(basketEntry);
10458
+ this.contextRegistryService.drawerErrorToast.emit({
10459
+ code: UNAVAILABLE_PRODUCT_ADD_ERROR_CODE
10460
+ });
10461
+ };
10462
+ ReplaceItemService.prototype.clearReplaceLoadingState = function () {
10234
10463
  this.recipeDetailsService.ingredientToBasketLoading = false;
10235
10464
  this.recipeDetailsService.updateIngredientFromBasketLoading = false;
10236
10465
  this.replaceItemLoading$.next(undefined);
10237
- this.basketEntry.relationships['selected-item'].data = this.previousItem;
10238
- if (this.timeOutAction) {
10239
- this.timeOutAction();
10240
- }
10241
10466
  };
10242
10467
  /**
10243
10468
  * Creates a ParsedMethod for adding an ingredient to a basket.
@@ -10259,7 +10484,7 @@
10259
10484
  * Updates loading states, creates an AddIngredientAction for the new basket entry,
10260
10485
  * and adds it to the synchronization service's action list.
10261
10486
  */
10262
- ReplaceItemService.prototype.handleAddToBasketResponse = function (newEntry, searchString, eventTrace) {
10487
+ ReplaceItemService.prototype.handleAddToBasketResponse = function (newEntry, searchString, eventTrace, attemptedItem) {
10263
10488
  this.updateLoadingStates();
10264
10489
  var newBasketEntry = deepCloneWithClass(this.basketEntry);
10265
10490
  newBasketEntry.quantityToAdd = newEntry.quantityToAdd;
@@ -10270,19 +10495,11 @@
10270
10495
  previousItem: this.previousItem,
10271
10496
  searchString: searchString
10272
10497
  }, this.basketsService);
10273
- this.handleAddIngredientActionErrors(addIngredient);
10274
- // No need to send entry.added event, because it is done in the action
10498
+ this.handleAddIngredientActionErrors(addIngredient, attemptedItem);
10275
10499
  return rxjs.of(addIngredient);
10276
10500
  };
10277
- /**
10278
- * Listens for errors or timeouts in the given AddIngredientAction and
10279
- * calls rollbackReplace() when one is encountered.
10280
- */
10281
- ReplaceItemService.prototype.handleAddIngredientActionErrors = function (addIngredient) {
10282
- var _this = this;
10283
- rxjs.merge(addIngredient.onError, addIngredient.onTimeout).pipe(operators.take(1)).subscribe(function () {
10284
- _this.rollbackReplace();
10285
- });
10501
+ ReplaceItemService.prototype.handleAddIngredientActionErrors = function (addIngredient, attemptedItem) {
10502
+ this.listenToAddActionFailure(addIngredient, attemptedItem);
10286
10503
  };
10287
10504
  /**
10288
10505
  * Updates the loading states for ingredient and basket operations.
@@ -10327,7 +10544,7 @@
10327
10544
  */
10328
10545
  ReplaceItemService.prototype.addToBasket = function (item, searchString, eventTrace) {
10329
10546
  var _this = this;
10330
- return this.recipeDetailsService.canAddProductToBasket(this.getAddIngredientMethod(eventTrace, searchString), eventTrace, this.recipe).pipe(operators.filter(function (passed) { return passed; }), operators.switchMap(function () { return _this.basketsService.basketEntryFromIngredient(_this.ingredient.id, +_this.recipe.modifiedGuests, true, item.id); }), operators.switchMap(function (newEntry) { return _this.handleAddToBasketResponse(newEntry, searchString, eventTrace); }));
10547
+ return this.recipeDetailsService.canAddProductToBasket(this.getAddIngredientMethod(eventTrace, searchString), eventTrace, this.recipe).pipe(operators.filter(function (passed) { return passed; }), operators.switchMap(function () { return _this.basketsService.basketEntryFromIngredient(_this.ingredient.id, +_this.recipe.modifiedGuests, true, item.id); }), operators.switchMap(function (newEntry) { return _this.handleAddToBasketResponse(newEntry, searchString, eventTrace, item); }));
10331
10548
  };
10332
10549
  /**
10333
10550
  * Replaces the item in the given basket entry with the given item
@@ -10366,7 +10583,7 @@
10366
10583
  };
10367
10584
  return ReplaceItemService;
10368
10585
  }());
10369
- ReplaceItemService.ɵfac = function ReplaceItemService_Factory(t) { return new (t || ReplaceItemService)(i0__namespace.ɵɵinject(BasketsService), i0__namespace.ɵɵinject(BasketEntriesService), i0__namespace.ɵɵinject(ItemsService), i0__namespace.ɵɵinject(RecipeDetailsService), i0__namespace.ɵɵinject(BasketsSynchronizerService), i0__namespace.ɵɵinject(IgnoredIngredientsService), i0__namespace.ɵɵinject(RecipesService), i0__namespace.ɵɵinject(AnalyticsService)); };
10586
+ ReplaceItemService.ɵfac = function ReplaceItemService_Factory(t) { return new (t || ReplaceItemService)(i0__namespace.ɵɵinject(BasketsService), i0__namespace.ɵɵinject(BasketEntriesService), i0__namespace.ɵɵinject(ItemsService), i0__namespace.ɵɵinject(RecipeDetailsService), i0__namespace.ɵɵinject(BasketsSynchronizerService), i0__namespace.ɵɵinject(IgnoredIngredientsService), i0__namespace.ɵɵinject(RecipesService), i0__namespace.ɵɵinject(AnalyticsService), i0__namespace.ɵɵinject(StoreOutOfStockService), i0__namespace.ɵɵinject(ContextRegistryService)); };
10370
10587
  ReplaceItemService.ɵprov = i0__namespace.ɵɵdefineInjectable({ token: ReplaceItemService, factory: ReplaceItemService.ɵfac, providedIn: 'root' });
10371
10588
  (function () {
10372
10589
  (typeof ngDevMode === "undefined" || ngDevMode) && i0__namespace.ɵsetClassMetadata(ReplaceItemService, [{
@@ -10374,17 +10591,19 @@
10374
10591
  args: [{
10375
10592
  providedIn: 'root'
10376
10593
  }]
10377
- }], function () { return [{ type: BasketsService }, { type: BasketEntriesService }, { type: ItemsService }, { type: RecipeDetailsService }, { type: BasketsSynchronizerService }, { type: IgnoredIngredientsService }, { type: RecipesService }, { type: AnalyticsService }]; }, null);
10594
+ }], function () { return [{ type: BasketsService }, { type: BasketEntriesService }, { type: ItemsService }, { type: RecipeDetailsService }, { type: BasketsSynchronizerService }, { type: IgnoredIngredientsService }, { type: RecipesService }, { type: AnalyticsService }, { type: StoreOutOfStockService }, { type: ContextRegistryService }]; }, null);
10378
10595
  })();
10379
10596
 
10380
10597
  var RecipeDetailsProductsService = /** @class */ (function () {
10381
- function RecipeDetailsProductsService(basketsService, priceService, ignoredBasketsService, replaceItemService, contextRegistryService, basketSynchroService) {
10598
+ function RecipeDetailsProductsService(basketsService, priceService, ignoredBasketsService, replaceItemService, contextRegistryService, basketSynchroService, storeOutOfStockService) {
10599
+ var _this = this;
10382
10600
  this.basketsService = basketsService;
10383
10601
  this.priceService = priceService;
10384
10602
  this.ignoredBasketsService = ignoredBasketsService;
10385
10603
  this.replaceItemService = replaceItemService;
10386
10604
  this.contextRegistryService = contextRegistryService;
10387
10605
  this.basketSynchroService = basketSynchroService;
10606
+ this.storeOutOfStockService = storeOutOfStockService;
10388
10607
  this.productsByCategory = {
10389
10608
  toPickProducts: [],
10390
10609
  oftenIgnoredProducts: []
@@ -10395,6 +10614,10 @@
10395
10614
  this.productsLoading$ = new rxjs.BehaviorSubject(false);
10396
10615
  this.ingredientToBasketLoading$ = new rxjs.BehaviorSubject(false);
10397
10616
  this.updateIngredientFromBasketLoading$ = new rxjs.BehaviorSubject(false);
10617
+ this.storeOutOfStockService.outOfStockPersisted$.subscribe(function () {
10618
+ _this.products$.value.forEach(function (product) { return _this.applyOutOfStockFromStorage(product); });
10619
+ _this.emitProductsUpdate();
10620
+ });
10398
10621
  }
10399
10622
  RecipeDetailsProductsService.prototype.initDefaultState = function () {
10400
10623
  this.resetProductsByCategory();
@@ -10446,6 +10669,7 @@
10446
10669
  var basketEntry = product.basketEntry;
10447
10670
  var status = basketEntry.status;
10448
10671
  _this.updateIngredientIgnoredStatus(product);
10672
+ _this.applyOutOfStockFromStorage(product);
10449
10673
  _this.updateProductAvailabilityStatus(product);
10450
10674
  _this.computeProductPriceDetails(product, _this.priceService.getPriceFromBasketEntry(basketEntry));
10451
10675
  // Check if the product is often ignored based on basketEntryStatus
@@ -10490,6 +10714,7 @@
10490
10714
  if (currentProduct) {
10491
10715
  currentProduct.basketEntry.quantity = product.basketEntry.quantity;
10492
10716
  currentProduct.basketEntry.quantityToAdd = product.basketEntry.quantityToAdd;
10717
+ _this.applyOutOfStockFromStorage(currentProduct);
10493
10718
  if (currentProduct.price) {
10494
10719
  _this.computeProductPriceDetails(currentProduct, currentProduct.price);
10495
10720
  }
@@ -10510,6 +10735,17 @@
10510
10735
  }
10511
10736
  });
10512
10737
  };
10738
+ RecipeDetailsProductsService.prototype.emitProductsUpdate = function () {
10739
+ this.products$.next(__spread(this.products$.value));
10740
+ this.productsByCategory$.next(this.productsByCategory);
10741
+ this.ingredientToBasketLoading$.next(false);
10742
+ this.updateIngredientFromBasketLoading$.next(false);
10743
+ };
10744
+ RecipeDetailsProductsService.prototype.applyOutOfStockFromStorage = function (product) {
10745
+ if (this.storeOutOfStockService.isOutOfStockFromStorage(product.basketEntry)) {
10746
+ product.basketEntry.status = 'out_of_stock';
10747
+ }
10748
+ };
10513
10749
  RecipeDetailsProductsService.prototype.updateIngredientIgnoredStatus = function (product) {
10514
10750
  if (this.ignoredBasketsService.isIngredientIgnored(product.ingredient.id)) {
10515
10751
  product.basketEntry.status = 'ignored';
@@ -10518,7 +10754,7 @@
10518
10754
  RecipeDetailsProductsService.prototype.updateProductAvailabilityStatus = function (product) {
10519
10755
  var basketEntry = product.basketEntry;
10520
10756
  var id = basketEntry.id, selectedItem = basketEntry.selectedItem, recipesIds = basketEntry.recipesIds, status = basketEntry.status;
10521
- if (['often_deleted', 'deleted', 'ignored'].includes(status)) {
10757
+ if (['often_deleted', 'deleted', 'ignored', 'out_of_stock'].includes(status)) {
10522
10758
  return;
10523
10759
  }
10524
10760
  if (id === '-1') {
@@ -10571,7 +10807,7 @@
10571
10807
  };
10572
10808
  return RecipeDetailsProductsService;
10573
10809
  }());
10574
- RecipeDetailsProductsService.ɵfac = function RecipeDetailsProductsService_Factory(t) { return new (t || RecipeDetailsProductsService)(i0__namespace.ɵɵinject(BasketsService), i0__namespace.ɵɵinject(PriceService), i0__namespace.ɵɵinject(IgnoredIngredientsService), i0__namespace.ɵɵinject(ReplaceItemService), i0__namespace.ɵɵinject(ContextRegistryService), i0__namespace.ɵɵinject(BasketsSynchronizerService)); };
10810
+ RecipeDetailsProductsService.ɵfac = function RecipeDetailsProductsService_Factory(t) { return new (t || RecipeDetailsProductsService)(i0__namespace.ɵɵinject(BasketsService), i0__namespace.ɵɵinject(PriceService), i0__namespace.ɵɵinject(IgnoredIngredientsService), i0__namespace.ɵɵinject(ReplaceItemService), i0__namespace.ɵɵinject(ContextRegistryService), i0__namespace.ɵɵinject(BasketsSynchronizerService), i0__namespace.ɵɵinject(StoreOutOfStockService)); };
10575
10811
  RecipeDetailsProductsService.ɵprov = i0__namespace.ɵɵdefineInjectable({ token: RecipeDetailsProductsService, factory: RecipeDetailsProductsService.ɵfac, providedIn: 'root' });
10576
10812
  (function () {
10577
10813
  (typeof ngDevMode === "undefined" || ngDevMode) && i0__namespace.ɵsetClassMetadata(RecipeDetailsProductsService, [{
@@ -10579,7 +10815,7 @@
10579
10815
  args: [{
10580
10816
  providedIn: 'root'
10581
10817
  }]
10582
- }], function () { return [{ type: BasketsService }, { type: PriceService }, { type: IgnoredIngredientsService }, { type: ReplaceItemService }, { type: ContextRegistryService }, { type: BasketsSynchronizerService }]; }, null);
10818
+ }], function () { return [{ type: BasketsService }, { type: PriceService }, { type: IgnoredIngredientsService }, { type: ReplaceItemService }, { type: ContextRegistryService }, { type: BasketsSynchronizerService }, { type: StoreOutOfStockService }]; }, null);
10583
10819
  })();
10584
10820
 
10585
10821
  var RecipeDetailsProductsPlannerService = /** @class */ (function () {
@@ -10954,7 +11190,7 @@
10954
11190
  };
10955
11191
  var NewRecipeDetailsService = /** @class */ (function () {
10956
11192
  /** ********* END VARIABLES FROM COMPONENT ********* **/
10957
- function NewRecipeDetailsService(analyticsService, basketsService, recipeService, pendingBasketMethodsService, posService, basketSynchroService, priceService, contextRegistryService, sponsorBlocksService, recipesService, seoService, storeLocatorService, recipeDetailsProductsService, recipeDetailsProductsPlannerService, ignoredBasketsService, locale) {
11193
+ function NewRecipeDetailsService(analyticsService, basketsService, recipeService, pendingBasketMethodsService, posService, basketSynchroService, priceService, contextRegistryService, sponsorBlocksService, recipesService, seoService, storeLocatorService, recipeDetailsProductsService, recipeDetailsProductsPlannerService, ignoredBasketsService, storeOutOfStockService, locale) {
10958
11194
  this.analyticsService = analyticsService;
10959
11195
  this.basketsService = basketsService;
10960
11196
  this.recipeService = recipeService;
@@ -10970,6 +11206,7 @@
10970
11206
  this.recipeDetailsProductsService = recipeDetailsProductsService;
10971
11207
  this.recipeDetailsProductsPlannerService = recipeDetailsProductsPlannerService;
10972
11208
  this.ignoredBasketsService = ignoredBasketsService;
11209
+ this.storeOutOfStockService = storeOutOfStockService;
10973
11210
  this.locale = locale;
10974
11211
  this.totalProducts$ = new rxjs.BehaviorSubject(0);
10975
11212
  this.allIngredientsToBasketLoading$ = new rxjs.BehaviorSubject(false);
@@ -11118,7 +11355,7 @@
11118
11355
  return _this.setupRecipe(recipeDisplay.recipe);
11119
11356
  }
11120
11357
  else {
11121
- _this.initDefaultState(); // This can also reset the state when no recipe is displayed
11358
+ _this.initDefaultState();
11122
11359
  _this.checkIfPosIsInitializedAndValid();
11123
11360
  return rxjs.of(null);
11124
11361
  }
@@ -11126,7 +11363,7 @@
11126
11363
  };
11127
11364
  NewRecipeDetailsService.prototype.listenToBasket = function () {
11128
11365
  var _this = this;
11129
- this.subscriptions.push(this.basketsService._basket$.pipe(operators.filter(function () { return !!_this.recipe; }), operators.switchMap(function () {
11366
+ this.subscriptions.push(this.basketsService._basket$.pipe(operators.filter(function () { var _a; return !!((_a = _this.recipesService.displayedRecipe$.value) === null || _a === void 0 ? void 0 : _a.recipe); }), operators.switchMap(function () {
11130
11367
  if (_this.recipesService.isRecipeDetailsFromPlanner) {
11131
11368
  return _this.loadPlannerProducts();
11132
11369
  }
@@ -11191,14 +11428,14 @@
11191
11428
  _this.basketsService.updateRecipesInBasket([_this.recipe.id], _this.recipe.modifiedGuests, eventTrace, true);
11192
11429
  _this.recipeDetailsProductsService.getIngredientsBasketEntries(_this.recipe.ingredients, _this.recipe.modifiedGuests, false).pipe(operators.take(1)).subscribe(function (products) {
11193
11430
  if (inBasket) {
11194
- var filteredProducts = products.filter(function (_a) {
11195
- var basketEntry = _a.basketEntry;
11431
+ var filteredProducts = products.filter(function (_b) {
11432
+ var basketEntry = _b.basketEntry;
11196
11433
  return basketEntry.status === 'active' &&
11197
11434
  basketEntry.id !== '-1' &&
11198
11435
  basketEntry.recipesIds.includes(+_this.recipe.id);
11199
11436
  });
11200
- var updateActions = filteredProducts.map(function (_a) {
11201
- var basketEntry = _a.basketEntry;
11437
+ var updateActions = filteredProducts.map(function (_b) {
11438
+ var basketEntry = _b.basketEntry;
11202
11439
  var newBasketEntry = deepCloneWithClass(basketEntry); // Clone for immutability
11203
11440
  return new UpdateRecipeFromGuestAction(newBasketEntry, {
11204
11441
  eventTrace: eventTrace,
@@ -11236,20 +11473,34 @@
11236
11473
  productsToAdd_1.push(re);
11237
11474
  }
11238
11475
  }); });
11239
- var addRecipes = productsToAdd_1.map(function (re) { return new AddRecipeAction(re.entry, {
11476
+ var addRecipes_1 = productsToAdd_1.map(function (re) { return new AddRecipeAction(re.entry, {
11240
11477
  recipeId: _this.recipe.id,
11241
11478
  ingredientId: re.ingredientId,
11242
11479
  eventTrace: eventTrace
11243
11480
  }, _this.basketsService); });
11244
- var onTimeouts = addRecipes.map(function (action) { return action.onTimeout; });
11245
- var onErrors = addRecipes.map(function (action) { return action.onError; });
11246
- var onSuccesses = addRecipes.map(function (action) { return action.onSuccess; });
11247
- if (addRecipes.length > 0) {
11248
- rxjs.merge.apply(void 0, __spread(onSuccesses, onTimeouts, onErrors)).pipe(operators.take(addRecipes.length)) // Take the same number of emissions as actions
11481
+ var failedAddCount_1 = 0;
11482
+ productsToAdd_1.forEach(function (productToAdd, index) {
11483
+ addRecipes_1[index].onTimeout.pipe(operators.take(1)).subscribe(function () {
11484
+ failedAddCount_1 += 1;
11485
+ _this.storeOutOfStockService.persistOutOfStock(productToAdd.entry);
11486
+ });
11487
+ });
11488
+ var onTimeouts = addRecipes_1.map(function (action) { return action.onTimeout; });
11489
+ var onErrors = addRecipes_1.map(function (action) { return action.onError; });
11490
+ var onSuccesses = addRecipes_1.map(function (action) { return action.onSuccess; });
11491
+ if (addRecipes_1.length > 0) {
11492
+ rxjs.merge.apply(void 0, __spread(onSuccesses, onTimeouts, onErrors)).pipe(operators.take(addRecipes_1.length)) // Take the same number of emissions as actions
11249
11493
  .subscribe({
11250
- complete: function () { return _this.allIngredientsToBasketLoading$.next(false); }
11494
+ complete: function () {
11495
+ _this.allIngredientsToBasketLoading$.next(false);
11496
+ if (failedAddCount_1 > 0) {
11497
+ _this.contextRegistryService.drawerErrorToast.emit({
11498
+ code: unavailableProductAddErrorCode(failedAddCount_1)
11499
+ });
11500
+ }
11501
+ }
11251
11502
  });
11252
- _this.basketSynchroService.addToActionList(addRecipes);
11503
+ _this.basketSynchroService.addToActionList(addRecipes_1);
11253
11504
  }
11254
11505
  else {
11255
11506
  _this.allIngredientsToBasketLoading$.next(false);
@@ -11279,7 +11530,13 @@
11279
11530
  searchString: null
11280
11531
  }, _this.basketsService); }), operators.tap(function (addIngredient) {
11281
11532
  _this.basketSynchroService.addToActionList([addIngredient]);
11282
- }), operators.switchMap(function (addIngredient) { return rxjs.merge(addIngredient.onError, addIngredient.onTimeout).pipe(operators.take(1), operators.tap(function () { return _this.recipeDetailsProductsService.ingredientToBasketLoading$.next(false); })); })).subscribe());
11533
+ }), operators.switchMap(function (addIngredient) { return rxjs.merge(addIngredient.onTimeout.pipe(operators.take(1), operators.tap(function () {
11534
+ _this.storeOutOfStockService.persistOutOfStock(product.basketEntry);
11535
+ _this.recipeDetailsProductsService.ingredientToBasketLoading$.next(false);
11536
+ _this.contextRegistryService.drawerErrorToast.emit({
11537
+ code: unavailableProductAddErrorCode(1)
11538
+ });
11539
+ })), addIngredient.onError.pipe(operators.take(1), operators.tap(function () { return _this.recipeDetailsProductsService.ingredientToBasketLoading$.next(false); }))); })).subscribe());
11283
11540
  };
11284
11541
  NewRecipeDetailsService.prototype.ingredientRemoved = function (ingredient, eventTrace) {
11285
11542
  var _this = this;
@@ -11362,7 +11619,7 @@
11362
11619
  };
11363
11620
  return NewRecipeDetailsService;
11364
11621
  }());
11365
- NewRecipeDetailsService.ɵfac = function NewRecipeDetailsService_Factory(t) { return new (t || NewRecipeDetailsService)(i0__namespace.ɵɵinject(AnalyticsService), i0__namespace.ɵɵinject(BasketsService), i0__namespace.ɵɵinject(RecipesService), i0__namespace.ɵɵinject(PendingBasketMethodsService), i0__namespace.ɵɵinject(PointOfSalesService), i0__namespace.ɵɵinject(BasketsSynchronizerService), i0__namespace.ɵɵinject(PriceService), i0__namespace.ɵɵinject(ContextRegistryService), i0__namespace.ɵɵinject(SponsorBlockService), i0__namespace.ɵɵinject(RecipesService), i0__namespace.ɵɵinject(SeoService), i0__namespace.ɵɵinject(StoreLocatorService), i0__namespace.ɵɵinject(RecipeDetailsProductsService), i0__namespace.ɵɵinject(RecipeDetailsProductsPlannerService), i0__namespace.ɵɵinject(IgnoredIngredientsService), i0__namespace.ɵɵinject(i0.LOCALE_ID)); };
11622
+ NewRecipeDetailsService.ɵfac = function NewRecipeDetailsService_Factory(t) { return new (t || NewRecipeDetailsService)(i0__namespace.ɵɵinject(AnalyticsService), i0__namespace.ɵɵinject(BasketsService), i0__namespace.ɵɵinject(RecipesService), i0__namespace.ɵɵinject(PendingBasketMethodsService), i0__namespace.ɵɵinject(PointOfSalesService), i0__namespace.ɵɵinject(BasketsSynchronizerService), i0__namespace.ɵɵinject(PriceService), i0__namespace.ɵɵinject(ContextRegistryService), i0__namespace.ɵɵinject(SponsorBlockService), i0__namespace.ɵɵinject(RecipesService), i0__namespace.ɵɵinject(SeoService), i0__namespace.ɵɵinject(StoreLocatorService), i0__namespace.ɵɵinject(RecipeDetailsProductsService), i0__namespace.ɵɵinject(RecipeDetailsProductsPlannerService), i0__namespace.ɵɵinject(IgnoredIngredientsService), i0__namespace.ɵɵinject(StoreOutOfStockService), i0__namespace.ɵɵinject(i0.LOCALE_ID)); };
11366
11623
  NewRecipeDetailsService.ɵprov = i0__namespace.ɵɵdefineInjectable({ token: NewRecipeDetailsService, factory: NewRecipeDetailsService.ɵfac, providedIn: 'root' });
11367
11624
  (function () {
11368
11625
  (typeof ngDevMode === "undefined" || ngDevMode) && i0__namespace.ɵsetClassMetadata(NewRecipeDetailsService, [{
@@ -11371,7 +11628,7 @@
11371
11628
  providedIn: 'root'
11372
11629
  }]
11373
11630
  }], function () {
11374
- return [{ type: AnalyticsService }, { type: BasketsService }, { type: RecipesService }, { type: PendingBasketMethodsService }, { type: PointOfSalesService }, { type: BasketsSynchronizerService }, { type: PriceService }, { type: ContextRegistryService }, { type: SponsorBlockService }, { type: RecipesService }, { type: SeoService }, { type: StoreLocatorService }, { type: RecipeDetailsProductsService }, { type: RecipeDetailsProductsPlannerService }, { type: IgnoredIngredientsService }, { type: undefined, decorators: [{
11631
+ return [{ type: AnalyticsService }, { type: BasketsService }, { type: RecipesService }, { type: PendingBasketMethodsService }, { type: PointOfSalesService }, { type: BasketsSynchronizerService }, { type: PriceService }, { type: ContextRegistryService }, { type: SponsorBlockService }, { type: RecipesService }, { type: SeoService }, { type: StoreLocatorService }, { type: RecipeDetailsProductsService }, { type: RecipeDetailsProductsPlannerService }, { type: IgnoredIngredientsService }, { type: StoreOutOfStockService }, { type: undefined, decorators: [{
11375
11632
  type: i0.Inject,
11376
11633
  args: [i0.LOCALE_ID]
11377
11634
  }] }];
@@ -11447,7 +11704,8 @@
11447
11704
  },
11448
11705
  eventSent$: this.analyticsService.eventEmitter,
11449
11706
  setAbTestKey: function (key) { return _this.analyticsService.setAbTestKey(key); },
11450
- attachRecipeCardShowTracking: attachRecipeCardShowTracking
11707
+ attachRecipeCardShowTracking: attachRecipeCardShowTracking,
11708
+ attachProductShowTracking: attachProductShowTracking
11451
11709
  },
11452
11710
  basket: {
11453
11711
  basketIsReady$: this.basketsService.basketStats$.pipe(operators.skipWhile(function (stats) { return !stats; }), operators.take(1), operators.map(function () { return true; })),
@@ -11761,6 +12019,9 @@
11761
12019
  config: {
11762
12020
  logLevel: this.logLevel$
11763
12021
  },
12022
+ drawer: {
12023
+ errorToast: function () { return _this.contextRegistryService.drawerErrorToast; }
12024
+ },
11764
12025
  planner: {
11765
12026
  itemToReplace$: this.recipeDetailsProductsPlannerService.itemToReplace$,
11766
12027
  itemsWithPricesList$: this.recipeDetailsProductsPlannerService.itemsWithPricesList$,
@@ -12157,6 +12418,7 @@
12157
12418
  if (!recipeId) {
12158
12419
  return rxjs.of(void 0);
12159
12420
  }
12421
+ ProductShowTracker.getInstance().clearForRecipe(recipeId);
12160
12422
  return this.recipesService.get(recipeId, { include: ['ingredients', 'recipe-steps', 'recipe-type', 'sponsors', 'tags', 'recipe-provider'] })
12161
12423
  .pipe(operators.take(1), operators.switchMap(function (recipe) {
12162
12424
  if (guests != null) {
@@ -16110,10 +16372,17 @@
16110
16372
  var _this = this;
16111
16373
  this.subscriptions.push(this.recipeDetailsService.productsByCategory$.subscribe(function () { return _this.isProductSponsored(); }), this.executeActionFromStorage(), this.loadOnRecipesActions(), this.loadOnIngredientsUpdate(), this.updateIngredientQuantityOnRecipeChange(), this.productIsBeingAdded(), this.scrollBackOnProductReplaced());
16112
16374
  this.getHeaderHeight();
16375
+ this.productShowTrackingHandle = attachProductShowTracking({
16376
+ element: this.elementRef.nativeElement,
16377
+ product: this.product,
16378
+ recipeId: this.recipeDetailsService.recipe.id,
16379
+ analyticsPath: this.path || undefined
16380
+ });
16113
16381
  };
16114
16382
  ProductCardComponent.prototype.ngOnDestroy = function () {
16115
- var _a;
16116
- (_a = this.subscriptions) === null || _a === void 0 ? void 0 : _a.forEach(function (sub) { return sub.unsubscribe(); });
16383
+ var _a, _b;
16384
+ (_a = this.productShowTrackingHandle) === null || _a === void 0 ? void 0 : _a.disconnect();
16385
+ (_b = this.subscriptions) === null || _b === void 0 ? void 0 : _b.forEach(function (sub) { return sub.unsubscribe(); });
16117
16386
  };
16118
16387
  ProductCardComponent.prototype.scrollBackOnProductReplaced = function () {
16119
16388
  var _this = this;
@@ -17066,12 +17335,14 @@
17066
17335
  }
17067
17336
  };
17068
17337
  BasketPreviewProductComponent.prototype.removeProduct = function () {
17338
+ var _a;
17069
17339
  this.analyticsService.sendEvent('entry.deleted', this.path, {
17070
17340
  entry_name: this.basketEntry.name,
17071
17341
  item_id: this.basketEntry.selectedItem.id,
17072
17342
  ext_item_id: this.basketEntry.selectedItem.attributes['ext-id'],
17073
17343
  item_ean: this.basketEntry.selectedItem.ean,
17074
17344
  product_quantity: this.basketEntry.quantity.toString(),
17345
+ product_base_price: ((_a = this.basketEntry.price) !== null && _a !== void 0 ? _a : 0).toString(),
17075
17346
  recipe_id: this.basketEntry.recipesIds.length > 0 ? this.basketEntry.recipesIds[0].toString() : null
17076
17347
  });
17077
17348
  var deletedBasketEntry = deepCloneWithClass(this.basketEntry);