Neste tutorial veremos como adicionar a possibilidade de calcular frete para outros países sem ter que mudar a moeda ou idioma.
Vale a pena explicar que nas lojas existem dois tipos de entidades para cada país configuradas na tela Configurações > Idiomas e Moedas.
- storefront_country: É o país que define o idioma e a moeda.
- shipping_country: É o país para o qual o pedido é enviado (pode ser escolhido em um dropdown no paso 1 do checkout)
Em todas as lojas storefront_country é igual a shipping_country, portanto se o usuário alterar o idioma em uma loja, por exemplo para Argentina, o país de envio também será Argentina.
Nos próximos passos veremos como tornar o shipping_country independente do storefront_country, portanto o usuário poderá comprar olhando a loja em sua versão Brasileira mas enviando para outro país sem ter que mudar o Storefront_country.
HTML
1. A primeira coisa que vamos fazer é adicionar o seguinte código para o texto que diz “Meios de envio para [Shipping country]” dentro do snipplet shipping-calculator.tpl dentro da pasta de snipplets / shipping. Devemos substituir o texto que diz "Meios de envio" pelo seguinte:
{% if languages | length > 1 %} {{ ' para ' | translate }} {% for language in languages %} {% if (shipping_country_id and language.id == shipping_country_id) or (not shipping_country_id and language.active) %} <a href="#" data-toggle="#{% if product_detail %}product{% else %}cart{% endif %}-shipping-country" class="js-modal-open js-shipping-country-label btn-link btn-link-primary text-capitalize"> {{ language.country_name }} </a> {% endif %} {% endfor %} {% endif %}
E então no final deste arquivo vamos adicionar o modal que permite a mudança de país:
{# Shipping country modal #} {% if languages | length > 1 %} {% if product_detail %} {% set country_modal_id = 'product-shipping-country' %} {% else %} {% set country_modal_id = 'cart-shipping-country' %} {% endif %} {% embed "snipplets/modal.tpl" with{modal_id: country_modal_id, modal_class: 'bottom modal-centered-small js-modal-shipping-country', modal_position: 'center', modal_transition: 'slide', modal_header: true, modal_footer: true, modal_width: 'centered', modal_zindex_top: true, modal_mobile_full_screen: false} %} {% block modal_head %} {{ 'País de entrega' | translate }} {% endblock %} {% block modal_body %} {% embed "snipplets/forms/form-select.tpl" with{select_label: true, select_label_name: 'País donde entregaremos tu compra' | translate, select_aria_label: 'País donde entregaremos tu compra' | translate, select_custom_class: 'js-shipping-country-select', select_group_custom_class: 'mt-4' } %} {% block select_options %} {% for language in languages %} <option value="{{ language.id }}" data-country-url="{{ language.url }}" {% if (shipping_country_id and language.id == shipping_country_id) or (not shipping_country_id and language.active) %}selected{% endif %}>{{ language.country_name }}</option> {% endfor %} {% endblock select_options%} {% endembed %} {% endblock %} {% block modal_foot %} <a href="#" class="js-save-shipping-country js-modal-close btn btn-primary float-right">{{ 'Aplicar' | translate }}</a> {% endblock %} {% endembed %} {% endif %}
2. Agora precisamos criar o snipplet para o componente modal ou pop-up dentro da pasta de snipplets. Este tpl é denominado modal.tpl e o código é:
{#/*============================================================================ #Shipping calculator ==============================================================================*/ #} {# /* // Change store country: From invalid zipcode message */ #} $(document).on("click", ".js-save-shipping-country", function(e) { e.preventDefault(); {# Change shipping country #} var $selected_country_select = $(this).closest(".js-modal").find(".js-shipping-country-select option:selected"); var selected_country_val_placeholder = $selected_country_select.text(); var selected_country_val = $selected_country_select.val(); // Sync multiple selects on DOM $(".js-shipping-country-select ").val(selected_country_val); // Update selected country with front end until backend request is ready $(".js-shipping-country-label, .js-zipcode-error-country").text(selected_country_val_placeholder); // If has selected shipping options, reset results and totals $(".js-shipping-calculator-response").hide(); LS.resetCalculatedShipping(); {# Save shipping country id #} LS.saveShippingCountry("/shipping_country/", selected_country_val, function(data){ {# Update selected wording #} $(".js-shipping-country-label, .js-zipcode-error-country").text(data.shipping_country.country_name); }); }); {# /*============================================================================ #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 {% if modal_mobile_full_screen %}js-fullscreen-modal{% endif %} 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 }}" {% if modal_form_hook %}data-store="{{ modal_form_hook }}"{% endif %}> {% endif %} <div class="js-modal-close {% if modal_mobile_full_screen %}js-fullscreen-modal-close{% endif %} 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>
3. Para usar o snipplet que criamos no passo anterior, teremos que criar um novo componente para o select ou dropdown. Para isso, criamos o arquivo form-select.tpl dentro da pasta snipplets/forms com o seguinte código:
{# /*============================================================================ #Form select ==============================================================================*/ #Properties #Group //select_group_custom_class for custom CSS classes #Label // select_label_name for name // select_label_id for ID // select_for for label for // select_label_custom_class for custom CSS classes #Select // select_id for id // select_name for name // select_custom_class for custom CSS classes // input_rows for textarea rows // select_options to insert select options // select_aria_label for aria-label attribute #} <div class="form-group {{ select_group_custom_class }}"> {% if select_label %} <label {% if select_label_id%}id="{{ select_label_id }}"{% endif %} class="form-label {{ select_label_custom_class }}" {% if select_for %}for="{{ select_for }}"{% endif %}>{{ select_label_name }}</label> {% endif %} <select {% if select_id %}id="{{ select_id }}"{% endif %} class="form-select {{ select_custom_class }} {% if select_inline %}form-control-inline{% endif %}" {% if select_data %}data-{{select_data}}="{{select_data_value}}"{% endif %} {% if select_name %}name="{{ select_name }}"{% endif %} {% if select_aria_label %}aria-label="{{ select_aria_label }}"{% endif %}> {% block select_options %} {% endblock select_options %} </select> <div class="form-select-icon"> {% include "snipplets/svg/chevron-down.tpl" with {svg_custom_class: "icon-inline icon-w-14 icon-lg svg-icon-text"} %} </div> </div>
4.Por fim, para a parte HTML, dentro da pasta snipplets/SVG adicionaremos os SVGs que usamos para o select, o ícone de filtros e o modal.
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>
chevron-left.tpl
<svg class="{{ svg_custom_class }}" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 512"><path d="M231.293 473.899l19.799-19.799c4.686-4.686 4.686-12.284 0-16.971L70.393 256 251.092 74.87c4.686-4.686 4.686-12.284 0-16.971L231.293 38.1c-4.686-4.686-12.284-4.686-16.971 0L4.908 247.515c-4.686 4.686-4.686 12.284 0 16.971L214.322 473.9c4.687 4.686 12.285 4.686 16.971-.001z"/></svg>
chevron-down.tpl
<svg class="{{ svg_custom_class }}" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path d="M441.9 167.3l-19.8-19.8c-4.7-4.7-12.3-4.7-17 0L224 328.2 42.9 147.5c-4.7-4.7-12.3-4.7-17 0L6.1 167.3c-4.7 4.7-4.7 12.3 0 17l209.4 209.4c4.7 4.7 12.3 4.7 17 0l209.4-209.4c4.7-4.7 4.7-12.3 0-17z"/></svg>
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. Adicionamos o seguinte SASS de cores em style-colors.scss.tpl (ou na stylesheet do seu layout que possui as cores e fontes da loja). Lembre-se de que as variáveis de cores e fontes podem variar em relação ao seu layout:
{# /* // Mixins */ #} {# This mixin adds browser prefixes to a CSS property #} @mixin prefix($property, $value, $prefixes: ()) { @each $prefix in $prefixes { #{'-' + $prefix + '-' + $property}: $value; } #{$property}: $value; } {# /* // Links */ #} a { color: $main-foreground; fill: $main-foreground; @include prefix(transition, all 0.4s ease, webkit ms moz o); &:hover, &:focus{ color: rgba($main-foreground, .5); fill: rgba($main-foreground, .5); } } .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); } } {# /* // Modals */ #} .modal{ color: $main-foreground; background-color:$main-background; } {# /* // Forms */ #} input, textarea { font-family: $body-font; } .form-control { display: block; padding: 8px; width: 100%; font-size: 16px; /* Hack to avoid autozoom on IOS */ border: 0; border-bottom: 1px solid rgba($main-foreground, .5); -webkit-appearance: none; -moz-appearance: none; appearance: none; color: $main-foreground; background-color: $main-background; &:focus{ outline: 0; } &-inline{ display: inline; } } .form-control::-webkit-input-placeholder { color: $main-foreground; } .form-control:-moz-placeholder { color: $main-foreground; } .form-control::-moz-placeholder { color: $main-foreground; } .form-control:-ms-input-placeholder { color: $main-foreground; } .form-select{ display: block; padding: 10px 0; width: 100%; font-size: 16px; /* Hack to avoid autozoom on IOS */ border: 0; border-bottom: 1px solid rgba($main-foreground, .5); border-radius: 0; -webkit-appearance: none; -moz-appearance: none; appearance: none; color: $main-foreground; background-color: $main-background; @extend %body-font; &-icon{ background: $main-background; } }
2. Adicione os estilos no arquivo static/style-critical.tpl
Se em seu layout você usar um stylesheet para o CSS crítico, precisaremos do seguinte código dentro dele, do contrário, você pode unificar o CSS dos passos 2 e 3 em um único arquivo.
{# /* // Forms */ #} .form-group { position: relative; width: 100%; } .form-group .form-select-icon{ position: absolute; bottom: 12px; right: 0; pointer-events: none; }
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 agregar el siguiente código dentro de la misma, pero si no es el caso podés unificar el CSS de los pasos 2 y 3 en un solo archivo.
{# /* // Mixins */ #} {# This mixin adds browser prefixes to a CSS property #} @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 0; clear: both; } &-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%, -50%), webkit ms moz o); .modal-body{ min-height: 150px; max-height: 400px; overflow: auto; } } } &-top.modal-show, &-bottom.modal-show { top: 0; &.modal-centered-small{ top: 50%; } } &-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; 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; &.modal-zindex-top{ z-index: 20000; } } {# /* // Forms */ #} .form-group{ @extend %element-margin; .form-label{ float: left; width: 100%; margin-bottom: 10px; } .alert{ margin: 10px 0 0 0; } } .form-select { display: block; width: 100%; &:focus{ outline:0; } &::-ms-expand { display: none; } } {#/*============================================================================ #Media queries ==============================================================================*/ #} {# /* // Min width 768px */ #} @media (min-width: 768px) { {# /* //// Components */ #} {# /* Modals */ #} .modal{ &-centered{ height: 80%; width: 80%; left: 10%; margin: 5% auto; &-small{ left: 50%; width: 30%; height: auto; max-height: 80%; margin: 0; } } &-docked-md{ width: 500px; &-centered{ left: calc(50% - 250px); bottom: auto; height: auto; } } &-bottom-sheet { top: 100%; &.modal-show { top: 0; bottom: auto; } } &-docked-small{ width: 350px; } }
JS
⚠️ A partir do dia 30 de janeiro de 2023, a biblioteca jQuery será removida do código de nossas lojas, portanto, a função "$" não poderá ser utilizada.
JavaScript precisam ser adicionados no arquivo store.js.tpl (ou onde você tem suas funções JS). O código que precisamos é o seguinte:
{#/*============================================================================ #Shipping calculator ==============================================================================*/ #} {# /* // Lang select */ #} changeLang = function(element) { var selected_country_url = element.find("option").filter((el) => el.selected).attr("data-country-url"); location.href = selected_country_url; }; jQueryNuvem(document).on("click", ".js-save-shipping-country", function(e) { e.preventDefault(); {# Change shipping country #} lang_select_option = jQueryNuvem(this).closest(".js-modal-shipping-country"); changeLang(lang_select_option); jQueryNuvem(this).text('{{ "Aplicando..." | translate }}').addClass("disabled"); }); {#/*============================================================================ #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(); } });
Traduções
Nesta etapa, adicionamos os textos para as traduções no arquivo config/translations.txt
es "Aplicar" pt "Aplicar" en "Apply" es_mx "Aplicar" es "Aplicando..." pt "Aplicando..." en "Aplying..." es_mx "Aplicando..." es "No encontramos este código postal{1}" pt "Não conseguimos encontrar esse CEP{1}" en "We couldn't find this zipcode{1}" es_mx "No encontramos este código postal{1}" es " para " pt " para " en " for " es_mx " para " es ". ¿Está bien escrito?" pt ". Está bem escrito?" en ". Is it written right?" es_mx ". ¿Está bien escrito?" es ". Podés intentar con otro o" pt ". Pode tentar outro ou" en ". You can try another or" es_mx ". Puedes intentar con otro o" es "cambiar tu país de entrega" pt "alterar o país de entrega" en "change your shipping country" es_mx "cambiar tu país de entrega" es "País de entrega" pt "Pais de entrega" en "Shipping country" es_mx "País de entrega" es "País donde entregaremos tu compra" pt "País onde entregaremos seu pedido" en "Country where we will deliver your order" es_mx "País donde entregaremos tu compra"
Activación
Por último recordá que para tener más de un idioma en tu tienda debes hacerlo desde la parte de “Configuraciones/Idiomas y monedas”.