From 8d661ccbcb62029b3abf82cb03221e52d07f00c7 Mon Sep 17 00:00:00 2001 From: bc-apostoliuk Date: Tue, 17 Feb 2026 14:59:06 +0200 Subject: [PATCH 1/5] feat(backorders): BACK-470 add backorder availability prompt for complex products - Dynamically show/hide backorder availability prompt when variant options change on complex products, mirroring the existing simple product behavior - Inject product-level backorder config into JS context and update the prompt in updateView based on variant stock data - Fix updateDefaultAttributesForOOS disabling controls when product attributes data is empty (undefined !== false) Co-authored-by: Cursor --- .../js/theme/common/product-details-base.js | 23 ++++++++++++++++++- .../components/products/product-view.html | 6 ++++- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/assets/js/theme/common/product-details-base.js b/assets/js/theme/common/product-details-base.js index 0ea8d23d7e..f964849f6b 100644 --- a/assets/js/theme/common/product-details-base.js +++ b/assets/js/theme/common/product-details-base.js @@ -199,6 +199,9 @@ export default class ProductDetailsBase { $text: $('.incrementTotal', $scope), $input: $('[name=qty\\[\\]]', $scope), }, + backorderPrompt: { + $container: $('[data-backorder-prompt]', $scope), + }, $bulkPricing: $('.productView-info-bulkPricing', $scope), $walletButtons: $('[data-add-to-cart-wallet-buttons]', $scope), }; @@ -273,6 +276,24 @@ export default class ProductDetailsBase { viewModel.stock.$input.text(data.stock); } + // Update backorder availability prompt for complex products + if (viewModel.backorderPrompt.$container.length && this.context.showBackorderAvailabilityPrompt) { + if (typeof data.stock === 'number' && data.stock > 0) { + const promptText = this.context.backorderAvailabilityPrompt; + const $prompt = viewModel.backorderPrompt.$container.find('.productView-backorder-availability-prompt'); + + if ($prompt.length) { + $prompt.show(); + } else if (promptText) { + viewModel.backorderPrompt.$container.html( + `(${promptText})`, + ); + } + } else { + viewModel.backorderPrompt.$container.find('.productView-backorder-availability-prompt').hide(); + } + } + this.updateDefaultAttributesForOOS(data); this.updateWalletButtonsView(data); @@ -365,7 +386,7 @@ export default class ProductDetailsBase { updateDefaultAttributesForOOS(data) { const viewModel = this.getViewModel(this.$scope); - if (!data.purchasable || !data.instock) { + if (data.purchasable === false || data.instock === false) { viewModel.$addToCart.prop('disabled', true); viewModel.$increments.prop('disabled', true); } else { diff --git a/templates/components/products/product-view.html b/templates/components/products/product-view.html index ece23c029e..29bd55244f 100644 --- a/templates/components/products/product-view.html +++ b/templates/components/products/product-view.html @@ -1,4 +1,6 @@ {{inject 'outOfStockDefaultMessage' (lang 'products.out_of_stock_default_message')}} +{{inject 'showBackorderAvailabilityPrompt' product.show_backorder_availability_prompt}} +{{inject 'backorderAvailabilityPrompt' product.backorder_availability_prompt}}
{{> components/products/add-to-cart with_wallet_buttons=true}} From 8c015473783dcf07796ee9d12c950a89a801af71 Mon Sep 17 00:00:00 2001 From: bc-apostoliuk Date: Tue, 17 Feb 2026 15:01:39 +0200 Subject: [PATCH 2/5] revert: remove unrelated updateDefaultAttributesForOOS change Co-authored-by: Cursor --- assets/js/theme/common/product-details-base.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/js/theme/common/product-details-base.js b/assets/js/theme/common/product-details-base.js index f964849f6b..cbe208f8bf 100644 --- a/assets/js/theme/common/product-details-base.js +++ b/assets/js/theme/common/product-details-base.js @@ -386,7 +386,7 @@ export default class ProductDetailsBase { updateDefaultAttributesForOOS(data) { const viewModel = this.getViewModel(this.$scope); - if (data.purchasable === false || data.instock === false) { + if (!data.purchasable || !data.instock) { viewModel.$addToCart.prop('disabled', true); viewModel.$increments.prop('disabled', true); } else { From 5a130bcc6aa275bf5bad74342192250716fa8930 Mon Sep 17 00:00:00 2001 From: bc-apostoliuk Date: Tue, 17 Feb 2026 15:13:14 +0200 Subject: [PATCH 3/5] refactor: extract backorder prompt container to local variable Co-authored-by: Cursor --- .../js/theme/common/product-details-base.js | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/assets/js/theme/common/product-details-base.js b/assets/js/theme/common/product-details-base.js index cbe208f8bf..947ec46f0d 100644 --- a/assets/js/theme/common/product-details-base.js +++ b/assets/js/theme/common/product-details-base.js @@ -277,20 +277,25 @@ export default class ProductDetailsBase { } // Update backorder availability prompt for complex products - if (viewModel.backorderPrompt.$container.length && this.context.showBackorderAvailabilityPrompt) { - if (typeof data.stock === 'number' && data.stock > 0) { - const promptText = this.context.backorderAvailabilityPrompt; - const $prompt = viewModel.backorderPrompt.$container.find('.productView-backorder-availability-prompt'); + const $backorderPromptContainer = viewModel.backorderPrompt.$container; + + if ($backorderPromptContainer.length && this.context.showBackorderAvailabilityPrompt) { + const $prompt = $backorderPromptContainer.find('.productView-backorder-availability-prompt'); + if (typeof data.stock === 'number' && data.stock > 0) { if ($prompt.length) { $prompt.show(); - } else if (promptText) { - viewModel.backorderPrompt.$container.html( - `(${promptText})`, - ); + } else { + const promptText = this.context.backorderAvailabilityPrompt; + + if (promptText) { + $backorderPromptContainer.html( + `(${promptText})`, + ); + } } } else { - viewModel.backorderPrompt.$container.find('.productView-backorder-availability-prompt').hide(); + $prompt.hide(); } } From 360e86e7bdf89995ab2c59656beb63b942e222d9 Mon Sep 17 00:00:00 2001 From: bc-apostoliuk Date: Tue, 17 Feb 2026 15:17:06 +0200 Subject: [PATCH 4/5] refactor: rename backorderPrompt to backorderAvailabilityPrompt in viewModel Co-authored-by: Cursor --- assets/js/theme/common/product-details-base.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/assets/js/theme/common/product-details-base.js b/assets/js/theme/common/product-details-base.js index 947ec46f0d..1c24c783ae 100644 --- a/assets/js/theme/common/product-details-base.js +++ b/assets/js/theme/common/product-details-base.js @@ -199,7 +199,7 @@ export default class ProductDetailsBase { $text: $('.incrementTotal', $scope), $input: $('[name=qty\\[\\]]', $scope), }, - backorderPrompt: { + backorderAvailabilityPrompt: { $container: $('[data-backorder-prompt]', $scope), }, $bulkPricing: $('.productView-info-bulkPricing', $scope), @@ -277,7 +277,7 @@ export default class ProductDetailsBase { } // Update backorder availability prompt for complex products - const $backorderPromptContainer = viewModel.backorderPrompt.$container; + const $backorderPromptContainer = viewModel.backorderAvailabilityPrompt.$container; if ($backorderPromptContainer.length && this.context.showBackorderAvailabilityPrompt) { const $prompt = $backorderPromptContainer.find('.productView-backorder-availability-prompt'); From 71ec9adeea0b536781aa342a594ef4eedd72fd4b Mon Sep 17 00:00:00 2001 From: bc-apostoliuk Date: Tue, 17 Feb 2026 15:18:04 +0200 Subject: [PATCH 5/5] refactor: rename $backorderPromptContainer to $backorderAvailabilityPromptContainer Co-authored-by: Cursor --- assets/js/theme/common/product-details-base.js | 10 +++++----- templates/components/products/product-view.html | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/assets/js/theme/common/product-details-base.js b/assets/js/theme/common/product-details-base.js index 1c24c783ae..d084b31c4b 100644 --- a/assets/js/theme/common/product-details-base.js +++ b/assets/js/theme/common/product-details-base.js @@ -200,7 +200,7 @@ export default class ProductDetailsBase { $input: $('[name=qty\\[\\]]', $scope), }, backorderAvailabilityPrompt: { - $container: $('[data-backorder-prompt]', $scope), + $container: $('[data-backorder-availability-prompt]', $scope), }, $bulkPricing: $('.productView-info-bulkPricing', $scope), $walletButtons: $('[data-add-to-cart-wallet-buttons]', $scope), @@ -277,10 +277,10 @@ export default class ProductDetailsBase { } // Update backorder availability prompt for complex products - const $backorderPromptContainer = viewModel.backorderAvailabilityPrompt.$container; + const $backorderAvailabilityPromptContainer = viewModel.backorderAvailabilityPrompt.$container; - if ($backorderPromptContainer.length && this.context.showBackorderAvailabilityPrompt) { - const $prompt = $backorderPromptContainer.find('.productView-backorder-availability-prompt'); + if ($backorderAvailabilityPromptContainer.length && this.context.showBackorderAvailabilityPrompt) { + const $prompt = $backorderAvailabilityPromptContainer.find('.productView-backorder-availability-prompt'); if (typeof data.stock === 'number' && data.stock > 0) { if ($prompt.length) { @@ -289,7 +289,7 @@ export default class ProductDetailsBase { const promptText = this.context.backorderAvailabilityPrompt; if (promptText) { - $backorderPromptContainer.html( + $backorderAvailabilityPromptContainer.html( `(${promptText})`, ); } diff --git a/templates/components/products/product-view.html b/templates/components/products/product-view.html index 29bd55244f..056ba48095 100644 --- a/templates/components/products/product-view.html +++ b/templates/components/products/product-view.html @@ -249,7 +249,7 @@