Neste artigo vamos adicionar a modalidade de compra por assinatura para os produtos que a permitirem.



Algumas considerações antes de avançar sobre esta modalidade de compra:
O pagamento é realizado em uma única vez através do Nuvem Pago.
Se um produto tiver a opção de "Compra rápida" e for elegível para assinatura, a funcionalidade de "Compra rápida" será desativada, direcionando o usuário diretamente para a página de detalhes do produto.
Ao avançar com a modalidade de assinatura, o usuário será levado diretamente ao checkout com esse produto, perdendo os itens que já estiverem no carrinho.
Antes de começar, é importante que você tenha os seguintes componentes privados implementados na sua loja:
Meios de pagamento
Selos de frete grátis, promoções e estoque
Promoções na página de detalhes do produto
HTML
1. Dentro do seu repositório, vamos procurar a seguinte div:
<div class="subtotal-price hidden" data-priceraw="{{ cart.total }}"></div>
E adicionar a classe "js-subtotal-price".
2. Na pasta snipplets, vamos procurar o arquivo relacionado ao item de produto nos listados neste caso, item.tpl, e buscar a seguinte condição que permite incluir o formulário de compra rápida:
{% if (settings.quick_shop or settings.product_color_variants) and product.available and product.display_price and product.variations and not reduced_item %} <div class="js-item-variants hidden"> .....
E substituir essa condição por algo como o seguinte:
{% if
((settings.quick_shop and not product.isSubscribable()) or settings.product_color_variants)
and product.available
and product.display_price
and product.variations and not reduced_item
%}Essa alteração garante que, se um produto for apto para assinatura, o usuário será enviado para a página de detalhes do produto, onde poderá escolher essa modalidade de compra.
Nesse mesmo arquivo, vamos incluir o componente da mensagem que mostra se um produto é apto para assinatura e se possui desconto nessa modalidade (recomendamos incluí-lo próximo ao contexto do preço do produto).
{{ component('subscriptions/subscription-message', {
subscription_classes: {
container: 'text-accent mt-2',
},
}) }}Deveria ficar semelhante a isto:

Parâmetros
| Parâmetro | Descrição | ||
|---|---|---|---|
| svg_sprites |
| ||
| subscription_icon | Booleano para permitir usar um ícone na mensagem. | ||
| subscription_icon_svg_id | Usado para o ID do SVG sprite do ícone. | ||
| subscription_custom_icon | Usado caso não se implemente SVG sprites, para aplicar HTML personalizado no ícone. |
CSS
| Parâmetro | Descrição |
|---|---|
| container | Usado para o contêiner da mensagem. |
| icon | Usado para o ícone da mensagem. |
| text | Usado para o contêiner do texto quando se usa um ícone. |
| value | Usado para o valor do desconto, caso exista. |
label |
Usado para o texto da mensagem (sem incluir o desconto). |
Por fim, neste arquivo precisamos adicionar um “falso” botão de “Compra rápida” que, em vez de abri-la, levará o usuário à página do produto. Para fazer essa alteração, é preciso localizar o seguinte trecho de código que contém os botões de “Compra rápida”.
{% if product.variations %}
{# Open quickshop popup if has variants #}
<a data-toggle="#quickshop-modal" data-modal-url="modal-fullscreen-quickshop" class="js-quickshop-modal-open {% if slide_item %}js-quickshop-slide{% endif %} js-modal-open js-fullscreen-modal-open btn btn-primary btn-small px-4" title="{{ 'Compra rápida de' | translate }} {{ product.name }}" aria-label="{{ 'Compra rápida de' | translate }} {{ product.name }}" data-component="product-list-item.add-to-cart" data-component-value="{{product.id}}">{{ 'Agregar al carrito' | translate }}</a>
{% else %}
{# If not variants add directly to cart #}
<form class="js-product-form" method="post" action="{{ store.cart_url }}">
<input type="hidden" name="add_to_cart" value="{{product.id}}" />
{% set state = store.is_catalog ? 'catalog' : (product.available ? product.display_price ? 'cart' : 'contact' : 'nostock') %}
{% set texts = {'cart': "Agregar al carrito", 'contact': "Consultar precio", 'nostock': "Sin stock", 'catalog': "Consultar"} %}
<input type="number" name="quantity" value="1" class="js-quantity-input hidden" aria-label="{{ 'Cambiar cantidad' | translate }}" >
<input type="submit" class="js-addtocart js-prod-submit-form btn btn-primary btn-small {{ state }} px-4 mb-1 mx-auto" value="{{ texts[state] | translate }}" {% if state == 'nostock' %}disabled{% endif %} data-component="product-list-item.add-to-cart" data-component-value="{{ product.id }}"/>
{# Fake add to cart CTA visible during add to cart event #}
{% include 'snipplets/placeholders/button-placeholder.tpl' with {custom_class: "js-addtocart-placeholder-inline btn-small mb-1 mx-auto"} %}
</form>
{% endif %}E substituí-lo pelo seguinte:
{# Trigger quickshop actions #}
{% set quickshop_button_classes = 'btn btn-primary btn-small px-4 mb-1 mx-auto' %}
{% set state = store.is_catalog ? 'catalog' : (product.available ? product.display_price ? 'cart' : 'contact' : 'nostock') %}
{% set texts = {'cart': "Agregar al carrito", 'contact': "Consultar precio", 'nostock': "Sin stock", 'catalog': "Consultar"} %}
<div class="item-actions mt-2">
{% if product.isSubscribable() %}
{# Product with subscription will link to the product page #}
<a href="{{ product_url_with_selected_variant }}" class="{{ quickshop_button_classes }}" title="{{ 'Compra rápida de' | translate }} {{ product.name }}" aria-label="{{ 'Compra rápida de' | translate }} {{ product.name }}">{{ texts[state] | translate }}</a>
{% else %}
{% if product.variations %}
{# Open quickshop popup if has variants #}
<a data-toggle="#quickshop-modal" data-modal-url="modal-fullscreen-quickshop" class="js-quickshop-modal-open {% if slide_item %}js-quickshop-slide{% endif %} js-modal-open js-fullscreen-modal-open {{ quickshop_button_classes }}" title="{{ 'Compra rápida de' | translate }} {{ product.name }}" aria-label="{{ 'Compra rápida de' | translate }} {{ product.name }}" data-component="product-list-item.add-to-cart" data-component-value="{{product.id}}">{{ 'Agregar al carrito' | translate }}</a>
{% else %}
{# If not variants add directly to cart #}
<form class="js-product-form" method="post" action="{{ store.cart_url }}">
<input type="hidden" name="add_to_cart" value="{{product.id}}" />
<input type="number" name="quantity" value="1" class="js-quantity-input hidden" aria-label="{{ 'Cambiar cantidad' | translate }}" >
<input type="submit" class="js-addtocart js-prod-submit-form {{ quickshop_button_classes }} {{ state }}" value="{{ texts[state] | translate }}" {% if state == 'nostock' %}disabled{% endif %} data-component="product-list-item.add-to-cart" data-component-value="{{ product.id }}"/>
{# Fake add to cart CTA visible during add to cart event #}
{% include 'snipplets/placeholders/button-placeholder.tpl' with {custom_class: "js-addtocart-placeholder-inline btn-small mb-1 mx-auto"} %}
</form>
{% endif %}
{% endif %}
</div>A parte mais relevante é onde abre a condicional {% if product.isSubscribable() %}, que inclui dentro o novo botão que leva à página do produto somente se ele for apto para assinatura.
3. Em seguida, precisamos adicionar as alterações no formulário de produto dentro do arquivo product-form.tpl, que é usado na página de detalhes do produto.
Primeiro, vamos adicionar a classe "js-price-container" ao div que contém os preços do produto (preço base e preço promocional).
Por fora dessa div e logo abaixo, vamos incluir o componente de preço por assinatura.
{{ component('subscriptions/subscription-price', {
subscription_classes: {
container: 'text-center text-md-left mb-3',
prices_container: 'mb-1',
price_compare: 'h4 price-compare mb-0',
price_with_subscription: 'h4 mb-0',
discount_container: 'h6 text-accent mb-1',
price_without_taxes_container: 'mb-2 font-small opacity-60',
},
}) }}
Deveria ficar semelhante a isto:

Os parâmetros que esse componente aceita são os seguintes:
Parâmetros
| Parâmetro | Descrição |
|---|---|
| subscription_discount_position | Usado para posicionar a mensagem de desconto acima dos preços (usando "above"), abaixo (usando "below") ou ao lado (usando "inline"). O padrão é abaixo. |
CSS
| Parâmetro | Descrição |
|---|---|
| container | Usado para o contêiner geral. |
| discount | Usado para o contêiner da mensagem de desconto. |
| discount_value | Usado para a porcentagem de desconto. |
| label | Usado para o texto do desconto. |
discount_container |
Usado para o contêiner principal da mensagem de desconto. |
| prices_container | Usado para o contêiner dos preços (preço base e preço com desconto). |
| price_compare | Usado para o preço riscado quando existe um desconto. |
| price_with_subscription |
Agora precisamos buscar o elemento que mostra a mensagem de frete grátis a partir de um valor mínimo. Esse geralmente tem a classe "free-shipping-message" e está dentro do seguinte condicional:
{% if not product.is_non_shippable and show_product_quantity and (has_free_shipping or has_product_free_shipping) %}
{% if not product.is_non_shippable and show_product_quantity and (has_free_shipping or has_product_free_shipping) %}
<div class="js-free-shipping-minimum-message free-shipping-message text-center text-md-left mb-4 pb-2">
<span class="d-inline-block">
{% include "snipplets/svg/truck.tpl" with {svg_custom_class: "icon-inline icon-w-18 icon-lg svg-icon-accent mr-2"} %}
</span>
<span class="d-inline-block">
<strong class="text-accent">{{ "Envío gratis" | translate }} </strong>
<span {% if has_product_free_shipping %}style="display: none;"{% else %}class="js-shipping-minimum-label"{% endif %}>
{{ "superando los" | translate }} <span>{{ cart.free_shipping.min_price_free_shipping.min_price }}</span>
</span>
</span>
{% if not has_product_free_shipping %}
<div class="js-free-shipping-discount-not-combinable font-small mt-1">
{{ "No acumulable con otras promociones" | translate }}
</div>
{% endif %}
</div>
{% endif %}Da mesma forma, buscamos o elemento com a mensagem "Genial! Você tem frete grátis" e adicionamos a classe "js-product-form-free-shipping-message", ficando da seguinte forma:
<div class="js-product-form-free-shipping-message {% if free_shipping_minimum_label_changes_visibility %}js-free-shipping-message{% endif %} text-accent font-weight-bold mb-4 text-center text-md-left h6" {% if not cart.free_shipping.cart_has_free_shipping %}style="display: none;"{% endif %}>
{{ "¡Genial! Tenés envío gratis" | translate }}
</div>Por último, logo acima do botão de compra, colocamos o componente que permite escolher entre 2 modalidades de compra: Compra única e Compra por assinatura.
{{ component('subscriptions/subscription-selector', {
subscription_classes: {
container: 'radio-button-container box p-0 mb-3',
radio_button: 'radio-button-item',
radio_button_text: 'row',
radio_button_icon: 'radio-button-icons',
purchase_option_info_container: 'col pr-0',
purchase_option_price: 'col-auto text-right font-weight-bold',
purchase_option_single_frequency: 'mt-2 pt-1 font-small opacity-80',
purchase_option_discount: 'label label-accent font-smallest px-2 py-1 ml-1',
dropdown_container: 'form-group font-small mt-2 mb-0',
dropdown_button: 'form-select position-relative',
dropdown_icon: 'form-select-icon icon-inline icon-w-14',
dropdown_options: 'form-select-options',
dropdown_option: 'form-select-option row no-gutters',
dropdown_option_info: 'col pr-4',
dropdown_option_price: 'col-auto font-weight-bold',
dropdown_option_discount: 'text-accent mt-1 font-weight-bold',
cart_alert: 'text-center subscription-btn-alert full-width-container mb-4 pb-2',
shipping_message: 'mt-2 mb-4',
shipping_message_title: 'font-weight-bold ml-1',
shipping_message_text: 'font-small mt-2 ml-4',
legal_message: 'font-smallest text-center mb-3',
legal_link: 'font-smallest d-inline-block btn-link btn-link-primary p-0',
legal_modal: 'bottom modal-centered-small modal-centered transition-soft',
legal_modal_header: 'modal-header row no-gutters align-items-center',
legal_modal_title: 'col',
legal_modal_close_button: 'col-auto mr-3 pb-0 order-first',
legal_modal_body: 'mb-4',
legal_modal_details_title: 'h6 mb-2',
legal_modal_details_paragraph: 'font-small pb-4 mb-0',
legal_modal_details_link: 'font-small d-inline-block btn-link btn-link-primary p-0'
},
svg_sprites: false,
dropdown_icon: true,
dropdown_custom_icon: include("snipplets/svg/chevron-down.tpl", { svg_custom_class: "icon-inline icon-sm svg-icon-text" }),
shipping_message_icon: true,
shipping_message_custom_icon: include("snipplets/svg/truck.tpl", { svg_custom_class: "icon-inline icon-lg icon-w-18 svg-icon-text" }),
legal_modal_close_custom_icon: include("snipplets/svg/times.tpl", { svg_custom_class: "icon-inline svg-icon-text" }),
}) }}
Parámetros
| Parâmetro | Descrição |
|---|---|
| svg_sprites | Booleano que determina se se usam SVG sprites. |
| dropdown_icon | Booleano para permitir usar um ícone no dropdown que permite escolher as opções dentro da modalidade de assinatura. |
| dropdown_icon_svg_id | Usado para o ID do SVG sprite do dropdown. |
| dropdown_custom_icon | Usado caso não se implemente SVG sprites, para aplicar HTML personalizado no ícone do dropdown. |
| cart_alert_icon | Booleano usado para o ícone na mensagem que avisa que os produtos do carrinho serão perdidos caso se avance com a assinatura. |
| cart_alert_icon_svg_id | Usado para o ID do SVG na mensagem sobre os produtos no carrinho. |
| cart_alert_custom_icon | Usado caso não se implemente SVG sprites, para aplicar HTML personalizado no ícone da mensagem sobre os produtos no carrinho. |
| shipping_message_icon | Booleano usado para o ícone na mensagem que avisa que as opções de envio serão escolhidas no checkout. |
| shipping_message_icon_svg_id | Usado para o ID do SVG sprite da mensagem sobre as opções de envio. |
| shipping_message_custom_icon | Usado caso não se implemente SVG sprites, para aplicar HTML personalizado no ícone da mensagem sobre as opções de envio. |
CSS
| Parâmetro | Descrição |
|---|---|
| container | Usado para o contêiner geral. |
| purchase_option_info_container | Usado para o contêiner das informações no radio button de cada modalidade de compra. |
| purchase_option_name | Usado para o nome da modalidade de compra em cada radio button. |
| purchase_option_discount | Usado para o desconto no radio button com a modalidade de compra por assinatura. |
purchase_option_price |
Usado para o preço da modalidade de compra em cada radio button. |
| purchase_option_single_frequency | Usado para o texto que mostra o tempo no radio button com a modalidade de compra por assinatura quando só tem 1 frequência disponível. |
| purchase_option_price_subscription | Usado para o preço riscado quando existe um desconto. |
| dropdown_option_info | Usado para a informação (frequência e desconto) em cada opção de assinatura dentro do menu suspenso. |
| dropdown_option_frequency | Usado para a informação do tempo em cada opção de assinatura dentro do dropdown. |
| dropdown_option_discount | Usado para o desconto em cada opção de assinatura dentro do dropdown suspenso. |
| dropdown_option_price | Usado para o preço em cada opção de assinatura dentro do dropdown. |
| cart_alert | Usado para a mensagem que comunica que os produtos no carrinho serão removidos ao avançar com a compra por assinatura. |
| cart_alert_icon | Usado para o ícone da mensagem que comunica que os produtos no carrinho serão removidos ao avançar com a compra por assinatura. |
| cart_alert_text | Usado para o texto da mensagem que comunica que os produtos no carrinho serão removidos ao avançar com a compra por assinatura. |
| shipping_message | Usado para a mensagem que comunica que as opções de envio serão escolhidas no checkout. |
| shipping_message_title_container | Usado para o contêiner do título na mensagem que comunica que as opções de envio serão escolhidas no checkout, caso seja usado um ícone. |
| shipping_message_icon | Usado para o ícone na mensagem que comunica que as opções de envio serão escolhidas no checkout. |
| shipping_message_title | Usado para o título na mensagem que comunica que as opções de envio serão escolhidas no checkout. |
| shipping_message_text | Usado para o texto na mensagem que comunica que as opções de envio serão escolhidas no checkout. |
| legal_message | Usado para a mensagem de termos e condições sobre a compra por assinatura. |
| legal_link | Usado para os links na mensagem de termos e condições sobre a compra por assinatura. |
| legal_modal | Usado para os modals de termos e condições sobre a compra por assinatura. |
| legal_modal_header | Usado para o cabeçalho nos modals de termos e condições sobre a compra por assinatura. |
| legal_modal_title | Usado para o título nos modals de termos e condições sobre a compra por assinatura. |
| legal_modal_close_icon | Usado para o ícone de fechamento nos modals de termos e condições sobre a compra por assinatura. |
| legal_modal_close_button | Usado para o botão de fechamento nos modals de termos e condições sobre a compra por assinatura. |
| legal_modal_body | Usado para o corpo nos modals de termos e condições sobre a compra por assinatura. |
| legal_modal_overlay | Usado para o overlay nos modals de termos e condições sobre a compra por assinatura. |
| legal_modal_details_title | Usado nos subtítulos para o conteúdo dos modals de termos e condições sobre a compra por assinatura. |
| legal_modal_details_paragraph | Usado nos parágrafos para o conteúdo dos modals de termos e condições sobre a compra por assinatura. |
| legal_modal_details_link |
Último passo opcional: se o seu design usa o arquivo button-placeholder.tpl e a transição ao adicionar um produto ao carrinho mostra o texto "Adicionando...", precisamos adicionar a classe "js-addtocart-adding-text" à div que tem a classe "js-addtocart-adding", ficando da seguinte forma:
<div class="js-addtocart js-addtocart-placeholder btn btn-primary btn-transition disabled {{ custom_class }}" style="display: none;">
<div class="d-inline-block">
<span class="js-addtocart-text transition-container btn-transition-start active">
{{ 'Agregar al carrito' | translate }}
</span>
<span class="js-addtocart-success transition-container btn-transition-success">
{{ '¡Listo!' | translate }}
</span>
<div class="js-addtocart-adding js-addtocart-adding-text transition-container btn-transition-progress">
{{ 'Agregando...' | translate }}
</div>
</div>
</div>CSS
Requisito:
Ter adicionado helper classes em seu layout. Você pode seguir este pequeno tutorial para fazer isso (é só copiar e colar algumas classes, não leva mais que 1 minuto).
1. Adicionar os estilos dentro do arquivo static/style-critical.tpl
Se no seu tema você usa uma folha de estilos para o CSS crítico, vamos precisar do seguinte código dentro dela.
.form-group .form-select-icon,
.form-select .form-select-icon{
position: absolute;
bottom: 12px;
right: 0;
pointer-events: none;
}
.form-select .form-select-icon {
top: 50%;
bottom: initial;
transform: translateY(-50%);
-webkit-transform: translateY(-50%);
-ms-transform: translateY(-50%);
}
.hidden-important{
display:none!important;
}Opcional: dentro desse mesmo arquivo, vamos adicionar os estilos relacionados ao componente de radio button. Caso você os tenha em outro arquivo de CSS, devemos trazê-los para o crítico, já que agora o componente de radio button passa a ser mais relevante.
.radio-button {
margin-bottom: 0;
-webkit-tap-highlight-color: rgba(0,0,0,0);
cursor: pointer;
}
.radio-button.disabled {
opacity: 0.6;
cursor: not-allowed;
}
.radio-button.disabled input[type="radio"] {
cursor: not-allowed;
}
.radio-button-content {
position: relative;
width: 100%;
float: left;
padding: 15px;
clear: both;
box-sizing: border-box;
}
.radio-button-icons-container {
position: absolute;
top: 14px;
left: 10px;
}
.radio-button-icons {
position: relative;
float: left;
}
.radio-button-icon {
width: 16px;
height: 16px;
border-radius: 50%;
}
.radio-button input[type="radio"] {
display: none;
}
.radio-button input[type="radio"] + .radio-button-content .unchecked {
float: left;
}
.radio-button input[type="radio"] + .radio-button-content .checked {
position: absolute;
top: 8px;
left: 8px;
width: 0;
height: 0;
-webkit-transform: translate(-50%,-50%);
-ms-transform: translate(-50%,-50%);
-moz-transform: translate(-50%,-50%);
-o-transform: translate(-50%,-50%);
transform: translate(-50%,-50%);
-webkit-transition: all 0.2s;
-ms-transition: all 0.2s;
-moz-transition: all 0.2s;
-o-transition: all 0.2s;
transition: all 0.2s;
}
.radio-button input[type="radio"]:checked + .radio-button-content .checked {
width: 8px;
height: 8px;
}
.radio-button-label {
width: 100%;
float: left;
padding-left: 30px;
}
.radio-button-item:last-of-type .radio-button {
margin-bottom: 0;
}2. Adicionar os estilos dentro do arquivo static/style-async.tpl
Se no seu design você usa uma folha de estilos para CSS assíncrono, vamos precisar do seguinte código dentro dela. Mas, se não for o caso, então você pode adicioná-lo na sua folha principal de CSS.
{# /* // Dropdown */ #}
.form-select {
display: block;
width: 100%;
&:focus{
outline:0;
}
&::-ms-expand {
display: none;
}
.form-select-icon {
@include prefix(transition, all 0.2s ease, webkit ms moz o);
}
&.open .form-select-icon {
@include prefix(transform, translateY(-50%) rotate(180deg), webkit ms moz o);
}
}
.form-select-options {
position: absolute;
top: 100%;
left: 0;
z-index: 200;
width: 100%;
max-height: 200px;
margin-top: 5px;
list-style: none;
overflow-y: auto;
@include prefix(transition, all 0.2s ease, webkit ms moz o);
opacity: 0;
&.open {
opacity: 1;
}
}
.form-select-option {
padding: 12px;
font-size: var(--font-small);
@include prefix(transition, all 0.4s ease, webkit ms moz o);
cursor: pointer;
}
{# /* // Buttons */ #}
.btn-transition {
position: relative;
overflow: hidden;
.transition-container {
position: absolute;
top: 50%;
left: 0;
width: 100%;
margin-top: -7px;
opacity: 0;
text-align: center;
@include prefix(transition, all 0.5s ease, webkit ms moz o);
cursor: not-allowed;
pointer-events: none;
&.active {
opacity: 1;
}
}
}
{# /* // Modals */ #}
.modal {
position: fixed;
top: 0;
display: block;
width: 80%;
height: 100%;
padding: 10px;
-webkit-overflow-scrolling: touch;
overflow-y: auto;
transition: all .2s cubic-bezier(.16,.68,.43,.99);
z-index: 20000;
&-img-full{
max-width: 100%;
max-height: 190px;
}
&-header{
width: calc(100% + 20px);
margin: -10px 0 10px -20px;
padding: 10px 15px 10px 25px;
font-size: 20px;
}
&-footer{
padding: 10px 0;
clear: both;
}
&-with-fixed-footer {
display: flex;
flex-direction: column;
height: 100%;
.modal-scrollable-area {
height: 100%;
overflow: auto;
}
}
&-full {
width: 100%;
}
&-docked-md{
width: 100%;
}
&-docked-small{
width: 80%;
}
&-top{
top: -100%;
left: 0;
}
&-bottom{
top: 100%;
left: 0;
}
&-left{
left: -100%;
}
&-right{
right: -100%;
}
&-centered{
height: 100%;
width: 100%;
&-small{
left: 50%;
width: 80%;
height: auto;
@include prefix(transform, translate(-50%, 0), webkit ms moz o);
.modal-body{
min-height: 150px;
max-height: 400px;
overflow: auto;
}
}
&-md.modal-show {
left: 50%;
transform: translateX(-50%);
&.modal-bottom-md,
&.modal-bottom {
top: 50%;
bottom: auto;
left: 50%;
height: fit-content;
transform: translate(-50%, -50%);
}
}
}
&-top.modal-show,
&-bottom.modal-show {
top: 0;
&.modal-centered-small{
top: 50%;
@include prefix(transform, translate(-50%, -50%), webkit ms moz o);
}
}
&-bottom-sheet {
top: initial;
bottom: -100%;
height: auto;
&.modal-show {
top: initial;
bottom: 0;
height: auto;
}
}
&-left.modal-show {
left: 0;
}
&-right.modal-show {
right: 0;
}
&-close {
display: inline-block;
padding: 1px 5px 5px 0;
margin-right: 5px;
font-size: 20px;
vertical-align: middle;
cursor: pointer;
border: none;
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
background: none;
}
.tab-group{
margin: 0 -10px 20px -10px;
}
}
.modal-overlay{
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: #00000047;
z-index: 10000;
&.modal-zindex-top{
z-index: 20000;
}
}
@media (min-width: 768px) {
{# /* Modals */ #}
.modal{
&-centered{
height: 80%;
width: 80%;
left: 10%;
margin: 5% auto;
&-small{
left: 50%;
width: 30%;
height: auto;
max-height: 80%;
margin: 0;
}
&-md-600px {
left: 50%;
width: 600px;
transform: translateX(-50%);
}
}
&-centered-md.modal-show {
left: initial;
transform: none;
&.modal-bottom {
top: 50%;
}
}
&-docked-md{
width: 500px;
overflow-x: hidden;
&-centered{
left: calc(50% - 250px);
bottom: auto;
height: auto;
}
}
&-bottom-sheet {
top: 100%;
&.modal-show {
top: 0;
bottom: auto;
}
}
&-docked-small{
width: 350px;
}
&-md-width-400px {
width: 400px;
max-width: 90vw;
}
}.form-select-options {
background-color: $main-background;
border: 1px solid rgba($main-foreground, .1);
}
.form-select-options::-webkit-scrollbar {
width: 7px;
}
.form-select-options::-webkit-scrollbar-track {
background: rgba($main-background, .5);
border-radius: 6px;
}
.form-select-options::-webkit-scrollbar-thumb {
background: rgba($main-foreground, .5);
border-radius: 6px;
}
.form-select-option:hover,
.form-select-option:active {
background-color: rgba($main-foreground, .05);
}
.form-select-option.selected {
background-color: rgba($main-foreground, .08);
}JS
1. O JavaScript precisamos adicioná-lo no arquivo store.js.tpl, dentro da função changeVariant, ao final dela:
LS.subscriptionChangeVariant(variant);
2. Procuramos o momento em que um produto é adicionado ao carrinho:
jQueryNuvem(document).on("click", ".js-addtocart:not(.js-addtocart-placeholder)", function (e) {E logo abaixo da seguinte parte:
if (!jQueryNuvem(this).hasClass('contact')) {Adicionamos este JavaScript:
{# Hide real button and show button placeholder during event #}
$productButton.hide();
$productButtonPlaceholder.show().addClass("active");
$productButtonText.removeClass("active");
setTimeout(function(){
$productButtonAdding.addClass("active");
},300);
{# Restore button state in case of error #}
function restore_button_initial_state(){
$productButtonPlaceholder.removeClass("active");
$productButtonText.fadeIn();
$productButtonAdding.removeClass("active");
$productButtonPlaceholder.hide();
$productButton.css('display' , 'inline-block');
}
{# Restore button state for subscriptions stock error #}
var subscription_callback_error = function() {
setTimeout(function() {
restore_button_initial_state();
}, 500);
}
{# Handle subscribable product submit #}
const subscriptionValidResult = LS.subscriptionSubmit($productContainer, subscription_callback_error, e);
if (subscriptionValidResult && subscriptionValidResult.changeCartSubmit) {
return;
}Isso fará com que, ao comprar um produto por assinatura, o usuário seja direcionado diretamente ao checkout com esse produto.
Ativação
Por enquanto, a ativação será aplicada pela equipe da Nuvemshop, então não hesite em nos contatar assim que terminar de aplicar essas mudanças em storefronts@tiendanube.com