(Shopify) How to update my inventory message on variant change using current theme JSON + AJAX setup - json

Background:
I am currently helping a company move their current website to Shopify and am trying to add a custom feature to the product page. I am a web designer, not a developer so I am having trouble implementing this.
Here is my situation:
At this point, our theme allows us to display an "In stock & ready to ship" or an "Out of stock" message on our product page if the variant.inventory_quantity is great than 0 or equal to 0, respectively. In my case, I have added another option that will display a message if the following is true:
section.settings.show_special_order_option and selected_variant.inventory_management and selected_variant.inventory_policy == 'continue' and selected_variant.inventory_quantity == 0
In this case, section.settings.show_special_order_option is a checkbox option I created that when checked will [ideally] display the message. I would like this message to only be displayed if the above conditions are met, and update as each new variation is selected in the event that the conditions are not met.
As of now, this is my code in the product-info.liquid section:
{%- if selected_variant.available -%}
{%- if selected_variant.inventory_management and selected_variant.inventory_policy == 'deny' and section.settings.low_inventory_threshold > 0 -%}
{%- if selected_variant.inventory_quantity <= section.settings.low_inventory_threshold -%}
<span class="product-form__inventory inventory inventory--low">{{ 'product.form.low_stock_with_quantity_count' | t: count: selected_variant.inventory_quantity }}</span>
{%- else -%}
<span class="product-form__inventory inventory inventory--high">{{ 'product.form.in_stock_with_quantity_count' | t: count: selected_variant.inventory_quantity }}</span>
{%- endif -%}
{%- elsif section.settings.show_special_order_option and selected_variant.inventory_management and selected_variant.inventory_policy == 'continue' -%}
{%- if selected_variant.inventory_quantity == 0 -%}
<span class="product-form__inventory inventory inventory--special-order">{{ 'product.form.no_stock_special_order' | t }}</span>
{%- else -%}
<span class="product-form__inventory inventory inventory--high">{{ 'product.form.in_stock' | t }}</span>
{%- endif -%}
{%- else -%}
<span class="product-form__inventory inventory inventory--high">{{ 'product.form.in_stock' | t }}</span>
{%- endif -%}
{%- else -%}
<span class="product-form__inventory inventory">{{ 'product.form.sold_out' | t }}</span>
{%- endif -%}
And this is my JSON Script:
<script type="application/json" data-product-json>
{
"product": {{ product | json }},
"options_with_values": {{ product.options_with_values | json }},
"selected_variant_id": {{ selected_variant.id }}
{%- if section.settings.show_inventory_quantity -%}
,"inventories": {
{%- for variant in product.variants -%}
{%- if variant.available -%}
{%- if variant.inventory_management and variant.inventory_policy == 'deny' and section.settings.low_inventory_threshold > 0 -%}
{%- if variant.inventory_quantity <= section.settings.low_inventory_threshold -%}
{%- capture inventory_message -%}{{ 'product.form.low_stock_with_quantity_count' | t: count: variant.inventory_quantity }}{%- endcapture -%}
{%- else -%}
{%- capture inventory_message -%}{{ 'product.form.in_stock_with_quantity_count' | t: count: variant.inventory_quantity }}{%- endcapture -%}
{%- endif -%}
{%- elsif section.settings.show_special_order_option and selected_variant.inventory_management and selected_variant.inventory_policy == 'continue' -%}
{%- if selected_variant.inventory_quantity == 0 -%}
{%- capture inventory_message -%}{{ 'product.form.no_stock_special_order' | t }}{%- endcapture -%}
{%- else -%}
{%- capture inventory_message -%}{{ 'product.form.in_stock' | t }}{%- endcapture -%}
{%- endif -%}
{%- else -%}
{%- capture inventory_message -%}{{ 'product.form.in_stock' | t }}{%- endcapture -%}
{%- endif -%}
{%- else -%}
{%- capture inventory_message -%}{{ 'product.form.sold_out' | t }}{%- endcapture -%}
{%- endif -%}
"{{ variant.id }}": {
"inventory_management": {{ variant.inventory_management | json }},
"inventory_policy": {{ variant.inventory_policy | json }},
"inventory_quantity": {{ variant.inventory_quantity | json }},
"inventory_message": {{ inventory_message | json }}
}{% unless forloop.last %},{% endunless %}
{%- endfor -%}
}
{%- endif -%}
}
</script>
And finally this is the JS snippet that is executed when there is a variation change:
function _updateinven(newVariant) {
if (!this.options['showInventoryQuantity'] || !newVariant) {
return;
}
var productFormInventoryElement = this.element.querySelector('.product-form__inventory'),
variantInventoryManagement = this.variantsInventories[newVariant['id']]['inventory_management'],
variantInventoryPolicy = this.variantsInventories[newVariant['id']]['inventory_policy'],
variantInventoryQuantity = this.variantsInventories[newVariant['id']]['inventory_quantity'],
variantInventoryMessage = this.variantsInventories[newVariant['id']]['inventory_message'];
productFormInventoryElement.classList.remove('inventory--high');
productFormInventoryElement.classList.remove('inventory--low');
productFormInventoryElement.classList.remove('inventory--special-order');
if (newVariant['available']) {
if (null !== variantInventoryManagement && variantInventoryPolicy === 'deny' && this.options['lowInventoryThreshold'] > 0) {
if (variantInventoryQuantity <= this.options['lowInventoryThreshold']) {
productFormInventoryElement.classList.add('inventory--low');
} else {
productFormInventoryElement.classList.add('inventory--high');
}
} else if (variantInventoryQuantity === 0) {
productFormInventoryElement.classList.add('inventory--special-order');
} else {
productFormInventoryElement.classList.add('inventory--high');
}
}
// We also need to update the stock countdown if setup
var stockCountdown = this.element.querySelector('.inventory-bar');
if (stockCountdown) {
var stockCountdownProgress = Math.min(Math.max(variantInventoryQuantity / parseInt(stockCountdown.getAttribute('data-stock-countdown-max')) * 100.0, 0), 100);
stockCountdown.classList.toggle('inventory-bar--hidden', stockCountdownProgress === 0);
stockCountdown.firstElementChild.style.width = stockCountdownProgress + '%';
}
productFormInventoryElement.innerHTML = variantInventoryMessage;
}
How the function currently works:
As of now, when clicking on a product that is available and has inventory greater than 0, the "In stock & ready to ship" message appears in green (the class 'inventory--high' is what makes it green). When clicking on another variation that is out of stock but set to 'Continue selling when out of stock' the message stays the same, but the color changes to orange (the class 'inventory--special-order' is what makes it orange).
What appears to be the problem:
I have narrowed this problem down to the liquid function and/or JSON script. When viewing the page source, I look at the computed JSON script and it is displaying the same message for all variations when it should be displaying unique messages depending on the conditions set forth. (In other words, every variation in the JSON script has the same inventory message of "In stock & ready to ship" or "Special order: this product will take 7-10 days to arrive" even if there multiple variations in stock and multiple not in stock but able to continue selling).
My question:
Assuming I have provided all the necessary information, I am in need of some guidance as to correct my function to display the proper inventory message for each variation as the user selects different variations. Could someone please help me solve this problem? Please let me know if you need any additional information from me.

