Instantly share code, notes, and snippets.

Embed
What would you like to do?
Booking Form allowing units, and updating qty with number of units * days
{% comment %}
Instructions:
1. Add a hidden quantity field for BTA products in the booking-form.liquid snippet:
<input type="hidden" id="quantity" name="quantity" value="1" />
2. Make sure the normal quantity input is not rendered for BTA products (e.g. in sections/product-template.liquid).
For example, this is how it should look on the Debut theme:
{% if section.settings.show_quantity_selector %}
{% unless product.metafields.bookthatapp.config %}
<div class="product-form__item product-form__item--quantity">
<label for="Quantity">{{ 'products.product.quantity' | t }}</label>
<input type="number" id="Quantity" name="quantity" value="1" min="1" class="product-form__input" pattern="[0-9]*">
</div>
{% endunless %}
{% endif %}
3. Add the input for units:
<input type="number" id="units" name="properties[Units]" value="1" min="1" pattern="[0-9]*" />
Note: this is just a regular line item property with the name 'Units'.
It should go just after <div class="booking-form">
You will probably need to copy the markup/CSS classes so that it looks nice. Here is how it should look for Debut:
<input type="hidden" id="quantity" name="quantity" value="1" />
<div class="product-form__item product-form__item--quantity">
<label for="Quantity">{{ 'products.product.quantity' | t }}</label>
<input type="number" id="units" name="properties[Units]" value="1" min="1" class="product-form__input" pattern="[0-9]*" />
</div>
4. Copy the ProductRental Javascript from below to the booking form snippet.
5. Update cart.liquid as per the cart template section below
6. If the theme shows a count of items in the cart header update it per the cart count section below
{% endcomment %}
{% if product.metafields.bookthatapp.config %}
{% include 'booking-guard' %}
<div class="booking-form" style="display:none">
<div>
{% capture attribute %}booking-start{% endcapture %}
<label for="{{ attribute }}-{{ product.handle }}">From:</label>
<input id="{{ attribute }}-{{ product.handle }}" type="text" name="properties[From]" size="12" class="datepicker bta required bta-load-enable bta-dp-start" disabled="disabled"
data-handle="{{ product.handle }}" data-variant="{{ product.selected_or_first_available_variant.id }}"
data-bta-product-config="{{ product.metafields.bookthatapp.config }}"
data-bta-variant-config="{% for variant in product.variants %}{{ variant.id }}:{{ variant.metafields.bookthatapp.config }}{% unless forloop.last %},{% endunless %}{% endfor %}"
data-bta-range-partner-id="#booking-finish-{{ product.handle }}"
data-bta-range-days-max="0" data-bta-range-days-min="0" />
</div>
<div>
{% capture attribute %}booking-finish{% endcapture %}
<label for="{{ attribute }}-{{ product.handle }}">To:</label>
<input id="{{ attribute }}-{{ product.handle }}" type="text" name="properties[To]" size="12" class="datepicker bta required bta-load-enable bta-dp-finish" disabled="disabled"
data-bta-range-partner-id="#booking-start-{{ product.handle }}" />
</div>
<div class="quantity">
<label for="quantity">Quantity:</label>
{% comment %}
<div class="rental-summary">
Days: <span class="rental-days"></span><br>
Total: <span class="rental-total"></span>
</div>
{% endcomment %}
<input type="number" id="units" name="properties[Units]" value="1" min="1" pattern="[0-9]*" />
</div>
<div class="bta-validation-messages" style="display:none">
<p class="bta-validation-date-missing">Please select a date</p>
</div>
<input type="hidden" id="quantity" name="quantity" value="1" />
</div>
<script>
// rental product behaviour
var ProductRental = function() {
_this = this;
this.configs = {};
this.durations = [];
this.init = function() {
'{{ product.metafields.bookthatapp.config }}'.replace(new RegExp("([^?=&]+)(=([^&]*))?", "g"), function($0, $1, $2, $3) {_this.configs[$1] = $3;});
_this.durations = JSON.parse(unescape(_this.configs.durations)).sort(function(a, b) {
return a.position - b.position;
});
_this.hideDurationOptionSelect();
_this.initFormListeners();
};
this.initFormListeners = function() {
var form = $('form[action="/cart/add"]');
form.on('bta.dataLoaded, bta.datetimeChange', function(event, form) {
_this.updateQuantity();
});
form.on('click', '.js-qty__adjust', function() {
_this.updateQuantity();
});
$('#units').change(function() {
_this.updateQuantity();
});
};
this.updateQuantity = function() {
var form = $('form[action="/cart/add"]').data('bta.bookingForm'),
start = form.getStartDateTime(),
finish = form.getFinishDateTime();
$('.rental-summary .rental-total').text('-');
$('.rental-summary .rental-days').text('-');
if (start && finish) {
var days = form.rangeCount();
// total rent = number of units * number of days * variant.price
var variantId = bta.findSelectedVariantId(),
units = parseInt($('#units').val(), 10),
variant = _this.findVariant(variantId),
total = variant.price * units * days;
$('#quantity').val(units * days);
$('.rental-summary .rental-days').text(days);
$('.rental-summary .rental-total').text(_this.formatMoney(total));
}
};
this.findVariant = function(id) {
for (var i = 0; i < _this.product.variants.length; i++) {
if (_this.product.variants[i].id == id) {
return _this.product.variants[i];
}
}
};
this.hideDurationOptionSelect = function() {
// hide option for duration (bypass .selector-wrapper inside booking form)
$('.selector-wrapper:nth-child(' + _this.configs['duration_option_position'] + ')').filter(function(index) {
return $(this).parents('.booking-form').length == 0;
}).hide();
};
this.formatMoney = function(total) {
return Shopify.formatMoney(total, '{{ shop.money_format }}')
};
this.product = {{ product | json }};
}
document.addEventListener("DOMContentLoaded", function(event) {
new ProductRental().init();
});
var bta = {
productId: {{ product.id }}
}
</script>
{% endif %}
// ============ Cart Template =============
1. Display line item property dates:
{% if item.product.metafields.bookthatapp.config %}
<p>{{ item.properties.From }} - {{ item.properties.To }}</p>
{% else %}
// ususal guff
{% endif %}
2. Show units for quantity
{% if item.product.metafields.bookthatapp.config %}
{{ item.quantity | divided_by: item.properties.Units }} days x {{ item.properties.Units }}
<input type="hidden" name="updates[]" id="Updates_{{ item.id }}" value="{{ item.quantity }}" data-line="{{ forloop.index }}">
<a href="/cart/change?line={{ forloop.index }}&quantity=0">Remove</a>
{% else %}
<input type="number" name="updates[]" id="updates_{{ item.id }}" value="{{ item.quantity }}" min="0" data-line="{{ forloop.index }}">
{% endif %}
// ============ Cart Count (Header) =============
{% assign cart_count = 0 %}
{% for item in cart.items %}
{% if item.product.metafields.bookthatapp.config %}
{% assign cart_count = cart_count | plus: item.properties.Units %}
{% else %}
{% assign cart_count = item.quantity %}
{% endif %}
{% endfor %}
<span class="icon-cart"></span> {{ 'layout.general.cart' | t }} (<span class="cart_count">{{ cart_count }}</span>)
// ============ CSS ============
/* BookThatApp */
.booking-form {
display: -ms-flex;
display: -webkit-flex;
display: flex;
> div {
padding: 10px 0;
margin-right: 20px;
}
> div:last-child {
margin-right: 0px;
}
#units {min-width: 50px}
}
@skristyn

This comment has been minimized.

skristyn commented Feb 15, 2018

Hi, thanks for this! There's just one thing confusing to me. Do I actually need to upload this booking-form-rental-period.liquid file to my 'templates' folder? Or just put the main code (starting with {% if product.metafields.bookthatapp.config %} {% include 'booking-guard' %} and ending with </script>{% endif %}) in booking form snippet and the other code in the cart liquid file(s) as specified?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment