En este tutorial, vemos cómo agregar visibilidad sobre medios de pago y cuotas en tu diseño:
El código en este tutorial incluye:
- Cuotas en el listado de productos, el detalle del producto y el carrito (solo aplica en tiendas de Brasil)
- Modal que muestra el detalle de los medios de pago ofrecidos por la tienda
Item del listado de productos
Formulario en el detalle de productos
Modal del detalle de medios de pago para Mercado Pago
El mismo modal solo que mostrando medios de pago por fuera de Mercado Pago, por ejemplo PayPal.
HTML
Lo primero que vamos a hacer es crear los tpls necesarios para la funcionalidad.
1. Agregamos la carpeta con el nombre payments dentro de la carpeta snipplets
2. Dentro de esta carpeta creamos los siguientes archivos, cada uno con su respectivo código. Es importante mantener los IDs y las clases “js-...” para garantizar su funcionamiento cuando agreguemos el JavaScript.
Installments.tpl
Este archivo lo usamos para mostrar la mejor opción de cuotas tanto en el detalle del producto como en el listado y en el carrito de compras (solo en tiendas de Brasil).
Antes de ver el código vamos a repasar algunos condicionales:
- {% if product %} lo usamos al incluir el snipplet para mostrar cuotas de un producto específico o cuotas debajo del total del carrito (solo en tiendas de Brasil)
- {% if product_detail %} define si vamos a usar el código para las cuotas en el detalle del producto o en el listado. De esta forma podemos agregar clases “js-...” o “IDs” para actualizar las cuotas usando JavaScript cuando el usuario cambia las variantes.
- {% if store.country == 'AR' %} o {% if store.country == BR %} sirve para mostrar las cuotas de forma distinta dependiendo a cada país.
El código de este tpl es el siguiente:
{% if product %} {# Product installments #} {% if product.show_installments and product.display_price %} {% set installments_info_base_variant = product.installments_info %} {% set installments_info = product.installments_info_from_any_variant %} {# If product detail installments, include container with "see installments" link #} {% if product_detail and ( installments_info or custom_payment.discount > 0 ) %} <div data-toggle="#installments-modal" data-modal-url="modal-fullscreen-payments" class="js-modal-open js-fullscreen-modal-open js-product-payments-container mb-2" {% if (not product.get_max_installments) and (not product.get_max_installments(false)) %}style="display: none;"{% endif %}> {% endif %} {# Cash discount #} {% if product_detail and custom_payment.discount > 0 %} <div class="text-center text-md-left mb-2"> <span><strong class="text-accent">{{ custom_payment.discount }}% {{'de descuento' | translate }}</strong> {{'pagando con' | translate }} {{ custom_payment.name }}</span> </div> {% endif %} {% set product_can_show_installments = product.show_installments and product.display_price and product.get_max_installments.installment > 1 %} {% if product_can_show_installments %} {% set max_installments_without_interests = product.get_max_installments(false) %} {% set max_installments_with_interests = product.get_max_installments %} {% set has_no_interest_payments = max_installments_without_interests and max_installments_without_interests.installment > 1 %} {% if has_no_interest_payments %} {% set card_icon_color = 'svg-icon-accent' %} {% else %} {% set card_icon_color = 'svg-icon-text' %} {% endif %} {# If NOT product detail, just include installments alone without link or container #} <div class="js-max-installments-container js-max-installments {% if product_detail %}text-center text-md-left mb-2{% else %}item-installments{% endif %}"> {% if has_no_interest_payments %} <div class="js-max-installments"> {% if product_detail %} {# Accent color used on product detail #} {{ "<span class='text-accent font-weight-bold'><span class='js-installment-amount installment-amount'>{1}</span> cuotas sin interés</span> de <span class='js-installment-price installment-price'>{2}</span>" | t(max_installments_without_interests.installment, max_installments_without_interests.installment_data.installment_value_cents | money) }} {% else %} {{ "<strong class='js-installment-amount installment-amount'>{1}</strong> cuotas sin interés de <strong class='js-installment-price installment-price'>{2}</strong>" | t(max_installments_without_interests.installment, max_installments_without_interests.installment_data.installment_value_cents | money) }} {% endif %} </div> {% else %} {% if store.country != 'AR' or product_detail %} {% set max_installments_with_interests = product.get_max_installments %} {% if max_installments_with_interests %} <div class="js-max-installments">{{ "<strong class='js-installment-amount installment-amount'>{1}</strong> cuotas de <strong class='js-installment-price installment-price'>{2}</strong>" | t(max_installments_with_interests.installment, max_installments_with_interests.installment_data.installment_value_cents | money) }}</div> {% else %} <div class="js-max-installments" style="display: none;"> {% if product.max_installments_without_interests %} {% if product_detail %} {# Accent color used on product detail #} {{ "<span class='text-accent font-weight-bold'><span class='js-installment-amount installment-amount'>{1}</span> cuotas sin interés</span> de <span class='js-installment-price installment-price'>{2}</span>" | t(null, null) }} {% else %} {{ "<strong class='js-installment-amount installment-amount'>{1}</strong> cuotas sin interés de <strong class='js-installment-price installment-price'>{2}</strong>" | t(null, null) }} {% endif %} {% else %} {{ "<strong class='js-installment-amount installment-amount'>{1}</strong> cuotas de <strong class='js-installment-price installment-price'>{2}</strong>" | t(null, null) }} {% endif %} </div> {% endif %} {% endif %} {% endif %} </div> {% endif %} {% if product_detail and installments_info %} <div class="form-row align-items-center align-items-start-md mb-4"> {% set has_payment_logos = settings.payments %} {% if has_payment_logos %} <ul class="list-inline col col-md-auto text-center text-md-left mb-1"> {% for payment in settings.payments %} {# Payment methods flags #} {% if store.country == 'BR' %} {% if payment in ['visa', 'mastercard'] %} <li> {{ payment | payment_new_logo | img_tag('',{class: 'card-img card-img-small lazyload'}) }} </li> {% endif %} {% else %} {% if payment in ['visa', 'amex', 'mastercard'] %} <li> {{ payment | payment_new_logo | img_tag('',{class: 'card-img card-img-small lazyload'}) }} </li> {% endif %} {% endif %} {% endfor %} <li> {% include "snipplets/svg/credit-card-blank.tpl" with {svg_custom_class: "icon-inline icon-w-18 icon-2x " ~ card_icon_color ~ ""} %} </li> </ul> {% endif %} <div class="col-12 col-md-auto text-center"> <a id="btn-installments" class="btn-link" {% if (not product.get_max_installments) and (not product.get_max_installments(false)) %}style="display: none;"{% endif %}> {% set store_set_for_new_installments_view = store.is_set_for_new_installments_view %} {% if store_set_for_new_installments_view %} {{ "Ver medios de pago" | translate }} {% else %} {{ "Ver el detalle de las cuotas" | translate }} {% endif %} </a> </div> </div> </div> {% endif %} {% endif %} {% else %} {# Cart installments #} {% if cart.installments.max_installments_without_interest > 1 %} {% set installment = cart.installments.max_installments_without_interest %} {% set total_installment = cart.total %} {% set interest = false %} {% set interest_value = 0 %} {% else %} {% set installment = cart.installments.max_installments_with_interest %} {% set total_installment = (cart.total * (1 + cart.installments.interest)) %} {% set interest = true %} {% set interest_value = cart.installments.interest %} {% endif %} <div {% if installment < 2 or ( store.country == 'AR' and interest == true ) %} style="display: none;" {% endif %} data-interest="{{ interest_value }}" data-cart-installment="{{ installment }}" class="js-installments-cart-total {% if template == 'cart' %}text-md-center{% endif %} text-right {% if interest == false %}text-accent font-weight-bold{% endif %}"> {{ 'O hasta' | translate }} {% if interest == true %} {{ "<strong class='js-cart-installments-amount'>{1}</strong> cuotas de <strong class='js-cart-installments installment-price'>{2}</strong>" | t(installment, (total_installment / installment) | money) }} {% else %} {{ "<span class='js-cart-installments-amount'>{1}</span> cuotas sin interés de <span class='js-cart-installments installment-price'>{2}</span>" | t(installment, (total_installment / installment) | money) }} {% endif %} </div> {% endif %}
payments.tpl
En este archivo tenemos el componente del modal que incluimos usando un embed (más adelante vamos a crear este componente para evitar errores).
Dentro del modal están las tabs que sirven para que el usuario pueda ver las distintas alternativas de pago con cada medio: Mercado Pago, PayPal, Todo Pago, etc.
También está el llamado a los dos snipplets que describimos más abajo: payments-info.tpl y payments-info-banks.tpl
Todo lo que está por fuera de {% if store_set_for_new_installments_view %} muestra un listado de cuotas para tiendas que no son Argentinas o Brasileras.
{% set installments_info_base_variant = product.installments_info %} {% set installments_info = product.installments_info_from_any_variant %} {% if installments_info %} {% set gateways = installments_info | length %} {% set store_set_for_new_installments_view = store.is_set_for_new_installments_view %} {# Get the array that contains the display settings for each payment method #} {% set payment_methods_config = product.payment_methods_config %} {% embed "snipplets/modal.tpl" with{modal_id: 'installments-modal', modal_position: 'bottom', modal_transition: 'slide', modal_header: true, modal_footer: true, modal_width: 'centered', modal_mobile_full_screen: 'true'} %} {% block modal_head %} {{'Medios de pago' | translate }} {% endblock %} {% block modal_body %} {# Modal header and gateways tab links #} <div class="js-tab-container"> <ul class="js-tab-group tab-group"> {% for method, installments in installments_info %} {% set method_clean = method | replace(" ", "_") | lower %} <li id="method_{{ method_clean }}" class="js-refresh-installment-data js-installments-gw-tab js-tab tab {% if loop.first %} active {% endif %}" data-code="{{ method }}"> <a href="#installment_{{ method_clean }}_{{ installment }}" class="js-tab-link tab-link">{{ method == 'paypal_multiple' ? 'PAYPAL' : (method == 'itaushopline'? 'ITAU SHOPLINE' : method == 'boleto_paghiper'? 'BOLETO PAGHIPER' : method | upper) }}</a> </li> {# Custom payment method #} {% if loop.last and custom_payment is not null %} <li id="method_{{ custom_payment.code }}" class="js-refresh-installment-data js-installments-gw-tab js-tab tab" data-code="{{ custom_payment.code }}"> <a href="#installment_{{ custom_payment.code }}" class="js-tab-link tab-link"> {{ custom_payment.name | upper }} {% if custom_payment.discount > 0 %} <span class="label label-accent ml-1"><strong>{{ custom_payment.discount }}% {{'OFF' | translate }}</strong></span> {% endif %} </a> </li> {% endif %} {% endfor %} </ul> {# Gateways tab content #} <div class="js-tabs-content tab-content"> {% for method, installments in installments_info %} {% set method_clean = method | replace(" ", "_") | lower %} {% set discount = product.get_gateway_discount(method) %} <div id="installment_{{ method_clean }}_" class="js-tab-panel tab-panel {% if loop.first %} active {% endif %} js-gw-tab-pane"> <div> {% if store_set_for_new_installments_view %} {# Payments info with readonly #} {# Evaluate whether the payment method should show complete installments data #} {% if payment_methods_config[method].show_full_installments %} {# Payments Gateways with banks: at the moment only MP AR #} {% include 'snipplets/payments/payments-info-banks.tpl' %} {% else %} {# Payments Gateways with cards only #} {% include 'snipplets/payments/payments-info.tpl' %} {% endif %} {% else %} {# Installments list for ROLA stores #} {% for installment, data_installment in installments %} <div id="installment_{{ method }}_{{ installment }}"> {% set rounded_installment_value = data_installment.installment_value | round(2) %} {% set total_value = (data_installment.without_interests ? data_installment.total_value : installment * data_installment.installment_value) %} {% set total_value_in_cents = total_value | round(2) * 100 %} <strong class="js-installment-amount">{{ installment }}</strong> {% if store.country != 'BR' %}cuota{% if installment > 1 %}s{% endif %} de{% else %}x{% endif %} <strong class="js-installment-price">{{ (rounded_installment_value * 100) | money }}</strong> {% if data_installment.without_interests %} {{ 'sin interés' | t }}{% endif %} </div> {% endfor %} {% endif %} </div> </div> {# Custom payment method #} {% if loop.last and custom_payment is not null %} <div class="js-tab-panel tab-panel js-gw-tab-pane" id="installment_{{ custom_payment.code }}"> <div class="box"> {# Custom method instructions #} <h6 class="mb-2">{{ 'Cuando termines la compra vas a ver la información de pago en relación a esta opción.' | translate }}</h6> {% if custom_payment.discount > 0 %} <div class="mb-1"> <span><strong>{{ custom_payment.discount }}% {{'de descuento' | translate }}</strong> {{'pagando con' | translate }} {{ custom_payment.name }}</span> </div> {% endif %} {# Price total #} <h4 class="mb-1 font-weight-normal"> <span>{{ 'Total:' | translate }}</span> {% if custom_payment.discount > 0 %} {% set price_with_discount = product.price - ((product.price * custom_payment.discount) / 100) %} <span class="price-compare">{{ product.price | money }}</span> <strong class="js-installments-one-payment h3 text-brand">{{ price_with_discount | money }}</strong> {% else %} <strong class="js-installments-one-payment ml-3">{{ product.price | money }}</strong> {% endif %} </h4> {% if custom_payment.discount > 0 %} <div class="mt-3">{{'El descuento será aplicado sobre el costo total de la compra (sin envío) al finalizar la misma.' | translate }}</div> {% endif %} </div> </div> {% endif %} {% endfor %} </div> </div> {% endblock %} {% block modal_foot %} <div class="text-right"> <span class="js-modal-close js-fullscreen-modal-close btn-link pull-right">{{ 'Volver al producto' | translate }}</span> </div> {% endblock %} {% endembed %} {% endif %}
payments-info.tpl
Dentro de este tpl tenemos información sobre tarjetas de credito y cuotas para los medios de pago que no son Mercado Pago, por ejemplo PayPal.
También incluimos una tabla de cuotas que solo aplica a medios de pago en tiendas que no sean argentinas, por eso el condicional {% if store.country != 'AR' %}
Dentro de la información que mostramos en argentina en estos medios de pago tenemos:
- Tipo de pago: Tarjeta de crédito y efectivo
- Cantidad máxima de cuotas:
- Si son sin interés mostramos el valor exacto de cada cuota, el CFT (costo financiero total), el total del pago y el total en un solo pago
- Sin son con interés solo se muestra la cantidad máxima de cuotas, sin el valor exacto, por ejemplo “Hasta 12 cuotas” y el total en 1 solo pago.
- Logos de las tarjetas de crédito aceptadas
{% set installments_data = installmentsv2['methods'][method] %} {# Gateways without banks: cards only #} {% if installments_data['cards'] %} {# Credit cards #} <h6 class="mb-1">{{'Tarjetas de crédito' | translate }}</h6> <div id="installment-credit-card-option-{{ method }}" class="box"> {# Credit cards max installments only for AR stores #} {% if store.country == 'AR' %} {% if installments_data['max_without_interests'] > 1 %} <h4 class="font-weight-normal mb-1"> {{ installments_data['max_without_interests'] }} {{ 'cuotas' | translate }} <span>{{ 'sin interés' | t }}</span> {{'de' | t}} <strong class="js-modal-installment-price" data-installment="{{ installments_data['max_without_interests'] }}"> {{ (product.price / installments_data['max_without_interests']) | money }}</strong> </h4> <h6 class="font-weight-normal mb-2"> <span class="mr-1"> <span>{{ 'CFT: ' | translate }}</span><strong>0,00%</strong> </span> <span class="mr-1"> <span>{{ 'Total: ' | translate }}</span><strong class="js-installments-one-payment">{{ product.price | money }}</strong> </span> <span class="mr-1"> <span>{{ 'En 1 pago: ' | translate }}</span><strong class="js-installments-one-payment">{{ product.price | money }}</strong> </span> </h6> {% elseif installments_data['max_with_interests'] > 1 %} <h4 class="font-weight-normal mb-1"> {{ 'Hasta' | translate }} {{ installments_data['max_with_interests'] }} {{ 'cuotas' | translate }} </h4> <h6 class="font-weight-normal mb-2"> <span> <span>{{ 'O en 1 pago de: ' | translate }}</span><strong class="js-installments-one-payment">{{ product.price | money }}</strong> </span> </h6> {% else %} <h4 class="font-weight-normal mb-1"> <span>{{ 'En 1 pago: ' | translate }}</span><strong class="js-installments-one-payment">{{ product.price | money }}</strong> </h4> {% endif %} {% endif %} {# Credit cards flags #} {% for logo in installments_data['cards'] %} <span class="mb-3"> <img src="{{ 'images/empty-placeholder.png' | static_url }}" data-src="{{ logo | payment_new_logo }}" class="lazyload card-img card-img-medium"> </span> {% endfor %} {% if store.country != 'AR' %} {# Installments list for non AR stores #} <table class="table"> <thead> <tr> <th colspan="2">{{ 'Cuotas ' | translate }}</th> <th class="text-right">{{ 'Total' | translate }}</th> </tr> </thead> <tbody> {% for installment, data_installment in installments %} {% set rounded_installment_value = data_installment.installment_value | round(2) %} {% set total_value = (data_installment.without_interests ? data_installment.total_value : installment * data_installment.installment_value) %} {% set total_value_in_cents = total_value | round(2) * 100 %} <tr id="installment_{{ method }}_{{ installment }}"> {# Installment amount #} <td> <strong><span class="js-installment-amount">{{ installment }}</span></strong> </span>{% if installment > 1 %}{{ 'cuotas' | translate }}{% else %}{{ 'cuota' | translate }}{% endif %}</span> </td> {# Installment price #} <td> <span>{{ 'de ' | translate }}</span> <strong><span class="js-installment-price">{{ (rounded_installment_value * 100) | money }}</span> </strong> {% if data_installment.without_interests or installments_data['max_with_interests'] == 0 %} {{ 'sin interés' | t }} {% endif %} </td> {# Total price #} <td class="js-installment-total-price text-right"> {{ total_value_in_cents | money }} </td> </tr> {% endfor %} </tbody> </table> {% endif %} </div> {% endif %} {# Cash methods #} {% if installments_data['direct'] %} {# Cash module title #} <h6 class="mb-1"> {% if store.country == 'BR' %} {% if wording_method_only_cash %} {{'Efectivo' | translate }} {% elseif wording_method_only_debit %} {{'Débito online' | translate }} {% else %} {{'Efectivo / Débito online' | translate }} {% endif %} {% else %} {{'Tarjeta de débito y efectivo' | translate }} {% endif %} </h6> {# If has debit card or cash #} <div id="installment-cash-option-{{ method }}" class="box"> {# Cash flags #} <div> {% for logo in installments_data['direct'] %} <span> <img src="{{ 'images/empty-placeholder.png' | static_url }}" data-src="{{ logo | payment_new_logo }}" class="lazyload card-img card-img-medium"> </span> {% endfor %} </div> {# Boleto message #} {% if method in ['boleto_paghiper'] and discount > 0.0 %} {% set price_with_boleto_discount = product.price - ((product.price * discount) / 100) %} <div class="my-1"> {{'Boleto Paghiper tiene un' | translate }} <strong>{{discount}}% {{'de descuento' | translate }}</strong></div> <h4 class="font-weight-normal mb-3"> <span>{{ 'Total:' | translate }}</span> <span class="price-compare">{{ product.price | money }}</span><strong class="js-installments-one-payment">{{ price_with_boleto_discount | money }}</strong> </h4> <div class="font-small">{{'El descuento será aplicado sobre el costo total de la compra (sin envío) al finalizar la misma.' | translate }}</div> {% else %} {# Cash total #} <h4 class="font-weight-normal mb-0"> <span>{{ 'Total:' | translate }}</span><strong class="js-installments-one-payment">{{ product.price | money }}</strong> </h4> {% endif %} </div> {% endif %} {# Supported Payment Methods #} {% if installments_data['supported_payment_methods'] %} {% for paymentMethod in installments_data['supported_payment_methods'] %} {# Payment Method Title #} <h6 class="mb-1">{{ paymentMethod.name }}</h6> <div id="info-payment-method-{{ paymentMethod.id }}" class="box"> {# Payment Method Logos #} <div> {% for logo in paymentMethod.logos %} <span> <img src="{{ 'images/empty-placeholder.png' | static_url }}" data-src="{{ logo | payment_new_logo }}" class="lazyload card-img card-img-medium"> </span> {% endfor %} </div> {# Payment Method Total #} <h4 class="font-weight-normal mb-0"> <span>{{ 'Total:' | translate }}</span><strong class="js-installments-one-payment">{{ product.price | money }}</strong> </h4> </div> {% endfor %} {% endif %}
payments-info-banks.tpl
Como su nombre lo indica, en este snipplet mostramos la información de pago en relación a los bancos. Solo aplica tiendas de Argentina con Mercado Pago.
La estructura es similar a los medios de pago en Argentina en payments-info.tpl solo que en lugar de mostrar un solo máximo de cuotas, mostramos varios bancos agrupados por la máxima cantidad de cuotas que ofrece cada uno.
Para las cuotas con interés mostramos la cantidad máxima de cuotas y el precio en un pago.
Y debajo, las formas de pago en efectivo con el precio en 1 pago.
{% set gateways = installmentsv2['methods'][method] %} {# Gateways with banks #} {# Credit cards #} {% if gateways.cc is not null %} <h6 class="mb-1">{{'Tarjetas de crédito' | translate }}</h6> <div class="box"> {# Installments without interest modules by groups, E.g: 3, 6, 9, 12 #} {% if gateways.cc is null or gateways.cc is empty is not null %} {% for installment, banks in gateways.cc.no_interest %} <div> {# Installment amount, cost, CFT, 1 payment info and total cost #} <h4 class="font-weight-normal mb-1"> {{ installment }} {{ 'cuotas' | translate }} <span>{{ 'sin interés' | t }}</span> {{'de' | t}} <strong class="js-modal-installment-price" data-installment="{{installment}}"> {{ (product.price / installment) | money }}</strong> </h4> <h6 class="font-weight-normal mb-2"> <span class="mr-1"> <span>{{ 'CFT: ' | translate }}</span><strong>0,00%</strong> </span> <span class="mr-1"> <span>{{ 'Total: ' | translate }}</span><strong class="js-installments-one-payment">{{ product.price | money }}</strong> </span> <span> <span>{{ 'En 1 pago: ' | translate }}</span><strong class="js-installments-one-payment">{{ product.price | money }}</strong> </span> </h6> {# Banks with installments without interest flags #} <div class="mb-3"> {% for bank in banks %} <span> <img src="{{ 'images/empty-placeholder.png' | static_url }}" data-src="{{ bank | bank_code_by_name | payment_new_logo }}" class="card-img card-img-big lazyload" alt="{{ bank }}"> </span> {% endfor %} </div> <div class="divider"></div> </div> {% endfor %} {% endif %} {# Installments with interest in one module #} {% if gateways.cc.interest is not null %} <div> {# Installment amount #} <h4 class="font-weight-normal mb-1"> {{ gateways.max_with_interests ~ ' cuotas con otras tarjetas' | translate }} </h4> <h6 class="font-weight-normal mb-2"> <span>{{ 'O en 1 pago de: ' | translate }}</span> <strong class="js-installments-one-payment">{{ product.price | money }}</strong> </h6> {# Banks with installments with interest flags #} {% for bank in gateways.cc.interest %} <span class="js-installments-flag-tab js-installments-cash-tab"> <img src="{{ 'images/empty-placeholder.png' | static_url }}" data-src="{{ bank | bank_code_by_name | payment_new_logo }}" class="lazyload card-img card-img-big" alt="{{ bank }}"> </span> {% endfor %} <div class="divider"></div> </div> {% endif %} </div> {% endif %} {# Cash methods #} {% if gateways.debit is not null or gateways.cash is not null or gateways.transfer is not null %} <h6 class="mb-1">{{'Tarjeta de débito y efectivo' | translate }}</h6> <div class="box"> {# Debit card #} {% if gateways.debit is not null %} {# Debit price #} <h4 class="font-weight-normal mb-1">{{ 'Débito' | translate }}</h4> <h6 class="font-weight-normal mb-2"> <span>{{ 'Precio:' | translate }} </span><strong class="js-installments-one-payment"> {{ product.price | money }}</strong> </h6> {# Debit flags #} {% for logo in gateways.debit %} <span data-type="dd" data-code="{{ card }}"> <img src="{{ 'images/empty-placeholder.png' | static_url }}" data-src="{{ logo | payment_new_logo }}" class="lazyload card-img card-img-big"> </span> {% endfor %} <div class="divider"></div> {% endif %} {# Cash #} {% if gateways.cash is not null %} {# Cash price #} <h4 class="font-weight-normal mb-1">{{'Efectivo' | translate }}</h4> <h6 class="font-weight-normal mb-2"> <span>{{ 'Precio:' | translate }} </span><strong class="js-installments-one-payment"> {{ product.price | money }}</strong> </h6> {# Cash flags #} {% for logo in gateways.cash %} <span data-type="dd" data-code="{{ card }}"> <img src="{{ 'images/empty-placeholder.png' | static_url }}" data-src="{{ logo | payment_new_logo }}" class="lazyload card-img card-img-big"> </span> {% endfor %} <div class="divider"></div> {% endif %} {# Wire transfer #} {% if gateways.transfer is not null %} {# Transfer price #} <h4 class="font-weight-normal mb-1 ">{{ 'Transferencia o déposito' | translate }}</h4> <h6 class="font-weight-normal mb-2"> <span>{{ 'Precio:' | translate }} </span><span class="js-installments-one-payment"> {{ product.price | money }}</span> </h6> {# Transfer logos #} {% for logo in gateways.transfer %} <span class="js-installments-flag-tab js-installments-cash-tab" data-type="dd" data-code="{{ card }}"> <img src="{{ 'images/empty-placeholder.png' | static_url }}" data-src="{{ logo | payment_new_logo }}" class="lazyload card-img card-img-big"> </span> {% endfor %} <div class="divider"></div> {% endif %} </div> {% endif %}
3. Agregamos el llamado a los snipplets creados en el paso 3 en el detalle del producto, el item del listado y el carrito.
Detalle del producto
Para el detalle del producto vamos a necesitar agregar installments.tpl y payments.tpl, en el theme Base lo hacemos en el snipplet product/product-form.tpl pero en tu diseño puede que tengas que hacerlo en el template templates/product.tpl
El llamado a la información de cuotas lo vamos a ubicar idealmente cerca del precio de la siguiente forma:
{# Product installments #} {% include "snipplets/payments/installments.tpl" with {'product_detail' : true} %}
Mientras que el snipplet con el modal de detalles de pago lo podemos ubicar al final del snipplet o el template
{# Product payments details #} {% include 'snipplets/payments/payments.tpl' %}
Item del listado
El item del listado de productos en Base se llama item.tpl pero puede que en tu diseño también sea single_product.tpl . Vamos a llamar a las cuotas así:
{% include 'snipplets/payments/installments.tpl' %}
Carrito de compras
Esto aplica solo a tiendas de Brasil, en el theme Base va a estar dentro del snipplet cart-totals.tpl, en tu diseño puede que sea el template templates/cart.tpl o snipplets/cart-panel-ajax.tpl, lo importante es que este debajo del total del carrito. Lo incluimos así:
{% include "snipplets/payments/installments.tpl" with {'product': false} %}
4. Vamos a necesitar agregar algunos IDs y clases en el detalle del producto para que las cuotas se actualicen al cambiar de variante. En el theme Base modificamos el template product.tpl y el snipplet product-form.tpl; pero en tu caso puede que solo necesites modificar product.tpl.
product.tpl
En el div padre que engloba a todo el contenido del detalle de producto agregar los siguientes IDs y selectores “js-...”
<div id="single-product" class=”js-product-detail js-product-container" data-variants="{{product.variants_object | json_encode }}" itemscope itemtype="http://schema.org/Product">
product-form.tpl
En este archivo vamos a reemplazar el código que tengas en relación al precio del producto por lo siguiente (recordá que este cambio aplica en este snipplet pero en tu caso podés aplicarlo donde sea que tengas el precio del producto)
{# Product price #} <div class="price-container text-center text-sm-left" itemprop="offers" itemscope itemtype="http://schema.org/Offer"> <span class="d-inline-block"> <h4 id="compare_price_display" class="js-compare-price-display price-compare {% if product_can_show_installments or (product.promotional_offer and not product.promotional_offer.script.is_percentage_off) %}mb-2{% endif %}" {% if not product.compare_at_price or not product.display_price %}style="display:none;"{% else %} style="display:block;"{% endif %}>{% if product.compare_at_price and product.display_price %}{{ product.compare_at_price | money }}{% endif %}</h4> </span> <spa class="d-inline-block"> <h4 class="js-price-display {% if product_can_show_installments or (product.promotional_offer and not product.promotional_offer.script.is_percentage_off) %}mb-2{% endif %}" id="price_display" itemprop="price"{% if product.display_price %} content="{{ product.price / 100 }}"{% endif %} {% if not product.display_price %}style="display:none;"{% endif %}>{% if product.display_price %}{{ product.price | money }}{% endif %}</h4> </span> <meta itemprop="priceCurrency" content="{{ product.currency }}" /> {% if product.stock_control %} <meta itemprop="inventoryLevel" content="{{ product.stock }}" /> <meta itemprop="availability" href="http://schema.org/{{ product.stock ? 'InStock' : 'OutOfStock' }}" content="{{ product.stock ? 'In stock' : 'Out of stock' }}" /> {% endif %} </div>
También necesitamos agregar la clase js-addtocart al botón de “Agregar al carrito” quedando así:
{% 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="submit" class="js-addtocart js-prod-submit-form btn btn-primary btn-block mb-4 {{ state }}" value="{{ texts[state] | translate }}" {% if state == 'nostock' %}disabled{% endif %} />
5. Ahora necesitamos crear el snipplet para el componente modal o popup dentro de la carpeta snipplets. Este tpl se llama modal.tpl y el código es:
{# /*============================================================================ #Modal ==============================================================================*/ #Properties // ID // Position - Top, Right, Bottom, Left // Transition - Slide and Fade // Width - Full and Box // modal_form_action - For modals that has a form #Head // Block - modal_head #Body // Block - modal_body #Footer // Block - modal_footer #} {% set modal_overlay = modal_overlay | default(true) %} <div id="{{ modal_id }}" class="js-modal modal modal-{{ modal_class }} modal-{{modal_position}} transition-{{modal_transition}} modal-{{modal_width}} transition-soft" style="display: none;"> {% if modal_form_action %} <form action="{{ modal_form_action }}" method="post" class="{{ modal_form_class }}"> {% endif %} <div class="js-modal-close modal-header"> <span class="modal-close"> {% include "snipplets/svg/times.tpl" with {svg_custom_class: "icon-inline svg-icon-text"} %} </span> {% block modal_head %}{% endblock %} </div> <div class="modal-body"> {% block modal_body %}{% endblock %} </div> {% if modal_footer %} <div class="modal-footer d-md-block"> {% block modal_foot %}{% endblock %} </div> {% endif %} {% if modal_form_action %} </form> {% endif %} </div>
6. Agregamos la clase js-variation-option dentro del select para las variantes del detalle del producto. En el theme Base esto está en el tpl product-variants.tpl dentro de la carpeta snipplets/product. En tu diseño puede que este archivo se llame variants.tpl o tengas que aplicar el cambio directamente en el template product.tpl
7. Por último para la parte de HTML, necesitamos agregar una carpeta SVG dentro de la carpeta snipplets. Acá vamos sumar los SVGs para el icono de la tarjeta de crédito en el detalle del producto con el nombre credit-card-blank.tpl, y el icono para cerrar el popup con el nombre times.tpl
credit-card-blank.tpl
<svg class="{{ svg_custom_class }}" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><path d="M527.9 32H48.1C21.5 32 0 53.5 0 80v352c0 26.5 21.5 48 48.1 48h479.8c26.6 0 48.1-21.5 48.1-48V80c0-26.5-21.5-48-48.1-48zm-6 400H54.1c-3.3 0-6-2.7-6-6V86c0-3.3 2.7-6 6-6h467.8c3.3 0 6 2.7 6 6v340c0 3.3-2.7 6-6 6zM192 364v8c0 6.6-5.4 12-12 12h-72c-6.6 0-12-5.4-12-12v-8c0-6.6 5.4-12 12-12h72c6.6 0 12 5.4 12 12zm192 0v8c0 6.6-5.4 12-12 12H236c-6.6 0-12-5.4-12-12v-8c0-6.6 5.4-12 12-12h136c6.6 0 12 5.4 12 12z"/></svg>
times.tpl
<svg class="{{ svg_custom_class }}" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><path d="M207.6 256l107.72-107.72c6.23-6.23 6.23-16.34 0-22.58l-25.03-25.03c-6.23-6.23-16.34-6.23-22.58 0L160 208.4 52.28 100.68c-6.23-6.23-16.34-6.23-22.58 0L4.68 125.7c-6.23 6.23-6.23 16.34 0 22.58L112.4 256 4.68 363.72c-6.23 6.23-6.23 16.34 0 22.58l25.03 25.03c6.23 6.23 16.34 6.23 22.58 0L160 303.6l107.72 107.72c6.23 6.23 16.34 6.23 22.58 0l25.03-25.03c6.23-6.23 6.23-16.34 0-22.58L207.6 256z"/></svg>
CSS
Requisito:
Tener agregados en tu diseño las clases helpers. Podés seguir este este pequeño tutorial para hacerlo (simplemente es copiar y pegar algunas clases, no toma más de 1 minuto).
1.Agregamos el siguiente SASS de colores en style-colors.scss.tpl (o la hoja de tu diseño que tenga los colores y tipografías de la tienda). Recordá que las variables de colores y tipografías pueden variar respecto a tu diseño:
@mixin prefix($property, $value, $prefixes: ()) { @each $prefix in $prefixes { #{'-' + $prefix + '-' + $property}: $value; } #{$property}: $value; } /* // Wrappers */ .box{ float: left; width: 100%; margin-bottom: 20px; padding:8px; border:1px solid rgba($main-foreground, .2); } /* // Dividers */ .divider{ margin-top: 20px; margin-bottom: 20px; clear: both; border-bottom: 1px solid rgba($main-foreground, .1); } /* // Modals */ .modal{ color: $main-foreground; background-color:$main-background; } /* // Links */ .btn-link{ color: $primary-color; fill: $primary-color; text-transform: uppercase; border-bottom: 1px solid; font-weight: bold; cursor: pointer; &:hover, &:focus{ color: rgba($primary-color, .5); fill: rgba($primary-color, .5); } } /* // Tables */ .table{ background-color: $main-background; color: $main-foreground; tbody{ tr:nth-child(odd){ background-color: rgba($main-foreground, .05); } } th{ padding: 8px; text-align: left; } } /* // Tabs */ .tab-group{ border-bottom: 1px solid rgba($main-foreground, .1); .tab{ &-link{ color: $main-foreground; } &.active{ .tab-link{ border-bottom: 2px solid rgba($primary-color, .5); color: $primary-color; } } } }
2. Agregar los estilos dentro del archivo static/style-critical.tpl
Si en tu diseño usas una hoja de estilos para el CSS crítico, vamos a necesitar el siguiente código dentro de la misma, pero si no es el caso entonces podés unificar el CSS de los pasos 2 y 3 en un solo archivo.
/* // Images */ .card-img{ margin: 0 5px 5px 0; border: 1px solid #00000012; } .card-img-small{ height: 25px; } .card-img-medium{ height: 35px; } .card-img-big{ height: 50px; }
3. Agregar los estilos dentro del archivo static/style-async.tpl
Si en tu diseño usas una hoja de estilos para CSS asíncrono, vamos a necesitar el siguiente código dentro de la misma, pero si no es el caso entonces podés unificar el CSS de los pasos 2 y 3 en un solo archivo.
@mixin prefix($property, $value, $prefixes: ()) { @each $prefix in $prefixes { #{'-' + $prefix + '-' + $property}: $value; } #{$property}: $value; } /* // 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; &-header{ width: calc(100% + 20px); margin: -10px 0 10px -10px; padding: 10px 15px; font-size: 20px; } &-footer{ padding: 10px; clear: both; } &-full { width: 100%; } &-docked-sm{ 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%; } &-top.modal-show, &-bottom.modal-show { top: 0; } &-left.modal-show { left: 0; } &-right.modal-show { right: 0; } &-close { display: inline-block; padding: 1px 5px 5px 0; margin-right: 5px; vertical-align: middle; cursor: pointer; } .tab-group{ margin: 0 -10px 20px -10px; } } .modal-overlay{ position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: #00000047; z-index: 10000; } /* // Tables */ .table{ width: 100%; border-collapse: collapse; border-spacing: 0; thead{ th{ padding: 8px; &:first-of-type{ padding-left: 0; } } } td{ padding: 8px; text-align: left; } } /* // Tabs */ .tab-group{ width: 100vw; padding: 0; overflow-x: scroll; white-space: nowrap; .tab{ display: inline-flex; float: none; &-link{ float: left; padding: 10px; text-align: center; } } } .tab-panel:not(.active){ display: none; } .tab-panel.active{ display: block; } /* // Min width 768px */ @media (min-width: 768px) { /* //// Components */ /* Modals */ .modal{ &-centered{ height: 80%; width: 80%; left: 10%; margin: 5% auto; } &-docked-sm{ width: 500px; } &-docked-small{ width: 350px; } } /* Tabs */ .tab-group{ width: calc(100% + 20px); overflow-x: auto; white-space: normal; .tab{ float: left; } } }
JS
⚠️ A partir del día 30 de enero de 2023, la librería jQuery será removida del código de nuestras tiendas, por lo tanto la función "$" no podrá ser utilizada.
1. El JavaScript necesitamos agregarlo en el archivo store.js.tpl (o donde tengas tus funciones de JS). Agregamos el siguiente código :
{# /* // Installments */ #} {# Installments without interest #} function get_max_installments_without_interests(number_of_installment, installment_data, max_installments_without_interests) { if (parseInt(number_of_installment) > parseInt(max_installments_without_interests[0])) { if (installment_data.without_interests) { return [number_of_installment, installment_data.installment_value.toFixed(2)]; } } return max_installments_without_interests; } {# Installments with interest #} function get_max_installments_with_interests(number_of_installment, installment_data, max_installments_with_interests) { if (parseInt(number_of_installment) > parseInt(max_installments_with_interests[0])) { if (installment_data.without_interests == false) { return [number_of_installment, installment_data.installment_value.toFixed(2)]; } } return max_installments_with_interests; } {# Refresh installments inside detail popup #} function refreshInstallmentv2(price){ jQueryNuvem(".js-modal-installment-price" ).each(function( el ) { const installment = Number(jQueryNuvem(el).data('installment')); jQueryNuvem(el).text(LS.currency.display_short + (price/installment).toLocaleString('de-DE', {maximumFractionDigits: 2, minimumFractionDigits: 2})); }); } {# Refresh price on payments popup with payment discount applied #} function refreshPaymentDiscount(price){ jQueryNuvem(".js-price-with-discount" ).each(function( el ) { const payment_discount = jQueryNuvem(el).data('paymentDiscount'); jQueryNuvem(el).text(LS.formatToCurrency(price - ((price * payment_discount) / 100))) }); }
Ubicá la función function changeVariant y reemplazala por lo siguiente:El código que necesitamos para el carrito es el siguiente:
{# /* // Change variant */ #} {# Updates price, installments, labels and CTA on variant change #} function changeVariant(variant){ jQueryNuvem(".js-product-detail .js-shipping-calculator-response").hide(); jQueryNuvem("#shipping-variant-id").val(variant.id); var parent = jQueryNuvem("body"); if (variant.element){ parent = jQueryNuvem(variant.element); } var sku = parent.find('#sku'); if(sku.length) { sku.text(variant.sku).show(); } var installment_helper = function($element, amount, price){ $element.find('.js-installment-amount').text(amount); $element.find('.js-installment-price').attr("data-value", price); $element.find('.js-installment-price').text(LS.currency.display_short + parseFloat(price).toLocaleString('de-DE', { minimumFractionDigits: 2 })); if(variant.price_short && Math.abs(variant.price_number - price * amount) < 1) { $element.find('.js-installment-total-price').text((variant.price_short).toLocaleString('de-DE', { minimumFractionDigits: 2 })); } else { $element.find('.js-installment-total-price').text(LS.currency.display_short + (price * amount).toLocaleString('de-DE', { minimumFractionDigits: 2 })); } }; if (variant.installments_data) { var variant_installments = JSON.parse(variant.installments_data); var max_installments_without_interests = [0,0]; var max_installments_with_interests = [0,0]; for (let payment_method in variant_installments) { let installments = variant_installments[payment_method]; for (let number_of_installment in installments) { let installment_data = installments[number_of_installment]; max_installments_without_interests = get_max_installments_without_interests(number_of_installment, installment_data, max_installments_without_interests); max_installments_with_interests = get_max_installments_with_interests(number_of_installment, installment_data, max_installments_with_interests); var installment_container_selector = '#installment_' + payment_method + '_' + number_of_installment; if(!parent.hasClass("js-quickshop-container")){ installment_helper(jQueryNuvem(installment_container_selector), number_of_installment, installment_data.installment_value.toFixed(2)); } } } var $installments_container = jQueryNuvem(variant.element + ' .js-max-installments-container .js-max-installments'); var $installments_modal_link = jQueryNuvem(variant.element + ' #btn-installments'); var $payments_module = jQueryNuvem(variant.element + ' .js-product-payments-container'); var $installmens_card_icon = jQueryNuvem(variant.element + ' .js-installments-credit-card-icon'); {% if product.has_direct_payment_only %} var installments_to_use = max_installments_without_interests[0] >= 1 ? max_installments_without_interests : max_installments_with_interests; if(installments_to_use[0] <= 0 ) { {% else %} var installments_to_use = max_installments_without_interests[0] > 1 ? max_installments_without_interests : max_installments_with_interests; if(installments_to_use[0] <= 1 ) { {% endif %} $installments_container.hide(); $installments_modal_link.hide(); $payments_module.hide(); $installmens_card_icon.hide(); } else { $installments_container.show(); $installments_modal_link.show(); $payments_module.show(); $installmens_card_icon.show(); installment_helper($installments_container, installments_to_use[0], installments_to_use[1]); } } if(!parent.hasClass("js-quickshop-container")){ jQueryNuvem('#installments-modal .js-installments-one-payment').text(variant.price_short).attr("data-value", variant.price_number); } if (variant.price_short){ var variant_price_clean = variant.price_short.replace('$', '').replace('R', '').replace(',', '').replace('.', ''); var variant_price_raw = parseInt(variant_price_clean, 10); parent.find('.js-price-display').text(variant.price_short).show(); parent.find('.js-price-display').attr("content", variant.price_number).data('productPrice', variant_price_raw); } else { parent.find('.js-price-display').hide(); } if ((variant.compare_at_price_short) && !(parent.find(".js-price-display").css("display") == "none")) { parent.find('.js-compare-price-display').text(variant.compare_at_price_short).show(); } else { parent.find('.js-compare-price-display').hide(); } var button = parent.find('.js-addtocart'); button.removeClass('cart').removeClass('contact').removeClass('nostock'); var $product_shipping_calculator = parent.find("#product-shipping-container"); {# Update CTA wording and status #} {% if not store.is_catalog %} if (!variant.available){ button.val('{{ "Sin stock" | translate }}'); button.addClass('nostock'); button.attr('disabled', 'disabled'); $product_shipping_calculator.hide(); } else if (variant.contact) { button.val('{{ "Consultar precio" | translate }}'); button.addClass('contact'); button.removeAttr('disabled'); $product_shipping_calculator.hide(); } else { button.val('{{ "Agregar al carrito" | translate }}'); button.addClass('cart'); button.removeAttr('disabled'); $product_shipping_calculator.show(); } {% endif %} {% if template == 'product' %} const base_price = Number(jQueryNuvem("#price_display").attr("content")); refreshInstallmentv2(base_price); refreshPaymentDiscount(variant.price_number); {% if settings.last_product and product.variations %} if(variant.stock == 1) { jQueryNuvem('.js-last-product').show(); } else { jQueryNuvem('.js-last-product').hide(); } {% endif %} {% endif %} {# Update shipping on variant change #} LS.updateShippingProduct(); zipcode_on_changevariant = jQueryNuvem("#product-shipping-container .js-shipping-input").val(); jQueryNuvem("#product-shipping-container .js-shipping-calculator-current-zip").text(zipcode_on_changevariant); }
Por último agregá la siguiente función:
{# /* // Product labels on variant change */ #} {# Stock, Offer and discount labels update #} jQueryNuvem(document).on("change", ".js-variation-option", function(e) { var $parent = jQueryNuvem(this).closest(".js-product-variants"); var $variants_group = jQueryNuvem(this).closest(".js-product-variants-group"); var $quickshop_parent_wrapper = jQueryNuvem(this).closest(".js-quickshop-container"); {# If quickshop is used from modal, use quickshop-id from the item that opened it #} var quick_id = $quickshop_parent_wrapper.attr("data-quickshop-id"); if($parent.hasClass("js-product-quickshop-variants")){ var $quickshop_parent = jQueryNuvem(this).closest(".js-item-product"); {# Target visible slider item if necessary #} if($quickshop_parent.hasClass("js-item-slide")){ var $quickshop_variant_selector = '.js-swiper-slide-visible .js-quickshop-container[data-quickshop-id="'+quick_id+'"]'; }else{ var $quickshop_variant_selector = '.js-quickshop-container[data-quickshop-id="'+quick_id+'"]'; } LS.changeVariant(changeVariant, $quickshop_variant_selector); {% if settings.product_color_variants %} {# Match selected color variant with selected quickshop variant #} if(($variants_group).hasClass("js-color-variants-container")){ var selected_option_id = jQueryNuvem(this).find("option:selected").val(); if($quickshop_parent.hasClass("js-item-slide")){ var $color_parent_to_update = jQueryNuvem('.js-swiper-slide-visible .js-quickshop-container[data-quickshop-id="'+quick_id+'"]'); }else{ var $color_parent_to_update = jQueryNuvem('.js-quickshop-container[data-quickshop-id="'+quick_id+'"]'); } $color_parent_to_update.find('.js-color-variant, .js-insta-variant').removeClass("selected"); $color_parent_to_update.find('.js-color-variant[data-option="'+selected_option_id+'"], .js-insta-variant[data-option="'+selected_option_id+'"]').addClass("selected"); } {% endif %} } else { LS.changeVariant(changeVariant, '#single-product'); } {# Offer and discount labels update #} var $this_product_container = jQueryNuvem(this).closest(".js-product-container"); if($this_product_container.hasClass("js-quickshop-container")){ var this_quickshop_id = $this_product_container.attr("data-quickshop-id"); var $this_product_container = jQueryNuvem('.js-product-container[data-quickshop-id="'+this_quickshop_id+'"]'); } var $this_compare_price = $this_product_container.find(".js-compare-price-display"); var $this_price = $this_product_container.find(".js-price-display"); var $installment_container = $this_product_container.find(".js-product-payments-container"); var $installment_text = $this_product_container.find(".js-max-installments-container"); var $this_add_to_cart = $this_product_container.find(".js-prod-submit-form"); // Get the current product discount percentage value var current_percentage_value = $this_product_container.find(".js-offer-percentage"); // Get the current product price and promotional price var compare_price_value = $this_compare_price.html(); var price_value = $this_price.html(); // Calculate new discount percentage based on difference between filtered old and new prices const percentageDifference = window.moneyDifferenceCalculator.percentageDifferenceFromString(compare_price_value, price_value); if(percentageDifference){ $this_product_container.find(".js-offer-percentage").text(percentageDifference); $this_product_container.find(".js-offer-label").css("display" , "table"); } if ($this_compare_price.css("display") == "none" || !percentageDifference) { $this_product_container.find(".js-offer-label").hide(); } if ($this_add_to_cart.hasClass("nostock")) { $this_product_container.find(".js-stock-label").show(); } else { $this_product_container.find(".js-stock-label").hide(); } if ($this_price.css('display') == 'none'){ $installment_container.hide(); $installment_text.hide(); }else{ $installment_text.show(); } });
2. Pero también necesitamos agregar el JS que hace funcionar al componente del modal en general y para las tabs
{#/*============================================================================ #Modals ==============================================================================*/ #} {# Full screen mobile modals back events #} if (window.innerWidth < 768) { {# Clean url hash function #} cleanURLHash = function(){ const uri = window.location.toString(); const clean_uri = uri.substring(0, uri.indexOf("#")); window.history.replaceState({}, document.title, clean_uri); }; {# Go back 1 step on browser history #} goBackBrowser = function(){ cleanURLHash(); history.back(); }; {# Clean url hash on page load: All modals should be closed on load #} if(window.location.href.indexOf("modal-fullscreen") > -1) { cleanURLHash(); } {# Open full screen modal and url hash #} jQueryNuvem(document).on("click", ".js-fullscreen-modal-open", function(e) { e.preventDefault(); var modal_url_hash = jQueryNuvem(this).data("modalUrl"); window.location.hash = modal_url_hash; }); {# Close full screen modal: Remove url hash #} jQueryNuvem(document).on("click", ".js-fullscreen-modal-close", function(e) { e.preventDefault(); goBackBrowser(); }); {# Hide panels or modals on browser backbutton #} window.onhashchange = function() { if(window.location.href.indexOf("modal-fullscreen") <= -1) { {# Close opened modal #} if(jQueryNuvem(".js-fullscreen-modal").hasClass("modal-show")){ {# Remove body lock only if a single modal is visible on screen #} if(jQueryNuvem(".js-modal.modal-show").length == 1){ jQueryNuvem("body").removeClass("overflow-none"); } var $opened_modal = jQueryNuvem(".js-fullscreen-modal.modal-show"); var $opened_modal_overlay = $opened_modal.prev(); $opened_modal.removeClass("modal-show"); setTimeout(() => $opened_modal.hide(), 500); $opened_modal_overlay.fadeOut(500); } } } } jQueryNuvem(document).on("click", ".js-modal-open", function(e) { e.preventDefault(); var modal_id = jQueryNuvem(this).data('toggle'); var $overlay_id = jQueryNuvem('.js-modal-overlay[data-modal-id="' + modal_id + '"]'); if (jQueryNuvem(modal_id).hasClass("modal-show")) { let modal = jQueryNuvem(modal_id).removeClass("modal-show"); setTimeout(() => modal.hide(), 500); } else { {# Lock body scroll if there is no modal visible on screen #} if(!jQueryNuvem(".js-modal.modal-show").length){ jQueryNuvem("body").addClass("overflow-none"); } $overlay_id.fadeIn(400); jQueryNuvem(modal_id).detach().appendTo("body"); $overlay_id.detach().insertBefore(modal_id); jQueryNuvem(modal_id).show().addClass("modal-show"); } }); jQueryNuvem(document).on("click", ".js-modal-close", function(e) { e.preventDefault(); {# Remove body lock only if a single modal is visible on screen #} if(jQueryNuvem(".js-modal.modal-show").length == 1){ jQueryNuvem("body").removeClass("overflow-none"); } var $modal = jQueryNuvem(this).closest(".js-modal"); var modal_id = $modal.attr('id'); var $overlay_id = jQueryNuvem('.js-modal-overlay[data-modal-id="#' + modal_id + '"]'); $modal.removeClass("modal-show"); setTimeout(() => $modal.hide(), 500); $overlay_id.fadeOut(500); {# Close full screen modal: Remove url hash #} if ((window.innerWidth < 768) && (jQueryNuvem(this).hasClass(".js-fullscreen-modal-close"))) { goBackBrowser(); } }); jQueryNuvem(document).on("click", ".js-modal-overlay", function(e) { e.preventDefault(); {# Remove body lock only if a single modal is visible on screen #} if(jQueryNuvem(".js-modal.modal-show").length == 1){ jQueryNuvem("body").removeClass("overflow-none"); } var modal_id = jQueryNuvem(this).data('modalId'); let modal = jQueryNuvem(modal_id).removeClass("modal-show"); setTimeout(() => modal.hide(), 500); jQueryNuvem(this).fadeOut(500); if (jQueryNuvem(this).hasClass("js-fullscreen-overlay") && (window.innerWidth < 768)) { cleanURLHash(); } }); {#/*============================================================================ #Tabs ==============================================================================*/ #} var $tab_open = jQueryNuvem('.js-tab'); $tab_open.on("click", function (e) { e.preventDefault(); var $tab_container = jQueryNuvem(e.currentTarget).closest(".js-tab-container"); $tab_container.find(".js-tab, .js-tab-panel").removeClass("active"); jQueryNuvem(e.currentTarget).addClass("active"); var tab_to_show = jQueryNuvem(e.currentTarget).find(".js-tab-link").attr("href"); $tab_container.find(tab_to_show).addClass("active"); });
3. Como en este tutorial usamos la técnica de lazy load con el plugin Lazysizes, necesitamos agregarlo. Para ver como hacerlo podés leer este corto artículo y luego continuar con este tutorial.
Traducciones
Para terminar agregamos los textos para las traducciones en el archivo config/translations.txt
--- Installments --- es "<strong class='js-installment-amount installment-amount'>{1}</strong> cuotas de <strong class='js-installment-price installment-price'>{2}</strong>" pt "<strong class='js-installment-amount installment-amount'>{1}</strong>x de <strong class='js-installment-price installment-price'>{2}</strong>" en "In up to <strong class='js-installment-amount installment-amount'>{1}</strong> installments of <strong class='js-installment-price installment-price'>{2}</strong>" es_mx "<strong class='js-installment-amount installment-amount'>{1}</strong> meses de <strong class='js-installment-price installment-price'>{2}</strong>" es "<strong class='js-installment-amount installment-amount'>{1}</strong> cuotas sin interés de <strong class='js-installment-price installment-price'>{2}</strong>" pt "<strong class='js-installment-amount installment-amount'>{1}</strong>x de <strong class='js-installment-price installment-price'>{2}</strong> sem juros" en "In up to <strong class='js-installment-amount installment-amount'>{1}</strong> installments of <strong class='js-installment-price installment-price'>{2}</strong> without interest" es_mx "<strong class='js-installment-amount installment-amount'>{1}</strong> meses sin intereses de <strong class='js-installment-price installment-price'>{2}</strong>" es "En hasta <strong class='js-installment-amount installment-amount'>{1}</strong> cuotas" pt "Em até <strong class='js-installment-amount installment-amount'>{1}</strong>x" en "In up to <strong class='js-installment-amount installment-amount'>{1}</strong> installments" es_mx "En hasta <strong class='js-installment-amount installment-amount'>{1}</strong> meses" es "<strong class='js-installment-amount installment-amount'>{1}</strong> cuotas sin interés de <strong class='js-installment-price installment-price'>{2}</strong>" pt "<strong class='js-installment-amount installment-amount'>{1}</strong>x de <strong class='js-installment-price installment-price'>{2}</strong> sem juros" en "In up to <strong class='js-installment-amount installment-amount'>{1}</strong> installments of <strong class='js-installment-price installment-price'>{2}</strong> without interest" es_mx "<strong class='js-installment-amount installment-amount'>{1}</strong> meses sin intereses de <strong class='js-installment-price installment-price'>{2}</strong>" es "<strong class='installment-amount'>{1}</strong> cuotas de <strong class='installment-price'>{2}</strong>" pt "<strong class='installment-amount'>{1}</strong>x de <strong class='installment-price'>{2}</strong>" en "In up to <strong class='installment-amount'>{1}</strong> installments of <strong class='installment-price'>{2}</strong>" es_mx "<strong class='installment-amount'>{1}</strong> meses de <strong class='installment-price'>{2}</strong>" es "<strong class='installment-amount'>{1}</strong> cuotas sin interés de <strong class='installment-price'>{2}</strong>" pt "<strong class='installment-amount'>{1}</strong>x de <strong class='installment-price'>{2}</strong> sem juros" en "In up to <strong class='installment-amount'>{1}</strong> installments of <strong class='installment-price'>{2}</strong> without interest" es_mx "<strong class='installment-amount'>{1}</strong> meses sin intereses de <strong class='installment-price'>{2}</strong>" es "En hasta <strong class='installment-amount'>{1}</strong> cuotas" pt "Em até <strong class='installment-amount'>{1}</strong>x" en "In up to <strong class='installment-amount'>{1}</strong> installments" es_mx "En hasta <strong class='installment-amount'>{1}</strong> meses" es "<strong class='installment-amount'>{1}</strong> cuotas sin interés de <strong class='installment-price'>{2}</strong>" pt "<strong class='installment-amount'>{1}</strong>x de <strong class='js-installment-price installment-price'>{2}</strong> sem juros" en "In up to <strong class='installment-amount'>{1}</strong> installments of <strong class='installment-price'>{2}</strong> without interest" es_mx "<strong class='installment-amount'>{1}</strong> meses sin intereses de <strong class='installment-price'>{2}</strong>" es "{1} cuotas en tarjeta de crédito" pt "{1}x no cartão" en "In up to {1} installments" es_mx "{1} meses con tarjeta de crédito" es "¡{1} cuotas sin interés!" pt "{1} x sem juros!" en "{1} installments without interest!" es_mx "¡{1} meses sin intereses!" es "Ver el detalle de las cuotas" pt "Ver detalhes das parcelas" en "See installments details" es_mx "Ver el detalle de las mensualidades" es "Ver financiación y medios de pago" pt "Ver financiación y medios de pago" en "Ver financiación y medios de pago" es_mx "Ver financiamiento y formas de pago" es "Financiación y medios de pago" pt "Financiación y medios de pago" en "Financiación y medios de pago" es_mx "financiamiento y formas de pago" es "Detalles de las cuotas" pt "Detalhes das parcelas" en "Installments details" es_mx "Detalle de las mensualidades" es "sin interés" pt "sem juros" en "without interests" es_mx "sin intereses" es "Hasta <strong class='js-installment-amount installment-amount'>{1}</strong> cuotas" pt "Até <strong class='js-installment-amount installment-amount'>{1}</strong>x" en "Up to <strong class='js-installment-amount installment-amount'>{1}</strong> installments" es_mx "Hasta <strong class='js-installment-amount installment-amount'>{1}</strong> meses" es "CFT:" pt "CFT:" en "CFT:" es_mx "CFT:" es "Precio en 1 pago:" pt "Preço em 1 parcela:" en "1 payment price:" es_mx "Precio en 1 mensualidad:" es "PTF:" pt "PTF:" en "PTF:" es_mx "PTF:" es "TEA:" pt "TEA:" en "TEA:" es_mx "TEA:" es "Total:" pt "Total:" en "Total:" es_mx "Total:" es "Sin interés" pt "Sem juros" en "Without interest" es_mx "Sin intereses" es "sin interés" pt "sem juros" en "without interest" es_mx "sin intereses" es "cuotas sin interés" pt "x sem juros" en "installments without interest" es_mx "meses sin intereses" es "Conocé las opciones de pago con" pt "Conheça as opções de pagamento com" en "See the payment options with" es_mx "Conoce las opciones de pago con" es "Conocé la cantidad de cuotas" pt "Conheça a quantidade de parcelas" en "See our installments amount" es_mx "Conoce el número de mensualidades" es "Boleto Paghiper tiene un" pt "Boleto PagHiper oferece" en "Boleto Paghiper offers a" es_mx "Boleto Paghiper tiene un" es "de descuento" pt "de desconto" en "discount" es_mx "de descuento" es "que será aplicado sobre el costo total de la compra al finalizar la misma." pt "a ser aplicado sobre o custo total da compra ao finalizá-la." en "that will be applied over the total cost of the order when the checkout process is finished." es_mx "que será aplicado sobre el costo total de la compra al finalizar la misma." es "Cantidad de cuotas" pt "Quantidade de parcelas" en "Installments amount" es_mx "Número de meses" es "Ver medios de pago y financiación" pt "Ver meios de pagamento e parcelamento" en "See payment options and installments" es_mx "Ver formas de pago y financiamiento" es "Ver medios de pago" pt "Ver meios de pagamento" en "See payment options" es_mx "Ver métodos de pago" es "Ver más medios de pago y financiación" pt "Ver mais meios de pagamento e parcelamento" en "See more payment options and installments" es_mx "Ver más métodos de pago y financiamiento" es "Ver más medios de pago" pt "Ver mais meios de pagamento" en "See payment options" es_mx "Ver más métodos de pago" es "Medios de pago y financiación" pt "Meios de pagamento e parcelamento" en "Payment options and installments" es_mx "Formas de pago y financiamiento" es "Detalles de las cuotas" pt "Detalhes das parcelas" en "Installments details" es_mx "Detalle de las mensualidades" es "Opciones de pago y financiación que vas a poder elegir a la hora de pagar por tu compra" pt "Estas são as opções de pagamento e parcelamento que poderá escolher na hora de pagar por sua compra" en "Payment options and installments that you will choose when you finish your purchase" es_mx "Opciones de pago y financiamiento que podrás elegir al pagar tu compra" es "Medio de pago elegido:" pt "Meio de pagamento escolhido:" en "Payment method chosen:" es_mx "Método de pago elegido:" es "de" pt "x" en "of" es_mx "de" es "x de" pt "x de" en "x of" es_mx "x de" es "cuotas" pt "x" en "installments" es_mx "meses" es "cuota" pt "x" en "installment" es_mx "mensualidad" es "Elegí un banco para poder elegir las cuotas" pt "Escolha um banco para definir suas parcelas" en "Choose a bank so you can choose the installments" es_mx "Selecciona un banco para las mensualidades" es "de " pt "de " en "of " es_mx "de " es "Cerrar" pt "Fechar" en "Close" es_mx "Cerrar" es "12 cuotas con otras tarjetas" pt "12x com outros cartões" en "12 installments with other cards" es_mx "12 meses con otras tarjetas" es "Cuotas" pt "x" en "Installments" es_mx "Meses" es "sin interés de" pt "sem juros de" en "without interest of" es_mx "sin intereses de" es "Precio:" pt "Preço:" en "Price:" es_mx "Precio:" es "Volver al producto" pt "Voltar ao produto" en "Back to product" es_mx "Regresar al producto" es "Volver al carrito" pt "Voltar ao carrinho" en "Back to cart" es_mx "Regresar al carrito" es "Hasta" pt "Até" en "Up to" es_mx "Hasta" es "Tarjetas de crédito" pt "Cartões de crédito" en "Credit cards" es_mx "Tarjetas de crédito" es "Tarjetas de débito y efectivo" pt "Cartões de débito e à vista" en "Debit cards and cash" es_mx "Tarjetas de débito y efectivo" es "Transferencia o depósito" pt "Transferência bancária ou depósito" en "Wire transfer o deposit" es_mx "Transferencia o depósito" es "En 1 pago:" pt "Em 1 parcela:" en "1 payment price:" es_mx "En 1 mensualidad:" es "O en 1 pago de:" pt "Ou em 1 parcela de:" en "Or in 1 payment of:" es_mx "O en 1 mensualidad de:" es "tiene un" pt "oferece" en "offers a" es_mx "tiene un" es "Cuando termines la compra vas a ver la información de pago en relación a esta opción." pt "Efetuada a compra, você verá as informações de pagamento em relação a esta opção." en "When you finish the purchase you will se the payment information related to this option." es_mx "Verás la información de pago de esta opción al finalizar la compra." es "de " pt "de " en "of " es_mx "de " es "O hasta" pt "Ou até" en "Or up to" es_mx "O hasta" es "cuotas de" pt "x de" en "x of" es_mx "meses de" es "sin interés" pt "sem juros" en "without interest" es_mx "sin intereses" es "Cuotas " pt "Parcelas" en "Installments" es_mx "Meses " es "A convenir" pt "A combinar" en "To be defined" es_mx "A convenir" es "Ver más opciones" pt "Ver mais opções" en "See more options" es_mx "Ver más opciones" es "Ver menos opciones" pt "Ver menos opções" en "See less options" es_mx "Ver menos opciones" es "Hasta <strong class='installment-amount'>{1}</strong> cuotas" pt "Até <strong class='installment-amount'>{1}</strong>x" en "Up to <strong class='installment-amount'>{1}</strong> installments" es_mx "Hasta <strong class='installment-amount'>{1}</strong> cuotas" es "Tarjeta de débito / Efectivo / Depósito o transferencia" pt "Cartão de débito / Dinheiro / Depósito ou transferência" en "Debit card / Cash / Deposit or wire transfer" es_mx "Tarjeta de débito / Efectivo / Depósito o transferencia" es "Conocé las opciones de pago" pt "Conheça as opções de pagamento" en "See the payment options" es_mx "Conocé las opciones de pago"
Listo, ya tenés en tu diseño la funcionalidad aplicada ¡Excelente!