Related

Append string to array and join back into string

I'm using Home Assistant templates, which run on Jinja2 script.
I have a group of entities (states.group.doors) that have attribute battery_level. I want to build an array of entities with battery_level < min_battery level and display as a string separated by commas.
I can't figure out what's wrong with my syntax. Two questions:
Is there just a better way overall to create a list that is filtered for battery_level < min_battery_level rather than building an array like I am?
If not, then there must be something wrong with the way I am building this array. Can someone spot it?
Thanks for the help.
The following code does successfully detect battery_level < 98 and display true if anything meets that criteria, so I'm almost there.
{% set min_battery_level = 98 -%}
{% set ns = namespace(found=false, entities=[]) -%}
{% set entities = [] -%}
{% for entity_id in states.group.doors.attributes.entity_id -%}
{% set parts = entity_id.split('.') -%}
{% if (state_attr(entity_id, 'battery_level') | replace("%","") | int) < min_battery_level -%}
{% set ns.found = true -%}
{% set entities = entities + [entity_id] -%}
{% endif -%}
{% endfor -%}
{{ ns.found }}
{{ entities | join(' ') }}
Welp... kept playing with it and got it working as follows:
{% set min_battery_level = 98 -%}
{% set ns = namespace(found=false, entities = []) -%}
{% for entity_id in states.group.doors.attributes.entity_id -%}
{% set name = state_attr(entity_id, 'friendly_name') | string -%}
{% set battery = state_attr(entity_id, 'battery_level') | replace("%","") | int -%}
{% if (battery) < min_battery_level -%}
{% set ns.found = true -%}
{% set ns.entities = ns.entities + [name+' ('+battery|string+'%)'] -%}
{% endif -%}
{% endfor -%}
{{ ns.found }}
{{ ns.entities | join(', ') }}

Shopify - Shorten/change 'Sort By' filter list options (not just changing default order)

I want to reduce the number of options available for users to sort by on my website. The default list of options is too long (e.g. there is no need to sort alphabetically, which is one of the default options), and I'd possibly like to rephrase some of them too.
I believe the code below is the most relevant. I can't seem to find where the sort_options are stored. I am using the Debut theme.
Note that I am not trying to create any new criteria to sort-by, only to reduce the options and rename them.
{% if section.settings.sort_enable %}
<div class="filters-toolbar__item-child">
{%- assign sort_by = collection.sort_by | default: collection.default_sort_by -%}
<label class="filters-toolbar__label select-label" for="SortBy">{{ 'collections.sorting.title' | t }}</label>
<div class="filters-toolbar__input-wrapper select-group">
<select name="sort_by" id="SortBy"
class="filters-toolbar__input hidden"
aria-describedby="a11y-refresh-page-message a11y-selection-message"
data-default-sortby="{{ collection.default_sort_by }}">
{%- for option in collection.sort_options -%}
<option value="{{ option.value }}" {% if option.value == sort_by %}selected="selected"{% endif %}>{{ option.name }}</option>
{%- endfor -%}
</select>
{% include 'icon-chevron-down' %}
</div>
</div>
{% endif %}
Create a variable with option keys you want to hide and then just check whether the current option key is a part of that variable within the for loop.
{%- assign sortOptionsToSkip = "title-ascending,title-descending" -%}
{%- for option in collection.sort_options -%}
{%- if sortOptionsToSkip contains option.value -%}
{%- continue -%}
{%- endif -%}
<option value="{{ option.value }}" {% if option.value == sort_by %}selected="selected"{% endif %}>{{ option.name }}</option>
{%- endfor -%}
Or just use the following if you only need to hide the alphabetical sort options:
{%- for option in collection.sort_options -%}
{%- if option.value contains "title" -%}
{%- continue -%}
{%- endif -%}
<option value="{{ option.value }}" {% if option.value == sort_by %}selected="selected"{% endif %}>{{ option.name }}</option>
{%- endfor -%}
Use case/when control flow tags to use other option names, again by matching it by key.
{%- for option in collection.sort_options -%}
<option value="{{ option.value }}" {% if option.value == sort_by %}selected="selected"{% endif %}>
{% case option.value %}
{%- when "best-selling" -%}
Best
{%- when "created-ascending" -%}
Old first
{%- else -%}
{{ option.name }}
{%- endcase -%}
</option>
{%- endfor -%}

Jekyll: link next/previous sorted posts within a category

