Subscription widget local installation
Learn how to speed up the subscription widget load time on your shop
Each theme is a bit different and some themes require some customization to make the subscription widget (aka subscription plan selector) work as expected. Local installation will help make sure the plan selector works and loads immediately. It's also a good way to make your own unique customizations.
Installing the plan selector locally requires some snippets which the Awtomic team will provide. Just reach out to [email protected] and ask for the files required to install the plan selector locally in your theme.
Installation
- Add the provided
awtomic-imports.liquid
file to the snippets folder in your theme - Add the provided
awtomic-plan-selector.liquid
file to the snippets folder in your theme - Render
awtomic-imports
just before the close of your body tag intheme.liquid
Notice that we've wrapped the import in a check to be sure that the file exists. This will ensure that an error message isn't rendered in your theme if you accidentally delete the{% comment %} Awtomic Subscriptions {% endcomment %} {% capture awtomic-imports %}{% render 'awtomic-imports' %}{% endcapture %} {% unless awtomic-imports contains "Liquid error" %} {{ awtomic-imports }} {% endunless %} {% comment %} End Awtomic Subscriptions {% endcomment %}
awtomic-imports.liquid
file from your theme. - Render
awtomic-plan-selector
in any product form in which you want the subscription plans selector to appear. It should be inside the form where the hidden input with name=”id” (ie the hidden selected variant id input) exists and is updated.
{% comment %} Awtomic Subscriptions {% endcomment %} {% capture awtomic_plan_selector %}{% render 'awtomic-plan-selector', product: product %}{% endcapture %} {% unless awtomic_plan_selector contains "Liquid error" %} {{ awtomic_plan_selector }} {% endunless %} {% comment %} End Awtomic Subscriptions {% endcomment %}
Optional: Translations Files
To update strings in the Customer Portal, Build-a-box UI and Subscription Widget or add translations for various languages you can follow these steps:
- Add the
awtomic-translations.liquid
file to the snippets folder - Add the provided locale file json addition to the locales json files under the field
awtomatic
at the root of the locale json you're updating - Render the
awtomic-translations
just before the close of the head tag in theme.liquid
{% comment %} Awtomic Subscriptions {% endcomment %} {% capture awtomic-translations %}{% render 'awtomic-translations' %}{% endcapture %} {% unless awtomic-translations contains "Liquid error" %} {{ awtomic-translations }} {% endunless %} {% comment %} End Awtomic Subscriptions {% endcomment %}
🚧
Future theme upgrades often require that you repeat local installation
Themes making major updates often require that you re-add any customizations that you've made in the past. Whenever you upgrade your theme make sure to check that your subscription widget is still loading as expected
Troubleshooting & FAQs:
- Sometimes when themes have custom variant selectors you'll have to use the
formDomChangeElementsSel
setting. To update this setting you can add the keyformDomChangeElementsSel
just abovehideIfUnavailable
in the settings object in theawtomic-imports.liquid
file and the value should be a CSS selector for elements that you want the plan selector to listen for clicks on. (ex"label.my-custom-variant-button-class"
) - If the price labels aren't updating and you've tried the
formDomChangeElementsSel
troubleshooting tip above then you might need to tell the plan selector the exact element where the price label is being rendered. To update this setting you can add the keypriceContainerSel
just abovehideIfUnavailable
in the settings object in theawtomic-imports.liquid
file and the value should be a CSS selector for element that contains the price label. (ex"div.my-custom-price-label-class"
) - Once you've done a local install, the control over whether or not to default to subscriptions is going to be set by the settings object locally instead of by the setting you've selected in the Awtomic app. To update it, update the
defaultSubscription
value in the object at the top of theawtomic-imports.liquid
file
Files
🚧
Reach out to support for the latest files required for local installation
These files are often updated or added to. To be sure you have the latest files, we ask that you contact [email protected] and ask for the files required to install the plan selector locally in your theme.
For immediate need - you can find a version of the files at the end of this article
awtomic-imports.liquid
awtomic-imports.liquid
<script>
(function () {
window.bundleapp = window.bundleapp || {};
window.bundleapp.settings = Object.assign(
{},
window.bundleapp.settings || {},
// All configs that where here have been moved to other places
// the moneyFormat is now fetched below via ajax
// other settings are set to the global namespace on the script tag handler
// So this code here is only a reminder that the attribute exists
/**
* Theme specific settings:
* This is a quick and dirty way to get some theme specific settings into the app.
*
* priceContainerSel Array<String> selector string for the price container
* comparePriceContainerSel Array<String> selector string for compare at price container
* priceBadgeContainer Array<String> selector string for the price Badge container
* hideIfUnavailable Boolean hides selling plans if variant is unavailable/out of stock
* containerClass String Adds a class to the container where the widget renders
* renderInsideSel String Selector to render the widget inside
* renderAfterSel String Selector to render the widget after
* renderBeforeSel String Selector to render the widget before
* useRadioButtonFrequencySelector Boolean use radio buttons instead of a select
* addToCartButtonSel Selector string to locale the add to cart button inside form[action='/cart/add']
* formDomChangeElementsSel String selector for items to listen to and trigger handleFormDomChanged (ex: 'li.variantSwatch' or 'li.variantSwatch, button.variantItem')
* - use this when the items to select variant are not input fields and as such would not be listened to by our script naturally
* */
{
hideIfUnavailable: false,
proxy: '/tools/bundle-subscriptions',
appPublic: true,
moneyFormat: '$\{\{amount\}\}',
shopStyleSettings: {},
defaultSubscription: true,
},
);
window.bundleapp.setThemeConfigs = (obj) => {
window.bundleapp.settings = Object.assign(
{},
window.bundleapp.settings,
obj,
);
};
var showPreviewModeControl = function () {
const previewModeControlHtml = `
<div id="awt-preview-mode-control">
<div class="awt-logo">
<div class="awt-logo--image"></div>
<span>Preview</span>
<a href="https://help.awtomatic.app/en/articles/5593674-previewing-and-testing-your-subscriptions" target="_blank"><div class="awt-logo--info-icon"></div></a>
</div>
<div>
<!-- <button id="awt-preview-mode-control__hide">Hide</button> -->
<button id="awt-preview-mode-control__close">Close</button>
</div>
</div>
`;
const $previewModelEl = document.createElement('div');
$previewModelEl.innerHTML = previewModeControlHtml;
document.querySelector('body').appendChild($previewModelEl);
// not removing this yet bc its set to be used in a v2 version of the preview mode controls
const hidePreviewControl = function () {
document
.querySelector('#awt-preview-mode-control')
.classList.add('hidden');
};
const closePreviewMode = function () {
window.localStorage.removeItem(PREVIEW_MODE_STORAGE_KEY);
insertUrlParam('awt-preview');
window.location.reload();
};
// document
// .querySelector('#awt-preview-mode-control__hide')
// .addEventListener('click', hidePreviewControl);
document
.querySelector('#awt-preview-mode-control__close')
.addEventListener('click', closePreviewMode);
};
var getShopInfo = function () {
const moneyFormat =
window.moneyFormat ||
(window.theme && window.theme.moneyFormat) ||
(window.theme &&
window.theme.strings &&
window.theme.strings.moneyFormat) ||
(window.theme &&
window.theme.settings &&
window.theme.settings.moneyFormat) ||
(window.bundleapp && window.bundleapp.settings && window.bundleapp.settings.moneyFormat);
if (moneyFormat) {
window.bundleapp.settings = Object.assign({}, window.bundleapp.settings, {
moneyFormat: moneyFormat,
});
return Promise.resolve();
}
return fetch(window.bundleapp.settings.proxy + '/shop-info')
.then(function (res) {
return res.json();
})
.then(function (data) {
window.bundleapp.settings = Object.assign(
{},
window.bundleapp.settings,
data,
);
});
};
var setCssVars = function (styleSettings) {
if (styleSettings) {
// update CSS vars
var root = document.querySelector(':root');
for (var styleSettingsKey in STYLE_VAR_MAP) {
styleSettings[styleSettingsKey] &&
root.style.setProperty(
STYLE_VAR_MAP[styleSettingsKey].cssVar,
styleSettings[styleSettingsKey],
);
}
}
};
var insertUrlParam = function (key, value) {
if (history.replaceState) {
let searchParams = new URLSearchParams(window.location.search);
if (value) {
searchParams.set(key, value);
} else {
searchParams.delete(key);
}
let newurl =
window.location.protocol +
'//' +
window.location.host +
window.location.pathname +
'?' +
searchParams.toString();
window.history.replaceState({ path: newurl }, '', newurl);
}
};
var formatMoney = function (cents, format) {
if (typeof cents == 'string') {
cents = cents.replace('.', '');
}
var value = '';
var placeholderRegex = /\{\{\s*(\w+)\s*\}\}/;
var formatString = format || window.bundleapp.settings.moneyFormat;
function defaultOption(opt, def) {
return typeof opt == 'undefined' ? def : opt;
}
function formatWithDelimiters(number, precision, thousands, decimal) {
precision = defaultOption(precision, 2);
thousands = defaultOption(thousands, ',');
decimal = defaultOption(decimal, '.');
if (isNaN(number) || number == null) {
return 0;
}
number = (number / 100.0).toFixed(precision);
var parts = number.split('.'),
dollars = parts[0].replace(
/(\d)(?=(\d\d\d)+(?!\d))/g,
'$1' + thousands,
),
cents = parts[1] ? decimal + parts[1] : '';
return dollars + cents;
}
switch (formatString.match(placeholderRegex)[1]) {
case 'amount':
value = formatWithDelimiters(cents, 2);
break;
case 'amount_no_decimals':
value = formatWithDelimiters(cents, 0);
break;
case 'amount_with_comma_separator':
value = formatWithDelimiters(cents, 2, '.', ',');
break;
case 'amount_no_decimals_with_comma_separator':
value = formatWithDelimiters(cents, 0, '.', ',');
break;
}
return formatString.replace(placeholderRegex, value);
};
// FIXME eslint error
// eslint-disable-next-line no-undef
debounce = function (func, wait, immediate) {
var timeout;
return function () {
var context = this,
args = arguments;
var later = function () {
timeout = null;
if (!immediate) func.apply(context, args);
};
var callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) func.apply(context, args);
};
};
// Method to get closes matching parent source: https://gomakethings.com/a-native-vanilla-javascript-way-to-get-the-closest-matching-parent-element/
if (window.Element && !Element.prototype.closest) {
Element.prototype.closest = function (s) {
var matches = (this.document || this.ownerDocument).querySelectorAll(s),
i,
el = this;
do {
i = matches.length;
// FIXME eslint error
// eslint-disable-next-line no-empty
while (--i >= 0 && matches.item(i) !== el) {}
} while (i < 0 && (el = el.parentElement));
return el;
};
}
var priceBadgeContainer = () =>
[
'form.product__form--add-to-cart .product__price', // Boundless
'.product-single__meta .price-container', //Brooklyn
'.product__price .price__pricing-group', // Debut
'.product__content .price__pricing-group', // Express
'.product-single__prices', // Minimal, Simple
'.product__content-header .product__price', // Narrative
'.inline-list.product-meta', // Supply
'.product-single__meta-list', // Venture
]
.concat(window.bundleapp.settings.priceBadgeContainer || [])
.join(', ');
const priceContainerSel = () =>
[
'.price__regular', // debut, express
'.product-single__price', // brooklin, minimal
'.product__current-price', // narrative
'#productPrice-product-template', // supply, venture
'.product__price--reg', // boundless
]
.concat(window.bundleapp.settings.priceContainerSel || [])
.join(', ');
const comparePriceContainerSel = () =>
[
'.price--on-sale .price__sale', // debut, express
'.product-single__price--compare-at', // brooklin
'.product-single__sale-price', // minimal
'.product__compare-price', //narrative
'.product-single__price--compare', // simple, venture
'.product__price .product__price--sale', // boundless
'.product-meta .sale-tag', // supply
]
.concat(window.bundleapp.settings.comparePriceContainerSel || [])
.join(', ');
const planSelectorSel = '.bundleapp-plan-selector-plan';
const addToCartButtonSel = () =>
['button[type=submit]']
.concat(window.bundleapp.settings.addToCartButtonSel || [])
.join(', ');
const PREVIEW_MODE_STORAGE_KEY = 'awtomatic-preview-mode';
/* Functions for gift form data validation */
const getField = (fieldId) => document.getElementById(fieldId);
const getFieldValue = (fieldId) => getField(fieldId).value;
const isEmpty = (str) => str === '';
const emailIsValid = (email) =>
/^[A-Z0-9._%+-]+@([A-Z0-9-]+\.)+[A-Z0-9]+$/i.test(email);
const isFieldValid = (fieldId) =>
getField(fieldId).getAttribute('data-invalid') == undefined;
const toggleAddToCart = (target, isValidForm) => {
const form = target.closest('form');
const addToCartBtn = form.querySelector(addToCartButtonSel());
if (addToCartBtn) {
if (isValidForm) {
addToCartBtn.removeAttribute('disabled');
} else {
addToCartBtn.setAttribute('disabled', true);
}
}
const customizeBtn = form.querySelector('#awt-customize-box-btn');
if (customizeBtn) {
if (isValidForm) {
customizeBtn.removeAttribute('disabled');
} else {
customizeBtn.setAttribute('disabled', true);
}
}
};
const toggleFieldValid = (field, isInvalid, message) => {
const msg = field.parentElement.getElementsByClassName('awt-error-msg');
if (isInvalid) {
field.classList.add('awt-input-error');
field.classList.add('awt-error');
field.setAttribute('data-invalid', true);
if (msg.length === 0) {
const errorLabel = document.createElement('div');
errorLabel.classList.add('awt-error-msg');
errorLabel.innerText = message;
field.parentElement.insertBefore(
errorLabel,
field.parentElement.lastElementChild,
);
}
} else {
field.classList.remove('awt-input-error');
field.classList.remove('awt-error');
if (msg.length > 0) {
msg[0].remove();
}
field.removeAttribute('data-invalid');
}
};
const isGiftFormValid = () =>
isFieldValid('awtGiftInfoFirstName') &&
isFieldValid('awtGiftInfoLastName') &&
isFieldValid('awtGiftInfoEmail');
const validateRequiredField = (event) => {
const isFieldEmpty = isEmpty(getFieldValue(event.target.id));
toggleFieldValid(event.target, isFieldEmpty, 'Required');
toggleAddToCart(event.target, isGiftFormValid());
};
const validateEmailField = (event) => {
const isValidEmail = emailIsValid(getFieldValue(event.target.id));
toggleFieldValid(event.target, !isValidEmail, 'Invalid email');
toggleAddToCart(event.target, isGiftFormValid());
};
/**
* Awtomatic Selector Widget
* */
function BundleAppWidget($selector, product) {
this.product = product;
this.isGiftSelected = false;
this.$selector = $selector;
this.$sellingPlanField = this.$selector.querySelector(
'input[name=selling_plan]',
);
this.$wrapper = this.$selector.closest(
'div[data-section-type], .shopify-section, body',
);
this.$planGroups = this.$selector.querySelectorAll(
'.bundleapp-plan-selector-group',
);
this.$form = this.$selector.closest('form');
this.$variantSelect = this.$form.querySelector('[name=id]');
this.$badgePriceEl = this.$wrapper.querySelector(priceBadgeContainer());
if (!this.$badgePriceEl)
console.warn('Did not find a badge container element');
// FIXME eslint error
// eslint-disable-next-line no-undef
this.setPlansVisibility = debounce(
this._baseSetPlansVisibility.bind(this),
50,
);
this.init();
this.setPlansVisibility();
if (window.bundleapp.settings.onInit) {
window.bundleapp.settings.onInit.call(this);
}
if (window.bundleapp.onInit) {
console.warn(
'onInit will be deprecated soon. Please use bundleapp.settings.onInit instead',
);
window.bundleapp.onInit.call(this);
}
}
Object.assign(BundleAppWidget.prototype, {
init: function () {
this.$selector.style.display = 'block';
this.$planGroups.forEach(
function (elem) {
var $planGroupRadio = elem.querySelector(
"input[name='bundleapp-plan-selector-group']",
);
var groupId = $planGroupRadio.value;
var selectedPlanGroup = this.product.selling_plans_by_id[
this.product.selected_selling_plan
? this.product.selected_selling_plan.id
: null
];
var isSelectedGroup = !!(
selectedPlanGroup &&
selectedPlanGroup.selling_plan_group_id === groupId
);
this.handleGiftVisibility(isSelectedGroup);
$planGroupRadio.addEventListener(
'change',
this.handlePlanRadioChange.bind(this),
);
elem
.querySelector('select')
.addEventListener(
'change',
this.handlePlanOptionsChange.bind(this),
);
var $radioButtonsfrequencySelector = elem.querySelectorAll(
'.bundleapp-plan-selector-radio__input',
);
for (
let index = 0;
index < $radioButtonsfrequencySelector.length;
index++
) {
$radioButtonsfrequencySelector[index].addEventListener(
'change',
this.handlePlanOptionsChange.bind(this),
);
}
}.bind(this),
);
//Ads an event listener to every element in the form
const $selectorElements = Array.from(
this.$selector.querySelectorAll('input, select'),
);
Array.from(this.$form.elements).forEach(
function (el) {
if (
!$selectorElements.includes(el) &&
el.tagName !== 'BUTTON' &&
el.type !== 'hidden'
) {
el.addEventListener('change', this.handleFormDomChanged.bind(this));
}
}.bind(this),
);
if (window.bundleapp.settings.defaultSubscription) {
const $oneTimeRadio = this.$selector.querySelector('[data-one-time]');
// Only if one time is available and defaultSubscription enabled we set checked
// the first selling plan group. If there is no one-time option, we already
// mark the first selling plan checked
if ($oneTimeRadio) {
const $firstSellingPlanGroup = this.$selector.querySelector(
'[data-one-time] + .bundleapp-plan-selector-group input',
);
$firstSellingPlanGroup.dispatchEvent(new Event('change'));
}
}
if (window.bundleapp.settings.formDomChangeElementsSel) {
const $variantEls = document.querySelectorAll(
window.bundleapp.settings.formDomChangeElementsSel,
);
$variantEls.forEach(
function (el) {
el.addEventListener('click', this.handleFormDomChanged.bind(this));
}.bind(this),
);
}
},
getTranslation: function (key, fallback) {
let translation = this.product?.translations[key];
if (translation.includes('ranslation missing')) {
translation = fallback;
}
return translation;
},
handleGiftVisibility: function (isSelectedGroup) {
let isGiftable =
this.product &&
this.product.gift_selling_plans &&
this.product.gift_selling_plans.includes(
this.product.selected_selling_plan &&
this.product.selected_selling_plan.id,
);
const giftCheckBox = document.querySelector('input[name="awt-is-gift"]');
if (isGiftable && !giftCheckBox && isSelectedGroup) {
this.isGiftSelected = true;
let isGiftCheckboxLabel = document.createElement('label');
isGiftCheckboxLabel.classList.add('awt-checkbox');
let isGiftCheckbox = document.createElement('input');
const giftTitle = this.getTranslation(
'gift_recipient_info_title',
'Recipient info',
);
const giftFirstNameLabel = this.getTranslation(
'gift_first_name_label',
'First name',
);
const giftFirstNamePlaceholder = this.getTranslation(
'gift_first_name_placeholder',
'First name',
);
const giftLastNameLabel = this.getTranslation(
'gift_last_name_label',
'Last name',
);
const giftLastNamePlaceholder = this.getTranslation(
'gift_last_name_placeholder',
'Last name',
);
const giftEmailLabel = this.getTranslation(
'gift_email_label',
'Email address',
);
const giftEmailPlaceholder = this.getTranslation(
'gift_email_placeholder',
'Email address',
);
const giftEmailWarning = this.getTranslation(
'gift_email_warning',
'Important: Gift emails will be sent here',
);
const giftNoteLabel = this.getTranslation('gift_note_label', 'Note');
const giftNotePlaceholder = this.getTranslation(
'gift_note_placeholder',
'Note',
);
isGiftCheckbox.addEventListener('change', function (event) {
const isGiftSelected = event.currentTarget.checked;
this.isGiftSelected = isGiftSelected;
if (isGiftSelected) {
const giftsMarkUp = `<div class="gift">
<p class="awt-gift-info-title">${giftTitle}</p>
<div class="awt-form-group">
<label for="awtGiftInfoFirstName" class="awt-floating-label">
<input type="text" id="awtGiftInfoFirstName" class="awt-gift-input" required name="properties[_giftFirstName]" placeholder="${giftFirstNamePlaceholder}" data-invalid>
<span class="awt-gift-label">${giftFirstNameLabel}*</span>
</label>
<label for="awtGiftInfoLastName" class="awt-floating-label">
<input type="text" id="awtGiftInfoLastName" class="awt-gift-input" required name="properties[_giftLastName]" placeholder="${giftLastNamePlaceholder}" data-invalid>
<span class="awt-gift-label">${giftLastNameLabel}*</span>
</label>
</div>
<div class="awt-gift-input-container">
<label for="awtGiftInfoEmail" class="awt-floating-label">
<input type="email" id="awtGiftInfoEmail" class="awt-gift-input" required name="properties[_giftEmail]" placeholder="${giftEmailPlaceholder}" data-invalid>
<span class="awt-gift-label">${giftEmailLabel}*</span>
<div class="awt-floating-label-warning">${giftEmailWarning}</div>
</label>
</div>
<div class="awt-gift-input-container">
<label for="awtGiftInfoNote" class="awt-floating-label">
<textarea id="awtGiftInfoNote" class="awt-gift-input" placeholder="${giftNotePlaceholder}" name="properties[_giftNote]"></textarea>
<span class="awt-gift-label">${giftNoteLabel}</span>
</label>
</div>
</div>`;
let giftInfoContainer = document.createElement('div');
giftInfoContainer.setAttribute('name', 'awt-gift-info-container');
giftInfoContainer.classList.add('awt-gift-info-container');
giftInfoContainer.innerHTML = giftsMarkUp;
isGiftCheckboxLabel.parentElement.appendChild(giftInfoContainer);
getField('awtGiftInfoFirstName').addEventListener(
'blur',
validateRequiredField,
);
getField('awtGiftInfoLastName').addEventListener(
'blur',
validateRequiredField,
);
getField('awtGiftInfoEmail').addEventListener(
'blur',
validateEmailField,
);
toggleAddToCart(getField('awtGiftInfoEmail'), isGiftFormValid());
} else {
const giftInfoContainer = document.querySelector(
'div[name="awt-gift-info-container"]',
);
getField('awtGiftInfoFirstName').removeEventListener(
'blur',
validateRequiredField,
);
getField('awtGiftInfoLastName').removeEventListener(
'blur',
validateRequiredField,
);
getField('awtGiftInfoEmail').removeEventListener(
'blur',
validateEmailField,
);
toggleAddToCart(getField('awtGiftInfoEmail'), true);
giftInfoContainer &&
giftInfoContainer.parentNode &&
giftInfoContainer.parentNode.removeChild(giftInfoContainer);
}
});
isGiftCheckbox.type = 'checkbox';
isGiftCheckbox.name = 'awt-is-gift';
isGiftCheckboxLabel.appendChild(isGiftCheckbox);
isGiftCheckboxLabel.appendChild(
document.createTextNode(
this.getTranslation('gift_checkbox_label', 'This is a gift'),
),
);
const planDescription = document.querySelector(
'.bundleapp-plan-selector-description',
);
planDescription &&
planDescription.parentNode.insertBefore(
isGiftCheckboxLabel,
planDescription.nextSibling,
);
}
if (!isGiftable) {
if (giftCheckBox) {
const giftCheckBoxLabel = giftCheckBox.parentNode;
giftCheckBoxLabel.parentNode.removeChild(giftCheckBoxLabel);
const giftInfoContainer = document.querySelector(
'div[name="awt-gift-info-container"]',
);
giftInfoContainer &&
giftInfoContainer.parentNode &&
giftInfoContainer.parentNode.removeChild(giftInfoContainer);
}
}
},
// This is marked as a private function because this is not intended to be used
// directly. Use the debounced version of it this.setPlansVisibility()
_baseSetPlansVisibility: function () {
if (window.bundleapp.settings.hideIfUnavailable) {
//If variant is not available hide the whole widget
this.$selector.style.display = this.product.selected_variant.available
? 'block'
: 'none';
}
var selling_plan_group_ids = this.product.selected_variant.selling_plan_allocations.map(
function (i) {
return i.selling_plan_group_id;
},
);
var planId = '';
const hasPlanWithMultipleDeliveries = this.product.selected_variant.selling_plan_allocations.some(
(sp) => sp.price !== sp.per_delivery_price,
);
// @TODO: translate suffix
const suffix = hasPlanWithMultipleDeliveries
? this.getTranslation('delivery_suffix', '/delivery')
: '';
this.$planGroups.forEach(
function (elem) {
var bundleAppSettings = window.bundleapp.settings || {};
var $planGroupRadio = elem.querySelector(
"input[name='bundleapp-plan-selector-group']",
);
var groupId = $planGroupRadio.value;
elem.style.display =
selling_plan_group_ids.includes(groupId) || !groupId
? 'block'
: 'none';
var selectedPlanGroup = this.product.selling_plans_by_id[
this.product.selected_selling_plan
? this.product.selected_selling_plan.id
: null
];
var isSelectedGroup = !!(
selectedPlanGroup &&
selectedPlanGroup.selling_plan_group_id === groupId
);
this.handleGiftVisibility(isSelectedGroup);
var $selectorPlan = elem.querySelector(planSelectorSel);
$selectorPlan.style.display = isSelectedGroup ? 'block' : 'none';
if (!$planGroupRadio.value && !isSelectedGroup) {
// If value is falsy a.k.a one-time purchase
$planGroupRadio.checked = true;
} else {
$planGroupRadio.checked = isSelectedGroup;
if (this.$planGroups.length === 1) {
elem.classList.add('bundleapp-plan-selector-group--single-group');
}
// if this is the selected group add the selected class
if (isSelectedGroup) {
elem.classList.add('bundleapp-plan-selector-group--selected');
if (bundleAppSettings.useRadioButtonFrequencySelector) {
const selectedGroupPlans = elem
.querySelector('.bundleapp-plan-selector-radio')
.getElementsByTagName('input');
const isPlanChecked = Array.from(selectedGroupPlans).some(
({ checked }) => checked,
);
if (!isPlanChecked) {
// if no plan is checked, select the first one
selectedGroupPlans[0].checked = true;
elem
.querySelector('.bundleapp-plan-selector-radio label')
.classList.add(
'bundleapp-plan-selector-radio__label--selected',
);
}
}
}
}
if (bundleAppSettings.useRadioButtonFrequencySelector) {
let $radioButtonPlanSelector = elem.querySelector(
'.bundleapp-plan-selector-radio',
);
$radioButtonPlanSelector.style.display = isSelectedGroup
? 'block'
: 'none';
let $selectPlanSelector = elem.querySelector(
'.bundleapp-plan-selector-select',
);
$selectPlanSelector.style.display = 'none';
} else {
let $radioButtonPlanSelector = elem.querySelector(
'.bundleapp-plan-selector-radio',
);
$radioButtonPlanSelector.style.display = 'none';
let $selectPlanSelector = elem.querySelector(
'.bundleapp-plan-selector-select',
);
$selectPlanSelector.style.display =
isSelectedGroup && $selectPlanSelector.options.length > 1
? 'block'
: 'none';
}
if (isSelectedGroup) {
const planSel = bundleAppSettings.useRadioButtonFrequencySelector
? `${planSelectorSel} .bundleapp-plan-selector-radio input:checked`
: `${planSelectorSel} select`;
planId =
elem.querySelector(planSel) && elem.querySelector(planSel).value;
}
//update pricing label of each group
const planGroup = this.product.selling_plan_groups.find(
(p) => p.id === groupId,
);
const $priceLabel = elem.querySelector(
'.bundleapp-plan-selector-group-pricing',
);
const currentVariant = this.product.variants.find(
(variant) => variant.id === this.product.selected_variant.id,
);
if (planGroup && $priceLabel) {
const values = planGroup.selling_plans.map(function (i) {
return parseInt(i.price_adjustments[0]?.value);
});
const sellingPlansFromCurrentGroup = currentVariant.selling_plan_allocations.filter(
(sp) => sp.selling_plan_group_id === planGroup.id,
);
if (sellingPlansFromCurrentGroup && values) {
const lowestPriceInGroup = Math.min.apply(
null,
sellingPlansFromCurrentGroup.map((sp) => sp.per_delivery_price),
);
const isSame = values.every(function (val) {
return val === values[0];
});
const prefix = isSame ? '' : this.getTranslation('from', 'from');
$priceLabel.innerHTML = `${prefix} ${formatMoney(
lowestPriceInGroup,
)}${suffix}`;
$priceLabel.dataset.test = `planPriceInSelector-${
sellingPlansFromCurrentGroup?.selling_plan_group_id ||
sellingPlansFromCurrentGroup[0]?.selling_plan_group_id
}`;
}
}
if (this.product?.selling_plan_groups?.length === 1) {
// check to see. IF just one selling plan group, and does not offer discount
// then we wont display itemized prices.
const $itemizedPrices = this.$selector.querySelectorAll(
'.bundleapp-plan-selector-group-pricing',
);
const oneTimePurchasePrice = currentVariant?.price;
const pricesInSellingPlans = currentVariant.selling_plan_allocations.map(
(sp) => sp.price,
);
const allEqual = (arr) => arr.every((val) => val === arr[0]);
if (allEqual([oneTimePurchasePrice, ...pricesInSellingPlans])) {
$itemizedPrices.forEach((price) => {
price.style.display = 'none';
});
}
}
}.bind(this),
);
//check if theres a visible option selected
//If not: check first
if (
!Array.from(this.$planGroups).filter(
(el) => el.offsetParent && el.querySelector('input').checked,
).length
) {
// get first input that's visible
const firstVisibleGroup = Array.from(this.$planGroups).filter(
(el) => el.offsetParent,
)[0];
if (firstVisibleGroup) {
const $firstRadio = firstVisibleGroup.querySelector('input');
if (!$firstRadio.checked) {
$firstRadio.checked = true;
$firstRadio.dispatchEvent(new Event('change'));
// if we are using radio buttons get the first plan and check it
if (window.bundleapp.settings.useRadioButtonFrequencySelector) {
let $radioButtonPlanSelector = firstVisibleGroup.querySelector(
'.bundleapp-plan-selector-radio',
);
const firstPlan = $radioButtonPlanSelector.querySelector('input');
if (!firstPlan.checked) {
firstPlan.checked = true;
firstPlan.dispatchEvent(new Event('change'));
}
}
}
}
}
//Updates input field
if (this.$sellingPlanField) {
this.$sellingPlanField.value = planId ? planId : '';
}
//Update url only if we are in the product page
if (window.location.pathname.includes('/products/')) {
insertUrlParam('selling_plan', planId);
}
//Updates one-time purchase price
const $oneTimePrice = this.$selector.querySelector(
'[data-one-time].bundleapp-plan-selector-group .bundleapp-plan-selector-group-pricing',
);
if ($oneTimePrice) {
$oneTimePrice.innerHTML = `${formatMoney(
this.product.selected_variant.price,
)}${suffix}`;
// data-test attribute for testing
$oneTimePrice.dataset.test = 'oneTimePriceInSelector';
}
// do not update prices if there are no selling plans groups on the product
if (this.product?.selling_plan_groups?.length) {
this.updatePrice(planId);
this.updateBadgePrice(planId);
}
this.replaceAddBtnForBundles();
},
replaceAddBtnForBundles: function () {
// only replace button on BaB bundle
if (
!this.product?.metafields?.isBundle ||
this.product?.metafields?.type === 'shuffle'
) {
return;
}
const $btn = this.$form.querySelector(addToCartButtonSel());
// remove "buy it now" button
const $shopifyPaymentBtn = $btn.parentElement.querySelector(
'.shopify-payment-button',
);
if ($shopifyPaymentBtn) {
$shopifyPaymentBtn.remove();
}
let $clonedBtn = this.$form.querySelector('#awt-customize-box-btn');
if (!$clonedBtn) {
$clonedBtn = $btn.cloneNode(true);
$clonedBtn.id = 'awt-customize-box-btn';
$clonedBtn.type = 'button';
$clonedBtn.innerText = this.getTranslation(
'customize_my_box',
'Customize my box',
);
$clonedBtn.classList.remove('btn--secondary-accent');
$clonedBtn.classList.add('btn--primary-accent');
$clonedBtn.removeAttribute('@click.prevent');
$clonedBtn.removeAttribute('x-show:');
$clonedBtn.removeAttribute(':disabled');
$clonedBtn.removeAttribute(':class');
// remove any href in case the theme has been customized and isn't a standard add-to-cart button
$clonedBtn.removeAttribute('href');
$btn.style.display = 'none';
$btn.classList.add('awt-hidden-add-to-cart-button');
$btn.parentNode.insertBefore($clonedBtn, $btn.nextSibling);
} else {
// clone the previous clone and replace
var secondClone = $clonedBtn.cloneNode(true);
$clonedBtn.parentNode.replaceChild(secondClone, $clonedBtn);
// reference new button so we can add event listener
$clonedBtn = secondClone;
}
var handleCustomizeBoxButtonClick = function (event) {
event.stopPropagation();
let params = new URLSearchParams(window.location.search);
let search = window.location.search;
// only use product.selected_variant.id arg if variant is not already in the
// url params. Otherwise we might be overriding the latest selection
if (!search.includes('variant') && this.product?.selected_variant?.id) {
params.append('variant', this.product?.selected_variant?.id);
}
// only use selectedSellingPlanId arg if variant is not already in the
// url params. Otherwise we might be overriding the latest selection
let selectedSellingPlanId = this.product?.selected_selling_plan?.id;
if (!selectedSellingPlanId) {
// if selectedSellingPlanId wasn't on the product check for the hidden input
selectedSellingPlanId = document.querySelector(
'input[name="selling_plan"]',
)?.value;
}
if (!search.includes('selling_plan') && selectedSellingPlanId) {
params.append('selling_plan', selectedSellingPlanId);
}
let priceAdjustments = this.product?.selected_selling_plan
?.price_adjustments;
if (
priceAdjustments &&
priceAdjustments[0] &&
priceAdjustments[0].value
) {
params.append(
'd',
`${priceAdjustments[0].value}-${
priceAdjustments[0].value_type === 'percentage' ? 'p' : 'q'
}`,
);
}
let url = new URL(
`${window.location.origin}${window.bundleapp.settings.proxy}/bundle/${this.product.id}`,
);
url.search = params.toString();
let isGiftable = this.product.gift_selling_plans.includes(
this.product.selected_selling_plan?.id,
);
if (isGiftable && this.isGiftSelected) {
const giftFirstName = document.getElementById('awtGiftInfoFirstName');
const giftLastName = document.getElementById('awtGiftInfoLastName');
const giftEmail = document.getElementById('awtGiftInfoEmail');
const giftNote = document.getElementById('awtGiftInfoNote');
const giftInformation = {
firstName: giftFirstName?.value,
lastName: giftLastName?.value,
email: giftEmail?.value,
note: giftNote?.value,
};
sessionStorage &&
sessionStorage.setItem(
'awt-gift-information',
JSON.stringify(giftInformation),
);
}
window.location.href = url.toString();
};
$clonedBtn.addEventListener(
'click',
handleCustomizeBoxButtonClick.bind(this),
);
},
updatePrice: function (planId) {
var planAllocation = this.product.selected_variant.selling_plan_allocations.find(
(allo) => allo.selling_plan_id == planId,
);
// var planOption = this.product.selling_plans_by_id[planId];
let $comparePriceSpan = this.$wrapper.querySelector(
'.bundleapp-compareAtPrice',
);
const $priceRegular = this.$wrapper.querySelector(priceContainerSel());
const $priceSale = this.$wrapper.querySelector(
comparePriceContainerSel(),
);
if (!$priceRegular) console.warn('Did not find a price element');
if (!$priceSale) console.warn('Did not find a compare price element');
if (!$comparePriceSpan) {
$comparePriceSpan = document.createElement('span');
$comparePriceSpan.classList.add('bundleapp-compareAtPrice');
if ($priceRegular) {
const priceStyles = window.getComputedStyle($priceRegular);
Array.from(priceStyles).forEach((key) =>
$comparePriceSpan.style.setProperty(
key,
priceStyles.getPropertyValue(key),
priceStyles.getPropertyPriority(key),
),
);
$priceRegular.after($comparePriceSpan);
}
$comparePriceSpan.style.textDecoration = 'line-through';
$comparePriceSpan.style.webkitTextDecorationLine = 'line-through';
$comparePriceSpan.style.marginLeft = '10px';
// for testing purpose
if ($comparePriceSpan)
$comparePriceSpan.dataset.test = 'bundlePriceOnSale';
}
setTimeout(
function () {
// Since the theme wants to change the price we need to wait a bit to make our changes,
// hence, the timeout
if (planAllocation) {
if ($priceRegular) {
$priceRegular.innerHTML = `${formatMoney(planAllocation.price)}`;
if (
planAllocation.compare_at_price &&
planAllocation.compare_at_price !== planAllocation.price
) {
$comparePriceSpan.innerHTML = `${formatMoney(
planAllocation.compare_at_price,
)}`;
$comparePriceSpan.style.display = 'inline';
} else if (
product.compare_at_price &&
product.compare_at_price !== planAllocation.price
) {
$comparePriceSpan.innerHTML = `${formatMoney(
product.compare_at_price,
)}`;
$comparePriceSpan.style.display = 'inline';
}
else {
$comparePriceSpan.style.display = 'none';
}
}
} else {
if ($priceRegular) {
$priceRegular.innerHTML = `${formatMoney(
this.product.selected_variant.price,
)}`;
}
if (this.product.selected_variant.compare_at_price) {
$comparePriceSpan.innerHTML = formatMoney(
this.product.selected_variant.compare_at_price,
);
$comparePriceSpan.style.display = 'inline';
} else {
$comparePriceSpan.style.display = 'none';
}
}
// make sure theme did not try to display=block
if ($priceRegular) $priceRegular.style.display = 'inline';
//for testing purposes
if ($priceRegular) $priceRegular.dataset.test = 'priceRegular';
// hide this because we are not longer using this elements.
if ($priceSale) $priceSale.style.display = 'none';
//for testing purposes
if ($priceSale) $priceSale.dataset.test = 'priceOnSale';
// make sure theme did not tried to decorate text
if ($priceRegular) $priceRegular.style.textDecoration = 'none';
if (
this.product?.selected_variant?.compare_at_price ===
this.product.selected_variant.price ||
this.product?.selected_variant?.compare_at_price ===
planAllocation?.price ||
this.product?.selected_variant?.price === planAllocation?.price
) {
if ($comparePriceSpan) $comparePriceSpan.style.display = 'none';
}
}.bind(this),
100,
);
},
updateBadgePrice: function (planId) {
var planAllocation = this.product.selected_variant.selling_plan_allocations.find(
(allo) => allo.selling_plan_id == planId,
);
var planOption = this.product.selling_plans_by_id[planId];
var $priceAdjustment = this.$wrapper.querySelector(
'.bundleapp-price-adjustment',
);
if (planAllocation) {
var value_type = planOption.price_adjustments[0].value_type;
var value = planOption.price_adjustments[0].value;
var symbol = value_type === 'percentage' ? '%' : '';
var strValue = value_type === 'percentage' ? value : formatMoney(value);
var discount = value > 0 ? `${strValue}${symbol}` : '';
// find out if this selling plan is part of a selling plan group with multiple selling plans/frequencies
const planAllocationBrothers = this.product.selected_variant.selling_plan_allocations.reduce(
(acc, curr) => {
if (
curr.selling_plan_group_id ===
planAllocation.selling_plan_group_id &&
curr.selling_plan_id !== planAllocation.selling_plan_id
) {
acc.brothers = acc.brothers + 1;
if (curr.price !== planAllocation.price) {
acc.dynamicSavings = acc.dynamicSavings + 1;
}
}
return acc;
},
{ brothers: 0, dynamicSavings: 0 },
);
const subscriptionLabel = `<span data-test="subscriptionLabel">${this.getTranslation(
'subscription',
'SUBSCRIPTION',
)}</span>`;
const dotSeparatorLabel =
'<span data-test="dotSeparatorLabel"> · </span>';
const saveLabel = `<span data-test="saveLabel">${this.getTranslation(
'save',
'Save',
)} ${discount}</span>`;
const extraDiscountLabel = `<span data-test="extraDiscountLabel">${this.getTranslation(
'extra',
'Extra',
)} ${discount} ${this.getTranslation('off', 'off')}</span>`;
let badgeLabel = subscriptionLabel + dotSeparatorLabel + saveLabel;
if (!discount) {
badgeLabel = subscriptionLabel;
}
if (
this.product.selected_variant.compare_at_price &&
this.product.selected_variant.price != planAllocation.price
) {
badgeLabel = extraDiscountLabel;
}
const html = `<span data-test="subscriptionBadge">${badgeLabel}</span>`;
if (this.$badgePriceEl) {
if ($priceAdjustment) {
$priceAdjustment.innerHTML = html;
} else {
var div = document.createElement('div');
div.classList.add('bundleapp-price-adjustment');
div.innerHTML = html;
this.$badgePriceEl.appendChild(div);
}
}
} else {
if ($priceAdjustment) {
$priceAdjustment.remove();
}
}
},
setSupportInformation: function () {
var $description = this.$selector.querySelector(
'.bundleapp-plan-selector-description',
);
if ($description) {
if (
this.product.selected_selling_plan &&
this.product.selected_selling_plan.description
) {
$description.innerHTML = `<span>${this.product.selected_selling_plan.description}</span>`;
} else {
$description.innerHTML = '';
}
}
},
handlePlanRadioChange: function (e) {
var target = e.currentTarget || e.target;
var $planGroup = target.closest('.bundleapp-plan-selector-group');
// remove selected class from previously selected group
var $previouslySelected = this.$form.querySelectorAll(
'.bundleapp-plan-selector-group--selected',
);
$previouslySelected.forEach((selectedGroup) =>
selectedGroup.classList.remove(
'bundleapp-plan-selector-group--selected',
),
);
// add selected class to newly selected group
$planGroup.classList.add('bundleapp-plan-selector-group--selected');
var $planSelect = $planGroup.querySelector('select');
var planId = $planSelect.value;
this.product.selected_selling_plan = this.product.selling_plans_by_id[
planId
];
this.setPlansVisibility();
this.setSupportInformation();
},
handlePlanOptionsChange: function (e) {
var target = e.currentTarget || e.target;
var planId = target.value;
if (window.bundleapp.settings.useRadioButtonFrequencySelector) {
// remove selected class from previously selected plan label
var $previouslySelectedLabels = document.querySelectorAll(
'.bundleapp-plan-selector-radio__label--selected',
);
$previouslySelectedLabels.forEach((selectedLabel) =>
selectedLabel.classList.remove(
'bundleapp-plan-selector-radio__label--selected',
),
);
// add selected class to selected label
var $label = document.querySelector(
`.bundleapp-plan-selector-radio__label[for="${planId}"]`,
);
if ($label) {
$label.classList.add(
'bundleapp-plan-selector-radio__label--selected',
);
}
}
this.product.selected_selling_plan = this.product.selling_plans_by_id[
planId
];
this.setPlansVisibility();
},
handleFormDomChanged: function (e) {
setTimeout(
function () {
var variantId = this.$form.querySelector('[name=id]').value;
this.product.selected_variant = this.product.variants.find(
(v) => v.id == variantId,
);
this.setPlansVisibility();
}.bind(this),
20,
);
},
});
//Validates app is public
const inPreviewMode = !!(
window.location.search.includes('awt-preview') ||
window.localStorage.getItem(PREVIEW_MODE_STORAGE_KEY) === 'true'
);
// make sure preview mode is set in local storage if in preview mode
inPreviewMode &&
window.localStorage.setItem(PREVIEW_MODE_STORAGE_KEY, inPreviewMode);
// if we're in preview mode we can get rid of the URL param
inPreviewMode && insertUrlParam('awt-preview');
// show preview control if !appPublic and inPreviewMode
if (inPreviewMode && !window.bundleapp.settings.appPublic) {
showPreviewModeControl();
}
// update CSS vars
setCssVars(window.bundleapp.settings.shopStyleSettings);
if (window.bundleapp.settings.appPublic || inPreviewMode) {
window.bundleapp.BundleAppWidget = BundleAppWidget;
document.dispatchEvent(new Event('bundleapp:ready'));
}
/**
* Iterating over forms
*/
var shopInfoPromise = getShopInfo();
window.bundleapp.initializeForms = function () {
var $forms = document.querySelectorAll("form[action='/cart/add']");
$forms.forEach(function (el) {
// if we found more than one form, lets make sure we're adding only to the
// forms that we want to..
if ($forms.length > 1) {
// check if this is the installments form (from Dawn theme) before
// initializing the plan selector widget
if (el.getAttribute('id') && el.getAttribute('id').includes('product-form-installment')) {
return;
}
// additional checks will go here
}
var settings = window.bundleapp.settings;
var $variantEl = el.querySelector('[name=id]');
// Checking if the widget has been initialized already
if (el.querySelector('[data-bundleapp-wrapper]')) {
console.warn('Bundleapp: Form already initialized');
return;
}
if ($variantEl) {
var pathname = window.location.pathname;
var search = window.location.search;
var searchParams = new URLSearchParams(search);
var reqParams = new URLSearchParams();
//gets product handle if in a product page
let productHandle = pathname.includes('/products/')
? pathname.split('/').filter(Boolean).pop()
: null;
// If there is a container selector we want to try to get the handle from it.
// This because in collection pages or product pages with recommended products it
// will be easier: ie <div data-bundleapp-widget="{{product.handle}}"/>
var $containerSel = el.querySelector(
settings.containerSel || '[data-bundleapp-widget]',
);
if (
$containerSel &&
$containerSel.dataset &&
$containerSel.dataset.bundleappWidget
) {
productHandle = $containerSel.dataset.bundleappWidget;
}
if (productHandle) {
reqParams.append('productHandle', productHandle);
}
var getProductSelector = function () {
return;
return fetch(
settings.productSelectorUrl
? settings.productSelectorUrl +
productHandle +
'?view=selector&' +
reqParams.toString()
: settings.proxy + '/product-selector?' + reqParams.toString(),
).then(function (res) {
if (!res.ok) {
throw new Error(res.statusText);
}
return res.text();
});
};
// gets selected variant
var variantId = $variantEl.value;
if (variantId) {
reqParams.append('variant', variantId);
}
// gets selected selling plan
var sellingPlan = searchParams.get('selling_plan');
if (sellingPlan) {
reqParams.append('selling_plan', sellingPlan);
}
var compareHtmlStrings = function (html1, html2) {
// We need to remove this uuid from the string since it is always different
// This is used to uniquely identify each widget. and it cannot be any product related
// attribute because we could have multiple selectors for the same product
var regexWrapperIdHtml = /data-bundleapp-wrapper=["|']\d+["|']/gm;
var regexWrapperIdJs = /"widget_id":\s?"\d+"/gm;
var str1 = (html1 || '')
.replace(regexWrapperIdHtml, '')
.replace(regexWrapperIdJs, '');
var str2 = (html2 || '')
.replace(regexWrapperIdHtml, '')
.replace(regexWrapperIdJs, '');
return str1 === str2;
};
var cachedSelector;
var selectorStorageKey = `bundleSelector1${productHandle + variantId}`;
// We check first if there is a cached selector in local storage
// to speed the render time
try {
cachedSelector = window.localStorage.getItem(selectorStorageKey);
if (cachedSelector && cachedSelector !== 'undefined') {
Promise.all([shopInfoPromise]).then(function () {
if (window.bundleapp.settings.appPublic || inPreviewMode) {
addAndInitiateSelector(
cachedSelector,
window.bundleapp.settings.shopStyleSettings,
);
}
});
}
} catch (err) {
console.error(err);
}
// we want to store the selector in a cache
// in case there is a new version we can remove it and
// create and initiate a new one
var cachedDivContainer;
var addAndInitiateSelector = function (htmlString, styleSettings) {
var addSelector = function ($el) {
// if selector style is selected use that
if (styleSettings && styleSettings.SelectorStyle) {
$el.classList.add(styleSettings.SelectorStyle);
}
// if a container class has been set in the settings this means
// that we've manually set them up, use this class
else if (settings.containerClass) {
$el.classList.add(settings.containerClass);
}
// if styleSettings exist but no selector style has been chosen, default to awt-style-1
else if (styleSettings && !styleSettings.SelectorStyle) {
$el.classList.add('awt-style-1');
}
// final default is bundleapp-container
else {
$el.classList.add('bundleapp-container');
}
$el.innerHTML = htmlString.trim();
var $renderBeforeEl, $renderAfterEl, $renderInsideEl;
if (
settings.renderAfterSel ||
(styleSettings && styleSettings.RenderAfterSel)
) {
$renderAfterEl = el.querySelector(
settings.renderAfterSel || styleSettings.RenderAfterSel,
);
} else if (
settings.renderBeforeEl ||
(styleSettings && styleSettings.RenderBeforeSel)
) {
$renderBeforeEl = el.querySelector(
settings.renderBeforeEl || styleSettings.RenderBeforeSel,
);
} else if (
settings.renderInsideSel ||
(styleSettings && styleSettings.RenderInsideSel)
) {
$renderInsideEl = el.querySelector(
settings.renderInsideSel || styleSettings.RenderInsideSel,
);
}
// If there is a custom selector to render the widget
if ($renderInsideEl) {
$renderInsideEl.append($el);
} else if ($containerSel) {
// if the default container selector is available
$containerSel.append($el);
} else if ($renderAfterEl) {
// else if renderAfterSel is set, render widget after that
$renderAfterEl.parentNode.insertBefore(
$el,
$renderAfterEl.nextSibling,
);
} else if ($renderBeforeEl) {
// else if renderBeforeSel is set, render widget after that
$renderBeforeEl.parentNode.insertBefore($el, $renderBeforeEl);
} else if ($variantEl) {
// else render the widget after the input field
$variantEl.parentNode.insertBefore($el, $variantEl.nextSibling);
}
};
if (cachedDivContainer) {
var newEl = document.createElement('div');
addSelector(newEl);
cachedDivContainer.parentNode.removeChild(cachedDivContainer);
} else {
cachedDivContainer = document.createElement('div');
addSelector(cachedDivContainer);
}
const scriptMatches = [
...htmlString.matchAll(/<script type=.*>(.+)<\/script>/gims),
];
if (scriptMatches.length && scriptMatches[0] && scriptMatches[0][1]) {
try {
var product = JSON.parse(scriptMatches[0][1]);
/**
* Initiates the widget selector
*/
var $selector = document.querySelector(
"[data-bundleapp-wrapper='" + product.widget_id + "']",
);
new window.bundleapp.BundleAppWidget($selector, product);
} catch (err) {
console.warn('Bundle: Could not parse and initiate widget', err);
}
}
};
try {
var prodSelectorPromise = getProductSelector();
Promise.all([prodSelectorPromise, shopInfoPromise]).then((values) => {
const responseText = values[0];
// replaces to minify a bit the html string
// .replace(/\n|\t/g, ' ') // replaces newlines and tabs
// .replace(/>[\s]*?</g, '><') // removes spaces between open and close brackets
// .replace(/[\s]*?</g, '<'); // removes spaces at start
if (
(window.bundleapp.settings.appPublic || inPreviewMode) &&
!compareHtmlStrings(responseText, cachedSelector)
) {
window.localStorage.setItem(selectorStorageKey, responseText);
addAndInitiateSelector(
responseText,
window.bundleapp.settings.shopStyleSettings,
);
}
});
} catch (err) {
console.error(
'Something went wrong while fetching and parsing the product',
err,
);
}
}
});
};
window.bundleapp.initializeForms();
})();
</script>
<style>
.bundleapp-wrapper {
border: none;
padding: 0;
margin-bottom: 20px;
width: 100%;
}
.bundleapp-wrapper legend {
font-size: 90%;
margin-bottom: 5px;
}
.bundleapp-wrapper .bundleapp-plan-selector-group {
margin-bottom: 5px;
}
.bundleapp-wrapper .bundleapp-plan-selector-group > label {
display: flex;
align-items: center;
font-size: 90%;
}
.bundleapp-wrapper .bundleapp-plan-selector-group label > input {
min-height: 30px;
margin-right: 10px;
}
.bundleapp-wrapper .bundleapp-plan-selector-group select {
width: 100%;
margin-bottom: 20px;
}
.bundleapp-wrapper .bundleapp-plan-selector-group-pricing {
margin-left: auto;
}
.bundleapp-wrapper .bundleapp-plan-selector-plan {
margin-left: 23px;
}
.bundleapp-wrapper .bundleapp-plan-selector-plan > label {
font-size: 80%;
display: block;
text-align: left;
}
.bundleapp-wrapper .bundleapp-plan-selector-description span {
border: solid 1px #dfe3e8;
padding: 16px;
height: auto;
margin-top: 16px;
border-radius: 0px;
font-size: 90%;
margin-left: 23px;
display: block;
}
.bundleapp-price-adjustment {
display: inline-block;
margin-left: 10px;
border: 1px solid;
border-color: inherit;
font-size: 80%;
padding-left: 10px;
padding-right: 10px;
border-radius: 3px;
}
.bundleapp-legend {
text-align: left;
margin-bottom: 5px;
}
.awt-hidden-add-to-cart-button {
display: none !important;
}
/**** PLAN SELECTOR BASICS ****/
.awt-style-1, .awt-style-2 {
width: 100%;
margin-top: 20px;
margin-bottom: 20px;
}
.awt-style-1 .bundleapp-wrapper, .awt-style-2 .bundleapp-wrapper {
margin-bottom: 0;
}
.awt-style-1 .bundleapp-wrapper .bundleapp-plan-selector-description span, .awt-style-2 .bundleapp-wrapper .bundleapp-plan-selector-description span {
margin-left: 0;
}
/**** PLAN SELECTOR BASICS ****/
/**** CUSTOM DROP DOWN ****/
.awt-style-1 .bundleapp-wrapper .bundleapp-plan-selector-group select.bundleapp-plan-selector-select, .awt-style-2 .bundleapp-wrapper .bundleapp-plan-selector-group select.bundleapp-plan-selector-select {
padding: 14px 50px 14px 17px;
margin-bottom: 8px;
appearance: none;
-webkit-appearance: none;
background-repeat: no-repeat;
background-position: center right;
background-image: url("data:image/svg+xml,%3Csvg width='44' height='24' xmlns='http://www.w3.org/2000/svg' role='presentation' focusable='false' aria-hidden='true' class='icon-chevron '%3E%3Cg%3E%3Ctitle%3ELayer 1%3C/title%3E%3Cpath transform='rotate(90 12.414 12.4145)' stroke='null' id='svg_1' d='m10.121,19.122l6,-6a1,1 0 0 0 0,-1.415l-6,-6a1,1 0 0 0 -1.414,0a1,1 0 0 0 0,1.415l5.293,5.292l-5.293,5.293a1,1 0 1 0 1.414,1.415z' fill-rule='evenodd' fill='currentColor'/%3E%3C/g%3E%3C/svg%3E");
background-size: 35px;
background-color: #fff;
border: 1px solid var(--awt-border-color);
outline: 0;
height: auto;
}
.awt-style-1 .bundleapp-wrapper .bundleapp-plan-selector-group select.bundleapp-plan-selector-select:focus-visible, .awt-style-2 .bundleapp-wrapper .bundleapp-plan-selector-group select.bundleapp-plan-selector-select:focus-visible {
box-shadow: none;
}
.awt-style-1 .bundleapp-wrapper .bundleapp-plan-selector-group select.bundleapp-plan-selector-select:hover, .awt-style-2 .bundleapp-wrapper .bundleapp-plan-selector-group select.bundleapp-plan-selector-select:hover {
cursor: pointer;
}
/**** END CUSTOM DROP DOWN ****/
/**** CUSTOM RADIO BUTTON ****/
.awt-style-1 .bundleapp-wrapper input[type='radio'], .awt-style-2 .bundleapp-wrapper input[type='radio'] {
/* Add if not using autoprefixer */
-webkit-appearance: none;
/* Remove most all native input styles */
appearance: none;
/* For iOS < 15 */
background-color: #fff;
/* Not removed via appearance */
margin: 0 10px 0 0;
color: currentColor;
width: 20px;
height: 20px;
border: 1px solid currentColor;
border-radius: 50%;
/* transform: translateY(-0.075em);
*/
display: grid;
place-content: center;
min-height: 0 !important;
min-width: 20px;
outline: none;
padding: 0;
}
.awt-style-1 .bundleapp-wrapper input[type='radio']:focus .awt-style-2 .bundleapp-wrapper input[type='radio']:focus {
/* remove outline */
outline: none;
}
.awt-style-1 .bundleapp-wrapper input[type='radio']::before, .awt-style-2 .bundleapp-wrapper input[type='radio']::before {
content: '';
width: 10px;
height: 10px;
border-radius: 50%;
transform: scale(0);
transition: 120ms transform ease-in-out;
background-color: var(--awt-radio-button-background);
transition: background-color 0.3s;
}
.awt-style-1 .bundleapp-wrapper input[type='radio']:checked::before, .awt-style-2 .bundleapp-wrapper input[type='radio']:checked::before {
transform: scale(1);
}
/**** END CUSTOM RADIO BUTTON ****/
/**** CUSTOM FREQUENCY BUTTONS ****/
.awt-style-1 .bundleapp-wrapper .bundleapp-plan-selector-radio input, .awt-style-2 .bundleapp-wrapper .bundleapp-plan-selector-radio input {
clip: rect(0, 0, 0, 0);
overflow: hidden;
position: absolute;
height: 1px;
width: 1px;
}
.awt-style-1 .bundleapp-wrapper .bundleapp-plan-selector-radio__label, .awt-style-2 .bundleapp-wrapper .bundleapp-plan-selector-radio__label {
display: inline-block;
text-align: center;
min-width: 36px;
padding: 6px 10px;
border: var(--awt-freq-button-border);
color: var(--awt-freq-button-color);
background-color: var(--awt-freq-button-background);
cursor: pointer;
border-radius: var(--awt-freq-button-border-radius);
margin: 6px 8px 6px 0;
transition: border 0.3s, color 0.3s, background-color 0.3s;
}
.awt-style-1 .bundleapp-wrapper .bundleapp-plan-selector-radio__label:hover, .awt-style-2 .bundleapp-wrapper .bundleapp-plan-selector-radio__label:hover {
color: var(--awt-freq-button-color-hover);
background-color: var(--awt-freq-button-background-hover);
border: var(--awt-freq-button-border-hover);
}
.awt-style-1 .bundleapp-wrapper .bundleapp-plan-selector-radio__label.bundleapp-plan-selector-radio__label--selected, .awt-style-2 .bundleapp-wrapper .bundleapp-plan-selector-radio__label.bundleapp-plan-selector-radio__label--selected {
background-color: var(--awt-freq-button-background-selected);
color: var(--awt-freq-button-color-selected);
border: var(--awt-freq-button-border-selected);
transition: border 0.3s, color 0.3s, background-color 0.3s;
}
.awt-style-1 .bundleapp-wrapper .bundleapp-plan-selector-radio__label.bundleapp-plan-selector-radio__label--selected:hover, .awt-style-2 .bundleapp-wrapper .bundleapp-plan-selector-radio__label.bundleapp-plan-selector-radio__label--selected:hover {
background-color: var(--awt-freq-button-background-hover-selected);
color: var(--awt-freq-button-color-hover-selected);
border: var(--awt-freq-button-border-hover-selected);
}
/**** END CUSTOM FREQUENCY BUTTONS ****/
/**** CUSTOM LABEL ACCENT ****/
.awt-style-1 .bundleapp-wrapper .bundleapp-plan-selector-group > label .awt-accent, .awt-style-2 .bundleapp-wrapper .bundleapp-plan-selector-group > label .awt-accent {
background-color: var(--awt-accent-background);
padding: 2px 8px;
border-radius: 20px;
font-size: 12px;
display: inline-block;
color: var(--awt-accent-color);
}
/**** END CUSTOM LABEL ACCENT ****/
/*****************************************/
/*****************************************/
/**************** STYLE 1 ****************/
/*****************************************/
/*****************************************/
/**** CUSTOM PLAN SELECTOR ****/
.awt-style-1 .bundleapp-wrapper .bundleapp-plan-selector-group > label {
font-weight: 400;
margin-bottom: 0;
}
.awt-style-1 .bundleapp-wrapper .bundleapp-plan-selector-group > label:hover {
cursor: pointer;
}
.awt-style-1 .bundleapp-wrapper .bundleapp-plan-selector-group {
margin-bottom: 10px;
padding: 10px 15px;
border-radius: var(--awt-border-radius);
border: 1px solid var(--awt-border-color);
transition: border 0.3s, color 0.3s, background-color 0.3s;
}
.awt-style-1 .bundleapp-wrapper .bundleapp-plan-selector-group .bundleapp-plan-selector-select {
margin-bottom: 0;
}
.awt-style-1 .bundleapp-wrapper .bundleapp-plan-selector-group .bundleapp-plan-selector-plan {
margin-top: 10px;
}
.awt-style-1 .bundleapp-wrapper .bundleapp-plan-selector-group .bundleapp-plan-selector-plan > label {
margin-bottom: 8px;
}
.awt-style-1 .bundleapp-wrapper .bundleapp-plan-selector-group.bundleapp-plan-selector-group--selected {
border-color: var(--awt-selected-plan-border-color);
background-color: var(--awt-selected-plan-background);
}
.awt-style-1 .bundleapp-wrapper .bundleapp-plan-selector-group.bundleapp-plan-selector-group--single-group {
//border: none;
background-color: initial;
border-color: var(--awt-border-color);
transition: border 0.3s, color 0.3s, background-color 0.3s;
}
/**** END CUSTOM PLAN SELECTOR ****/
/*****************************************/
/*****************************************/
/**************** STYLE 2 ****************/
/*****************************************/
/*****************************************/
/**** CUSTOM PLAN SELECTOR ****/
.awt-style-2 .bundleapp-wrapper {
border: 1px solid var(--awt-border-color);
}
.awt-style-2 .bundleapp-wrapper .bundleapp-plan-selector-group:not(:last-child) {
border-bottom: 1px solid var(--awt-border-color);
}
.awt-style-2 .bundleapp-wrapper .bundleapp-plan-selector-group > label {
font-weight: 400;
margin-bottom: 0;
}
.awt-style-2 .bundleapp-wrapper .bundleapp-plan-selector-group > label:hover {
cursor: pointer;
}
.awt-style-2 .bundleapp-wrapper .bundleapp-plan-selector-group .bundleapp-plan-selector-plan {
margin-top: 10px;
}
.awt-style-2 .bundleapp-wrapper .bundleapp-plan-selector-group .bundleapp-plan-selector-plan > label {
margin-bottom: 8px;
}
.awt-style-2 .bundleapp-wrapper .bundleapp-plan-selector-group {
margin-bottom: 0;
padding: 8px 20px;
transition: border 0.3s, color 0.3s, background-color 0.3s;
}
.awt-style-2 .bundleapp-wrapper .bundleapp-plan-selector-group.bundleapp-plan-selector-group--selected {
border-color: var(--awt-selected-plan-border-color);
background-color: var(--awt-selected-plan-background);
border-bottom: 0;
}
/**** END CUSTOM PLAN SELECTOR ****/
/**** CUSTOMIZE CP ****/
.bundle-button.button--primary:enabled {
background-color: var(--awt-primary-button-background);
color: var(--awt-primary-button-color);
border-style: solid;
border: var(--awt-primary-button-border);
border-radius: var(--awt-primary-button-border-radius);
transition: color 0.3s, background-color 0.3s, border-color 0.3s, border-width 0.3s;
}
.bundle-button.button--primary:enabled:hover {
background-color: var(--awt-primary-button-background-hover);
color: var(--awt-primary-button-color-hover);
border: var(--awt-primary-button-border-hover);
}
/**** END CUSTOMIZE CP ****/
/**** BaB UI ****/
.bundleapp-modal__logo {
background-image: url();
background-position: center;
}
.awtomatic-progress-item {
transition: border 0.3s, color 0.3s, background-color 0.3s;
}
.awtomatic-progress-item.awtomatic-progress-item--completed {
background-color: var(--awt-progress-bar-background);
}
.awtomatic-progress-item.awtomatic-progress-item--completed .bundle-text--subheading {
color: var(--awt-progress-bar-color);
}
.bundle-stepper-counter--complete {
background-color: var(--awt-progress-bar-background);
color: var(--awt-progress-bar-color);
}
.bundle-stepper-item:after {
transition: border-color 0.3s;
}
.bundle-stepper-item--complete:after {
border-bottom-color: var(--awt-progress-bar-background);
}
/**** End BaB UI ****/
/**** Gift styling ****/
.awt-gift-info-title {
font-weight: 400;
font-size: 16px;
text-align: start;
}
.awt-error-msg {
color: #ff2a51;
text-align: start;
}
.awt-input-error.awt-error {
background-color: rgba(255,42,81,.1);
border: 2px solid #ff2a51;
}
.awt-floating-label {
display: block;
position: relative;
border-radius: 5px;
width: 100%;
}
.awt-floating-label:last-child {
margin-bottom: 0;
}
.awt-floating-label input, .awt-floating-label textarea {
font-family: inherit;
width: 100%;
height: 40px;
border: none;
outline: none;
background-color: #f8f8f8;
border-radius: 8px;
padding: 0 12px;
font-size: 16px;
margin: 0px;
font-family: inherit;
}
.awt-floating-label textarea {
height: 120px;
padding: 12px;
}
.awt-floating-label input::placeholder, .awt-floating-label textarea::placeholder {
opacity: 0;
}
.awt-floating-label span {
position: absolute;
top: 20px;
left: 8px;
transform: translateY(-50%);
font-size: 16px;
padding: 0 4px;
transition-duration: 300ms;
border-radius: 5px;
}
label.awt-floating-label:focus-within > span, .awt-floating-label input:not(:placeholder-shown) + span, .awt-floating-label textarea:not(:placeholder-shown) + span {
transform: translateY(-150%);
font-size: 11px;
background-color: rgba(255, 255, 255);
}
.awt-floating-label-warning {
font-size: 14px;
padding: 0 2px;
text-align: start;
}
/*! form field group in 2 columns */
.awt-form-group {
display: flex;
justify-content: space-between;
}
.awt-form-group > * {
width: calc(50% - 4px);
padding-bottom: 16px;
}
.awt-gift-input-container {
padding-bottom:16px
}
/*! custom checkbox */
.awt-checkbox {
display: flex;
align-items: center;
margin: 16px 0;
}
.awt-checkbox input[type="checkbox"] {
/* Add if not using autoprefixer */
-webkit-appearance: none;
/* Remove most all native input styles */
appearance: none;
/* For iOS < 15 */
background-color: #fff;
width: 18px;
height: 18px;
margin-right: 4px;
position: relative;
border-radius: 2px;
box-shadow: 0 0 0 1px inset #000;
}
.awt-checkbox input[type="checkbox"]:checked {
/* For iOS < 15 */
background-color: #000000;
}
.awt-checkbox input[type="checkbox"]:checked::before {
content: "";
position: absolute;
left: 4px;
top: 8px;
background: white;
width: 2px;
height: 2px;
box-shadow: 2px 0 0 white, 4px 0 0 white, 4px -2px 0 white, 4px -4px 0 white, 4px -6px 0 white, 4px -8px 0 white;
transform: rotate(45deg);
}
.awt-checkbox input[type="checkbox"]:disabled {
background-color: #f8f8f8;
cursor: not-allowed;
}
/**** End Gift styling ****/
/*** PREVIEW CONTROL ***/
#awt-preview-mode-control {
position: fixed;
bottom: 15px;
left: 50%;
transform: translateX(-50%);
height: 75px;
z-index: 2147483647;
background-color: #fff;
color: #222;
padding: 17px;
display: flex;
justify-content: space-between;
align-content: center;
box-shadow: 0px 4px 16px rgba(0, 0, 0, 0.08);
border-radius: 8px;
box-sizing: border-box;
}
#awt-preview-mode-control.hidden {
display: none;
}
#awt-preview-mode-control div.awt-logo {
display: flex;
height: 100%;
align-items: center;
flex-wrap: nowrap;
}
#awt-preview-mode-control div.awt-logo span {
margin-top: 3px;
font-size: 16px;
font-family: sans-serif;
color: #3700ff;
}
#awt-preview-mode-control div.awt-logo .awt-logo--image {
display: block;
background-image: url('https://bundle-public-assets.s3.amazonaws.com/customerPortal/awtomatic-no-dot.svg');
background-repeat: no-repeat;
background-size: contain;
background-position: center;
width: 95px;
height: 100%;
margin-right: 6px;
}
#awt-preview-mode-control div.awt-logo .awt-logo--info-icon {
display: block;
background-image: url('https://bundle-public-assets.s3.amazonaws.com/customerPortal/info-icon.svg');
background-repeat: no-repeat;
background-size: contain;
background-position: center;
width: 15px;
height: 15px;
margin: 5px 25px 0 9px;
}
#awt-preview-mode-control button {
display: inline-block;
border: 1px solid #eee;
background-color: #fff;
border-radius: 4px;
padding: 5px 15px;
margin: 0;
text-decoration: none;
color: #000;
font-family: sans-serif;
font-size: 15px;
cursor: pointer;
text-align: center;
transition: background 250ms ease-in-out, transform 150ms ease;
-webkit-appearance: none;
-moz-appearance: none;
font-weight: bold;
height: 100%;
outline: none;
box-shadow: none;
}
#awt-preview-mode-control button:hover, #awt-preview-mode-control button:focus {
background-color: #fbfbfb;
}
#awt-preview-mode-control button:focus {
outline: none;
}
#awt-preview-mode-control button:active {
transform: scale(0.99);
}
button#awt-preview-mode-control__hide {
background-color: #fff;
color: #000;
}
#awt-preview-mode-control button#awt-preview-mode-control__hide:hover, #awt-preview-mode-control button#awt-preview-mode-control__hide:focus {
background: #eee;
}
@media only screen and (max-width: 480px) {
#awt-preview-mode-control button#awt-preview-mode-control__hide {
display: none;
}
}
/*** END PREVIEW CONTROL ***/
</style>
awtomic-plan-selector.liquid
awtomic-plan-selector.liquid
{% liquid
assign hiddenShopGroupNames = ''
assign visibleProductGroupNames = ''
if shop.metafields.awtomic.hidden_selling_plan_groups
assign hiddenShopGroupNames = shop.metafields.awtomic.hidden_selling_plan_groups.value | join: ","
endif
if product.metafields.awtomic.visible_selling_plan_groups
assign visibleProductGroupNames = product.metafields.awtomic.visible_selling_plan_groups.value | join: ","
endif
assign hasNonHiddenPlans = false
for group in product.selling_plan_groups
unless hiddenShopGroupNames contains group.name
assign hasNonHiddenPlans = true
endunless
if visibleProductGroupNames contains group.name
assign hasNonHiddenPlans = true
endif
endfor
assign isBaB = false
if product.metafields.bundle.isBundle and product.metafields.bundle.type != 'shuffle'
assign isBaB = true
endif
%}
{% if hasNonHiddenPlans or isBaB %}
{%- comment -%} Awtomic App {%- endcomment -%}
{%- assign selected_selling_plan_id = product.selected_selling_plan -%}
{% assign min = 10 %}
{% assign max = 80 %}
{% assign diff = max | minus: min %}
{% assign uuid = "now" | date: "%N" | modulo: diff | plus: min | append: product.id %}
{%- assign variant = product.selected_or_first_available_variant -%}
{% comment %} Translations {% endcomment %}
{% assign one_time_purchase_label = 'awtomatic.plan_selector.one_time_purchase_label' | t %}
{% if one_time_purchase_label contains 'translation missing' %}
{% assign one_time_purchase_label = 'One-time purchase' %}
{% endif %}
{% assign purchase_options_label = 'awtomatic.plan_selector.purchase_options_label' | t %}
{% if purchase_options_label contains 'translation missing' %}
{% assign purchase_options_label = 'Purchase options' %}
{% endif %}
<div class="awt-style-1">
<fieldset data-bundleapp-wrapper="{{uuid}}" class="bundleapp-wrapper" style="display: none">
{%- if product and product.selling_plan_groups.size > 0 -%}
<input type="hidden" name="selling_plan" value="{{selected_selling_plan_id}}" />
{% if product.selling_plan_groups.size > 1 %}
<legend class="bundleapp-legend">{{ purchase_options_label }}</legend>
{% endif %}
{% unless product.requires_selling_plan %}
<div class="bundleapp-plan-selector-group {% if selected_selling_plan_id == nil %}bundleapp-plan-selector-group--selected{% endif %}"
data-one-time data-test="oneTimePrice">
<label>
<input type="radio" name="bundleapp-plan-selector-group" value="" {%- if selected_selling_plan_id==nil
-%}checked{%-endif-%} />
<span>{{ one_time_purchase_label }}</span>
<span class="bundleapp-plan-selector-group-pricing">{{ product.selected_or_first_available_variant.price |
money}}</span>
</label>
<div class="bundleapp-plan-selector-plan" style="display: none">
<select class="bundleapp-plan-selector-select">
<option value=""></option>
</select>
<div class="bundleapp-plan-selector-radio" style="display: none">
<input type="radio" name="plan" value="" id="one-time-purchase" {%- if selected_selling_plan_id==nil
-%}checked{%-endif-%}>
</div>
</div>
</div>
{% endunless %}
{% for selling_plan_group in product.selling_plan_groups %}
{% liquid
assign isHiddenGroupName = false
if hiddenShopGroupNames contains selling_plan_group.name
assign isHiddenGroupName = true
endif
if visibleProductGroupNames contains selling_plan_group.name
assign isHiddenGroupName = false
endif
%}
{%- unless isHiddenGroupName -%}
{%- if selling_plan_group.app_id == "bundlesubscriptions" -%}
<div class="bundleapp-plan-selector-group" style="display: none" data-test="{{ selling_plan_group.id }}">
{%- comment -%}
Labels:
This first code block figures out the largest value of a subscription plan to now the "up to N"
{%- endcomment -%}
{% assign is_same_value = true %}
{% assign prev_value = selling_plan_group.selling_plans[0].price_adjustments[0] %}
{% assign largest_value = selling_plan_group.selling_plans[0].price_adjustments[0] %}
{% for plan in selling_plan_group.selling_plans %}
{% if prev_value.value != plan.price_adjustments[0].value %}
{% assign is_same_value = false %}
{% endif %}
{% if largest_value.value < plan.price_adjustments[0].value %} {% assign largest_value=plan.price_adjustments[0]
%} {% endif %} {% assign prev_value=plan.price_adjustments[0] %} {% endfor %} {%- comment -%} This numbers
helps figuring out when to show or hide certain aspects of the UI ie: Hide the radio button when there is
only a single subscription plan. {%- endcomment -%} {%- assign show_thresshold=0 -%} {%- if
product.requires_selling_plan -%} {%- assign show_thresshold=1 -%} {%- endif -%} <label>
<input type="radio" name="bundleapp-plan-selector-group" value="{{ selling_plan_group.id }}"
style="{% if product.selling_plan_groups.size <= show_thresshold %}display:none{% endif %}" />
<span>{{ selling_plan_group.name }}</span>
<span class="bundleapp-plan-selector-group-pricing"></span>
</label>
<div class="bundleapp-plan-selector-plan" id="bundleapp-plan-selector-{{selling_plan_group.id}}"
style="display: none">
{%- if selling_plan_group.selling_plans.size > 1 -%}
{% unless selling_plan_group.options[0].name == '*no_prefix*' %}
<label data-test="prefixFromSellingPlanGroup-{{ selling_plan_group.id }}">{{
selling_plan_group.options[0].name }}</label>
{% endunless %}
{%- else -%}
<label>
{% unless selling_plan_group.options[0].name == '*no_prefix*' %}
<span data-test="prefixFromSellingPlanGroup">{{ selling_plan_group.options[0].name }} </span>
{% endunless %}
<span data-test="prefixFromSellingPlan-{{ selling_plan_group.selling_plans[0].id }}">{{
selling_plan_group.selling_plans[0].options[0].value }}</span>
</label>
{%- endif -%}
<select class="bundleapp-plan-selector-select"
style="{% if selling_plan_group.selling_plans.size <= 1 %}display:none{% endif %}">
{% for plan in selling_plan_group.selling_plans %}
<option data-test="prefixFromSellingPlan-{{ plan.id }}" value="{{ plan.id }}" {%- if
selected_selling_plan_id==plan.id -%}selected{%-endif-%}>
{%- capture value -%}
{% if plan.price_adjustments[0].value_type == "percentage" %}
{% if plan.price_adjustments[0].value != 0 %} (Save {{plan.price_adjustments[0].value}}%){%
endif %}
{% else %}
{% if plan.price_adjustments[0].value != 0 %} (Save {{plan.price_adjustments[0].value |
money}}){% endif %}
{% endif %}
{%- endcapture -%}
{{ plan.options[0].value }}{% if is_same_value == false %} {{value}}{% endif %}
</option>
{% endfor %}
</select>
<div class="bundleapp-plan-selector-radio" style="display: none">
<div style="{% if selling_plan_group.selling_plans.size <= 1 %}display:none{% endif %}">
{% for plan in selling_plan_group.selling_plans %}
{%- capture valueOption -%}
{% if plan.price_adjustments[0].value_type == "percentage" %}
{% if plan.price_adjustments[0].value != 0 %} (Save {{plan.price_adjustments[0].value}}%){%
endif %}
{% else %}
{% if plan.price_adjustments[0].value != 0 %} (Save {{plan.price_adjustments[0].value |
money}}){% endif %}
{% endif %}
{%- endcapture -%}
<label
class="bundleapp-plan-selector-radio__label {% if selected_selling_plan_id == plan.id %}bundleapp-plan-selector-radio__label--selected{% endif %}"
for={{ plan.id }}>
<input
class="bundleapp-plan-selector-radio__input {% if selected_selling_plan_id == plan.id %}bundleapp-plan-selector-radio__input--selected{% endif %}"
type="radio" name="plan" data-test="prefixFromSellingPlan-{{ plan.id }}"
value="{{ plan.id }}" id="{{ plan.id }}" {%- if selected_selling_plan_id==plan.id
-%}checked{%-endif-%}>
{{ plan.options[0].value }}{% if is_same_value == false %} {{valueOption}}{% endif %}
</label>
{% endfor %}
</div>
</div>
</div>
</div>
{%- endif -%}
{%- endunless -%}
{% endfor %}
<div class="bundleapp-plan-selector-description">
{% if product.selected_selling_plan.description and product.selected_selling_plan.description != '' %}<span>{{
product.selected_selling_plan.description }}</span>{% endif %}
</div>
{%- endif -%}
</fieldset>
</div>
<script type="application/json">
{%- assign selectedVariantId = variant.id -%}
{%- assign selectedSellingPlanId = product.selected_selling_plan.id -%}
{
"widget_id": {{uuid | json}},
"id": {{ product.id | json }},
"title": {{ product.title | json }},
"selling_plan_groups": {{ product.selling_plan_groups | json }},
"variants": {{ product.variants | json }},
"selected_variant": {%- liquid
for variant in product.variants
if variant.id == selectedVariantId
echo variant | json
endif
endfor
-%},
"selling_plans_by_id": {
{%- for group in product.selling_plan_groups -%}
{%- assign parentLoop = forloop -%}
{%- for plan in group.selling_plans -%}
{%- capture planJson -%}
{
"id": {{- plan.id | json-}},
"name": {{plan.name | json}},
"description": {{plan.description | json}},
"options": {{plan.options | json}},
"recurring_deliveries": {{plan.recurring_deliveries | json}},
"price_adjustments": {{plan.price_adjustments | json}},
"selling_plan_group_id": {{group.id | json}}
}
{%- endcapture -%}
{%- if plan.id == selectedSellingPlanId -%}
{%- assign selectedSellingPlan = planJson -%}
{%- endif -%}
"{{plan.id}}": {{planJson}}
{%- unless forloop.last == true and parentLoop.last == true -%},{%- endunless -%}
{%- endfor -%}
{%- endfor -%}
},
"metafields": {{ product.metafields.bundle | json }},
"selectedSellingPlanId": {{selectedSellingPlanId | json}},
"selected_selling_plan": {%- if selectedSellingPlan -%}{{selectedSellingPlan}}{%- else -%}null{%- endif -%},
"translations": {
"subscription": "{{ 'awtomatic.plan_selector.subscription' | t }}",
"save": "{{ 'awtomatic.plan_selector.save' | t }}",
"extra": "{{ 'awtomatic.plan_selector.extra' | t }}",
"off": "{{ 'awtomatic.plan_selector.off' | t }}",
"customize_my_box": "{{ 'awtomatic.plan_selector.customize_my_box' | t }}",
"from": "{{ 'awtomatic.plan_selector.from' | t }}",
"delivery_suffix": "{{ 'awtomatic.plan_selector.delivery_suffix' | t }}"
}
}
</script>
<script>
(function () {
var product = {};
product.id = {{ product.id | json }};
product.title = {{ product.title | json }};
product.selling_plan_groups = {{ product.selling_plan_groups | json }};
product.variants = {{ product.variants | json }};
product.selected_selling_plan = {{product.selected_selling_plan | json}};
product.selected_variant = {{ variant | json }};
product.translations = {
subscription: "{{ 'awtomatic.plan_selector.subscription' | t }}",
save: "{{ 'awtomatic.plan_selector.save' | t }}",
extra: "{{ 'awtomatic.plan_selector.extra' | t }}",
off: "{{ 'awtomatic.plan_selector.off' | t }}",
customize_my_box: "{{ 'awtomatic.plan_selector.customize_my_box' | t }}",
from: "{{ 'awtomatic.plan_selector.from' | t }}",
delivery_suffix: "{{ 'awtomatic.plan_selector.delivery_suffix' | t }}",
gift_first_name_placeholder: "{{ 'awtomatic.plan_selector.gift_first_name_placeholder' | t }}",
gift_first_name_label: "{{ 'awtomatic.plan_selector.gift_first_name_label' | t }}",
gift_last_name_placeholder: "{{ 'awtomatic.plan_selector.gift_last_name_placeholder' | t }}",
gift_last_name_label: "{{ 'awtomatic.plan_selector.gift_last_name_label' | t }}",
gift_email_placeholder: "{{ 'awtomatic.plan_selector.gift_email_placeholder' | t }}",
gift_email_label: "{{ 'awtomatic.plan_selector.gift_email_label' | t }}",
gift_email_warning: "{{ 'awtomatic.plan_selector.gift_email_warning' | t }}",
gift_note_placeholder: "{{ 'awtomatic.plan_selector.gift_note_placeholder' | t }}",
gift_note_label: "{{ 'awtomatic.plan_selector.gift_note_label' | t }}",
gift_checkbox_label: "{{ 'awtomatic.plan_selector.gift_checkbox_label' | t }}",
gift_recipient_info_title: "{{ 'awtomatic.plan_selector.gift_recipient_info_title' | t }}"
}
product.metafields = {{ product.metafields.bundle | json }};
product.gift_selling_plans = {%- if shop.metafields.sellingPlans.gifts -%} {{ shop.metafields.sellingPlans.gifts }} {%- else -%}[]{%- endif -%};
product.selling_plans_by_id = (function() {
var ret = {};
for (group of product.selling_plan_groups) {
for (plan of group.selling_plans) {
ret[plan.id] = JSON.parse(JSON.stringify(plan));
ret[plan.id].selling_plan_group_id = group.id;
}
}
return ret;
})();
document.addEventListener("bundleapp:ready", function(){
if(window.bundleapp && window.bundleapp.BundleAppWidget) {
var $selector = document.querySelector("[data-bundleapp-wrapper='{{uuid}}']");
var widget = new window.bundleapp.BundleAppWidget($selector, product);
}
});
})();
</script>
{% endif %}
awtomic-translations.liquid
awtomic-translations.liquid
{% comment %} Awtomic Subscriptions {% endcomment %}
<script>
var TRANSLATIONS = {
en: {
translation: {
titles: {
return_to_account: "{{ 'awtomatic.customer_portal.titles.return_to_account' | t }}",
subscription: "{{ 'awtomatic.customer_portal.titles.subscription' | t }}",
subscriptions: "{{ 'awtomatic.customer_portal.titles.subscriptions' | t }}",
loading: "{{ 'awtomatic.customer_portal.titles.loading' | t }}",
cancel: "{{ 'awtomatic.customer_portal.titles.cancel' | t }}",
save: "{{ 'awtomatic.customer_portal.titles.save' | t }}",
continue: "{{ 'awtomatic.customer_portal.titles.continue' | t }}",
pause: "{{ 'awtomatic.customer_portal.titles.pause' | t }}",
edit: "{{ 'awtomatic.customer_portal.titles.edit' | t }}",
my_subscriptions: "{{ 'awtomatic.customer_portal.titles.my_subscriptions' | t }}",
frequency: "{{ 'awtomatic.customer_portal.titles.frequency' | t }}",
next_order: "{{ 'awtomatic.customer_portal.titles.next_order' | t }}",
confirm: "{{ 'awtomatic.customer_portal.titles.confirm' | t }}",
remove: "{{ 'awtomatic.customer_portal.titles.remove' | t }}",
description: "{{ 'awtomatic.customer_portal.titles.description' | t }}",
add: "{{ 'awtomatic.customer_portal.titles.add' | t }}",
},
email_gate: {
no_subs: "{{ 'awtomatic.customer_portal.email_gate.no_subs' | t }}",
check_email: "{{ 'awtomatic.customer_portal.email_gate.check_email' | t }}",
cta_access_link: "{{ 'awtomatic.customer_portal.email_gate.cta_access_link' | t }}",
},
subscriptions: {
error: "{{ 'awtomatic.customer_portal.subscriptions.error' | t }}",
no_subs: "{{ 'awtomatic.customer_portal.subscriptions.no_subs' | t }}",
link_to_account: "{{ 'awtomatic.customer_portal.subscriptions.link_to_account' | t }}",
},
subscription: {
fetch_error: "{{ 'awtomatic.customer_portal.subscription.fetch_error' | t }}",
auth_error: "{{ 'awtomatic.customer_portal.subscription.auth_error' | t }}",
plan: "{{ 'awtomatic.customer_portal.subscription.plan' | t }}",
active: "{{ 'awtomatic.customer_portal.subscription.active' | t }}",
activate: "{{ 'awtomatic.customer_portal.subscription.activate' | t }}",
pause: "{{ 'awtomatic.customer_portal.subscription.pause' | t }}",
paused: "{{ 'awtomatic.customer_portal.subscription.paused' | t }}",
cancel: "{{ 'awtomatic.customer_portal.subscription.cancel' | t }}",
cancelled: "{{ 'awtomatic.customer_portal.subscription.cancelled' | t }}",
shipment_info: "{{ 'awtomatic.customer_portal.subscription.shipment_info' | t }}",
update_shipping: "{{ 'awtomatic.customer_portal.subscription.update_shipping' | t }}",
payment_method: "{{ 'awtomatic.customer_portal.subscription.payment_method' | t }}",
pay_pal_payment_method: "{{ 'awtomatic.customer_portal.subscription.pay_pal_payment_method' | t }}",
ending_in: "{{ 'awtomatic.customer_portal.subscription.ending_in' | t }}",
expires: "{{ 'awtomatic.customer_portal.subscription.expires' | t }}",
add_phone: "{{ 'awtomatic.customer_portal.subscription.add_phone' | t }}",
add_name: "{{ 'awtomatic.customer_portal.subscription.add_name' | t }}",
add_lastName: "{{ 'awtomatic.customer_portal.subscription.add_lastName' | t }}",
billing_attepts: "{{ 'awtomatic.customer_portal.subscription.billing_attepts' | t }}",
percentage_dynamic_discount: "{{ 'awtomatic.customer_portal.subscription.percentage_dynamic_discount' | t }}",
fixed_amount_dynamic_discount:
"{{ 'awtomatic.customer_portal.subscription.fixed_amount_dynamic_discount' | t }}",
then_dynamic_discount: "{{ 'awtomatic.customer_portal.subscription.then_dynamic_discount' | t }}",
orders_dynamic_discount: "{{ 'awtomatic.customer_portal.subscription.orders_dynamic_discount' | t }}",
billing_attepts_empty: "{{ 'awtomatic.customer_portal.subscription.billing_attepts_empty' | t }}",
billing_attepts_error: "{{ 'awtomatic.customer_portal.subscription.billing_attepts_error' | t }}",
subs_details: "{{ 'awtomatic.customer_portal.subscription.subs_details' | t }}",
update_payment_btn: "{{ 'awtomatic.customer_portal.subscription.update_payment_btn' | t }}",
cancel_prompt_title: "{{ 'awtomatic.customer_portal.subscription.cancel_prompt_title' | t }}",
cancel_error: "{{ 'awtomatic.customer_portal.subscription.cancel_error' | t }}",
no_payment_method_error: "{{ 'awtomatic.customer_portal.subscription.no_payment_method_error' | t }}",
no_payment_method_error_add: "{{ 'awtomatic.customer_portal.subscription.no_payment_method_error_add' | t }}",
new_payment_method_success: "{{ 'awtomatic.customer_portal.subscription.new_payment_method_success' | t }}",
new_payment_method_error: "{{ 'awtomatic.customer_portal.subscription.new_payment_method_error' | t }}",
remove_line_error: "{{ 'awtomatic.customer_portal.subscription.remove_line_error' | t }}",
remove_line_success: "{{ 'awtomatic.customer_portal.subscription.remove_line_success' | t }}",
ships_to: "{{ 'awtomatic.customer_portal.subscription.ships_to' | t }}",
update_payment_success: "{{ 'awtomatic.customer_portal.subscription.update_payment_success' | t }}",
update_payment_success_dunning:
"{{ 'awtomatic.customer_portal.subscription.update_payment_success_dunning' | t }}",
update_payment_success_payment_error:
"{{ 'awtomatic.customer_portal.subscription.update_payment_success_payment_error' | t }}",
update_payment_error: "{{ 'awtomatic.customer_portal.subscription.update_payment_error' | t }}",
sales_tax: "{{ 'awtomatic.customer_portal.subscription.sales_tax' | t }}",
skip_billing_interval_prompt:
"{{ 'awtomatic.customer_portal.subscription.skip_billing_interval_prompt' | t }}",
skip_billing_interval_success:
"{{ 'awtomatic.customer_portal.subscription.skip_billing_interval_success' | t }}",
skip_billing_interval_error: "{{ 'awtomatic.customer_portal.subscription.skip_billing_interval_error' | t }}",
skip_until: "{{ 'awtomatic.customer_portal.subscription.skip_until' | t }}",
skip_next: "{{ 'awtomatic.customer_portal.subscription.skip_next' | t }}",
order_now: "{{ 'awtomatic.customer_portal.subscription.order_now' | t }}",
next_order_success: "{{ 'awtomatic.customer_portal.subscription.next_order_success' | t }}",
next_order_error: "{{ 'awtomatic.customer_portal.subscription.next_order_error' | t }}",
add_new_address: "{{ 'awtomatic.customer_portal.subscription.add_new_address' | t }}",
address_update_success: "{{ 'awtomatic.customer_portal.subscription.address_update_success' | t }}",
address_update_failed: "{{ 'awtomatic.customer_portal.subscription.address_update_failed' | t }}",
pause_subscription: "{{ 'awtomatic.customer_portal.subscription.pause_subscription' | t }}",
cancel_subscription: "{{ 'awtomatic.customer_portal.subscription.cancel_subscription' | t }}",
pause_disclaimer_date: "{{ 'awtomatic.customer_portal.subscription.pause_disclaimer_date' | t }}",
pause_disclaimer_indefinitely:
"{{ 'awtomatic.customer_portal.subscription.pause_disclaimer_indefinitely' | t }}",
pause_success: "{{ 'awtomatic.customer_portal.subscription.pause_success' | t }}",
reactivate_btn: "{{ 'awtomatic.customer_portal.subscription.reactivate_btn' | t }}",
reactivate: "{{ 'awtomatic.customer_portal.subscription.reactivate' | t }}",
reactivate_success: "{{ 'awtomatic.customer_portal.subscription.reactivate_success' | t }}",
reactivate_error: "{{ 'awtomatic.customer_portal.subscription.reactivate_error' | t }}",
frequency_days: "{{ 'awtomatic.customer_portal.subscription.frequency_days' | t }}",
frequency_weeks: "{{ 'awtomatic.customer_portal.subscription.frequency_weeks' | t }}",
frequency_months: "{{ 'awtomatic.customer_portal.subscription.frequency_months' | t }}",
order_now_modal_body: "{{ 'awtomatic.customer_portal.subscription.order_now_modal_body' | t }}",
amount: "{{ 'awtomatic.customer_portal.subscription.amount' | t }}",
frequency_update_success: "{{ 'awtomatic.customer_portal.subscription.frequency_update_success' | t }}",
frequency_update_error: "{{ 'awtomatic.customer_portal.subscription.frequency_update_error' | t }}",
customer_update_success: "{{ 'awtomatic.customer_portal.subscription.customer_update_success' | t }}",
customer_update_error: "{{ 'awtomatic.customer_portal.subscription.customer_update_error' | t }}",
customer_update_info: "{{ 'awtomatic.customer_portal.subscription.customer_update_info' | t }}",
verified_phone: "{{ 'awtomatic.customer_portal.subscription.verified_phone' | t }}",
not_verified_phone: "{{ 'awtomatic.customer_portal.subscription.not_verified_phone' | t }}",
verify_phone: "{{ 'awtomatic.customer_portal.subscription.verify_phone' | t }}",
enter_code: "{{ 'awtomatic.customer_portal.subscription.enter_code' | t }}",
verify_code_error: "{{ 'awtomatic.customer_portal.subscription.verify_code_error' | t }}",
verify_code_retry_error: "{{ 'awtomatic.customer_portal.subscription.verify_code_retry_error' | t }}",
verify_code_success: "{{ 'awtomatic.customer_portal.subscription.verify_code_success' | t }}",
code_received: "{{ 'awtomatic.customer_portal.subscription.code_received' | t }}",
code_enter: "{{ 'awtomatic.customer_portal.subscription.code_enter' | t }}",
code_send: "{{ 'awtomatic.customer_portal.subscription.code_send' | t }}",
qty: "{{ 'awtomatic.customer_portal.subscription.qty' | t }}",
manage: "{{ 'awtomatic.customer_portal.subscription.manage' | t }}",
your_next_order: "{{ 'awtomatic.customer_portal.subscription.your_next_order' | t }}",
swap_product: "{{ 'awtomatic.customer_portal.subscription.swap_product' | t }}",
swap_product_cta: "{{ 'awtomatic.customer_portal.subscription.swap_product_cta' | t }}",
or: "{{ 'awtomatic.customer_portal.subscription.or' | t }}",
update_variant: "{{ 'awtomatic.customer_portal.subscription.update_variant' | t }}",
update_bundle: "{{ 'awtomatic.customer_portal.subscription.update_bundle' | t }}",
save: "{{ 'awtomatic.customer_portal.subscription.save' | t }}",
failed_payment_message: "{{ 'awtomatic.customer_portal.subscription.failed_payment_message' | t }}",
failed_payment_cta: "{{ 'awtomatic.customer_portal.subscription.failed_payment_cta' | t }}",
order_now_success: "{{ 'awtomatic.customer_portal.subscription.order_now_success' | t }}",
order_now_error: "{{ 'awtomatic.customer_portal.subscription.order_now_error' | t }}",
update_current_payment_method:
"{{ 'awtomatic.customer_portal.subscription.update_current_payment_method' | t }}",
add_new_card: "{{ 'awtomatic.customer_portal.subscription.add_new_card' | t }}",
delayed: "{{ 'awtomatic.customer_portal.subscription.delayed' | t }}",
delayedNotification: "{{ 'awtomatic.customer_portal.subscription.delayedNotification' | t }}",
update_success: "{{ 'awtomatic.customer_portal.subscription.update_success' | t }}",
update_line_success: "{{ 'awtomatic.customer_portal.subscription.update_line_success' | t }}",
update_error: "{{ 'awtomatic.customer_portal.subscription.update_error' | t }}",
},
new_payment_method: {
title: "{{ 'awtomatic.customer_portal.new_payment_method.title' | t }}",
payment_info: "{{ 'awtomatic.customer_portal.new_payment_method.payment_info' | t }}",
card_number: "{{ 'awtomatic.customer_portal.new_payment_method.card_number' | t }}",
exp_date: "{{ 'awtomatic.customer_portal.new_payment_method.exp_date' | t }}",
security_code: "{{ 'awtomatic.customer_portal.new_payment_method.security_code' | t }}",
first_name: "{{ 'awtomatic.customer_portal.new_payment_method.first_name' | t }}",
last_name: "{{ 'awtomatic.customer_portal.new_payment_method.last_name' | t }}",
billing_address: "{{ 'awtomatic.customer_portal.new_payment_method.billing_address' | t }}",
address1: "{{ 'awtomatic.customer_portal.new_payment_method.address1' | t }}",
address2: "{{ 'awtomatic.customer_portal.new_payment_method.address2' | t }}",
city: "{{ 'awtomatic.customer_portal.new_payment_method.city' | t }}",
country: "{{ 'awtomatic.customer_portal.new_payment_method.country' | t }}",
state: "{{ 'awtomatic.customer_portal.new_payment_method.state' | t }}",
zip: "{{ 'awtomatic.customer_portal.new_payment_method.zip' | t }}",
phone: "{{ 'awtomatic.customer_portal.new_payment_method.phone' | t }}",
},
tables: {
ID: "{{ 'awtomatic.customer_portal.tables.ID' | t }}",
status: "{{ 'awtomatic.customer_portal.tables.status' | t }}",
item: "{{ 'awtomatic.customer_portal.tables.item' | t }}",
created: "{{ 'awtomatic.customer_portal.tables.created' | t }}",
next_order: "{{ 'awtomatic.customer_portal.tables.next_order' | t }}",
skip: "{{ 'awtomatic.customer_portal.tables.skip' | t }}",
product: "{{ 'awtomatic.customer_portal.tables.product' | t }}",
quantity: "{{ 'awtomatic.customer_portal.tables.quantity' | t }}",
price: "{{ 'awtomatic.customer_portal.tables.price' | t }}",
total: "{{ 'awtomatic.customer_portal.tables.total' | t }}",
shipping: "{{ 'awtomatic.customer_portal.tables.shipping' | t }}",
originated: "{{ 'awtomatic.customer_portal.tables.originated' | t }}",
frequency: "{{ 'awtomatic.customer_portal.tables.frequency' | t }}",
phone: "{{ 'awtomatic.customer_portal.tables.phone' | t }}",
name: "{{ 'awtomatic.customer_portal.tables.name' | t }}",
lastName: "{{ 'awtomatic.customer_portal.tables.lastName' | t }}",
date: "{{ 'awtomatic.customer_portal.tables.date' | t }}",
sold_out: "{{ 'awtomatic.customer_portal.tables.sold_out' | t }}",
partial_inventory: "{{ 'awtomatic.customer_portal.tables.partial_inventory' | t }}",
qty_sold_out: "{{ 'awtomatic.customer_portal.tables.qty_sold_out' | t }}",
order_number: "{{ 'awtomatic.customer_portal.tables.order_number' | t }}",
subtotal: "{{ 'awtomatic.customer_portal.tables.subtotal' | t }}",
loading_fulfillments: "{{ 'awtomatic.customer_portal.tables.loading_fulfillments' | t }}",
last_fulfillment: "{{ 'awtomatic.customer_portal.tables.last_fulfillment' | t }}",
next_fulfillment: "{{ 'awtomatic.customer_portal.tables.next_fulfillment' | t }}",
merchant_discount: "{{ 'awtomatic.customer_portal.tables.merchant_discount' | t }}",
one_time_discount_label: "{{ 'awtomatic.customer_portal.tables.one_time_discount_label' | t }}",
finite_discount_label: "{{ 'awtomatic.customer_portal.tables.finite_discount_label' | t }}",
edit: "{{ 'awtomatic.customer_portal.tables.edit' | t }}",
box_discount: {
percentage: "{{ 'awtomatic.customer_portal.tables.box_discount.percentage' | t }}",
amount: "{{ 'awtomatic.customer_portal.tables.box_discount.amount' | t }}",
},
},
login: {
start_intro: "{{ 'awtomatic.customer_portal.login.start_intro' | t }}",
welcome: "{{ 'awtomatic.customer_portal.login.welcome' | t }}",
send_link: "{{ 'awtomatic.customer_portal.login.send_link' | t }}",
email_address: "{{ 'awtomatic.customer_portal.login.email_address' | t }}",
continue: "{{ 'awtomatic.customer_portal.login.continue' | t }}",
welcome_back: "{{ 'awtomatic.customer_portal.login.welcome_back' | t }}",
choose_login: "{{ 'awtomatic.customer_portal.login.choose_login' | t }}",
send_secure_link: "{{ 'awtomatic.customer_portal.login.send_secure_link' | t }}",
login_password: "{{ 'awtomatic.customer_portal.login.login_password' | t }}",
check_email: "{{ 'awtomatic.customer_portal.login.check_email' | t }}",
secure_email_sent: "{{ 'awtomatic.customer_portal.login.secure_email_sent' | t }}",
not_received: "{{ 'awtomatic.customer_portal.login.not_received' | t }}",
new_link: "{{ 'awtomatic.customer_portal.login.new_link' | t }}",
different_email: "{{ 'awtomatic.customer_portal.login.different_email' | t }}",
no_subscriptions: "{{ 'awtomatic.customer_portal.login.no_subscriptions' | t }}",
invalid_email: "{{ 'awtomatic.customer_portal.login.invalid_email' | t }}",
no_subscriptions_message: "{{ 'awtomatic.customer_portal.login.no_subscriptions_message' | t }}",
},
product_swap: {
title_select: "{{ 'awtomatic.customer_portal.product_swap.title_select' | t }}",
title_confirm: "{{ 'awtomatic.customer_portal.product_swap.title_confirm' | t }}",
sold_out: "{{ 'awtomatic.customer_portal.product_swap.sold_out' | t }}",
choose: "{{ 'awtomatic.customer_portal.product_swap.choose' | t }}",
back: "{{ 'awtomatic.customer_portal.product_swap.back' | t }}",
confirm: "{{ 'awtomatic.customer_portal.product_swap.confirm' | t }}",
save: "{{ 'awtomatic.customer_portal.product_swap.save' | t }}",
update: "{{ 'awtomatic.customer_portal.product_swap.update' | t }}",
original_item: "{{ 'awtomatic.customer_portal.product_swap.original_item' | t }}",
replacement_item: "{{ 'awtomatic.customer_portal.product_swap.replacement_item' | t }}",
update_success: "{{ 'awtomatic.customer_portal.product_swap.update_success' | t }}",
update_error: "{{ 'awtomatic.customer_portal.product_swap.update_error' | t }}",
},
bundles: {
build_box: "{{ 'awtomatic.customer_portal.bundles.build_box' | t }}",
of: "{{ 'awtomatic.customer_portal.bundles.of' | t }}",
back: "{{ 'awtomatic.customer_portal.bundles.back' | t }}",
continue: "{{ 'awtomatic.customer_portal.bundles.continue' | t }}",
add_cart: "{{ 'awtomatic.customer_portal.bundles.add_cart' | t }}",
select_plan: "{{ 'awtomatic.customer_portal.bundles.select_plan' | t }}",
build_your_box: "{{ 'awtomatic.customer_portal.bundles.build_your_box' | t }}",
addons: "{{ 'awtomatic.customer_portal.bundles.addons' | t }}",
checkout: "{{ 'awtomatic.customer_portal.bundles.checkout' | t }}",
edit_my_box: "{{ 'awtomatic.customer_portal.bundles.edit_my_box' | t }}",
box_size: "{{ 'awtomatic.customer_portal.bundles.box_size' | t }}",
add_cart_error: "{{ 'awtomatic.customer_portal.bundles.add_cart_error' | t }}",
update_price: "{{ 'awtomatic.customer_portal.bundles.update_price' | t }}",
update_success: "{{ 'awtomatic.customer_portal.bundles.update_success' | t }}",
update_error: "{{ 'awtomatic.customer_portal.bundles.update_error' | t }}",
delivery_options: "{{ 'awtomatic.customer_portal.bundles.delivery_options' | t }}",
one_time_delivery: "{{ 'awtomatic.customer_portal.bundles.one_time_delivery' | t }}",
additional_products: "{{ 'awtomatic.customer_portal.bundles.additional_products' | t }}",
},
addons: {
title: "{{ 'awtomatic.customer_portal.addons.title' | t }}",
selection_unavailable: "{{ 'awtomatic.customer_portal.addons.selection_unavailable' | t }}",
success_added: "{{ 'awtomatic.customer_portal.addons.success_added' | t }}",
subtitle: "{{ 'awtomatic.customer_portal.addons.subtitle' | t }}",
subscribe: "{{ 'awtomatic.customer_portal.addons.subscribe' | t }}",
select: "{{ 'awtomatic.customer_portal.addons.select' | t }}",
see_products: "{{ 'awtomatic.customer_portal.addons.see_products' | t }}",
modal_title: "{{ 'awtomatic.customer_portal.addons.modal_title' | t }}",
save: "{{ 'awtomatic.customer_portal.addons.save' | t }}",
type: "{{ 'awtomatic.customer_portal.addons.type' | t }}",
},
sms: {
title: "{{ 'awtomatic.customer_portal.sms.title' | t }}",
enable: "{{ 'awtomatic.customer_portal.sms.enable' | t }}",
edit_phone: "{{ 'awtomatic.customer_portal.sms.edit_phone' | t }}",
enable_phone: "{{ 'awtomatic.customer_portal.sms.enable_phone' | t }}",
phone_placeholder: "{{ 'awtomatic.customer_portal.sms.phone_placeholder' | t }}",
enable_phone_text: "{{ 'awtomatic.customer_portal.sms.enable_phone_text' | t }}",
phone: "{{ 'awtomatic.customer_portal.sms.phone' | t }}",
invalid_phone: "{{ 'awtomatic.customer_portal.sms.invalid_phone' | t }}",
invalid_country: "{{ 'awtomatic.customer_portal.sms.invalid_country' | t }}",
duplicated_phone_error: "{{ 'awtomatic.customer_portal.sms.duplicated_phone_error' | t }}",
},
cancelModal: {
header: "{{ 'awtomatic.customer_portal.cancelModal.header' | t }}",
neverMind: "{{ 'awtomatic.customer_portal.cancelModal.neverMind' | t }}",
back: "{{ 'awtomatic.customer_portal.cancelModal.back' | t }}",
continue: "{{ 'awtomatic.customer_portal.cancelModal.continue' | t }}",
continueWithCancellation: "{{ 'awtomatic.customer_portal.cancelModal.continueWithCancellation' | t }}",
continueCancel: "{{ 'awtomatic.customer_portal.cancelModal.continueCancel' | t }}",
confirmCancellation: "{{ 'awtomatic.customer_portal.cancelModal.confirmCancellation' | t }}",
error: "{{ 'awtomatic.customer_portal.cancelModal.error' | t }}",
tryAgain: "{{ 'awtomatic.customer_portal.cancelModal.tryAgain' | t }}",
TOO_MUCH: "{{ 'awtomatic.customer_portal.cancelModal.TOO_MUCH' | t }}",
MOVING: "{{ 'awtomatic.customer_portal.cancelModal.MOVING' | t }}",
COMPETITOR: "{{ 'awtomatic.customer_portal.cancelModal.COMPETITOR' | t }}",
BUDGET: "{{ 'awtomatic.customer_portal.cancelModal.BUDGET' | t }}",
NOT_ENJOY: "{{ 'awtomatic.customer_portal.cancelModal.NOT_ENJOY' | t }}",
NO_NEED: "{{ 'awtomatic.customer_portal.cancelModal.NO_NEED' | t }}",
TRAVELLING: "{{ 'awtomatic.customer_portal.cancelModal.TRAVELLING' | t }}",
OTHER: "{{ 'awtomatic.customer_portal.cancelModal.OTHER' | t }}",
frequency: {
modalTitle: "{{ 'awtomatic.customer_portal.cancelModal.frequency.modalTitle' | t }}",
title: "{{ 'awtomatic.customer_portal.cancelModal.frequency.title' | t }}",
subtitle: "{{ 'awtomatic.customer_portal.cancelModal.frequency.subtitle' | t }}",
updateFrequency: "{{ 'awtomatic.customer_portal.cancelModal.frequency.updateFrequency' | t }}",
formModalTitle: "{{ 'awtomatic.customer_portal.cancelModal.frequency.formModalTitle' | t }}",
formTitle: "{{ 'awtomatic.customer_portal.cancelModal.frequency.formTitle' | t }}",
formDelivery: "{{ 'awtomatic.customer_portal.cancelModal.frequency.formDelivery' | t }}",
setFrequency: "{{ 'awtomatic.customer_portal.cancelModal.frequency.setFrequency' | t }}",
},
address: {
modalPromptTitle: "{{ 'awtomatic.customer_portal.cancelModal.address.modalPromptTitle' | t }}",
promptTitle: "{{ 'awtomatic.customer_portal.cancelModal.address.promptTitle' | t }}",
promptSubtitle: "{{ 'awtomatic.customer_portal.cancelModal.address.promptSubtitle' | t }}",
promptUpdateAddress: "{{ 'awtomatic.customer_portal.cancelModal.address.promptUpdateAddress' | t }}",
modalFormTitle: "{{ 'awtomatic.customer_portal.cancelModal.address.modalFormTitle' | t }}",
noShippingOptions: "{{ 'awtomatic.customer_portal.cancelModal.address.noShippingOptions' | t }}",
confirmModalTitle: "{{ 'awtomatic.customer_portal.cancelModal.address.confirmModalTitle' | t }}",
confirmTitle: "{{ 'awtomatic.customer_portal.cancelModal.address.confirmTitle' | t }}",
confirmAcceptShipping: "{{ 'awtomatic.customer_portal.cancelModal.address.confirmAcceptShipping' | t }}",
newAddress: "{{ 'awtomatic.customer_portal.cancelModal.address.newAddress' | t }}",
oldAddress: "{{ 'awtomatic.customer_portal.cancelModal.address.oldAddress' | t }}",
},
pause: {
header: "{{ 'awtomatic.customer_portal.cancelModal.pause.header' | t }}",
title: "{{ 'awtomatic.customer_portal.cancelModal.pause.title' | t }}",
description: "{{ 'awtomatic.customer_portal.cancelModal.pause.description' | t }}",
cta: "{{ 'awtomatic.customer_portal.cancelModal.pause.cta' | t }}",
success: "{{ 'awtomatic.customer_portal.cancelModal.pause.success' | t }}",
},
skip: {
header: "{{ 'awtomatic.customer_portal.cancelModal.skip.header' | t }}",
title: "{{ 'awtomatic.customer_portal.cancelModal.skip.title' | t }}",
description: "{{ 'awtomatic.customer_portal.cancelModal.skip.description' | t }}",
ctaReschedule: "{{ 'awtomatic.customer_portal.cancelModal.skip.ctaReschedule' | t }}",
ctaSkip: "{{ 'awtomatic.customer_portal.cancelModal.skip.ctaSkip' | t }}",
success: "{{ 'awtomatic.customer_portal.cancelModal.skip.success' | t }}",
},
reschedule: {
header: "{{ 'awtomatic.customer_portal.cancelModal.reschedule.header' | t }}",
confirmReschedule: "{{ 'awtomatic.customer_portal.cancelModal.reschedule.confirmReschedule' | t }}",
dateFormat: "{{ 'awtomatic.customer_portal.cancelModal.reschedule.dateFormat' | t }}",
inputLabel: "{{ 'awtomatic.customer_portal.cancelModal.reschedule.inputLabel' | t }}",
description: "{{ 'awtomatic.customer_portal.cancelModal.reschedule.description' | t }}",
},
confirm: {
header: "{{ 'awtomatic.customer_portal.cancelModal.confirm.header' | t }}",
modalTitle: "{{ 'awtomatic.customer_portal.cancelModal.confirm.modalTitle' | t }}",
title: "{{ 'awtomatic.customer_portal.cancelModal.confirm.title' | t }}",
button: "{{ 'awtomatic.customer_portal.cancelModal.confirm.button' | t }}",
success: "{{ 'awtomatic.customer_portal.cancelModal.confirm.success' | t }}",
},
},
drawers: {
updateAddress: {
title: "{{ 'awtomatic.customer_portal.drawers.updateAddress.title' | t }}",
},
},
babFilters: {
filters: "{{ 'awtomatic.babFilters.filters' | t }}",
reset: "{{ 'awtomatic.babFilters.reset' | t }}",
},
billingHistory: {
title: "{{ 'awtomatic.customer_portal.billingHistory.title' | t }}",
order: "{{ 'awtomatic.customer_portal.billingHistory.order' | t }}",
date: "{{ 'awtomatic.customer_portal.billingHistory.date' | t }}",
status: "{{ 'awtomatic.customer_portal.billingHistory.status' | t }}",
submitted: "{{ 'awtomatic.customer_portal.billingHistory.submitted' | t }}",
challenged: "{{ 'awtomatic.customer_portal.billingHistory.challenged' | t }}",
failed: "{{ 'awtomatic.customer_portal.billingHistory.failed' | t }}",
success: "{{ 'awtomatic.customer_portal.billingHistory.success' | t }}",
loadMore: "{{ 'awtomatic.customer_portal.billingHistory.loadMore' | t }}",
bankChallenge: "{{ 'awtomatic.customer_portal.billingHistory.bankChallenge' | t }}",
},
pauseModal: {
delaySubscriptionTitle: "{{ 'awtomatic.customer_portal.pauseModal.delaySubscriptionTitle' | t }}",
delaySubscriptionSubtitle: "{{ 'awtomatic.customer_portal.pauseModal.delaySubscriptionSubtitle' | t }}",
delaySubscriptionBody: "{{ 'awtomatic.customer_portal.pauseModal.delaySubscriptionBody' | t }}",
delaySubscriptionCta: "{{ 'awtomatic.customer_portal.pauseModal.delaySubscriptionCta' | t }}",
pauseModalTitle: "{{ 'awtomatic.customer_portal.pauseModal.pauseModalTitle' | t }}",
pauseModalLabel: "{{ 'awtomatic.customer_portal.pauseModal.pauseModalLabel' | t }}",
pauseModalBody: "{{ 'awtomatic.customer_portal.pauseModal.pauseModalBody' | t }}",
continueToPause: "{{ 'awtomatic.customer_portal.pauseModal.continueToPause' | t }}",
neverMind: "{{ 'awtomatic.customer_portal.pauseModal.neverMind' | t }}",
back: "{{ 'awtomatic.customer_portal.pauseModal.back' | t }}",
save: "{{ 'awtomatic.customer_portal.pauseModal.save' | t }}",
confirmPause: "{{ 'awtomatic.customer_portal.pauseModal.confirmPause' | t }}",
delaySubscription: "{{ 'awtomatic.customer_portal.pauseModal.delaySubscription' | t }}",
ONE_MONTH: "{{ 'awtomatic.customer_portal.pauseModal.ONE_MONTH' | t }}",
TWO_MONTH: "{{ 'awtomatic.customer_portal.pauseModal.TWO_MONTH' | t }}",
THREE_MONTH: "{{ 'awtomatic.customer_portal.pauseModal.THREE_MONTH' | t }}",
CUSTOM: "{{ 'awtomatic.customer_portal.pauseModal.CUSTOM' | t }}",
},
discountCodeCard: {
title: "{{ 'awtomatic.customer_portal.discountCodeCard.title' | t }}",
addBtn: "{{ 'awtomatic.customer_portal.discountCodeCard.addBtn' | t }}",
cancelBtn: "{{ 'awtomatic.customer_portal.discountCodeCard.cancelBtn' | t }}",
inputPlaceholder: "{{ 'awtomatic.customer_portal.discountCodeCard.inputPlaceholder' | t }}",
applyBtn: "{{ 'awtomatic.customer_portal.discountCodeCard.applyBtn' | t }}",
successMsg: "{{ 'awtomatic.customer_portal.discountCodeCard.successMsg' | t }}",
removeSuccessMsg: "{{ 'awtomatic.customer_portal.discountCodeCard.removeSuccessMsg' | t }}",
loadingMsg: "{{ 'awtomatic.customer_portal.discountCodeCard.loadingMsg' | t }}",
errorMsg: "{{ 'awtomatic.customer_portal.discountCodeCard.errorMsg' | t }}",
boxDiscount: "{{ 'awtomatic.customer_portal.discountCodeCard.boxDiscount' | t }}",
},
},
},
};
window.bundleapp = window.bundleapp || {};
window.bundleapp.settings = {
...window.bundleapp.settings,
translations: TRANSLATIONS,
};
</script>
locale file json addition
"awtomatic": {
"plan_selector": {
"subscription": "SUBSCRIPTION",
"save": "Save",
"extra": "Extra",
"off": "off",
"customize_my_box": "Customize my box",
"from": "Starting from",
"delivery_suffix": "\/delivery",
"one_time_purchase_label": "One-time purchase",
"purchase_options_label": "Purchase options",
"gift_first_name_placeholder":"First name",
"gift_first_name_label":"First name",
"gift_last_name_placeholder":"Last name",
"gift_last_name_label":"Last name",
"gift_email_placeholder":"Email address",
"gift_email_label":"Email address",
"gift_email_warning":"Important: Gift emails will be sent here",
"gift_note_placeholder":"Note",
"gift_note_label":"Note",
"gift_checkbox_label":"This is a gift",
"gift_recipient_info_title":"Recipient info"
},
"customer_portal": {
"titles": {
"return_to_account": "Return to account details",
"subscription": "Subscription",
"subscriptions": "Subscriptions",
"loading": "Loading...",
"cancel": "Cancel",
"save": "Save",
"continue": "Continue",
"pause": "Pause",
"edit": "Edit",
"my_subscriptions": "My Subscriptions",
"frequency": "Frequency",
"next_order": "Next Order",
"confirm": "Confirm",
"remove": "Remove",
"description": "Description",
"add": "Add"
},
"email_gate": {
"no_subs": "You do not have any subscriptions yet",
"check_email": "Check your email to access your subscriptions, {{email}}",
"cta_access_link": "Email me an access link"
},
"subscriptions": {
"error": "There was an error loading your subscriptions",
"no_subs": "You do not have any subscriptions yet",
"link_to_account": "Return to Account Details"
},
"subscription": {
"fetch_error": "There was an error fetching the detail subscription",
"auth_error": "There was authorization error, please login again",
"plan": "Plan",
"active": "Active",
"activate": "Activate",
"pause": "Pause",
"paused": "Paused",
"cancel": "Cancel",
"cancelled": "Cancelled",
"shipment_info": "Shipment info",
"update_shipping": "Update shipping",
"payment_method": "Payment method",
"pay_pal_payment_method": "PayPal",
"ending_in": "ending in",
"expires": "Expires",
"add_phone": "Enter a phone number",
"add_name": "Enter your name",
"add_lastName": "Enter your last name",
"billing_attepts": "Billing Attempts",
"percentage_dynamic_discount": "{{discount}} % off",
"fixed_amount_dynamic_discount": "{{discount}} off",
"then_dynamic_discount": " then {{discount}}",
"orders_dynamic_discount": " for {{orderQty}} orders,",
"billing_attepts_empty": "There are no billing attempts",
"billing_attepts_error": "There was an error loading the billing attempts",
"subs_details": "Subscription Details",
"update_payment_btn": "Update payment",
"cancel_prompt_title": "Canceling subscription cannot be undone. If you want to resume shipments, you will need to create a new order in the store.",
"cancel_error": "There was an error trying to cancel your subscription",
"no_payment_method_error": "Payment method missing from subscripiton.",
"no_payment_method_error_add": "Please add a new card.",
"new_payment_method_success": "Succesfully updated the payment method.",
"new_payment_method_error": "Unknown error adding your payment method. Please try again.",
"remove_line_error": "There was an error trying to remove the product",
"remove_line_success": "Succesfully removed the product",
"ships_to": "Ships to",
"update_payment_success": "An email was sent to {{email}} with a secure link to update payment information.",
"update_payment_success_dunning": "An email was sent to {{email}} with a secure link to update payment information. After payment information is updated we will retry the order.",
"update_payment_success_payment_error": "Unable to add payment method. An email was sent to {{email}} with a secure link to update payment.",
"update_payment_error": "There was an error updating your payment information",
"sales_tax": "Sales tax (if applicable) is not displayed because it is calculated with each new order.",
"skip_billing_interval_prompt": "Are you sure you want to skip?",
"skip_billing_interval_success": "Your next order has been rescheduled to {{date}}",
"skip_billing_interval_error": "There was an error skipping your next order",
"skip_until": "Skip until",
"skip_next": "Skip next",
"order_now": "Order now",
"next_order_success": "Your next order has been rescheduled to {{date}}",
"next_order_error": "There was an error setting the subscription's next order date.",
"add_new_address": "Add new address",
"address_update_success": "Address updated successfully",
"address_update_failed": "We encountered an unknown error. Please try again.",
"pause_subscription": "Pause subscription",
"cancel_subscription": "Cancel subscription",
"pause_disclaimer_date": "We will postpone your subscription. Your subscription will remain active, but you will not be charged until {{date}}. You can pause indefinitely or reschedule anytime.",
"pause_disclaimer_indefinitely": "You will not be charged while your subscription is paused. Reactivate your subscription anytime in your account.",
"pause_success": "Your subscription was paused successfully",
"reactivate_btn": "Reactivate now",
"reactivate": "Reactivate",
"reactivate_success": "Your subscription has been successfully reactivated.",
"reactivate_error": "Something went wrong while reactivating your subscription. Please try again",
"frequency_days": "day(s)",
"frequency_weeks": "week(s)",
"frequency_months": "month(s)",
"order_now_modal_body": "You will be charged {{amount}} and your shipment will go out as soon as possible.",
"amount": "Amount",
"frequency_update_success": "Delivery frequency updated to {{ frequency }}.",
"frequency_update_error": "Unknown error. Delivery frequency could not be updated.",
"customer_update_success": "Customer information has been updated",
"customer_update_error": "Unknown error. Customer information could not be updated.",
"customer_update_info": "Adding your phone number, you will receive free SMS with information about your next subscription, allowing you to skip it,or editing it through your phone",
"verified_phone": "Phone verified",
"not_verified_phone": "Phone not verified",
"verify_phone": "Verify",
"enter_code": "Enter the verification code",
"verify_code_error": "There was an error verifying your phone number.",
"verify_code_retry_error": "You have tried to verify your phone number too many times. Please, try it later.",
"verify_code_success": "Phone number verified successfully",
"code_received": "You must have received a confirmation code in the provided phone number.",
"code_enter": "Please enter the code below to verify your phone number.",
"code_send": "Send code",
"qty": "Qty",
"manage": "Manage",
"your_next_order": "Your next order",
"swap_product": "Swap products",
"swap_product_cta": "Swap for another product...",
"or": "Or",
"update_variant": "Change selection",
"update_bundle": "Update Box",
"save": "Save {{percentage}}%",
"failed_payment_message": "Failed payment with saved method. We will automatically retry payment.",
"failed_payment_cta": "Update Payment method",
"order_now_success": "Order placed successfully",
"order_now_error": "Unknown error. Please try again.",
"update_current_payment_method": "Update current payment method",
"add_new_card": "Add new card",
"delayed": "Delayed",
"delayedNotification": "Subscription delayed due to inventory. Order will ship when item becomes available. To receive order more quickly, please update selection below.",
"update_success": "Your subscription has been updated successfully.",
"update_line_success": "Subscription updated. The order will ship out as soon as possible.",
"update_error": "Something went wrong. Your subscription has not been changed."
},
"new_payment_method": {
"title": "Enter payment method",
"payment_info": "Payment information",
"card_number": "Card number",
"exp_date": "Exp date",
"security_code": "CVV",
"first_name": "First Name",
"last_name": "Last Name",
"billing_address": "Billing address",
"address1": "Address",
"address2": "Apartment, suite, etc. (optional)",
"city": "City",
"country": "Country\/Region",
"state": "State\/Province",
"zip": "Zip code",
"phone": "Phone (optional)"
},
"tables": {
"ID": "ID",
"status": "Status",
"item": "Item",
"created": "Created",
"next_order": "Next Order",
"skip": "Skip",
"product": "Product",
"quantity": "Quantity",
"price": "Price",
"total": "Total",
"shipping": "Shipping",
"originated": "Originated",
"frequency": "Frequency",
"phone": "Phone",
"name": "Name",
"lastName": "Last name",
"date": "Date",
"sold_out": "Sold out",
"partial_inventory": "Partial inventory",
"qty_sold_out": " • {{quantity}} sold out",
"order_number": "Order Number",
"subtotal": "Subtotal",
"loading_fulfillments": "Loading...",
"last_fulfillment": "Last fulfillment",
"next_fulfillment": "Next fulfillment",
"merchant_discount": "Discount",
"one_time_discount_label": "(One-time)",
"finite_discount_label": "({{usageCount}} of {{recurringCycleLimit}})",
"edit": "Edit",
"box_discount": {
"percentage": "Box discount ({{value}}%)",
"amount": "Box discount ({{value}})"
}
},
"login": {
"start_intro": "Enter your email address to get started",
"welcome": "Welcome",
"send_link": "We'll email you a secure link to sign in.",
"email_address": "Email address",
"continue": "Continue",
"welcome_back": "Welcome back",
"choose_login": "Choose how to login for",
"send_secure_link": "Send me secure link",
"login_password": "Login with password",
"check_email": "Check your email for login link",
"secure_email_sent": "We've sent a secure email to {{email}}. The link expires in 24 hours.",
"not_received": "Didn't receive it?",
"new_link": "Send me a new link",
"different_email": "Try different email",
"no_subscriptions": "No subscriptions found",
"invalid_email": "Invalid Email",
"no_subscriptions_message": "We couldn't find any subscriptions associated with"
},
"product_swap": {
"title_select": "Choose replacement item",
"title_confirm": "Confirm",
"sold_out": "Sold out",
"choose": "Choose",
"back": "Back",
"confirm": "Confirm",
"save": "Save",
"update": "Update",
"original_item": "Original item",
"replacement_item": "Replacement item",
"update_success": "Successfully swapped items for the next order.",
"update_error": "Something went wrong. Your subscription has not been changed."
},
"bundles": {
"build_box": "Build your box",
"of": "of",
"back": "Back",
"continue": "Continue",
"add_cart": "Add to cart",
"select_plan": "Select plan",
"build_your_box": "Build your box",
"addons": "Add-ons",
"checkout": "Checkout",
"edit_my_box": "Edit my box",
"box_size": "Box size",
"add_cart_error": "There was an error updating your cart.",
"update_price": "Cost of the box has changed from {{prevBasePrice}} to {{basePrice}}",
"update_success": "Box successfully updated",
"update_error": "Something went wrong trying to update the box",
"delivery_options": "Delivery options",
"one_time_delivery": "One time",
"additional_products": "Additional products"
},
"addons": {
"title": "Add Items",
"selection_unavailable": "Selection unavailable",
"success_added": "Successfully added items to your subscription.",
"subtitle": "Add items to your upcoming order",
"subscribe": "Subscribe",
"select": "Add",
"see_products": "See all products",
"modal_title": "Add products to your next order",
"save": "Save",
"type": "Type"
},
"sms": {
"title": "Text updates",
"enable": "Enable",
"edit_phone": "Edit phone number",
"enable_phone": "Enable text updates",
"phone_placeholder": "Enter phone number",
"enable_phone_text": "Text messages allow you to update subscriptions on the go. Message and. data rates may apply.",
"phone": "Phone number",
"invalid_phone": "Invalid phone number",
"invalid_country": "Only US and CA phone numbers are supported.",
"duplicated_phone_error": "Phone number already in use"
},
"cancelModal": {
"header": "Reason for cancelling",
"neverMind": "Never mind",
"back": "Back",
"continue": "Continue",
"continueWithCancellation": "Continue with cancellation",
"continueCancel": "Continue cancel",
"confirmCancellation": "Confirm cancellation",
"error": "There was an unknown error",
"tryAgain": "Try again.",
"TOO_MUCH": "I have too much",
"MOVING": "Moving to a new place",
"COMPETITOR": "Switching to a competitor",
"BUDGET": "Doesn’t fit my budget",
"NOT_ENJOY": "I didn’t enjoy the product",
"NO_NEED": "No longer need this",
"TRAVELLING": "I’m traveling",
"OTHER": "Other",
"frequency": {
"modalTitle": "Frequency update available",
"title": "Update frequency instead of losing subscription",
"subtitle": "By updating your delivery frequency, you can control how often you recieve shipments.",
"updateFrequency": "Update frequency",
"formModalTitle": "Edit frequency",
"formTitle": "You control how often your receive deliveries. Upcoming orders will be delivered on the frequency selected below.",
"formDelivery": "Delivery every",
"setFrequency": "Set frequency"
},
"address": {
"modalPromptTitle": "Address update available",
"promptTitle": "Moving doesn’t have to mean cancellation",
"promptSubtitle": "By updating your address, you have the option of keeping your subscription with you wherever you go!",
"promptUpdateAddress": "Update address",
"modalFormTitle": "Enter address",
"noShippingOptions": "There are no shipping options available for that address.",
"confirmModalTitle": "Shipping confirmation",
"confirmTitle": "The price of shipping for your subscription will change from {{oldPrice}} to {{newPrice}} because your new address is in a different delivery zone.",
"confirmAcceptShipping": "Accept shipping",
"newAddress": "New address:",
"oldAddress": "Old address:"
},
"pause": {
"header": "Pause available",
"title": "Did you know you can pause instead?",
"description": "Instead of cancelling, pausing your subscription allows you to keep billing and shipping details saved when you’re ready to reactivate your subscription.",
"cta": "Pause subscription",
"success": "Your subscription was paused successfully"
},
"skip": {
"header": "Skip available",
"title": "You can skip or reschedule your next order",
"description": "Keep your shipments coming once you’re back from your trip. Consider skipping or rescheduling instead of canceling.",
"ctaReschedule": "Reschedule next order",
"ctaSkip": "Skip next",
"success": "Your next order has been rescheduled to {{date}}"
},
"reschedule": {
"header": "Schedule next order",
"confirmReschedule": "Reschedule",
"dateFormat": "mm\/dd\/yyyy",
"inputLabel": "Next order",
"description": "Your subscription will remain active, but we will postpone your subscription until the date you select below."
},
"confirm": {
"modalTitle": "Additional feedback",
"title": "Please provide additional feedback (optional)",
"button": "Confirm cancellation",
"success": "Subscription cancelled"
}
},
"drawers": {
"updateAddress": {
"title": "Enter address"
}
},
"billingHistory": {
"title": "Billing history",
"order": "Order",
"date": "Date",
"status": "Status",
"submitted": "Submitted",
"challenged": "Challenged",
"failed": "Failed",
"success": "Success",
"loadMore": "Load more",
"bankChallenge": "Complete Challenge"
},
"pauseModal": {
"delaySubscriptionTitle": "Delay subscription available",
"delaySubscriptionSubtitle": "Consider delaying subscription instead",
"delaySubscriptionBody": "By delaying your subscription, you can make sure orders start again without needing to remember to turn them back on.",
"delaySubscriptionCta": "Delay subscription",
"pauseModalTitle": "Pause subscription",
"pauseModalLabel": "Pause subscription for",
"pauseModalBody": "You will not be charged while your subscription is paused. Reactivate your subscription anytime in your account",
"continueToPause": "Continue to Pause",
"neverMind": "Never mind",
"back": "Back",
"save": "Save",
"confirmPause": "Confirm",
"delaySubscription": "Delay subscription",
"ONE_MONTH": "1 month",
"TWO_MONTH": "2 months",
"THREE_MONTH": "3 months",
"CUSTOM": "Custom"
},
"discountCodeCard": {
"title": "Discount Code",
"addBtn": "Add",
"cancelBtn": "Cancel",
"inputPlaceholder": "Enter code",
"applyBtn": "Apply",
"successMsg": "Discount applied to subscription",
"removeSuccessMsg": "Discount removed from subscription",
"loadingMsg": "Adding discount code...",
"errorMsg": "Invalid code",
"boxDiscount": "Box discount"
},
},
"babFilters": {
"filters": "Filters",
"reset": "Reset"
}
}
Updated about 2 months ago