I need to generate two buttons linking to the next and the previous posts from the same category. Posts are sorted with the front-matter order and an integer value.
My solution is still not perfect. I need to exclude the previous button for the first post and the next button for the last post. However, it's not working and I can't understand why. This is my code:
{% capture the_cat %}{{page.categories | first}}{% endcapture %}
{%- assign sorted_posts = site.categories[the_cat] | sort: 'order' -%}
{%- for post in sorted_posts -%}
{% if post.url == page.url %}
{% assign post_index0 = forloop.index0 %}
{% assign post_index1 = forloop.index | plus: 1 %}
{% endif %}
{%- endfor -%}
{%- for post in sorted_posts -%}
{% if post_index0 == post.order %}
{% assign prev_post = post %}
{% endif %}
{% if post_index1 == post.order %}
{% assign next_post = post %}
{% endif %}
{%- endfor -%}
And finally...
{%- if prev_post != null -%} ... {%- endif -%}
{%- if next_post != null -%} ... {%- endif -%}
The main loop seems correct. In a category with 3 posts sorted, it returns 1, 2, 3. How can I fix it? Could be fixed with only one loop, making the code more efficient? Thanks!
PD: I used this plugin successfully, however this plugin sorts posts by date, not by order.
Finally, I got the solution:
{%- capture the_cat -%}{{page.categories | first}}{%- endcapture -%}
{%- assign sorted_posts = site.categories[the_cat] | sort: 'order' -%}
{%- for post in sorted_posts -%}
{%- if post.url == page.url -%}
{%- assign currIndex = forloop.index0 -%}
{%- assign prevIndex = currIndex | minus: 1 -%}
{%- assign nextIndex = currIndex | plus: 1 -%}
{%- assign articleIndexLength = forloop.length | minus: 1 -%}
{%- if currIndex == articleIndexLength -%}
{%- assign prev_post = sorted_posts[prevIndex] -%}
{%- endif -%}
{%- if currIndex < articleIndexLength and currIndex != 0 -%}
{%- assign prev_post = sorted_posts[prevIndex] -%}
{%- assign next_post = sorted_posts[nextIndex] -%}
{%- endif -%}
{%- if currIndex == 0 -%}
{%- assign next_post = sorted_posts[nextIndex] -%}
{%- endif -%}
{%- endif -%}
{%- endfor -%}
I only needed one loop with three conditionals.

Assign products to collection based on size

I created a new collection template and I want to assign to it all products that have 'XL' size in their size variant.
I started from create and array of sizes for every product but now I'm stuck.
{% assign sizes = '' %}
{% for variant in product.variants %}
{% if variant.available %}
{% assign sizes = sizes | append: variant.options[0] | append: '_' %}
{% endif %}
{% endfor %}
{% assign sizesArr = sizes | split: '_' | uniq %}
What should I do next for showing only products that contains 'XL'?
It depends what do you mean by all products.
By default collection can show up to 50 products but you can overwrite this with a paginate object.
So if you have under 50 products you can just do this.
{%- for product in collection.products -%}
{%- assign show_product = false -%}
{%- for option in product.options_with_values -%}
{%- for value in option.values -%}
{%- assign value_handle = value | handle -%}
{%- if value_handle == 'xl' -%}
{%- assign show_product = true -%}
{%- endif -%}
{%- endfor -%}
{%- endfor -%}
{%- if show_product -%}
Add your product here
{%- endif -%}
{%- endfor -%}
If you have more than 50 products you will need to wrap the above code in paginate object:
{% paginate collection.products by 9999 %}
{%- for product in collection.products -%}
{%- assign show_product = false -%}
{%- for option in product.options_with_values -%}
{%- for value in option.values -%}
{%- assign value_handle = value | handle -%}
{%- if value_handle == 'xl' -%}
{%- assign show_product = true -%}
{%- endif -%}
{%- endfor -%}
{%- endfor -%}
{%- if show_product -%}
Add your product here
{%- endif -%}
{%- endfor -%}
{% endpaginate %}
But this request will take a toll on the server and slow down your site. So please consider this issue as well.

Compare forloop.index|modulo:4 with 0 in Liquid (Jekyll)

I'm trying to get my home.html layout to return a row for every forth column. But this keeps up giving me true everytime.
{%- if forloop.index0|modulo:4 == 0 -%}{%- endif -%}
Any ideas?
The problem with the above code is that Jekyll expects an end_of_string but found pipe in forloop.index0|modulo:4 == 0.
You have to assign the modulo calculation to a variable and then use it in the if clause, inside the for loop:
{% assign loopindex = forloop.index0 | modulo: 4 %}
{%- if loopindex == 0 -%}
<p style="background-color:red">Hey look! I'm a fourth element</p>
{%- endif -%}