Los snipplets son la unidad mínima de HTML dentro de una plantilla y se encuentran dentro de la carpeta con su mismo nombre.
Representan componentes aislados que pueden ser incluidos en uno o varios lugares de la tienda como por ejemplo el ítem de producto en el listado:
Pueden ser elementos genéricos como un simple input para un formulário
O algo específico de una funcionalidad como el calculador de envíos
Podés agregar todos los snipplets que necesites en tu diseño usando el nombre que creas mejor, lo importante es entender que cuanto más aislado esté el componente, más fácil va a ser su mantenimiento sin afectar otros archivos.
En las plantillas existen solo 4 snipplets que no pueden cambiar de ubicación ni nombre dentro de los archivos por razones de backend (es decir que son usados desde el PHP de Tiendanube) :
- product_grid.tpl → Se usa para cargar más productos en las páginas de categoría cuando el usuário llega al footer
- shipping_options.tpl → Se usa para las opciones de envío
- cart-item-ajax.tpl → Es el item del producto agregado al carrito
- shipping_suboptions → es una carpeta que contiene solo un tpl que se usa cuando el medio de envío tiene más opciones, por ejemplo un desplegable para sucursales de OCA.
A continuación podrás ver en detalle algunos de los snipplets más importantes de una plantilla:
Grid
Dentro de la carpeta Grid tenemos todos los snipplets en relación a la grilla o listado de productos:
- filters.tpl
- categories.tpl
- sort-by.tpl
- item.tpl
- quick-shop.tpl
filters.tpl
En este snipplet encontrarás todo lo relacionado a los filtros de propiedades para los productos:
Separamos los filtros en 4 grupos:
- Filtros de colores principales, los cuales se muestran con un botón circular con el fondo del color.
- Filtros para colores secundarios, con los colores más específicos como “Salmón”. Estos se muestran con un botón normal cuyo texto será el nombre del color.
- Filtros para talles
- Otros filtros, para los más personalizados.
Usamos los filtros solamente para la página de categorías dentro del archivo category.tpl
{% set default_lang = current_language.lang %} {% set filter_colors = insta_colors|length > 0 %} {% set filter_more_colors = other_colors|length > 0 %} {% set filter_sizes = size_properties_values|length > 0 %} {% set filter_other = variants_properties|length > 0 %} {% if default_lang == 'pt' %} {% set color_name = 'Cor' %} {% set size_name = 'Tamanho' %} {% endif %} {% if default_lang == 'es' %} {% set color_name = 'Color' %} {% set size_name = 'Talle' %} {% endif %} {% if default_lang == 'en' %} {% set color_name = 'Color' %} {% set size_name = 'Size' %} {% endif %} <div id="filters" data-store="filters-nav"> {% if filter_colors or filter_more_colors %} <div class="mb-4" data-store="filters-group"> <h6 class="mb-3">{{ 'Color' | translate }}</h6> {% for name,color in insta_colors %} <label class="checkbox-container font-weight-bold {% if mobile %}mb-3{% else %}mb-2{% endif %}" onclick="LS.urlAddParam('{{ color_name|replace("'","%27") }}', '{{ name|replace("'","%27") }}');"> <span class="checkbox"> <input type="checkbox" autocomplete="off"> <span class="checkbox-icon"></span> <span class="checkbox-text">{{ name }}</span> <span class="checkbox-color" style="background-color: {{ color[name] }};" title="{{ name }}"></span> </span> </label> {% endfor %} {% for color in other_colors %} <label class="checkbox-container font-weight-bold {% if mobile %}mb-3{% else %}mb-2{% endif %}" onclick="LS.urlAddParam('{{ color_name|replace("'","%27") }}', '{{ color|replace("'","%27") }}');"> <span class="checkbox"> <input type="checkbox" autocomplete="off"> <span class="checkbox-icon"></span> <span class="checkbox-text">{{ color }}</span> </span> </label> {% endfor %} </div> {% endif %} {% if filter_sizes %} <div class="mb-4" data-store="filters-group"> <h6 class="mb-3">{{ 'Talle' | translate }}</h6> {% for size in size_properties_values %} <label class="checkbox-container font-weight-bold {% if mobile %}mb-3{% else %}mb-2{% endif %}" onclick="LS.urlAddParam('{{ size_name|replace("'","%27") }}', '{{ size|replace("'","%27") }}');"> <span class="checkbox"> <input type="checkbox" autocomplete="off"> <span class="checkbox-icon"></span> <span class="checkbox-text">{{ size }}</span> </span> </label> {% endfor %} </div> {% endif %} {% for variants_property in variants_properties %} {% if filter_other %} <div class="mb-4" data-store="filters-group"> <h6 class="mb-3">{{ variants_property }}</h6> {% for value in variants_properties_values[variants_property] %} <label class="checkbox-container font-weight-bold {% if mobile %}mb-3{% else %}mb-2{% endif %}" onclick="LS.urlAddParam('{{ variants_property|replace("'","%27") }}', '{{ value|replace("'","%27") }}');"> <span class="checkbox"> <input type="checkbox" autocomplete="off"> <span class="checkbox-icon"></span> <span class="checkbox-text">{{value}}</span> </span> </label> {% endfor %} </div> {% endif %} {% endfor %} </div>
Si no tenés esta funcionalidad en tu diseño y querés implementarla te sugerimos este tutorial.
categories.tpl
En este snipplet encontrarás todo lo relacionado a los filtros por categorías para productos:
{% if filter_categories %} <div class="filters-container mb-5"> <h6 class="mb-3">{{ "Categorías" | translate }}</h6> <ul class="list-unstyled"> {% for category in filter_categories %} <li data-item="{{ loop.index }}" class="mb-3"> <a href="{{ category.url }}" title="{{ category.name }}" class="text-primary"> {{ category.name }} </a> </li> {% if loop.index == 8 and filter_categories | length > 8 %} <div class="js-accordion-container" style="display: none;"> {% endif %} {% if loop.last and filter_categories | length > 8 %} </div> <a href="#" class="js-accordion-toggle btn-link d-inline-block mt-1 pl-0"> <span class="js-accordion-toggle-inactive"> {{ 'Ver más' | translate }} </span> <span class="js-accordion-toggle-active" style="display: none;"> {{ 'Ver menos' | translate }} </span> </a> {% endif %} {% endfor %} </ul> </div> {% endif %}
Usamos este archivo dentro del template category.tpl
sort-by.tpl
El sort-by.tpl sirve para ordenar los productos con los siguientes criterios:
- Precio: mayor a menor y menor a mayor.
- Alfabético: A -Z y Z - A.
- Tiempo: más nuevo al más viejo y más viejo al más nuevo.
- Más vendidos.
Usamos el sort by solamente para la página de categorías dentro del archivo category.tpl
{% set sort_text = { 'user': 'Destacado', 'price-ascending': 'Precio: Menor a Mayor', 'price-descending': 'Precio: Mayor a Menor', 'alpha-ascending': 'A - Z', 'alpha-descending': 'Z - A', 'created-ascending': 'Más Viejo al más Nuevo', 'created-descending': 'Más Nuevo al más Viejo', 'best-selling': 'Más Vendidos', } %} {% embed "snipplets/forms/form-select.tpl" with{select_label: false, select_custom_class: 'js-sort-by', select_group_custom_class: 'mb-0', select_aria_label: 'Ordenar por:' | translate } %} {% block select_options %} {% for sort_method in sort_methods %} {# This is done so we only show the user sorting method when the user chooses it #} {% if sort_method != 'user' or category.sort_method == 'user' %} <option value="{{ sort_method }}" {% if sort_by == sort_method %}selected{% endif %}>{{ sort_text[sort_method] | t }}</option> {% endif %} {% endfor %} {% endblock select_options%} {% endembed %}
Si no tenés esta funcionalidad en tu diseño y querés implementarla te sugerimos este tutorial.
item.tpl
El item es el snipplet que representa al producto dentro del listado:
A través de este item mostramos información importante sobre el producto como:
- Imagen
- Nombre
- Precio
- Cuotas
En algunos casos inclímos la funcionalidad de “Quickshop” o “Popup de compras rápida” así como una imágen secundaria cuando se hace hover sobre el item con el mouse en su versión de escritorio.
En este snipplet incluimos la implementación para lazy load con Lazysizes.
Incluimos el item en las siguientes partes del theme:
- Productos destacados de la página de inicio.
- Productos relacionados en el detalle del producto.
- Listado en la página de categorías.
- Listado en los resultados de búsqueda.
{# /*============================================================================ #Item grid ==============================================================================*/ #Properties #Slide Item #} {% set slide_item = slide_item | default(false) %} {% set columns = settings.grid_columns %} {% set has_color_variant = false %} {% if settings.product_color_variants %} {% for variation in product.variations if variation.name in ['Color', 'Cor'] and variation.options | length > 1 %} {% set has_color_variant = true %} {% endfor %} {% endif %} <div class="js-item-product {% if slide_item %}js-item-slide swiper-slide{% else %}col{% if columns == 2 %}-6 col-md-3{% else %}-12 col-md-4{% endif %}{% endif %} item item-product{% if not product.display_price %} no-price{% endif %}" data-product-type="list" data-product-id="{{ product.id }}" data-store="product-item-{{ product.id }}"> {% if settings.quick_shop or settings.product_color_variants %} <div class="js-product-container js-quickshop-container {% if product.variations %}js-quickshop-has-variants{% endif %}" data-variants="{{ product.variants_object | json_encode }}" data-quickshop-id="quick{{ product.id }}{% if slide_item and section_name %}-{{ section_name }}{% endif %}"> {% endif %} {% set product_url_with_selected_variant = has_filters ? ( product.url | add_param('variant', product.selected_or_first_available_variant.id)) : product.url %} {% if has_color_variant %} {# Item image will be the first avaiable variant #} {% set item_img_spacing = product.featured_variant_image.dimensions['height'] / product.featured_variant_image.dimensions['width'] * 100 %} {% set item_img_srcset = product.featured_variant_image %} {% set item_img_alt = product.featured_variant_image.alt %} {% else %} {# Item image will be the first image regardless the variant #} {% set item_img_spacing = product.featured_image.dimensions['height'] / product.featured_image.dimensions['width'] * 100 %} {% set item_img_srcset = product.featured_image %} {% set item_img_alt = product.featured_image.alt %} {% endif %} <div class="js-item-image item-image mb-2"> <div style="padding-bottom: {{ item_img_spacing }}%;" class="p-relative" data-store="product-item-image-{{ product.id }}"> <a href="{{ product_url_with_selected_variant }}" title="{{ product.name }}"> <img alt="{{ item_img_alt }}" data-sizes="auto" data-expand="-10" src="{{ 'images/empty-placeholder.png' | static_url }}" data-srcset="{{ item_img_srcset | product_image_url('small')}} 240w, {{ item_img_srcset | product_image_url('medium')}} 320w, {{ item_img_srcset | product_image_url('large')}} 480w" class="lazyautosizes lazyload img-absolute img-absolute-centered fade-in" /> <div class="placeholder-fade"></div> </a> {% if settings.product_color_variants %} {% include 'snipplets/labels.tpl' with {color: true} %} {% include 'snipplets/grid/item-colors.tpl' %} {% else %} {% include 'snipplets/labels.tpl' %} {% endif %} </div> </div> {% if (settings.quick_shop or settings.product_color_variants) and product.variations %} {# Hidden product form to update item image and variants: Also this is used for quickshop popup #} <div class="js-item-variants hidden"> <form id="product_form" class="js-product-form" method="post" action="{{ store.cart_url }}"> <input type="hidden" name="add_to_cart" value="{{product.id}}" /> {% if product.variations %} {% include "snipplets/product/product-variants.tpl" with {quickshop: true} %} {% endif %} {% if product.available and product.display_price and settings.quick_shop %} {% include "snipplets/product/product-quantity.tpl" with {quickshop: true} %} {% endif %} {% 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"} %} {# Add to cart CTA #} <input type="submit" class="js-addtocart js-prod-submit-form btn btn-primary btn-block {{ state }}" value="{{ texts[state] | translate }}" {% if state == 'nostock' %}disabled{% endif %} /> {# Fake add to cart CTA visible during add to cart event #} {% include 'snipplets/placeholders/button-placeholder.tpl' with {custom_class: "btn-block"} %} </form> </div> {% endif %} <div class="item-description" data-store="product-item-info-{{ product.id }}"> <a href="{{ product_url_with_selected_variant }}" title="{{ product.name }}" class="item-link"> <div class="js-item-name item-name mb-1" data-store="product-item-name-{{ product.id }}">{{ product.name }}</div> {% if product.display_price %} <div class="item-price-container mb-1" data-store="product-item-price-{{ product.id }}"> <span class="js-compare-price-display price-compare" {% if not product.compare_at_price or not product.display_price %}style="display:none;"{% else %}style="display:inline-block;"{% endif %}> {{ product.compare_at_price | money }} </span> <span class="js-price-display item-price"> {{ product.price | money }} </span> </div> {% endif %} </a> </div> {% include 'snipplets/payments/installments.tpl' %} {% if settings.quick_shop and product.available and product.display_price %} {# Trigger quickshop actions #} <div class="item-actions mt-2"> {% 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 }}" >{{ 'Agregar al carrito' | translate }}</a> {% else %} {# If not variants add directly to cart #} <form id="product_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" value="{{ texts[state] | translate }}" {% if state == 'nostock' %}disabled{% endif %} /> {# 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"} %} </form> {% endif %} </div> {% endif %} {# Structured data to provide information for Google about the product content #} {% include 'snipplets/structured_data/item-structured-data.tpl' %} {% if settings.quick_shop or settings.product_color_variants %} </div> {% endif %} </div>
quick-shop.tpl
El item es el snipplet que representa al popup de compra rápida dentro del listado:
{% if settings.quick_shop %} {% embed "snipplets/modal.tpl" with{modal_id: 'quickshop-modal', modal_class: 'quickshop text-center', modal_position: 'bottom modal-bottom-sheet', modal_transition: 'slide', modal_header: true, modal_footer: true, modal_width: 'centered modal-docked-md modal-docked-md-centered', modal_mobile_full_screen: 'true' } %} {% block modal_body %} <div class="js-item-product" data-product-id=""> <div class="js-product-container js-quickshop-container js-quickshop-modal js-quickshop-modal-shell" data-variants="" data-quickshop-id=""> <div class="js-item-variants"> <div class="js-item-name h1 mb-1" data-store="product-item-name-{{ product.id }}"></div> <div class="item-price-container mb-4" data-store="product-item-price-{{ product.id }}"> <span class="js-compare-price-display h4 price-compare"></span> <span class="js-price-display h4"></span> </div> {# Image is hidden but present so it can be used on cart notifiaction #} <img srcset="" class="js-quickshop-img hidden"/> <div id="quickshop-form"></div> </div> </div> </div> {% endblock %} {% endembed %} {% endif %}
Si no tenés esta funcionalidad en tu diseño y querés implementarla te sugerimos este tutorial.
product_grid.tpl
Aunque no incluyamos a este archivo dentro de la carpeta Grid (por razones de backend), es importante mencionarlo.
Este tpl incluye al item.tpl para listar los items en las páginas de categorías y resultados de búsqueda, pero su rol principal es agregar más productos a medida que el usuario hace scroll en la pantalla. Es por esta funcionalidad que no debes modificar su nombre o ubicación dentro las carpetas del theme.
Incluimos este snipplet en:
- La página de categorías
- La página de resultados de búsqueda
{% if products and pages.is_last %} <div class="last-page" style="display:none;"></div> {% endif %} {% for product in products %} {% include 'snipplets/grid/item.tpl' %} {% endfor %}
Forms
Dentro de la carpeta Forms ubicamos todos los snipplets en relación los formulários:
form-input.tpl
Para cada input en un formulario usamos este snipplet, ya sea en la página de registro de un usuario o en el calculador de envíos.
Dentro del form-input.tpl podés elegir qué propiedades usar a la hora de incluirlo (usando un embed). Entre estas podés:
- Usar clases personalizadas para el input
- Mostrar o no un label arriba del input
- Agregar contenido entre el label y el input
- Definir si el input será un textarea o un input normal
- Elegir que tipo de input será: number, text, password, etc
Para ver en detalles todas las propiedades de este snipplet podés guiarte mirando los comentarios en el código del mismo.
{# /*============================================================================ #Form input ==============================================================================*/ #Properties #Group //input_group_custom_class for custom CSS classes #Label // input_label_id for ID // input_for for label for // input_label_custom_class for custom CSS classes // input_label_text for label text #Prepend // input_prepend_content to add content before input #Container (Only if has prepend or append) // form_control_container_custom_class for container custom class. E.g: col #Input // Can be text_area or input // input_type to define type (text, tel, number or passowrd) // input_id for id // input_name for name // input_value for val // input_placeholder for placeholder // input_custom_class for custom CSS classes // input_rows for textarea rows // input_data_attr for data attributes // input_data_val for input_data_attr value // input_aria_label for aria-label attribute #Append // input_append_content to add content after input #Alerts // input_form_alert to insert alerts #} <div class="form-group {{ input_group_custom_class }}"> {% if input_label_text %} <label {% if input_label_id %}id="{{ input_label_id }}"{% endif %} class="form-label {{ input_label_custom_class }}" {% if input_for %}for="{{ input_name }}"{% endif %}>{{ input_label_text }}</label> {% endif %} {% block input_prepend_content %} {% endblock input_prepend_content %} {% if input_append_content or input_prepend_content %} <div class="form-control-container {{ form_control_container_custom_class }}"> {% endif %} {% if text_area %} <textarea {% if input_id %}id="{{ input_id }}"{% endif %} class="form-control form-control-area {{ input_custom_class }} {% if input_append_content %}form-control-inline{% endif %}" autocorrect="off" autocapitalize="off" {% if input_name %}name="{{ input_name }}"{% endif %} {% if input_value %}value="{{ input_value }}"{% endif %} {% if input_rows %}rows="{{ input_rows }}"{% endif %} {% if input_placeholder %}placeholder="{{ input_placeholder }}"{% endif %} {% if input_data_attr %}data-{{ input_data_attr }}="{{ input_data_val }}"{% endif %}></textarea> {% else %} <input type="{% if type_text %}text{% elseif type_number %}number{% elseif type_tel %}tel{% elseif type_password %}password{% elseif type_hidden %}hidden{% endif %}" {% if input_id %}id="{{ input_id }}"{% endif %} class="form-control {{ input_custom_class }} {% if input_append_content %}form-control-inline{% endif %}" autocorrect="off" autocapitalize="off" {% if type_password %}autocomplete="off"{% endif %} {% if input_name %}name="{{ input_name }}"{% endif %} {% if input_value %}value="{{ input_value }}"{% endif %} {% if input_min %}min="{{ input_min }}"{% endif %} {% if input_placeholder %}placeholder="{{ input_placeholder }}"{% endif %} {% if input_data_attr %}data-{{ input_data_attr }}="{{ input_data_val }}"{% endif %} {% if input_aria_label %}aria-label="{{ input_aria_label }}"{% endif %}/> {% endif %} {% if input_append_content or input_prepend_content %} </div> {% endif %} {% block input_append_content %} {% endblock input_append_content %} {% if input_help %} <div class="mt-4 text-center"> <a href="{{ input_help_link }}" class="btn-link {{ input_link_class }}">{% block input_help_text %}{% endblock input_help_text %}</a> </div> {% endif %} {% block input_form_alert %} {% endblock input_form_alert %} </div>
Incluimos este snipplet en los snipplets newsletter.tpl, product-quantity.tpl, shipping-calculator.tpl y en los templates password.tpl, contact.tpl, reset.tpl, register.tpl, login.tpl, newpass.tpl, info.tpl y address.tpl
form-select.tpl
Así como usamos el form-input.tpl para los inputs, tenemos el form-select.tpl para los select.
Con este snipplet también podés definir si mostrar o no un label ,o también agregar clases personalizadas, pero lo que lo hace diferente es la posibilidad de agregar opciones dentro del desplegable usando {% block select_options %}. Dentro de este tag de Twig es posible agregar cualquier opción.
Para más información sobre cómo usar un {% block %} te sugerimos que leas este artículo sobre Twig.
{# /*============================================================================ #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>
Incluimos este snipplet más que nada en el sort-by y en las variantes de productos.
form.tpl
Usamos el form.tpl para incluir un formulario clásico con el método “post” que por defecto muestra un botón para enviar.
Con este snipplet podés agregar los campos que necesites dentro del {% block form_body %} y elegir si mostrar o no un botón de “cancelar” usando la condición {% if cancel %}.
Para más información sobre cómo usar un {% block %} te sugerimos que leas este artículo sobre Twig.
{# /*============================================================================ #Form ==============================================================================*/ #Properties // id // action // custom_class for custom CSS classes // Cancel if cancel button is needed #} <form id="{{ form_id }}" action="{{ form_action }}" method="post" class="form {{ form_custom_class }}"> {% block form_body %} {% endblock%} {% if cancel %} <a href="#" class="{{ cancel_custom_class }} btn btn-default">{{ cancel_text }}</a> {% endif %} <input class="btn btn-primary {{ submit_custom_class }}" type="submit" value="{{ submit_text }}" name="{{ submit_name }}" {{ submit_prop }}/> </form>
Incluimos este snipplet en todos los formularios clásicos del theme como por ejemplo:
- Login
- Registro
- Actualizar o crear datos de envío
- Actualizar o crear datos de usuario
- Contacto
Metas
Los metas son snipplets que usamos información a la hora de compartir un producto en Facebook y Twitter.
facebook-category-og.tpl
Almacena la información de una categoría al ser compartida como el nombre, una url y una imágen destacada.
{{ category.url | og('url') }} {{ category.name | og('title') }} {{ category.description | og('description') }} {{ ('http:' ~ category.featured_image | product_image_url('huge')) | og('image') }} {{ ('https:' ~ category.featured_image | product_image_url('huge')) | og('image:secure_url') }}
general-og.tpl
Contiene la información general de una tienda como el nombre, la imagen OG y una url.
{{ store.name | og('site_name') }} {% if template == 'home' %} {{ 'website' | og('type') }} {{ page_title | og('title') }} {{ page_description | og('description') }} {{ store.url_with_protocol | og('url') }} {% set og_image_src = store.og_image_src %} {% if og_image_src %} {{ ('http:' ~ og_image_src) | og('image') }} {{ ('https:' ~ og_image_src) | og('image:secure_url') }} {% endif %} {% endif %}
facebook-product-og.tpl
Tiene la información general de un producto como el nombre, el precio, una descripción y una url.
{{ product.social_url | og('url') }} {{ product.name | og('title') }} {{ page_description | og('description') }} {{ "#{fb_app.namespace}:product" | og('type') }} {{ ('http:' ~ product.featured_image | product_image_url('huge')) | og('image') }} {{ ('https:' ~ product.featured_image | product_image_url('huge')) | og('image:secure_url') }} {% if product.display_price %} {{ (product.price / 100) | og_fb_app('price') }} {% endif %} {% if product.stock_control %} {{ product.stock | og_fb_app('stock') }} {% endif %}
twitter-product-og.tpl
Muestra la información general de un producto como el nombre, el precio, una descripción y una url.
<meta name="twitter:card" content="product"> <meta name="twitter:url" content="{{ product.social_url }}"> <meta name="twitter:image:src" content="{{ ('http:' ~ product.featured_image | product_image_url('huge')) }}"> {% if store.twitter_user %} <meta name="twitter:site" content="{{ store.twitter_user }}"> {% endif %} <meta name="twitter:title" content="{{ product.name }}"> <meta name="twitter:data1" content="{{ product.display_price ? product.price | money_long : 'Consultar' | translate }}"> <meta name="twitter:label1" content="{{ 'Precio' | translate | upper }}"> <meta name="twitter:data2" content="{{ product.stock_control ? (product.stock > 0 ? product.stock : 'No' | translate) : 'Si' | translate }}"> <meta name="twitter:label2" content="{{ 'Stock' | translate | upper }}">
Incluimos todos los snipplets de Meta en el layout.tpl
Structured data
Los datos estructurados son snipplets que usamos para proveer el contenido de la tienda a Google. Además de los datos generales de la página, se relaciona con la información de los productos como nombre, precio, url, stock, etc.
webpage-structured-data.tpl
Se incluye antes del cierre de la etiqueta <head>, en layout.tpl y password.tpl, y brinda la siguiente información de la página:
Titulo = {{ page_title | replace('"', '\"') }}
Descripción = {{ page_description | replace('"', '\"') }}
Detalle del breadcrumb = {{ crumb.name | replace('"', '\"') }}
Además, si estás en el detalle de un producto se suma información relacionada a este:
Nombre = {{ product.name | replace('"', '\"') }}
Imagen = {{ 'https:' ~ product.featured_image | product_image_url('large') }}
Descripción = {{ page_description | replace('"', '\"') }}
SKU = {{ product.sku }}
Marca = {{ product.brand | replace('"', '\"') }}
Peso = {{ product.weight }}
Oferta
URL = {{ product.url }}
Moneda = {{ product.currency }}
Precio = {{ product.price / 100 }}
Stock (disponibilidad y cantidad) = {{ product.stock }}
Vendedor = {{ store.name | replace('"', '\"') }}
<script type="application/ld+json"> { "@context": "https://schema.org/", "@type": "WebPage", "name": "{{ page_title | replace('"', '\"') }}", {% if page_description %} "description": "{{ page_description | replace('"', '\"') }}", {% endif %} "breadcrumb": { "@type": "BreadcrumbList", "itemListElement": [ { "@type": "ListItem", "position": 1, "name": "{{ 'Inicio' | translate }}", "item": "{{ store.url }}" }{% if template != 'home' %},{% endif %} {% if template == 'page' %} { "@type": "ListItem", "position": 2, "name": "{{ page.name | replace('"', '\"') }}", "item": "{{ page.url }}" } {% elseif template == 'cart' %} { "@type": "ListItem", "position": 2, "name": "{{ 'Carrito de compras' | translate }}", "item": "{{ store.url }}{{ store.cart_url }}" } {% elseif template == 'search' %} { "@type": "ListItem", "position": 2, "name": "{{ 'Resultados de búsqueda' | translate }}", "item": "{{ store.url }}{{ store.search_url }}" } {% elseif template == 'account.order' %} { "@type": "ListItem", "position": 2, "name": "{{ 'Orden {1}' | translate(order.number) }}", "item": "{{ store.url }}{{ store.customer_order_url(order) }}" } {% else %} {% for crumb in breadcrumbs %} { "@type": "ListItem", "position": {{ loop.index + 1 }}, "name": "{{ crumb.name | replace('"', '\"') }}", "item": "{{ store.url }}{{ crumb.url }}" }{% if not crumb.last %},{% endif %} {% endfor %} {% endif %} ] }{% if template == 'product' %}, "mainEntity": { "@type": "Product", "@id": "{{ product.canonical_url }}", "name": "{{ product.name | replace('"', '\"') }}", "image": "{{ 'https:' ~ product.featured_image | product_image_url('large') }}", "description": "{{ page_description | replace('"', '\"') }}", {% if product.sku %} "sku": "{{ product.sku }}", {% endif %} {% if product.brand %} "brand": { "@type": "Thing", "name": "{{ product.brand | replace('"', '\"') }}" }, {% endif %} {% if product.weight %} "weight": { "@type": "QuantitativeValue", "unitCode": "{{ product.weight_unit | iso_to_uncefact }}", "value": "{{ product.weight }}" }, {% endif %} "offers": { "@type": "Offer", "url": "{{ product.url }}", "priceCurrency": "{{ product.currency }}", "price": "{{ product.price / 100 }}", {% if product.stock_control %} "availability": "http://schema.org/{{ product.stock ? 'InStock' : 'OutOfStock' }}", "inventoryLevel": { "@type": "QuantitativeValue", "value": "{{ product.stock }}" }, {% endif %} "seller": { "@type": "Organization", "name": "{{ store.name | replace('"', '\"') }}" } } } {% endif %} } </script>
item-structured-data.tpl
Se incluye antes del cierre de la última etiqueta <div>, en item.tpl de la carpeta snipplets/grid, dependiendo cuan actualizado esté tu código, puede que este cambio tengas que aplicarlo en el snipplet single_product.tpl, y brinda la siguiente información:
Nombre = {{ product.name | replace('"', '\"') }}
Imagen = {{ 'https:' ~ product.featured_image | product_image_url('large') }}
Descripción = {{ page_description | replace('"', '\"') }}
SKU = {{ product.sku }}
Marca = {{ product.brand | replace('"', '\"') }}
Peso = {{ product.weight }}
Oferta
URL = {{ product.url }}
Moneda = {{ product.currency }}
Precio = {{ product.price / 100 }}
Stock (disponibilidad y cantidad) = {{ product.stock }}
Vendedor = {{ store.name | replace('"', '\"') }}
<script type="application/ld+json"> { "@context": "https://schema.org/", "@type": "Product", "mainEntityOfPage": { "@type": "WebPage", "@id": "{{ product.canonical_url }}" }, "name": "{{ product.name | replace('"', '\"') }}", "image": "{{ 'https:' ~ product.featured_image | product_image_url('large') }}", "description": "{{ product.seo_description | replace('"', '\"') }}", {% if product.sku %} "sku": "{{ product.sku }}", {% endif %} {% if product.brand %} "brand": { "@type": "Thing", "name": "{{ product.brand | replace('"', '\"') }}" }, {% endif %} {% if product.weight %} "weight": { "@type": "QuantitativeValue", "unitCode": "{{ product.weight_unit | iso_to_uncefact }}", "value": "{{ product.weight }}" }, {% endif %} "offers": { "@type": "Offer", "url": "{{ product.url }}", "priceCurrency": "{{ product.currency }}", "price": "{{ product.price / 100 }}", {% if product.stock_control %} "availability": "http://schema.org/{{ product.stock ? 'InStock' : 'OutOfStock' }}", "inventoryLevel": { "@type": "QuantitativeValue", "value": "{{ product.stock }}" }, {% endif %} "seller": { "@type": "Organization", "name": "{{ store.name | replace('"', '\"') }}" } } } </script>
SVG
En la carpeta SVG tenemos todos los snipplets de iconos de la tienda. Estos son archivos SVG que al terminar con “.tpl” nos permiten usar twig para reemplazar la propiedad svg_custom_class por cualquier clase que necesitemos a la hora de incluirlo.
Un ejemplo de un snipplet de SVG es el ícono de la lupa para buscar:
<svg class="{{ svg_custom_class }}" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M508.5 468.9L387.1 347.5c-2.3-2.3-5.3-3.5-8.5-3.5h-13.2c31.5-36.5 50.6-84 50.6-136C416 93.1 322.9 0 208 0S0 93.1 0 208s93.1 208 208 208c52 0 99.5-19.1 136-50.6v13.2c0 3.2 1.3 6.2 3.5 8.5l121.4 121.4c4.7 4.7 12.3 4.7 17 0l22.6-22.6c4.7-4.7 4.7-12.3 0-17zM208 368c-88.4 0-160-71.6-160-160S119.6 48 208 48s160 71.6 160 160-71.6 160-160 160z"/></svg>
Que luego incluimos dentro del snipplet header-utilities.tpl
{% include "snipplets/svg/shopping-bag.tpl" with {svg_custom_class: "icon-inline icon-w-14 svg-icon-text"} %}
Shipping
En esta carpeta ubicamos todos los snipplets en relación a uno de los puntos más críticos del flujo de compra, medios de envío. Recomendamos modificar lo menos posible estos archivos debido a su complejidad e importancia dentro del theme.
Además de los archivos de esta carpeta, por razones de backend tenemos por fuera de la carpeta Shipping:
- La carpeta shipping_suboptions
- El snipplet shipping_options.tpl
branches.tpl
En este tpl listamos los locales físicos, los cuales están visibles a modo informativo en el detalle del producto y luego se pueden seleccionar en el carrito.Estos locales podés configurarlos en el Administrador nube desde la sección Configuraciones/Envíos y Locales.
<div class="js-toggle-branches w-100"> <span class="form-row"> <div class="col-auto"> {% include "snipplets/svg/store.tpl" with {svg_custom_class: "icon-inline icon-lg link-module-icon svg-icon-text"} %} </div> <div class="col-6"> <div {% if store.branches|length > 1 %}class="mb-1"{% endif %}> {% if store.branches|length > 1 %} {{ 'Nuestros locales' | translate }} {% else %} {{ 'Nuestro local' | translate }} {% endif %} </div> {% if store.branches|length > 1 %} <div class="btn-link float-left"> <span class="js-see-branches"> {{ 'Ver opciones' | translate }} </span> {% include "snipplets/svg/chevron-down.tpl" with {svg_custom_class: "js-see-branches icon-inline ml-1"} %} <span class="js-hide-branches" style="display: none;"> {{ 'Ocultar opciones' | translate }} {% include "snipplets/svg/chevron-up.tpl" with {svg_custom_class: "icon-inline ml-1"} %} </span> </div> {% endif %} </div> </span> </div> {# Store branches #} {% if not product_detail %} <ul class="js-store-branches-container list-unstyled radio-button-container mt-4" {% if store.branches|length > 1 %}style="display: none;"{% endif %}> {# Selectable branches #} {% for branch in store.branches %} <li class="radio-button-item"> <label class="js-shipping-radio js-branch-radio radio-button" data-loop="branch-radio-{{loop.index}}"> <input class="js-branch-method {% if cart.shipping_data.code == branch.code %} js-selected-shipping-method {% endif %} shipping-method" data-price="0" {% if cart.shipping_data.code == branch.code %}checked{% endif %} type="radio" value="{{branch.code}}" data-name="{{ branch.name }} - {{ branch.extra }}" data-code="{{branch.code}}" data-cost="{{ 'Gratis' | translate }}" name="option" style="display:none"> <span class="shipping-option row-fluid radio-button-content"> <span class="radio-button-icons"> <span class="radio-button-icon unchecked"></span> <span class="radio-button-icon checked"></span> <span class="radio-button-icon checked checked-invert"></span> </span> <span class="radio-button-label"> <h6 class="text-primary mb-1 d-inline-block">{{ 'Gratis' | translate }}</h6> <span class="radio-button-text"> {{ branch.name }} - {{ branch.extra }} </span> </span> </span> </label> </li> {% endfor %} </ul> {% else %} <ul class="js-store-branches-container list-unstyled list mt-4" {% if store.branches|length > 1 %}style="display: none;"{% endif %}> {% for branch in store.branches %} <li class="list-item"> <span class="list-item-content"> <h6 class="text-primary mb-1">{{ 'Gratis' | translate }}</h6> <div>{{ branch.name }} - {{ branch.extra }}</div> </span> </li> {% endfor %} </ul> {% endif %}
Incluimos este tpl en el snipplet product-form.tpl para el detalle del producto y cart-totals.tpl para el carrito de compras.
shipping-calculator.tpl
El snipplet shipping-calculator.tpl representa al calculador de envíos tanto en el detalle del producto como en el carrito.
<div class="{% if product_detail %}product-shipping-calculator{% endif %} mb-2 w-100" data-store="shipping-calculator"> <div class="js-shipping-calculator-head shipping-calculator-head position-relative transition-soft {% if cart.shipping_zipcode %}with-zip{% else %}with-form{% endif %}"> <div class="js-shipping-calculator-with-zipcode {% if cart.shipping_zipcode %}js-cart-saved-zipcode transition-up-active{% endif %} mt-3 mb-4 w-100 transition-up position-absolute"> <div class="container p-0"> <div class="row align-items-center"> <span class="col pr-0"> <span class="font-small align-sub"> <span>{{ "Entregas para el CP:" | translate }}</span> <strong class="js-shipping-calculator-current-zip">{{ cart.shipping_zipcode }}</strong> </span> </span> <div class="col-auto pl-0"> <a class="js-shipping-calculator-change-zipcode btn btn-secondary btn-small float-right py-1 px-2 px-sm-3" href="#">{{ "Cambiar CP" | translate }}</a> </div> </div> </div> </div> <div class="js-shipping-calculator-form shipping-calculator-form transition-up position-absolute"> {# Shipping calcualtor input #} {% embed "snipplets/forms/form-input.tpl" with{type_tel: true, input_value: cart.shipping_zipcode, input_name: 'zipcode', input_custom_class: 'js-shipping-input', input_placeholder: "Tu código postal" | translate, input_aria_label: 'Tu código postal' | translate, input_label: false, input_append_content: true, input_group_custom_class: 'form-row align-items-center mb-3', form_control_container_custom_class: 'col-5'} %} {% block input_prepend_content %} <div class="col-12 mb-2"> {% include "snipplets/svg/truck.tpl" with {svg_custom_class: "icon-inline icon-w-18 icon-lg svg-icon-text mr-2"} %} {# Free shipping achieved label #} <span class="js-free-shipping-message font-weight-bold text-accent" {% if not cart.free_shipping.cart_has_free_shipping %}style="display: none;"{% endif %}> {{ "¡Genial! Tenés envío gratis" | translate }} </span> {# Free shipping with min price label #} <span class="js-shipping-calculator-label font-weight-bold" {% if cart.free_shipping.cart_has_free_shipping or not cart.free_shipping.min_price_free_shipping.min_price %}style="display: none;"{% endif %}> {{ "<strong class='text-accent'>Envío gratis</strong> superando los" | translate }} <span>{{ cart.free_shipping.min_price_free_shipping.min_price }}</span> </span> {# Shipping default label #} <span class="js-shipping-calculator-label-default" {% if cart.free_shipping.cart_has_free_shipping or cart.free_shipping.min_price_free_shipping.min_price %}style="display: none;"{% endif %}> {# Regular shipping calculator label #} {{ 'Medios de envío' | translate }} </span> </div> {% endblock input_prepend_content %} {% block input_form_alert %} {% if store.country == 'BR' or 'AR' or 'MX' %} {% set zipcode_help_ar = 'https://www.correoargentino.com.ar/formularios/cpa' %} {% set zipcode_help_br = 'http://www.buscacep.correios.com.br/sistemas/buscacep/' %} {% set zipcode_help_mx = 'https://www.correosdemexico.gob.mx/datosabiertos/gobmx/gobmx_Descarga.html' %} <div class="col-12"> <a class="font-small text-primary mt-3 mb-2 d-block" href="{% if store.country == 'AR' %}{{ zipcode_help_ar }}{% elseif store.country == 'BR' %}{{ zipcode_help_br }}{% elseif store.country == 'MX' %}{{ zipcode_help_mx }}{% endif %}" target="_blank">{{ "No sé mi código postal" | translate }}</a> </div> {% endif %} <div class="col-12"> <div class="js-ship-calculator-error invalid-zipcode alert alert-danger" style="display: none;"> {# Specific error message considering if store has multiple languages #} {% for language in languages %} {% if language.active %} {% if languages | length > 1 %} {% set wrong_zipcode_wording = ' para ' | translate ~ language.country_name ~ '. Podés intentar con otro o' | translate %} {% else %} {% set wrong_zipcode_wording = '. ¿Está bien escrito?' | translate %} {% endif %} {{ "No encontramos este código postal{1}" | translate(wrong_zipcode_wording) }} {% if languages | length > 1 %} <a href="#" data-toggle="#{% if product_detail %}product{% else %}cart{% endif %}-shipping-country" class="js-modal-open btn-link btn-link-primary text-lowercase"> {{ 'cambiar tu país de entrega' | translate }} </a> {% endif %} {% endif %} {% endfor %} </div> <div class="js-ship-calculator-error js-ship-calculator-common-error alert alert-danger" style="display: none;">{{ "Ocurrió un error al calcular el envío. Por favor intentá de nuevo en unos segundos." | translate }}</div> <div class="js-ship-calculator-error js-ship-calculator-external-error alert alert-danger" style="display: none;">{{ "El calculo falló por un problema con el medio de envío. Por favor intentá de nuevo en unos segundos." | translate }}</div> </div> {% endblock input_form_alert %} {% block input_append_content %} <span class="col-6"> <button class="js-calculate-shipping btn btn-default btn-block" aria-label="{{ 'Calcular envío' | translate }}"> <span class="js-calculate-shipping-wording">{{ "Calcular" | translate }}</span> <span class="js-calculating-shipping-wording" style="display: none;">{{ "Calculando" | translate }}</span> </button> {% if shipping_calculator_variant %} <input type="hidden" name="variant_id" id="shipping-variant-id" value="{{ shipping_calculator_variant.id }}"> {% endif %} </span> {% endblock input_append_content %} {% endembed %} </div> </div> <div class="js-shipping-calculator-spinner shipping-spinner-container mb-3 float-left w-100 transition-soft text-center" style="display: none;"> <div class="spinner-ellipsis"> <div class="point"></div> <div class="point"></div> <div class="point"></div> <div class="point"></div> </div> </div> <div class="js-shipping-calculator-response mb-3 float-left w-100 {% if product_detail %}list list-readonly{% endif %}" style="display: none;"></div> </div> {# 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.country }}" data-country-url="{{ language.url }}" {% if language.active %}selected{% endif %}>{{ language.country_name }}</option> {% endfor %} {% endblock select_options%} {% endembed %} {% endblock %} {% block modal_foot %} <a href="#" class="js-save-shipping-country btn btn-primary float-right">{{ 'Aplicar' | translate }}</a> {% endblock %} {% endembed %} {% endif %}
Incluimos este tpl en el snipplet product-form.tpl para el detalle del producto y cart-totals.tpl para el carrito de compras.
shipping_options.tpl
En este tpl llamamos al snipplet de la opción de envío, es el elemento padre del listado. A la hora de mostrar los resultados del cálculo de envío insertamos por backend este archivo dentro del div js-shipping-calculator-response ubicado en shipping-calculator.tpl. Es por este comportamiento que no podés cambiarle el nombre o cambiar su ubicación en el repositorio del theme.
Dentro del listado priorizamos primero los envíos más baratos y luego los más rápidos (siempre considerando la combinación de dos parámetros).
{% if options %} {% if store.show_shipping_emergency_message %} <div class="alert alert-warning">{{ store.shipping_emergency_message }}</div> {% endif %} <div class="{% if cart.items_count > 0 %}js-product-shipping-label{% endif %} font-small mb-4 pb-1" style="display: none;"> {{ 'Opciones para tu compra <strong>si sumás este producto</strong>.' | translate }} </div> {# Check for only shipping featured options #} {% set has_featured_shipping = false %} {% for option in options_to_show if option.shipping_type == 'ship' or option.shipping_type == 'delivery' or (option.method == 'table' and option.shipping_type == 'custom') %} {% if option |length >= 1 %} {% set has_featured_shipping = true %} {% endif %} {% endfor %} {# Check for only non featured shipping options #} {% set has_non_featured_shipping = false %} {% for option in options_to_hide if option.shipping_type == 'ship' or option.shipping_type == 'delivery' or (option.method == 'table' and option.shipping_type == 'custom') %} {% if option |length >= 1 %} {% set has_non_featured_shipping = true %} {% endif %} {% endfor %} {# Pickup featured options #} {% set has_non_featured_pickup = false %} {% set has_featured_pickup = false %} {# Check for only pickup featured options #} {% for option in options_to_show if option.shipping_type == 'pickup' and option.method != 'branch' %} {% if option |length >= 1 %} {% set has_featured_pickup = true %} {% endif %} {% endfor %} {# Check for only non featured pickup options #} {% for option in options_to_hide if option.shipping_type == 'pickup' and option.method != 'branch' %} {% if option |length >= 1 %} {% set has_non_featured_pickup = true %} {% endif %} {% endfor %} {# Shipping options #} {% if has_featured_shipping %} <div class="full-width-container {% if has_featured_pickup %}mb-4{% endif %}"> <div class="form-label mb-2"> {% include "snipplets/svg/truck.tpl" with {svg_custom_class: "icon-inline icon-lg svg-icon-text mr-2 align-bottom"} %} {{ "Envío a domicilio" | translate }} </div> <ul class="box radio-button-container p-0 mb-0 list-unstyled"> {# Smart shipping hides similar shipping options on a toggle div and also shows an improved shipping item #} {# Check if smart shipping is needed #} {# Include branch options inside calculador #} {% for option in options_to_show if option.shipping_type == 'ship' or option.shipping_type == 'delivery' or (option.method == 'table' and option.shipping_type == 'custom') %} {% include "snipplets/shipping/shipping-calculator-item.tpl" with {'featured_option': true} %} {% endfor %} {% if has_non_featured_shipping %} <div class="js-other-shipping-options w-100 float-left shipping-extra-options" style="display: none;"> {# Smart shipping hides similar shipping options on a toggle div and also shows an improved shipping item #} {# Check if smart shipping is needed #} {# Include branch options inside calculador #} {% for option in options_to_hide if option.shipping_type == 'ship' or option.shipping_type == 'delivery' or (option.method == 'table' and option.shipping_type == 'custom') %} {% include "snipplets/shipping/shipping-calculator-item.tpl" %} {% endfor %} </div> {% endif %} </ul> {% if has_non_featured_shipping %} <div class="js-toggle-more-shipping-options js-show-more-shipping-options w-100 float-left text-center mt-2"> <a href="#" class="btn-link"> <span class="js-shipping-see-more"> {{ 'Ver más opciones de envío' | translate }} </span> <span class="js-shipping-see-less" style="display: none;"> {{ 'Ver menos opciones de envío' | translate }} </span> </a> </div> {% endif %} </div> {% endif %} {# Pickup featured options #} {% if has_featured_pickup %} <div class="full-width-container mb-2"> <div class="form-label mb-2"> {% include "snipplets/svg/map-marker-alt.tpl" with {svg_custom_class: "icon-inline icon-lg svg-icon-text mr-2 align-bottom"} %} {{ "Retirar por" | translate }} </div> <ul class="list-unstyled box radio-button-container p-0 mb-0"> {# Smart shipping hides similar shipping options on a toggle div and also shows an improved shipping item #} {# List only pickup featured options #} {% for option in options_to_show if option.shipping_type == 'pickup' and option.method != 'branch' %} {% include "snipplets/shipping/shipping-calculator-item.tpl" with {'featured_option': true, 'pickup' : true} %} {% endfor %} {% if has_non_featured_pickup %} <div class="js-other-pickup-options w-100 float-left shipping-extra-options" style="display: none;"> {# Smart shipping hides similar shipping options on a toggle div and also shows an improved shipping item #} {# List only pickup featured options: same logic as for featured pickups but for non featured #} {% for option in options_to_hide if option.shipping_type == 'pickup' and option.method != 'branch' %} {% include "snipplets/shipping/shipping-calculator-item.tpl" with {'pickup' : true} %} {% endfor %} </div> {% endif %} </ul> {% if has_non_featured_pickup %} <div class="js-toggle-more-shipping-options js-show-other-pickup-options w-100 float-left text-center mt-2"> <a href="#" class="btn-link"> <span class="js-shipping-see-more"> {{ 'Ver más opciones de retiro' | translate }} </span> <span class="js-shipping-see-less" style="display: none;"> {{ 'Ver menos opciones de retiro' | translate }} </span> </a> </div> {% endif %} </div> {% endif %} {% if store.has_smart_dates and show_time %} <div class="font-small float-left w-100 mb-3">{{"El tiempo de entrega <strong>no considera feriados</strong>." | translate}}</div> {% endif %} {% else %} <span>{{"No hay costos de envío para el código postal dado." | translate}}</span> {% endif %} {# Don't remove this #} <input type="hidden" name="after_calculation" value="1"/> <input type="hidden" name="zipcode" value="{{zipcode}}"/>
Incluimos este tpl usando backend luego de calcular envío dentro del div con la clase js-shipping-calculator-response en el shipping-calculator.tpl.
shipping-calculator-item.tpl
Como su nombre lo indica, este snipplet lo usamos para mostrar cada opción de envío listada, en el detalle del producto son solo informativas mientras que en el carrito muestran un radiobutton para que el usuário pueda elegirla.
{# On first calculation select as default the first option: If store only has pickup option selects pickup else selects shipping option #} {% if has_featured_shipping %} {% set checked_option = featured_option and loop.first and not pickup %} {% else %} {% set checked_option = featured_option and loop.first and pickup %} {% endif %} {% if store.has_smart_shipping_no_auto_select %} {% set checked_option = false %} {% endif %} <li class="js-shipping-list-item radio-button-item list-item" data-store="shipping-calculator-item-{{ option.code }}"> <label class="js-shipping-radio radio-button" data-loop="shipping-radio-{{loop.index}}" data-shipping-type="{% if pickup %}pickup{% else %}delivery{% endif %}"> <input id="{% if featured_option %}featured-{% endif %}shipping-{{loop.index}}" class="js-shipping-method {% if not featured_option %}js-shipping-method-hidden{% endif %} {% if pickup %}js-pickup-option{% endif %} shipping-method" data-price="{{option.cost.value}}" data-code="{{option.code}}" data-name="{{option.name}}" data-cost="{% if option.show_price %} {% if option.cost.value == 0 %}{{ 'Gratis' | translate }}{% else %}{{option.cost}}{% endif %}{% else %} {{ 'A convenir' | translate }} {% endif %}" type="radio" value="{{option.code}}" {% if checked_option %}checked="checked"{% endif %} name="option" data-amplitude-event-name="cart_shipping_option_select" style="display:none" /> <span class="radio-button-content"> <div class="radio-button-icons-container"> <span class="radio-button-icons"> <span class="radio-button-icon unchecked"></span> <span class="radio-button-icon checked"></span> </span> </div> <div class="radio-button-label"> {# Improved shipping option with no carrier img and ordered shipping info #} <div class="radio-button-text row"> <div class="col-8 pr-0"> <div class="font-small {% if option.payment_rules or option.time or option.suboptions is not empty %}mb-1{% endif %}"> {{option.short_name}} {% if option.method == 'branch' %}<span class="ml-1">{{ option.extra.extra }}</span>{% endif %} </div> {% if option.time %} <div class="{% if option.suboptions is not empty or option.payment_rules %}mb-1{% endif %}"> {% if store.has_smart_dates %} {{option.dates}} {% else %} {{option.time}} {% endif %} </div> {% endif %} {% if option.suboptions is not empty %} <div {% if option.payment_rules %}class="mb-1"{% endif %}> {% include "snipplets/shipping_suboptions/#{option.suboptions.type}.tpl" with {'suboptions': option.suboptions} %} </div> {% endif %} {% if option.payment_rules %} <div> <span class="d-table float-left mr-1"> {% include "snipplets/svg/info-circle.tpl" with {svg_custom_class: "icon-inline svg-icon-text mr-2"} %} </span> <span class="d-table"> <i>{{option.payment_rules}}</i> </span> </div> {% endif %} {% if option.warning['enable'] %} <div class="w-100 mb-0 mt-3 mx-3 alert alert-warning"> {{ option.warning['message'] }} </div> {% endif %} </div> {% if option.show_price %} <div class="col-4 text-right"> <span class="h5 shipping-price d-inline-block font-weight-bold {% if option.cost.value == 0 %}text-accent{% else %}text-primary{% endif %}"> <div class="mb-1"> {% if option.cost.value == 0 %} {{ 'Gratis' | translate }} {% else %} {{option.cost}} {% endif %} </div> {% if option.cost.value == 0 and option.old_cost.value %} <div class="price-compare text-foreground mr-0 font-weight-normal">{{option.old_cost}}</div> {% endif %} </span> </div> {% endif %} </div> </div> </span> </label> </li>
Incluimos este tpl en el snipplet shipping_options.tpl
shipping_suboptions
Dentro de esta carpeta encontramos el archivo select.tpl que solo usamos para el medio de envío argentino OCA (por ahora), para listar sucursales de ese correo dentro de un select.
{% set selected_option = loop.first or cart.shipping_option == option.name %} <div class="js-shipping-suboption {{suboptions.name}}"> {% if suboptions.options %} {# Read only suboptions inside popup #} {% set modal_id_val = (suboptions.name | sanitize) ~ '-pickup-modal-' ~ random() %} <div data-toggle="#{{ modal_id_val }}" class="js-modal-open mt-2"> {% include "snipplets/svg/map-marker-alt.tpl" with {svg_custom_class: "icon-inline icon-lg mr-1"} %} <span class="btn-link btn-link-primary align-bottom">{{ 'Ver puntos de retiro' | translate }}</span> </div> {% embed "snipplets/modal.tpl" with{modal_id: modal_id_val, modal_class: 'bottom modal-centered-small js-modal-shipping-suboptions', modal_position: 'center', modal_transition: 'slide', modal_header: true, modal_footer: false, modal_width: 'centered', modal_zindex_top: true} %} {% block modal_head %} {{ 'Puntos de retiro' | translate }} {% endblock %} {% block modal_body %} <ul class="list-unstyled py-2"> {% for option in suboptions.options %} <li class="text-capitalize mb-3">{% include "snipplets/svg/map-marker-alt.tpl" with {svg_custom_class: "icon-inline svg-icon-primary d-flex float-left mr-2"} %} <span class="d-flex">{{ option.name | lower }}</span></li> {% endfor %} </ul> <div class="mt-4"><span class="opacity-50">{{ 'Cercanos al CP:'}}</span> <span class="text-primary font-weight-bold">{{cart.shipping_zipcode}}</span></div> <div class="mt-2 font-small"> {% include "snipplets/svg/info-circle.tpl" with {svg_custom_class: "icon-inline svg-icon-text"} %} <i>{{ "Vas a poder elegir estas opciones antes de finalizar tu compra" | translate }}</i> </div> {% endblock %} {% endembed %} {% else %} <input type="hidden" name="{{suboptions.name}}"/> <div>{{ suboptions.no_options_message | translate }}</div> {% endif %} </div>
Incluimos este tpl en el snipplet shipping-calculator-item.tpl
Payments
En la carpeta Payments tenemos todos los archivos en relación a medios de pago. Así como con la carpeta Shipping, recomendamos modificar lo menos posible estos archivos debido a su complejidad e importancia dentro del theme.
installments.tpl
Como su nombre en inglés lo indica, este snipplet lo usamos para mostrar las mejores cuotas ofrecidas por la tienda en el item del listado de productos, el detalle de productos y en el total del carrito de compras (solo en tiendas de Brasil).
Dentro de este snipplet usamos la condición {% if product %} para diferenciar entre las cuotas de cada producto y las cuotas del carrito.
De la misma forma usamos {% if product_detail %} para diferenciar entre el detalle del producto y el item del listado, y así usar JavaScript a través de las clases “js-...” actualizando el valor de las cuotas cuando el usuario cambia de variante dentro del detalle de un producto.
En tiendas de Argentina mostramos el valor exacto de las cuotas cuando son sin interés, de lo contrario mostramos un texto solo en el detalle del producto que dice "Hasta X cuotas", siendo "X" el máximo número de cuotas con interés.
{% 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 %}
Incluimos este tpl en los snipplets cart-totals.tpl, item.tpl y product-form.tpl
payments.tpl
Este archivo es el componente padre del popup que muestra los detalles de cada medio de pago usando el embed para modals, modal.tpl
Cada medio de pago tiene una tab que al ser cliqueado por el usuario muestra el detalle del mismo.
En este archivo incluidmos los tpls payments-info-banks.tpl para mostrar información de pagos para cada banco (solo aplica a tiendas argentinas) y payments-info.tpl para mostrar información en medios de pago independiente de bancos como PayPal por ejemplo.
{% 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 %}
Incluimos este tpl en el snipplet product-form.tpl
payments-info-banks.tpl
Representa la información de medios de pago para bancos, agrupados por cuotas sin interés (especificando el valor de las cuotas) y cuotas con interés (sin especificar el valor ya que no es la mejor alternativa de pago y el usuario podrá verlo a la hora de pagar) , la cual por el momento solo aplica a Mercado Pago en Argentina.
Además también mostramos en este tpl información sobre medios de pago en efectivo para Argentina.
{% 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 %}
Incluimos este tpl en el snipplet payments.tpl
payments-info.tpl
Este tpl muestra información sobre medios de pago que no dependen de un banco en Argentina, y también incluye una tabla con el detalle de las cuotas para medios de pago de Brasil.
{% 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'] != '0' %} <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'] > 0 %} <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 class=""> {% 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 %}
Incluimos este tpl en el snipplet payments.tpl
Product
Dentro de la carpeta Product encontramos todos los snipplets en relación al detalle del producto.
product-form.tpl
En este snipplet está el código del formulario de producto donde incluimos:
- El nombre
- El precio
- El texto de promociones
- Las cuotas y el link para ver más sobre medios de pago
- Las variantes
- El botón de comprar
- El calculador de envíos y los locales
Además de estos archivos, por fuera del formulario de HTML pero aún dentro de product-form.tpl también encontramos:
- El popup con el detalle de medios de pago
- Los links para compartir en redes sociales
- La descripción del producto subida desde el Administrador nube.
El JavaScript en relación a este formulario podés encontrarlo dentro de store.js.tpl
{# Product name and breadcrumbs #} {% embed "snipplets/page-header.tpl" %} {% block page_header_text %}{{ product.name }}{% endblock page_header_text %} {% endembed %} {# Product price #} <div class="price-container text-center text-md-left" data-store="product-price-{{ product.id }}"> <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> <span 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" {% if not product.display_price %}style="display:none;"{% endif %}>{% if product.display_price %}{{ product.price | money }}{% endif %}</h4> </span> </div> {# Promotional text #} {% if product.promotional_offer and not product.promotional_offer.script.is_percentage_off and product.display_price %} <div class="js-product-promo-container text-center text-md-left"> {% if product.promotional_offer.script.is_discount_for_quantity %} {% for threshold in product.promotional_offer.parameters %} <h4 class="mb-2 text-accent"><strong>{{ "¡{1}% OFF comprando {2} o más!" | translate(threshold.discount_decimal_percentage * 100, threshold.quantity) }}</strong></h4> {% endfor %} {% else %} <h4 class="mb-2 text-accent"><strong>{{ "¡Llevá {1} y pagá {2}!" | translate(product.promotional_offer.script.quantity_to_take, product.promotional_offer.script.quantity_to_pay) }}</strong></h4> {% endif %} {% if product.promotional_offer.scope_type == 'categories' %} <p>{{ "Válido para" | translate }} {{ "este producto y todos los de la categoría" | translate }}: {% for scope_value in product.promotional_offer.scope_value_info %} {{ scope_value.name }}{% if not loop.last %}, {% else %}.{% endif %} {% endfor %}</br>{{ "Podés combinar esta promoción con otros productos de la misma categoría." | translate }}</p> {% elseif product.promotional_offer.scope_type == 'all' %} <p>{{ "Vas a poder aprovechar esta promoción en cualquier producto de la tienda." | translate }}</p> {% endif %} </div> {% endif %} {# Product installments #} {% include "snipplets/payments/installments.tpl" with {'product_detail' : true} %} {# Product form, includes: Variants, CTA and Shipping calculator #} <form id="product_form" class="js-product-form" method="post" action="{{ store.cart_url }}" data-store="product-form-{{ product.id }}"> <input type="hidden" name="add_to_cart" value="{{product.id}}" /> {% if product.variations %} {% include "snipplets/product/product-variants.tpl" %} {% endif %} {% if product.available and product.display_price %} {% include "snipplets/product/product-quantity.tpl" %} {% endif %} {% 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"} %} {# Add to cart CTA #} <div class="mb-4"> <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 %} /> {# Fake add to cart CTA visible during add to cart event #} {% include 'snipplets/placeholders/button-placeholder.tpl' with {custom_class: "btn-block mb-4"} %} <div class="js-added-to-cart-product-message float-leftt w-100 mb-3 text-center text-md-left" style="display: none;"> {{'Ya agregaste este producto.' | translate }}<a href="#" class="js-modal-open js-fullscreen-modal-open btn btn-link ml-1" data-toggle="#modal-cart" data-modal-url="modal-fullscreen-cart">{{ 'Ver carrito' | translate }}</a> </div> </div> {# Define contitions to show shipping calculator and store branches on product page #} {% set show_product_fulfillment = settings.shipping_calculator_product_page and (store.has_shipping or store.branches) and not product.free_shipping and not product.is_non_shippable %} {% if show_product_fulfillment %} <div class="divider"></div> {# Shipping calculator and branch link #} <div id="product-shipping-container" class="product-shipping-calculator list" {% if not product.display_price or not product.has_stock %}style="display:none;"{% endif %}> {# Shipping Calculator #} {% if store.has_shipping %} {% include "snipplets/shipping/shipping-calculator.tpl" with {'shipping_calculator_variant' : product.selected_or_first_available_variant, 'product_detail': true} %} {% endif %} {% if store.branches %} {# Link for branches #} {% include "snipplets/shipping/branches.tpl" with {'product_detail': true} %} {% endif %} </div> <div class="divider"></div> {% endif %} </form> {# Product payments details #} {% include 'snipplets/payments/payments.tpl' %} {# Product share #} {% include 'snipplets/social/social-share.tpl' %} {# Product description #} {% if product.description is not empty %} <div class="product-description user-content" data-store="product-description-{{ product.id }}"> <h5 class="my-3">{{ "Descripción" | translate }}</h5> {{ product.description }} </div> {% endif %}
Incluimos este tpl en el template product.tpl
product-image.tpl
Dentro de product-image.tpl está la imagen del producto, los carteles de oferta, stock y envío graits; y un placeholder de vectores que se muestra hasta que el slider que muestra la imagen esté cargado.
En este snipplet también incluimos las clases necesarias para el zoom con Fancybox así como la implementación para lazy load con Lazysizes.
El JavaScript en relación al slider y el zoom podés encontrarlo dentro de store.js.tpl
{% set has_multiple_slides = product.images_count > 1 or product.video_url %} {% if product.images_count > 0 %} <div class="js-swiper-product nube-slider-product swiper-container" style="visibility:hidden; height:0;"> {% include 'snipplets/labels.tpl' with {'product_detail': true} %} <div class="swiper-wrapper"> {% for image in product.images %} <div class="swiper-slide js-product-slide slider-slide" data-image="{{image.id}}" data-image-position="{{loop.index0}}"> <a href="{{ image | product_image_url('huge') }}" data-fancybox="product-gallery" class="d-block p-relative" style="padding-bottom: {{ image.dimensions['height'] / image.dimensions['width'] * 100}}%;"> <img src="{{ 'images/empty-placeholder.png' | static_url }}" data-srcset='{{ image | product_image_url('large') }} 480w, {{ image | product_image_url('huge') }} 640w' data-sizes="auto" class="js-product-slide-img product-slider-image img-absolute img-absolute-centered lazyautosizes lazyload" {% if image.alt %}alt="{{image.alt}}"{% endif %}/> <img src="{{ image | product_image_url('tiny') }}" class="js-product-slide-img product-slider-image img-absolute img-absolute-centered blur-up" {% if image.alt %}alt="{{image.alt}}"{% endif %} /> </a> </div> {% endfor %} {% include 'snipplets/product/product-video.tpl' %} </div> <div class="js-swiper-product-pagination swiper-pagination swiper-pagination-white"></div> {% if has_multiple_slides %} <div class="js-swiper-product-prev swiper-button-prev d-none d-md-block">{% include "snipplets/svg/chevron-left.tpl" with {svg_custom_class: "icon-inline icon-w-8 icon-2x svg-icon-text"} %}</div> <div class="js-swiper-product-next swiper-button-next d-none d-md-block">{% include "snipplets/svg/chevron-right.tpl" with {svg_custom_class: "icon-inline icon-w-8 icon-2x svg-icon-text"} %}</div> {% endif %} </div> {% snipplet 'placeholders/product-detail-image-placeholder.tpl' %} {% endif %}
Incluimos este tpl en el template product.tpl
product-video.tpl
En este tpl tenemos el código para el video en el produto.
{% if product.video_url %} {% if product.images_count > 1 %} {% set video_index = product.images_count %} {% else %} {% set video_index = 1 %} {% endif %} <div class="js-product-slide js-product-video-slide swiper-slide slider-slide" data-image-position="{{ video_index }}"> <div class="product-video-container"> <div class="product-video"> {# Visible video inside slider #} {% include 'snipplets/video-item.tpl' with {product_modal_trigger: true, product_video: true} %} {# Hidden video inside modal #} {% include 'snipplets/video-item.tpl' with {product_modal: true, product_video: true} %} </div> </div> </div> {% endif %}
Usamos este tpl en snipplet product-image.tpl
product-quantity.tpl
Como su nombre indica, en este tpl está el código para la cantidad de unidades a agregar al carrito.
El JavaScript en relación a la cantidad podes encontrarlo dentro de store.js.tpl
{# Product quantity #} {% if not quickshop %} <div class="row"> <div class="col col-md-4"> {% endif %} {% embed "snipplets/forms/form-input.tpl" with{type_number: true, input_value: '1', input_name: 'quantity' ~ item.id, input_custom_class: 'js-quantity-input text-center', input_label: false, input_append_content: true, input_group_custom_class: 'js-quantity form-row align-items-center', form_control_container_custom_class: 'col-6', input_min: '1', input_aria_label: 'Cambiar cantidad' | translate } %} {% block input_prepend_content %} <span class="js-quantity-down col-3 text-center"> {% include "snipplets/svg/minus.tpl" with {svg_custom_class: "icon-inline icon-w-12 icon-lg svg-icon-text"} %} </span> {% endblock input_prepend_content %} {% block input_append_content %} <span class="js-quantity-up col-3 text-center"> {% include "snipplets/svg/plus.tpl" with {svg_custom_class: "icon-inline icon-w-12 icon-lg svg-icon-text"} %} </span> {% endblock input_append_content %} {% endembed %} {% if not quickshop %} </div> </div> {% endif %}
Incluimos este tpl en los snipplets product-form.tpl y item.tpl
product-variants.tpl
En este archivo listamos todas las variantes de un producto (un máximo de 3) y sus propiedades en forma de un select.
El JavaScript en relación a la cantidad encontrarlo dentro de store.js.tpl
<div class="js-product-variants{% if quickshop %} js-product-quickshop-variants text-left{% endif %} form-row"> {% for variation in product.variations %} <div class="js-product-variants-group {% if variation.name in ['Color', 'Cor'] %}js-color-variants-container{% endif %} {% if loop.length == 3 %} {% if quickshop %}col-4{% else %}col-12{% endif %} col-md-4 {% elseif loop.length == 2 %} col-6 {% else %} col {% if quickshop %}col-md-12{% else %}col-md-6{% endif %}{% endif %}"> {% embed "snipplets/forms/form-select.tpl" with{select_label: true, select_label_name: '' ~ variation.name ~ '', select_for: 'variation_' ~ loop.index , select_data: 'variant-id', select_data_value: 'variation_' ~ loop.index, select_name: 'variation' ~ '[' ~ variation.id ~ ']', select_custom_class: 'js-variation-option js-refresh-installment-data'} %} {% block select_options %} {% for option in variation.options %} <option value="{{ option.id }}" {% if product.default_options[variation.id] == option.id %}selected="selected"{% endif %}>{{ option.name }}</option> {% endfor %} {% endblock select_options%} {% endembed %} </div> {% endfor %} </div>
Incluimos este tpl en los snipplets product-form.tpl y item.tpl
product-related.tpl
En este snipplet tenemos la funcionalidad de productos relacionados los cuales se muestran en un slider.
El JavaScript del slider de productos relacionados podés encontrarlo dentro de store.js.tpl
{# /*============================================================================ #Product Related Grid ==============================================================================*/ #Properties #Related Slider #} {% set related_products_ids = product.metafields.related_products.related_products_ids %} {% if related_products_ids %} {% set related_products = related_products_ids | get_products %} {% set show = (related_products | length > 0) %} {% endif %} {% if not show %} {% set related_products = category.products | shuffle | take(8) %} {% set show = (related_products | length > 1) %} {% endif %} <section id="related-products" class="section-products-related"> {% if show %} <div class="container"> <div class="row"> {% if settings.products_related_title %} <div class="col-12 text-center"> <h3>{{ settings.products_related_title }}</h3> </div> {% endif %} <div class="col-12"> <div class="js-swiper-related swiper-container"> <div class="swiper-wrapper"> {% for related in related_products %} {% if product.id != related.id %} {% include 'snipplets/grid/item.tpl' with {'product': related, 'slide_item': true} %} {% endif %} {% endfor %} </div> {% if related_products | length < 4 %} <div class="d-md-none"> {% endif %} <div class="js-swiper-related-pagination swiper-pagination"></div> <div class="js-swiper-related-prev swiper-button-prev">{% include "snipplets/svg/chevron-left.tpl" with {svg_custom_class: "icon-inline icon-w-8 icon-2x svg-icon-text"} %}</div> <div class="js-swiper-related-next swiper-button-next">{% include "snipplets/svg/chevron-right.tpl" with {svg_custom_class: "icon-inline icon-w-8 icon-2x svg-icon-text"} %}</div> {% if related_products | length < 4 %} </div> {% endif %} </div> </div> </div> </div> {% endif %} </section>
Incluimos este tpl en el snipplet product-form.tpl
Header
Dentro de la carpeta header están todos los archivos en relación al head de la tienda, es decir la parte donde están la navegación, el logo, el buscador y el ícono del carrito.
header.tpl
Este es el tpl “padre” del header de la tienda donde además de lo mencionado anteriormente también tenemos el modal que muestra el carrito de compras, cart-panel.tpl, el cual se incluye usando un embed.
{# Site Overlay #} <div class="js-overlay site-overlay" style="display: none;"></div> {# Header #} {% set show_transparent_head = template == 'home' and settings.head_transparent and settings.slider and not settings.slider is empty %} <header class="js-head-main head-main {% if show_transparent_head %}head-transparent {% if settings.head_fix %}head-transparent-fixed{% else %}head-transparent-absolute{% endif %}{% endif %} head-{{ settings.head_background }} {% if settings.head_fix %}head-fix{% endif %} {% if not settings.head_fix and show_transparent_head %}head-absolute{% endif %}" data-store="head"> {# Advertising #} {% if settings.ad_bar and settings.ad_text %} {% snipplet "header/header-advertising.tpl" %} {% endif %} <div class="container"> <div class="row no-gutters align-items-center"> <div class="col">{% snipplet "navigation/navigation.tpl" %}</div> <div class="col text-center">{% snipplet "header/header-logo.tpl" %}</div> <div class="col text-right">{% snipplet "header/header-utilities.tpl" %}</div> </div> </div> {% include "snipplets/notification.tpl" with {order_notification: true} %} {% if settings.head_fix and settings.ajax_cart %} {% include "snipplets/notification.tpl" with {add_to_cart: true} %} {% endif %} </header> {% if not settings.head_fix %} {% include "snipplets/notification.tpl" with {add_to_cart: true, add_to_cart_fixed: true} %} {% endif %} {# Hamburger panel #} {% embed "snipplets/modal.tpl" with{modal_id: 'nav-hamburger',modal_class: 'nav-hamburger modal-docked-small', modal_position: 'left', modal_transition: 'fade', modal_width: 'full' } %} {% block modal_body %} {% snipplet "navigation/navigation-panel.tpl" %} {% endblock %} {% endembed %} {# Notifications #} {# Modal Search #} {% embed "snipplets/modal.tpl" with{modal_id: 'nav-search', modal_position: 'right', modal_transition: 'slide', modal_width: 'docked-md' } %} {% block modal_body %} {% snipplet "header/header-search.tpl" %} {% endblock %} {% endembed %} {% if not store.is_catalog and settings.ajax_cart and template != 'cart' %} {# Cart Ajax #} {% embed "snipplets/modal.tpl" with{modal_id: 'modal-cart', modal_position: 'right', modal_transition: 'slide', modal_width: 'docked-md', modal_form_action: store.cart_url, modal_form_class: 'js-ajax-cart-panel', modal_mobile_full_screen: true, modal_form_hook: 'cart-form' } %} {% block modal_head %} {% block page_header_text %}{{ "Carrito de Compras" | translate }}{% endblock page_header_text %} {% endblock %} {% block modal_body %} {% snipplet "cart-panel.tpl" %} {% endblock %} {% endembed %} {% endif %}
Incluímos este tpl en el template layout.tpl
header-advertising.tpl
En este tpl tenemos el código en relación a una barra de anuncio donde la persona que administra la tienda puede agregar un texto con un link.
<section class="section-advertising"> <div class="container"> <div class="row-fluid"> <div class="col text-center"> {% if settings.ad_bar and settings.ad_text %} {% if settings.ad_url %} <a class="link-contrast" href="{{ settings.ad_url }}"> {% endif %} {% if settings.ad_text %} {{ settings.ad_text }} {% endif %} {% if settings.ad_url %} </a> {% endif %} {% endif %} </div> </div> </div> </section>
Incluimos este tpl en el snipplet header.tpl
header-logo.tpl
En este archivo tenemos el logo de la tienda, tanto cuando la tienda tiene una imagen cargada como cuando no y mostramos su nombre.
{% if template == 'home' %} <h1 class="m-0"> {% endif %} <div id="logo" class="logo-img-container {% if not has_logo %}hidden{% endif %}"> {{ store.logo('medium') | img_tag(store.name, {class: 'logo-img transition-soft-slow'}) | a_tag(store.url) }} </div> <div id="no-logo" class="logo-text-container {% if has_logo %} hidden{% endif %}"> <a class="logo-text h1" href="{{ store.url }}">{{ store.name }}</a> </div> {% if template == 'home' %} </h1> {% endif %}
Incluimos este tpl en el snipplet header.tpl y en el template password.tpl
header-search-results.tpl
Este snipplet lo usamos para mostrar sugerencias cuando el usuario busca productos. Muestra imagen, nombre, precio y cuotas de los productos sugeridos; así como un link para ir a la página de resultados de búsqueda donde mostramos todos los resultados.
Mostramos hasta 6 sugerencias dentro de este archivo pero pueden modificarse cambiando la variable search_suggestions.
<ul class="search-suggest-list"> {% set search_suggestions = products | take(6) %} {% for product in search_suggestions %} <li class="search-suggest-item container-fluid"> <a href="{{ product.url }}" class="search-suggest-link row justify-content-md-center"> <div class="search-suggest-image-container col-xs-auto"> {{ product.featured_image | product_image_url("tiny") | img_tag(product.featured_image.alt, {class: 'search-suggest-image'}) }} </div> <div class="search-suggest-text col"> <p class="search-suggest-name"> {{ product.name | highlight(query) }} </p> {% if product.display_price %} <p> {{ product.price | money }} {% 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) %} {% if max_installments_without_interests and max_installments_without_interests.installment > 1 %} <span>| {{ "Hasta <strong class='installment-amount'>{1}</strong> cuotas sin interés" | t(max_installments_without_interests.installment) }}</span> {% endif %} {% endif %} </p> {% endif %} </div> <div class="col-xs-auto"> {% include "snipplets/svg/chevron-right.tpl" with {svg_custom_class: "icon-inline search-suggest-icon"} %} </div> </a> </li> {% endfor %} <a href="#" class="js-search-suggest-all-link btn btn-primary d-block">{{ 'Ver todos los resultados' | translate }}</a> </ul>
Es agregado dentro del div con la clase js-search-suggest usando JavaScript dentro del archivo store.js.tpl
header-search.tpl
Este archivo representa al formulario de búsqueda que tiene un campo y un botón submit. Además de esto tiene un div con la clase js-search-suggest donde se inserta el tpl header-search-results.tpl que muestra las sugerencias de búsqueda.
<form class="js-search-container js-search-form" action="{{ store.search_url }}" method="get"> <div class="form-group m-0"> <input class="js-search-input form-control search-input" autocomplete="off" type="search" name="q" placeholder="{{ 'Buscar' | translate }}" aria-label="{{ 'Buscador' | translate }}"/> <button type="submit" class="btn search-input-submit" value="{{ 'Buscar' | translate }}" aria-label="{{ 'Buscar' | translate }}"> {% include "snipplets/svg/search.tpl" with {svg_custom_class: "icon-inline svg-icon-text"} %} </button> </div> </form> <div class="js-search-suggest search-suggest"> {# AJAX container for search suggestions #} </div>
Incluimos este tpl en el snipplet header.tpl
header-utilities.tpl
Dentro de este snipplet incluimos los iconos del head del theme, excepto el icono que abre la navegación (hamburger), ya que este está dentro del grupo de snipplets de la navegación.
<div class="utilities-container"> <div class="utilities-item"> <a href="#" class="js-modal-open js-toggle-search utilities-link" data-toggle="#nav-search" aria-label="{{ 'Buscador' | translate }}"> {% include "snipplets/svg/search.tpl" with {svg_custom_class: "icon-inline icon-w-16 svg-icon-text"} %} </a> </div> {% if not store.is_catalog %} <div class="utilities-item"> <div id="ajax-cart" class="cart-summary"> <a href="#" {% if template != 'cart' %}class="js-modal-open js-toggle-cart"{% endif %} data-toggle="#modal-cart"> {% include "snipplets/svg/shopping-bag.tpl" with {svg_custom_class: "icon-inline icon-w-14 svg-icon-text"} %} <span class="js-cart-widget-amount cart-widget-amount">{{ "{1}" | translate(cart.items_count ) }}</span> </a> </div> </div> {% endif %} </div>
Incluimos este tpl en el snipplet header.tpl
Footer
Footer.tpl
El footer del theme lo incluimos en este tpl, el cual incluye:
- El formulario de newsletter, que es un snipplet en sí mismo
- Los iconos (con links) de las redes sociales, que también tiene su propio snipplet
- La navegación en el footer (contenido en su propio snipplet)
- Los links de contacto (un snipplet usado también en la página de contacto)
- Logos de medios de pago y envío
- Información de copyright
- Sellos personalizados, que son imágenes subidas por el administrador de la tienda en el footer
{% set has_social_network = store.facebook or store.twitter or store.pinterest or store.instagram %} {% set has_footer_contact_info = store.phone or store.email or store.blog or store.address %} {% set has_footer_menu = settings.footer_menu %} {% set has_payment_logos = settings.payments %} {% set has_shipping_logos = settings.shipping %} {% set has_shipping_payment_logos = has_payment_logos or has_shipping_logos %} <footer class="js-hide-footer-while-scrolling display-when-content-ready" data-store="footer"> <div class="container"> {% if template != 'password' %} {# Newsletter #} {% if settings.news_show %} {% include "snipplets/newsletter.tpl" %} {% endif %} {% endif %} {# Social #} {% if has_social_network %} <div class="row element-footer"> <div class="col text-center">{% include "snipplets/social/social-links.tpl" %}</div> </div> {% endif %} {% if template != 'password' %} {# Foot Nav #} {% if has_footer_menu %} <div class="row element-footer"> <div class="col text-center">{% include "snipplets/navigation/navigation-foot.tpl" %}</div> </div> {% endif %} {% endif %} {# Contact #} {% if has_footer_contact_info %} <div class="row element-footer"> <div class="col text-center">{% include "snipplets/contact-links.tpl" %}</div> </div> {% endif %} {# Logos Payments and Shipping #} {% if has_shipping_payment_logos %} <div class="row element-footer footer-payments-shipping-logos"> {% if has_payment_logos %} <div class="col text-center">{% include "snipplets/logos-icons.tpl" with {'payments': true} %}</div> {% endif %} <div class="w-100 my-2"></div> {% if has_shipping_logos %} <div class="col text-center">{% include "snipplets/logos-icons.tpl" with {'shipping': true} %}</div> {% endif %} </div> {% endif %} <div class="row element-footer"> <div class="col-md-6 text-center text-md-left"> {# La leyenda que aparece debajo de esta linea de código debe mantenerse con las mismas palabras y con su apropiado link a Tienda Nube; como especifican nuestros términos de uso: http://www.tiendanube.com/terminos-de-uso . Si quieres puedes modificar el estilo y posición de la leyenda para que se adapte a tu sitio. Pero debe mantenerse visible para los visitantes y con el link funcional. Os créditos que aparece debaixo da linha de código deverá ser mantida com as mesmas palavras e com seu link para Nuvem Shop; como especificam nossos Termos de Uso: http://www.nuvemshop.com.br/termos-de-uso. Se você quiser poderá alterar o estilo e a posição dos créditos para que ele se adque ao seu site. Porém você precisa manter visivél e com um link funcionando. #} {{ new_powered_by_link }} </div> <div class="col-md-6 copyright text-center text-md-right pt-4 pt-md-0"> {{ "Copyright {1} - {2}. Todos los derechos reservados." | translate( (store.business_name ? store.business_name : store.name), "now" | date('Y') ) }} </div> </div> {# AFIP - EBIT - Custom Seal #} {% if store.afip or ebit or settings.custom_seal_code or ("seal_img.jpg" | has_custom_image) %} {% if store.afip or ebit %} <div class="row element-footer"> <div class="col text-center"> {% if store.afip %} <div class="footer-logo afip seal-afip"> {{ store.afip | raw }} </div> {% endif %} {% if ebit %} <div class="footer-logo ebit seal-ebit"> {{ ebit }} </div> {% endif %} </div> </div> {% endif %} {% if "seal_img.jpg" | has_custom_image or settings.custom_seal_code %} <div class="row element-footer"> <div class="col text-center"> {% if "seal_img.jpg" | has_custom_image %} <div class="footer-logo custom-seal"> {% if settings.seal_url != '' %} <a href="{{ settings.seal_url }}" target="_blank"> {% endif %} <img src="{{ 'images/empty-placeholder.png' | static_url }}" data-src="{{ "seal_img.jpg" | static_url }}" class="custom-seal-img lazyload" alt="{{ 'Sello de' | translate }} {{ store.name }}"/> {% if settings.seal_url != '' %} </a> {% endif %} </div> {% endif %} {% if settings.custom_seal_code %} <div class="custom-seal custom-seal-code"> {{ settings.custom_seal_code | raw }} </div> {% endif %} </div> </div> {% endif %} {% endif %} </div> </footer>
Incluimos este tpl en los templates layout.tpl y password.tpl
Navigation
navigation.tpl
En este tpl incluimos el ícono que abre el modal de navegación
<div class="utilities-container"> <div class="utilities-item"> <a href="#" class="js-modal-open utilities-link" data-toggle="#nav-hamburger" aria-label="{{ 'Menú' | translate }}"> {% include "snipplets/svg/bars.tpl" with {svg_custom_class: "icon-inline icon-w-14 svg-icon-text"} %} </a> </div> </div>
Incluimos este tpl en el snipplet header.tpl
navigation-panel.tpl
Este tpl muestra el contenido del modal donde tenemos los links a las páginas y categorías, para logearse, crear una cuenta y para cambiar el idioma de la tienda.
Dentro de este archivo llamamos al snipplet para el listado de links navigation-nav-list.tpl.
<div class="nav-primary"> <ul class="nav-list" data-store="navigation"> {% snipplet "navigation/navigation-nav-list.tpl" %} </ul> </div> <div class="nav-secondary"> {% if languages | length > 1 %} <div class="languages"> {% for language in languages %} {% set class = language.active ? "" : "opacity-50" %} <a href="{{ language.url }}" class="{{ class }}"> <img class="lazyload" src="{{ 'images/empty-placeholder.png' | static_url }}" data-src="{{ language.country | flag_url }}" alt="{{ language.name }}" /> </a> {% endfor %} </div> {% endif %} <ul class="nav-account" data-store="account-links"> {% if not customer %} {% if 'mandatory' not in store.customer_accounts %} <li class="nav-accounts-item">{{ "Crear cuenta" | translate | a_tag(store.customer_register_url, '', 'nav-accounts-link') }}</li> {% endif %} <li class="nav-accounts-item">{{ "Iniciar sesión" | translate | a_tag(store.customer_login_url, '', 'nav-accounts-link') }}</li> {% else %} <li class="nav-accounts-item">{{ "Mi cuenta" | translate | a_tag(store.customer_home_url, '', 'nav-accounts-link') }}</li> <li class="nav-accounts-item">{{ "Cerrar sesión" | translate | a_tag(store.customer_logout_url, '', 'nav-accounts-link') }}</li> {% endif %} </ul> </div>
Incluimos este tpl en el snippler header.tpl
navigation-nav-list.tpl
Este es el for que itera o recorre todos los links dentro del Menú Principal de una tienda. En este archivo mezclamos las dos cosas en un solo for, tanto páginas como categorías.
{% for item in navigation %} {% if item.subitems %} <li class="item-with-subitems"> <div class="js-nav-list-toggle-accordion"> <a class="nav-list-link" href="{{ item.url }}">{{ item.name }}</a> <span class="js-toggle-page-accordion nav-list-arrow transition-soft"> {% include "snipplets/svg/chevron-down.tpl" with {svg_custom_class: "icon-inline svg-icon-text"} %} </span> </div> <ul class="js-pages-accordion list-subitems nav-list-accordion" style="display:none;"> {% snipplet "navigation/navigation-nav-list.tpl" with navigation = item.subitems %} </ul> </li> {% else %} <li> <a class="nav-list-link" href="{{ item.url }}">{{ item.name }}</a> </li> {% endif %} {% endfor %}
Incluimos este tpl en el snipplet navigation-panel.tpl y dentro de si mismo navigation-nav-list.tpl
navigation-foot.tpl
Este snipplet muestra los links de la navegación del footer, la cual es elegida por el administrador de la tienda desde la sección de Personalizar el diseño actual.
<ul class="footer-menu m-0 p-0"> {% for item in menus[settings.footer_menu] %} <li class="footer-menu-item my-4"> <a class="footer-menu-link" href="{{ item.url }}" {% if item.url | is_external %}target="_blank"{% endif %}>{{ item.name }}</a> </li> {% endfor %} </ul>
Incluimos este tpl en el snipplet footer.tpl
Cart
cart-panel.tpl
Dentro de este archivo está el contenido del modal del carrito incluyendo el for que llama a los items del carrito, los mensajes para cuando el carrito está vacío o no hay más stock de un producto y se incluye el snipplet cart-totals.tpl
<div class="js-ajax-cart-list cart-row"> {# Cart panel items #} {% if cart.items %} {% for item in cart.items %} {% include "snipplets/cart-item-ajax.tpl" %} {% endfor %} {% endif %} </div> <div class="js-empty-ajax-cart cart-row" {% if cart.items_count > 0 %}style="display:none;"{% endif %}> {# Cart panel empty #} <div class="alert alert-info">{{ "El carrito de compras está vacío." | translate }}</div> </div> <div id="error-ajax-stock" style="display: none;"> <div class="alert alert-warning"> {{ "¡Uy! No tenemos más stock de este producto para agregarlo al carrito. Si querés podés" | translate }}<a href="{{ store.products_url }}" class="btn-link ml-1">{{ "ver otros acá" | translate }}</a> </div> </div> <div class="cart-row"> {% include "snipplets/cart-totals.tpl" %} </div>
Incluimos este tpl en el snipplet header.tpl
cart-item-ajax.tpl
Este es el item de cada producto que se agrega al carrito, no solo se usa para el carrito en su versión “popup”, sino también en la página de Carrito de compras.
Si bien el theme Base no muestra por defecto el carrito de compras en su versión “página”, tiene el template cart.tpl listo para ser usado.
Este item muestra:
- Una imágen del producto agregado
- Su nombre y variante
- Su precio
- Un botón para removerlo del carrito
- Los controles para aumentar o disminuir la cantidad
<div class="js-cart-item cart-item {% if item.product.is_non_shippable %}js-cart-item-non-shippable{% else %}js-cart-item-shippable{% endif %} {% if cart_page %}row align-items-md-center {% if loop.last %}mb-2{% else %}mb-5{% endif %} {% else %}form-row{% endif %}" data-item-id="{{ item.id }}" data-store="cart-item-{{ item.product.id }}"> {% set show_free_shipping_label = item.product.free_shipping and not (cart.free_shipping.cart_has_free_shipping or cart.free_shipping.min_price_free_shipping.min_price) %} {# Cart item image #} <div class="{% if cart_page %}col-3 col-md-2{% else %}col-3 pr-3{% endif %}"> <a href="{{ item.url }}"> <img src="{{ item.featured_image | product_image_url('medium') }}" class="img-fluid" /> </a> </div> <div class="{% if cart_page %}col-9 col-md pl-0 pl-md-3{% else %}col-9 d-flex align-items-center{% endif %}"> {# Cart item name #} <div class="{% if cart_page %}row align-items-center{% else %}w-100{% endif %}"> <h6 class="font-weight-normal {% if cart_page %}col-12 col-md-6 h4-md mb-2 mb-md-0{% else %}cart-item-name mb-0{% endif %}"> <a href="{{ item.url }}"> {{ item.short_name }} </a> <small>{{ item.short_variant_name }}</small> {% if show_free_shipping_label %} <div class="my-2"> <span class="label label-secondary font-smallest">{{ "Envío gratis" | translate }}</span> </div> {% endif %} </h6> {% if cart_page %} {% set cart_quantity_class = 'float-md-none m-auto ' %} {% else %} {% set cart_quantity_class = 'float-left ' %} {% endif %} <div class="cart-item-quantity {% if cart_page %}col-7 col-md-3{% endif %}"> {% embed "snipplets/forms/form-input.tpl" with{ type_number: true, input_value: item.quantity, input_name: 'quantity[' ~ item.id ~ ']', input_data_attr: 'item-id', input_data_val: item.id, input_group_custom_class: cart_quantity_class ~ 'form-quantity w-auto mb-2', input_custom_class: 'js-cart-quantity-input cart-item-input form-control', input_label: false, input_append_content: true} %} {% block input_prepend_content %} <div class="row m-0 justify-content-md-center {% if cart_page %}align-items-center{% endif %}"> <span class="js-cart-quantity-btn cart-item-btn btn" onclick="LS.minusQuantity({{ item.id }}{% if not cart_page %}, true{% endif %})"> {% include "snipplets/svg/minus.tpl" with {svg_custom_class: "icon-inline svg-icon-text"} %} </span> {% endblock input_prepend_content %} {% block input_append_content %} <span class="js-cart-input-spinner cart-item-spinner" style="display: none;"> {% include "snipplets/svg/sync-alt.tpl" with {svg_custom_class: "icon-inline icon-spin svg-icon-text"} %} </span> <span class="js-cart-quantity-btn cart-item-btn btn" onclick="LS.plusQuantity({{ item.id }}{% if not cart_page %}, true{% endif %})"> {% include "snipplets/svg/plus.tpl" with {svg_custom_class: "icon-inline svg-icon-text"} %} </span> </div> {% endblock input_append_content %} {% endembed %} </div> {# Cart item subtotal #} <h6 class="js-cart-item-subtotal cart-item-subtotal {% if cart_page %}col-5 col-md-3 text-right text-md-center h4-md font-weight-bold{% endif %}" data-line-item-id="{{ item.id }}">{{ item.subtotal | money }}</h6> </div> </div> {# Cart item delete #} <div class="cart-item-delete {% if cart_page %}position-relative-md col-auto col-md-1{% else %}col-1{% endif %} text-right"> <button type="button" class="btn h6 {% if cart_page %}h5-md{% endif %} m-0" onclick="LS.removeItem({{ item.id }}{% if not cart_page %}, true{% endif %})"> {% include "snipplets/svg/trash-alt.tpl" with {svg_custom_class: "icon-inline icon-lg svg-icon-text"} %} </button> </div> </div>
Incluimos este tpl en el snipplet cart-panel.tpl y en el template cart.tpl. No es posible cambiar su nombre o su ubicación debido a que es usado desde el backend.
cart-totals.tpl
Este snipplet incluye:
- El subtotal del carrito, que no incluye el costo de envío ni promociones
- El calculador de envíos
- Los locales físicos (no aplica en Brasil)
- El mensaje de promociones, por ejemplo 2x1
- El total del carrito que incluye el costo de envío y promociones
- Las cuotas en base al total, solo aplica en Brasil
- El botón para finalizar la compra
- El link para continuar comprando
También usamos la condición {% if cart_page %} ya que el snipplet es usado tanto para el carrito en su formato modal o popup, como en su formato “página”.
{# IMPORTANT Do not remove this hidden subtotal, it is used by JS to calculate cart total #} <div class="subtotal-price hidden" data-priceraw="{{ cart.total }}"></div> {# Define contitions to show shipping calculator and store branches on cart #} {% set show_calculator_on_cart = settings.shipping_calculator_cart_page and store.has_shipping %} {% set show_cart_fulfillment = settings.shipping_calculator_cart_page and (store.has_shipping or store.branches) %} {# Used to assign currency to total #} <div id="store-curr" class="hidden">{{ cart.currency }}</div> {% if not cart_page %} {# Cart popup subtotal #} <h5 class="js-visible-on-cart-filled {% if not cart_page %}row no-gutters{% else %}text-right{% endif %} mb-1 {% if cart_page %}text-center-xs{% endif %}" {% if cart.items_count == 0 %}style="display:none;"{% endif %}> <span {% if not cart_page %}class="col"{% endif %}> {{ "Subtotal" | translate }} <small class="js-subtotal-shipping-wording" {% if not (cart.has_shippable_products or show_calculator_on_cart) %}style="display: none"{% endif %}>{{ " (sin envío)" | translate }}</small> : </span> <strong class="js-ajax-cart-total js-cart-subtotal {% if not cart_page %}col{% endif %} text-right" data-priceraw="{{ cart.subtotal }}">{{ cart.subtotal | money }}</strong> </h5> {# Cart popup promos #} <div class="js-total-promotions text-accent font-weight-bold"> <span class="js-promo-title" style="display:none;">{{ "Promo" | translate }}</span> <span class="js-promo-in" style="display:none;">{{ "en" | translate }}</span> <span class="js-promo-all" style="display:none;">{{ "todos los productos" | translate }}</span> <span class="js-promo-buying" style="display:none;"> {{ "comprando" | translate }}</span> <span class="js-promo-units-or-more" style="display:none;"> {{ "o más" | translate }}</span> {% for promotion in cart.promotional_discount.promotions_applied %} {% if(promotion.scope_value_id) %} {% set id = promotion.scope_value_id %} {% else %} {% set id = 'all' %} {% endif %} <span class="js-total-promotions-detail-row row" id="{{ id }}"> <span class="col"> {% if promotion.discount_script_type == "NAtX%off" %} {{ promotion.selected_threshold.discount_decimal_percentage * 100 }}% OFF {% else %} {{ "Promo" | translate }} {{ promotion.discount_script_type }} {% endif %} {{ "en" | translate }} {% if id == 'all' %}{{ "todos los productos" | translate }}{% else %}{{ promotion.scope_value_name }}{% endif %} {% if promotion.discount_script_type == "NAtX%off" %} <span>{{ "Comprando {1} o más" | translate(promotion.selected_threshold.quantity) }}</span> {% endif %} : </span> <span class="col text-right">-{{ promotion.total_discount_amount_short }}</span> </span> {% endfor %} </div> {% endif %} {% if cart_page %} <div class="divider {% if cart_page %}d-none d-md-block{% endif %}"></div> <div class="container p-0"> <div class="row"> {% endif %} {% if cart_page %} <div class="col-12 col-md-5"> {% endif %} {% if show_cart_fulfillment %} <div class="js-fulfillment-info js-allows-non-shippable" {% if not cart.has_shippable_products %}style="display: none"{% endif %}> <div class="js-visible-on-cart-filled divider {% if cart_page %}d-md-none{% endif %}" {% if cart.items_count == 0 %}style="display:none;"{% endif %}></div> <div class="js-visible-on-cart-filled js-has-new-shipping js-shipping-calculator-container container-fluid"> {# Saved shipping not available #} <div class="js-shipping-method-unavailable alert alert-warning row" style="display: none;"> <div> <strong>{{ 'El medio de envío que habías elegido ya no se encuentra disponible para este carrito. ' | translate }}</strong> </div> <div> {{ '¡No te preocupes! Podés elegir otro.' | translate}} </div> </div> {# Shipping calculator and branch link #} <div id="cart-shipping-container" class="row" {% if cart.items_count == 0 %} style="display: none;"{% endif %} data-shipping-url="{{ store.shipping_calculator_url }}"> {# Used to save shipping #} <span id="cart-selected-shipping-method" data-code="{{ cart.shipping_data.code }}" class="hidden">{{ cart.shipping_data.name }}</span> {# Shipping Calculator #} {% if store.has_shipping %} {% include "snipplets/shipping/shipping-calculator.tpl" with {'product_detail': false} %} {% endif %} {# Store branches #} {% if store.branches %} {# Link for branches #} {% include "snipplets/shipping/branches.tpl" with {'product_detail': false} %} {% endif %} </div> </div> <div class="js-visible-on-cart-filled divider {% if cart_page %}d-md-none{% endif %} {% if not store.branches %} mt-0{% endif %}" {% if cart.items_count == 0 %}style="display:none;"{% endif %}></div> </div> {% endif %} {% if cart_page %} </div> {% endif %} {% if cart_page %} <div class="col-12 col-md-7" {% if cart.items_count == 0 %}style="display:none;"{% endif %}> <div id="cart-sticky-summary" class="position-sticky-md container-fluid"> <div class="row justify-content-md-end mt-4 mt-md-0"> <div class="col-12 col-md-auto"> {# Cart page subtotal #} <h5 class="js-visible-on-cart-filled row no-gutters justify-content-end justify-content-md-center mb-1" {% if cart.items_count == 0 %}style="display:none;"{% endif %}> <span class="col col-md-auto"> {{ "Subtotal" | translate }} {% if settings.shipping_calculator_cart_page %} <small>{{ " (sin envío)" | translate }}</small> {% endif %} : </span> <strong class="js-cart-subtotal col col-md-auto text-right" data-priceraw="{{ cart.subtotal }}">{{ cart.subtotal | money }}</strong> </h5> {# Cart page promos #} <div class="js-total-promotions"> <span class="js-promo-title" style="display:none;">{{ "Promo" | translate }}</span> <span class="js-promo-in" style="display:none;">{{ "en" | translate }}</span> <span class="js-promo-all" style="display:none;">{{ "todos los productos" | translate }}</span> <span class="js-promo-buying" style="display:none;"> {{ "comprando" | translate }}</span> <span class="js-promo-units-or-more" style="display:none;"> {{ "o más" | translate }}</span> {% for promotion in cart.promotional_discount.promotions_applied %} {% if(promotion.scope_value_id) %} {% set id = promotion.scope_value_id %} {% else %} {% set id = 'all' %} {% endif %} <span class="js-total-promotions-detail-row row" id="{{ id }}"> <span class="col"> {% if promotion.discount_script_type == "NAtX%off" %} {{ promotion.selected_threshold.discount_decimal_percentage * 100 }}% OFF {% else %} {{ "Promo" | translate }} {{ promotion.discount_script_type }} {% endif %} {{ "en" | translate }} {% if id == 'all' %}{{ "todos los productos" | translate }}{% else %}{{ promotion.scope_value_name }}{% endif %} {% if promotion.discount_script_type == "NAtX%off" %} <span>{{ "Comprando {1} o más" | translate(promotion.selected_threshold.quantity) }}</span> {% endif %} : </span> <span class="col text-right">-{{ promotion.total_discount_amount_short }}</span> </span> {% endfor %} </div> {% endif %} {# Cart total #} <div class="js-cart-total-container js-visible-on-cart-filled mb-3" {% if cart.items_count == 0 %}style="display:none;"{% endif %} data-store="cart-total"> <h2 class="row no-gutters text-primary mb-0 {% if cart_page %}justify-content-end justify-content-md-center{% endif %}"> <span class="col {% if cart_page %}col-md-auto{% endif %} mr-1">{{ "Total" | translate }}:</span> <span class="js-cart-total {% if cart.free_shipping.cart_has_free_shipping %}js-free-shipping-achieved{% endif %} {% if cart.shipping_data.selected %}js-cart-saved-shipping{% endif %} col {% if cart_page %}col-md-auto{% endif %} text-right">{{ cart.total | money }}</span> </h2> {# IMPORTANT Do not remove this hidden total, it is used by JS to calculate cart total #} <div class='total-price hidden'> {{ "Total" | translate }}: {{ cart.total | money }} </div> {% include "snipplets/payments/installments.tpl" with {'product': false} %} </div> <div class="js-visible-on-cart-filled {% if not cart_page %}container-fluid{% endif %}" {% if cart.items_count == 0 %}style="display:none;"{% endif %}> {# No stock alert #} <div id="error-ajax-stock" class='alert alert-warning' role='alert' style="display:none;"> {{ "¡Uy! No tenemos más stock de este producto para agregarlo al carrito. Si querés podés" | translate }}<a href="{{ store.products_url }}" class="btn-link">{{ "ver otros acá" | translate }}</a> </div> {# Cart panel CTA #} {% set cart_total = (settings.cart_minimum_value * 100) %} {# Cart CTA Module for cart popup or cart page #} {% if cart_page %} {% if cart.checkout_enabled %} <input id="go-to-checkout" class="btn btn-primary btn-block mb-3" type="submit" name="go_to_checkout" value="{{ 'Iniciar Compra' | translate }}"/> {% else %} {# Cart minium alert #} <div class="alert alert-warning mt-4"> {{ "El monto mínimo de compra es de {1} sin incluir el costo de envío" | t(cart_total | money) }} </div> {% endif %} {% else %} <div class="js-ajax-cart-submit row mb-3" {{ not cart.checkout_enabled ? 'style="display:none"' }} id="ajax-cart-submit-div"> <input class="btn btn-primary btn-block" type="submit" name="go_to_checkout" value="{{ 'Iniciar Compra' | translate }}"/> </div> {# Cart minium alert #} <div class="js-ajax-cart-minimum alert alert-warning mt-4" {{ cart.checkout_enabled ? 'style="display:none"' }} id="ajax-cart-minumum-div"> {{ "El monto mínimo de compra es de {1} sin incluir el costo de envío" | t(cart_total | money) }} </div> <input type="hidden" id="ajax-cart-minimum-value" value="{{ cart_total }}"/> {% endif %} {# Cart panel continue buying link #} {% if settings.continue_buying %} <div class="row mb-2"> <div class="text-center w-100"> <a href="{% if cart_page %}{{ store.products_url }}{% else %}#{% endif %}" class="{% if not cart_page %}js-modal-close js-fullscreen-modal-close{% endif %} btn btn-link">{{ 'Ver más productos' | translate }}</a> </div> </div> {% endif %} </div> {% if cart_page %} </div> </div> </div> </div> </div> </div> {% endif %}
Incluimos este tpl en el snipplet cart-panel.tpl y el template cart.tpl
Home
Todos los archivos dentro de la carpeta home hacen referencia a las funcionalidades de la página inicial de la tienda. Estas son:
- Banners (con imágen, texto y un botón)
- Productos destacados
- Feed de Instagram
- Módulos con imágen y texto
- Slider de imágenes
- Video
- Mensaje de bienvenida
home-banners.tpl
Como dice su nombre, son banners dentro de la página inicial (hasta 4) y, como los módulos de imágen y texto, muestran una imagen, un título, un párrafo y un botón (con un link).
<section class="section-banners-home" data-store="banner-home-categories"> <div class="container{% if settings.banners_full %}-fluid p-0{% endif %}"> <div class="row {% if settings.banners_full %}no-gutters{% endif %} align-items-center"> {% set num_banners = 0 %} {% for banner in ['banner_01', 'banner_02', 'banner_03'] %} {% set banner_show = attribute(settings,"#{banner}_show") %} {% set banner_title = attribute(settings,"#{banner}_title") %} {% set banner_button_text = attribute(settings,"#{banner}_button") %} {% set has_banner = banner_show and (banner_title or banner_description or "#{banner}.jpg" | has_custom_image) %} {% if has_banner %} {% set num_banners = num_banners + 1 %} {% endif %} {% endfor %} {% for banner in ['banner_01', 'banner_02', 'banner_03'] %} {% set banner_show = attribute(settings,"#{banner}_show") %} {% set banner_title = attribute(settings,"#{banner}_title") %} {% set banner_description = attribute(settings,"#{banner}_description") %} {% set banner_button_text = attribute(settings,"#{banner}_button") %} {% set banner_url = attribute(settings,"#{banner}_url") %} {% set has_banner = banner_show and (banner_title or banner_description or "#{banner}.jpg" | has_custom_image) %} {% set has_banner_text = banner_title or banner_description or banner_button_text %} {% if has_banner %} <div class="col-md"> <div class="textbanner"> {% if banner_url %} <a class="textbanner-link" href="{{ banner_url }}"{% if banner_title %} title="{{ banner_title }}" aria-label="{{ banner_title }}"{% else %} title="{{ 'Banner de' | translate }} {{ store.name }}" aria-label="{{ 'Banner de' | translate }} {{ store.name }}"{% endif %}> {% endif %} <div class="textbanner-image{% if has_banner_text and textoverimage %} overlay{% endif %}"> <img class="textbanner-image-background lazyautosizes lazyload blur-up-big" src="{{ "#{banner}.jpg" | static_url | settings_image_url('tiny') }}" data-srcset="{{ "#{banner}.jpg" | static_url | settings_image_url('large') }} 480w, {{ "#{banner}.jpg" | static_url | settings_image_url('huge') }} 640w" data-sizes="auto" data-expand="-10" {% if banner_title %}alt="{{ banner_title }}"{% else %}alt="{{ 'Banner de' | translate }} {{ store.name }}"{% endif %} /> </div> <div class="textbanner-text{% if textoverimage %} over-image{% endif %}"> {% if banner_title %} <div class="h1 textbanner-title">{{ banner_title }}</div> {% endif %} {% if banner_description %} <div class="textbanner-paragraph">{{ banner_description }}</div> {% endif %} {% if banner_url and banner_button_text %} <button class="btn btn-line btn-small">{{ banner_button_text }}</button> {% endif %} </div> {% if banner_url %} </a> {% endif %} </div> </div> {% endif %} {% endfor %} </div> </div> </section>
Incluimos este tpl en el template home.tpl
home-featured-products.tpl
En este tpl tenemos a al slider que muestra los productos relacionados en la página del detalle de un producto.
{# /*============================================================================ #Home featured grid ==============================================================================*/ #Properties #Featured Slider #} {% if sections.primary.products %} <section class="section-featured-home" data-store="products-home-featured"> <div class="container"> <div class="row"> {% if settings.featured_products_title %} <div class="col-12 text-center"> <h3>{{ settings.featured_products_title }}</h3> </div> {% endif %} <div class="col-12"> <div class="js-swiper-featured swiper-container"> <div class="swiper-wrapper"> {% for product in sections.primary.products %} {% include 'snipplets/grid/item.tpl' with {'slide_item': true} %} {% endfor %} </div> <div class="js-swiper-featured-pagination swiper-pagination"></div> <div class="js-swiper-featured-prev swiper-button-prev d-none d-md-block">{% include "snipplets/svg/chevron-left.tpl" with {svg_custom_class: "icon-inline icon-w-8 icon-2x svg-icon-text"} %}</div> <div class="js-swiper-featured-next swiper-button-next d-none d-md-block">{% include "snipplets/svg/chevron-right.tpl" with {svg_custom_class: "icon-inline icon-w-8 icon-2x svg-icon-text"} %}</div> </div> </div> </div> </div> </section> {% set section_name = 'primary' %} {% endif %}
Incluimos este tpl en el template home.tpl
home-instafeed.tpl
Acá incluimos todo lo que tiene que ver con notificaciones dentro del Storefront, las cuales por ahora son dos: el mensaje cuando un producto es agregado al carrito, y el mensaje cuando el usuario finalizó su compra y puede hacer un seguimiento.
{% if settings.show_instafeed and store.instagram %} <section class="section-instafeed-home" data-store="instagram-feed"> <div class="container"> <div class="row"> {% set instuser = store.instagram|split('/')|last %} <div class="col-12 text-center"> {% if store.instagram %} <a target="_blank" href="{{ store.instagram }}" class="instafeed-title" aria-label="{{ 'Instagram de' | translate }} {{ store.name }}"> {% include "snipplets/svg/instagram.tpl" with {svg_custom_class: "icon-inline icon-2x svg-icon-text"} %} <h3 class="instafeed-user">{{ instuser }}</h3> </a> {% else %} <div class="instafeed-title"> {% include "snipplets/svg/instagram.tpl" with {svg_custom_class: "icon-inline icon-2x svg-icon-text"} %} <h3 class="instafeed-user">{{ "Instagram" | translate }}</h3> </div> {% endif %} </div> </div> </div> <div id="instafeed" class="row no-gutters"> </div> </section> {% endif %}
Incluimos este tpl en el template home.tpl
home-modules.tpl
Este tpl lo usamos para mostrar hasta 2 módulos que permiten mostrar una imágen (alineada a la izquierda o derecha en desktop), un título, un párrafo y un botón (con un link).
<section class="section-home-modules"> <div class="container{% if settings.modules_full %}-fluid p-0{% endif %}"> {% set num_modules = 0 %} {% for module in ['module_01', 'module_02'] %} {% set module_show = attribute(settings,"#{module}_show") %} {% set module_title = attribute(settings,"#{module}_title") %} {% set module_button_text = attribute(settings,"#{module}_button") %} {% set has_module = module_show and (module_title or module_description or "#{module}.jpg" | has_custom_image) %} {% if has_module %} {% set num_modules = num_modules + 1 %} {% endif %} {% endfor %} {% for module in ['module_01', 'module_02'] %} {% set module_show = attribute(settings,"#{module}_show") %} {% set module_align = attribute(settings,"#{module}_align") %} {% set module_title = attribute(settings,"#{module}_title") %} {% set module_description = attribute(settings,"#{module}_description") %} {% set module_button_text = attribute(settings,"#{module}_button") %} {% set module_url = attribute(settings,"#{module}_url") %} {% set has_module = module_show and (module_title or module_description or "#{module}.jpg" | has_custom_image) %} {% set has_module_text = module_title or module_description or module_button_text %} {% if has_module %} {% if module_url %} <a class="module-with-text-link" href="{{ module_url }}"{% if module_title %} title="{{ module_title }}" aria-label="{{ module_title }}"{% else %} title="{{ 'Módulo de' | translate }} {{ store.name }}" aria-label="{{ 'Módulo de' | translate }} {{ store.name }}"{% endif %}> {% endif %} <div class="row {% if settings.modules_full %}no-gutters{% endif %} align-items-center"> <div class="col-md {% if module_align == 'right' %}order-md-2{% endif %}"> <div class="textbanner"> <div class="textbanner-image{% if has_banner_text and textoverimage %} overlay{% endif %}"> <img class="textbanner-image-background lazyautosizes lazyload fade-in" src="{{ 'images/empty-placeholder.png' | static_url }}" data-srcset='{{ "#{module}.jpg" | static_url | settings_image_url('large') }} 480w, {{ "#{module}.jpg" | static_url | settings_image_url('huge') }} 640w' data-sizes="auto" {% if module_title %}alt="{{ module_title }}"{% else %}alt="{{ 'Módulo de' | translate }} {{ store.name }}"{% endif %} data-expand="-10"/> </div> </div> </div> <div class="col-md"> <div class="textbanner-text{% if textoverimage %} over-image{% endif %}"> {% if module_title %} <div class="h1 textbanner-title">{{ module_title }}</div> {% endif %} {% if module_description %} <div class="textbanner-paragraph">{{ module_description }}</div> {% endif %} {% if module_url and module_button_text %} <button class="btn btn-primary btn-small">{{ module_button_text }}</button> {% endif %} </div> </div> {% if module_url %} </a> {% endif %} {% endif %} {% endfor %} </div> </section>
Incluimos este tpl en el template home.tpl
home-slider.tpl
Usamos este snipplet para el slider principal del Storefront, mostrado en la página de inicio.
<section class="js-home-slider-container section-slider {% if not settings.slider or settings.slider is empty %} hidden {% endif %}" data-store="slider-main"> <div class="js-home-slider nube-slider-home swiper-container swiper-container-horizontal"> <div class="swiper-wrapper"> {% for slide in settings.slider %} <div class="swiper-slide slide-container"> {% if slide.link %} <a href="{{ slide.link }}" aria-label="{{ 'Carrusel' | translate }} {{ loop.index }}"> {% endif %} <div class="slider-slide"> <img {% if loop.first %}src="{{ slide.image | static_url | settings_image_url('1080p') }}" class="slider-image"{% else %}src="{{ slide.image | static_url | settings_image_url('tiny') }}" data-src="{{ slide.image | static_url | settings_image_url('1080p') }}" class="slider-image blur-up-big swiper-lazy" data-sizes="auto"{% endif %} alt="{{ 'Carrusel' | translate }} {{ loop.index }}"/> </div> {% if slide.link %} </a> {% endif %} </div> {% endfor %} </div> <div class="js-swiper-home-pagination swiper-pagination swiper-pagination-bullets swiper-pagination-white"> {% if settings.slider > 1 and not params.preview %} {% for slide in settings.slider %} <span class="swiper-pagination-bullet"></span> {% endfor %} {% endif %} </div> <div class="js-swiper-home-prev swiper-button-prev d-none d-md-block">{% include "snipplets/svg/chevron-left.tpl" with {svg_custom_class: "icon-inline icon-w-8 icon-lg svg-icon-invert"} %}</div> <div class="js-swiper-home-next swiper-button-next d-none d-md-block">{% include "snipplets/svg/chevron-right.tpl" with {svg_custom_class: "icon-inline icon-w-8 icon-lg svg-icon-invert"} %}</div> </div> </section>
Incluimos este tpl en el template home.tpl
home-video.tpl
Como su nombre dice, es un snipplet para la funcionalidad del video que se muestra en la página de inicio, la cual está lista para aceptar URLs de Youtube y Vimeo.
{% if settings.video_embed %} <section class="section-video-home" data-store="video-home"> <div class="container{% if settings.video_full %}-fluid p-0{% endif %}"> <div class="row no-gutters"> <div class="col"> {% include 'snipplets/video-item.tpl' %} </div> </div> </div> </section> {% endif %}
Incluimos este tpl en el template home.tpl
home-welcome-message.tpl
A través del home-welcome-message.tpl mostramos un mensaje de bienvenida compuesto por un título y un párrafo en la página inicial del Storefront.
{% if settings.welcome_message %} <section class="section-welcome-home"> <div class="container"> <div class="row"> <div class="col-md-8 offset-md-2"> <h2 class="welcome-title">{{ settings.welcome_message }}</h2> {% if settings.welcome_text %} <p class="welcome-text">{{ settings.welcome_text }}</p> {% endif %} </div> </div> </div> </section> {% endif %}
Incluimos este tpl en el template home.tpl
modal.tpl
Este snipplet representa a todos los popups o modales del theme, en este caso lo usamos para 5 partes:
- Medios de pago
- Navegación
- Carrito de compras
- Filtros
- Búsqueda
Es un componente con parámetros que permiten agregar clases, Ids, definir cómo va a ser la animación del modal y desde que parte de la pantalla aparece, entre otras cosas. Para ver más detalles dentro del propio archivo están explicadas todas las propiedades.
{# /*============================================================================ #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>
Incluimos este tpl en los snipplets header.tpl, payments.tpl y category.tpl
notification.tpl
Acá incluimos todo lo que tiene que ver con notificaciones dentro del Storefront, las cuales por ahora son dos: el mensaje cuando un producto es agregado al carrito, y el mensaje cuando el usuario finalizó su compra y un link a la página de seguimiento.
{# Order notification #} {% if order_notification and status_page_url %} <div data-url="{{ status_page_url }}" class="js-notification notification notification-secondary" style="display:none;"> <div class="container"> <div class="row"> <div class="col"> {{ "Seguí acá" | translate | a_tag(status_page_url, {class: 'btn btn-link'}) }} <span>{{ "tu última compra" | translate }}</span> <a class="js-notification-close ml-3" href="#"> {% include "snipplets/svg/times.tpl" with {svg_custom_class: "icon-inline svg-icon-primary icon-lg"} %} </a> </div> </div> </div> </div> {% endif %} {# Add to cart notification #} {% if add_to_cart %} <div class="js-alert-added-to-cart notification-floating notification-hidden {% if add_to_cart_fixed %}notification-fixed{% endif %}" style="display: none;"> <div class="notification notification-primary notification-with-arrow position-relative {% if not add_to_cart_mobile %}col-12 float-right{% endif %}"> <div class="h6 text-center mb-3 mr-3"> <strong>{{ '¡Ya agregamos tu producto al carrito!' | translate }}</strong> </div> <div class="js-cart-notification-close notification-close"> {% include "snipplets/svg/times.tpl" with {svg_custom_class: "icon-inline svg-icon-primary"} %} </div> <div class="js-cart-notification-item row"> <div class="col-3 pr-0 notification-img"> <img src="" class="js-cart-notification-item-img img-fluid" /> </div> <div class="col-9 text-left"> <div class="mb-1"> <span class="js-cart-notification-item-name"></span> <span class="js-cart-notification-item-variant-container" style="display: none;"> (<span class="js-cart-notification-item-variant"></span>) </span> </div> <div class="mb-1"> <span class="js-cart-notification-item-quantity"></span> <span> x </span> <span class="js-cart-notification-item-price"></span> </div> </div> </div> <div class="row text-primary h5 font-weight-normal mt-2 mb-3"> <span class="col-auto text-left"> <strong>{{ "Total" | translate }}</strong> (<span class="js-cart-widget-amount"> {{ "{1}" | translate(cart.items_count ) }} </span> <span class="js-cart-counts-plural" style="display: none;"> {{ 'productos' | translate }}): </span> <span class="js-cart-counts-singular" style="display: none;"> {{ 'producto' | translate }}): </span> </span> <strong class="js-cart-total col text-right">{{ cart.total | money }}</strong> </div> <a href="#" class="js-modal-open js-cart-notification-close js-fullscreen-modal-open btn btn-primary btn-medium w-100 d-inline-block" data-toggle="#modal-cart" data-modal-url="modal-fullscreen-cart"> {{'Ver carrito' | translate }} </a> </div> </div> {% endif %}
Incluimos este tpl en el snipplet header.tpl
labels.tpl
Los labels son los carteles de oferta, promociones, sin stock y envío gratis que se muestran tanto en el item en la lista de productos como en el detalle del producto.
En este tpl usamos el condicional {% if product_detail %} para determinar si usamos o no JavaScript en detalle del producto cuando se cambian las variantes, por ejemplo cuando una de estas no tiene stock o tiene un precio diferente que impacta en el porcentaje de descuento.
{% if product.compare_at_price > product.price %} {% set price_discount_percentage = ((product.compare_at_price) - (product.price)) * 100 / (product.compare_at_price) %} {% endif %} {% if color %} {% set show_labels = settings.product_color_variants %} {% else %} {% set show_labels = not product.has_stock or product.free_shipping or product.compare_at_price or product.promotional_offer %} {% endif %} {% if show_labels %} <div class="{% if product.video_url and product %}js-labels-group{% endif %} labels"> {% if not product.has_stock %} <div class="{% if product_detail %}js-stock-label {% endif %}label label-default">{{ "Sin stock" | translate }}</div> {% else %} {% if product_detail or color %} <div class="js-stock-label label label-default" {% if product.has_stock %}style="display:none;"{% endif %}>{{ "Sin stock" | translate }}</div> {% endif %} {% if product.compare_at_price or product.promotional_offer %} <div class="{% if not product.promotional_offer and product %}js-offer-label{% endif %} label label-accent" {% if (not product.compare_at_price and not product.promotional_offer) or not product.display_price %}style="display:none;"{% endif %}> {% if product.promotional_offer.script.is_percentage_off %} {{ product.promotional_offer.parameters.percent * 100 }}% OFF {% elseif product.promotional_offer.script.is_discount_for_quantity %} <div>{{ product.promotional_offer.selected_threshold.discount_decimal_percentage * 100 }}% OFF</div> <div class="label-small p-right-quarter p-left-quarter">{{ "Comprando {1} o más" | translate(product.promotional_offer.selected_threshold.quantity) }}</div> {% elseif product.promotional_offer %} {% if store.country == 'BR' %} {{ "Leve {1} Pague {2}" | translate(product.promotional_offer.script.quantity_to_take, product.promotional_offer.script.quantity_to_pay) }} {% else %} {{ "Promo" | translate }} {{ product.promotional_offer.script.type }} {% endif %} {% else %} <span {% if product_detail or color %}class="js-offer-percentage"{% endif %}>{{ price_discount_percentage |round }}</span>% OFF {% endif %} </div> {% endif %} {% if product.free_shipping %} <div class="label label-secondary">{{ "Envío gratis" | translate }}</div> {% endif %} {% endif %} </div> {% endif %}
Incluimos este tpl en el snipplet product-image.tpl y item.tpl
logos-icons.tpl
Son las banderas que muestran medios de pago y envío
{% if payments %} {% for payment in settings.payments %} <img src="{{ 'images/empty-placeholder.png' | static_url }}" data-src="{{ payment | payment_new_logo }}" class="icon-logo lazyload" alt="{{ payment }}"> {% endfor %} {% elseif shipping %} {% for shipping in settings.shipping %} <img src="{{ 'images/empty-placeholder.png' | static_url }}" data-src="{{ shipping | shipping_logo }}" class="icon-logo lazyload" alt="{{ shipping }}"> {% endfor %} {% endif %}
Incluimos este tpl en el snipplet footer.tpl
Social
Social-links.tpl
Contiene todos los links e iconos de redes sociales que se muestran en el footer. Deben ser configurados en el Administrador nube desde la sección Mi Cuenta/Redes Sociales.
Incluye:
- Google+
{% for sn in ['facebook', 'twitter', 'google_plus', 'pinterest', 'instagram'] %} {% set sn_url = attribute(store,sn) %} {% if sn_url %} <a class="social-icon" href="{{ sn_url }}" target="_blank" {% if sn == 'google_plus' %}rel="publisher"{% endif %} aria-label="{{ sn }} {{ store.name }}"> {% if sn == "google_plus" %} {% include "snipplets/svg/google-plus.tpl" with {svg_custom_class: "icon-inline"} %} {% elseif sn == "facebook" %} {% include "snipplets/svg/facebook-f.tpl" with {svg_custom_class: "icon-inline"} %} {% elseif sn == "instagram" %} {% include "snipplets/svg/instagram.tpl" with {svg_custom_class: "icon-inline"} %} {% elseif sn == "pinterest" %} {% include "snipplets/svg/pinterest.tpl" with {svg_custom_class: "icon-inline"} %} {% else %} {% include "snipplets/svg/twitter.tpl" with {svg_custom_class: "icon-inline"} %} {% endif %} </a> {% endif %} {% endfor %}
Incluimos este tpl en el snipplet footer.tpl
Social-share.tpl
Son los links para compartir un producto en las siguientes redes sociales:
{# Mobile Sharing #} <div class="social-share text-center"> <h5 class="my-3">{{ "Compartir" | translate }}</h5> {# Whatsapp button #} <a class="social-share-button" data-network="whatsapp" target="_blank" href="whatsapp://send?text={{ product.social_url }}" title="{{ 'Compartir en WhatsApp' | translate }}" aria-label="{{ 'Compartir en WhatsApp' | translate }}"> {% include "snipplets/svg/whatsapp.tpl" with {svg_custom_class: "icon-inline svg-icon-text"} %} </a> {# Facebook button #} <a class="social-share-button" data-network="facebook" target="_blank" href="https://www.facebook.com/sharer/sharer.php?u={{ product.social_url }}" title="{{ 'Compartir en Facebook' | translate }}" aria-label="{{ 'Compartir en Facebook' | translate }}"> {% include "snipplets/svg/facebook-f.tpl" with {svg_custom_class: "icon-inline svg-icon-text"} %} </a> {# Twitter button #} <a class="social-share-button" data-network="twitter" target="_blank" href="https://twitter.com/share?url={{ product.social_url }}" title="{{ 'Compartir en Twitter' | translate }}" aria-label="{{ 'Compartir en Twitter' | translate }}"> {% include "snipplets/svg/twitter.tpl" with {svg_custom_class: "icon-inline svg-icon-text"} %} </a> {# Pinterest button #} <a class="js-pinterest-share social-share-button" data-network="pinterest" target="_blank" href="#" title="{{ 'Compartir en Pinterest' | translate }}" aria-label="{{ 'Compartir en Pinterest' | translate }}"> {% include "snipplets/svg/pinterest.tpl" with {svg_custom_class: "icon-inline svg-icon-text"} %} </a> <div class="pinterest-hidden social-share-button" style="display: none;" data-network="pinterest"> {{product.social_url | pin_it('https:' ~ product.featured_image | product_image_url('large'))}} </div> </div>
Incluimos este tpl en el snipplet product-form.tpl
Placeholders
Los placeholders son iconos vectorizados que se muestran con una animación de opacidad hasta que la imagen o el slider de imagenes al cual reemplazan, esté listo para verse. Una vez que el slider o la imagen se carga, el placeholder se oculta.
El JavaScript que oculta y muestra los placeholders está en el archivo store.js.tpl.
home-slider-placeholder.tpl
Se muestra hasta que el slider principal de la página de inicio está cargado.
{# Mobile home slider placeholder: to be hidden after content loaded #} {% if settings.slider and settings.slider is not empty %} <div class="js-home-slider-placeholder placeholder-full-height placeholder-container d-md-none overflow-hidden"> <div class="container home-placeholder-icons"> <div class="row"> <div class="col-10 offset-1 mb-3 placeholder-line-medium placeholder-color"> </div> <div class="col-8 offset-2 mb-3 placeholder-line-medium placeholder-color"> </div> <div class="col-6 offset-3 mb-3 placeholder-line-medium placeholder-color"> </div> </div> </div> <div class="placeholder-shine"> </div> </div> <div class="js-slider-desktop-placeholder slider-desktop-placeholder"> </div> {% endif %}
Incluimos este tpl en el snipplet home-slider.tpl
product-detail-image-placeholder.tpl
Se muestra hasta que el slider del detalle de producto está cargado.
{# Mobile product detail placeholder: to be hidden after content loaded #} {# Define sizes of mobile img placeholder based on the tallest element #} {% set max_product_height = product.featured_image.dimensions['height'] %} {% set max_product_width = product.featured_image.dimensions['width'] %} {% if product.images_count > 1 %} {% for image in product.images %} {% set max_product_height = max(image.dimensions['height'], max_product_height) %} {% if image.dimensions['height'] == max_product_height %} {% set max_product_width = max(image.dimensions['width'], max_product_width) %} {% endif %} {% endfor %} {% endif %} {% set has_image_sizes = max_product_width > 0 %} <div class="js-product-slider-placeholder d-md-none product-placeholder-container placeholder-container {% if product.images_count == 1 %}hidden-when-content-ready{% endif %}"> <div class="p-relative overflow-none" {% if has_image_sizes %}style="padding-bottom: {{ max_product_height / max_product_width * 100}}%;"{% endif %}> <div class="product-placeholder img-absolute"> </div> </div> <div class="placeholder-icon"> {% include "snipplets/svg/shopping-bag.tpl" with {svg_custom_class: "icon-inline icon-w-14 icon-8x svg-icon-text"} %} </div> <div class="placeholder-shine"> </div> </div>
Incluimos este tpl en el snipplet product-image.tpl
page-header.tpl
Representa todos los títulos de todas las páginas del theme. Dentro de si incluye el snipplet de breadcrumbs.
{# /*============================================================================ #Page header ==============================================================================*/ #Properties #Title #Breadcrumbs #} <section class="page-header {% if template == 'product' %}mt-md-3{% else %}mt-3{% endif %}" {% if template != 'product' %}data-store="page-title"{% endif %}> {% if template != 'product' %} <div class="container"> <div class="row"> {% endif %} <div class="{% if template != 'product' %}col text-center{% endif %} {% if template == 'product' %}text-center text-md-left{% endif %} {% if template == 'category' %}col-lg-6 offset-lg-3{% endif %}"> {% include 'snipplets/breadcrumbs.tpl' %} <h1 {% if template == 'product' %}class="js-product-name" data-store="product-name-{{ product.id }}"{% endif %} >{% block page_header_text %}{% endblock %}</h1> {% if template == 'category' and category.description %} <p class="page-header-text font-md-normal">{{ category.description }}</p> <div class="divider col-2 offset-5 background-primary"></div> {% endif %} </div> {% if template != 'product' %} </div> </div> {% endif %} </section>
Incluimos este tpl en los templates 404.tpl, address.tpl, addresses.tpl, info.tpl, login.tpl, newpass.tlp, order.tpl, orders.tpl, register.tpl, reset.tpl, cart.tpl, category.tpl, contact.tpl, page.tpl y search.tpl. También en el snipplet product-form.tpl
breadcrumbs.tpl
Son los breadcrumbs de una página incluidos dentro del snipplet product-header.tpl pero listos para ser usados por separado.
{# /*============================================================================ #Page breadcrmubs ==============================================================================*/ #Properties #Breadcrumb //breadcrumbs_custom_class for custom CSS classes #} {% if breadcrumbs %} <div class="breadcrumbs {{ breadcrumbs_custom_class }}" itemprop="breadcrumb" itemscope itemtype="http://www.schema.org/WebPage" itemid="body"> <a class="crumb" href="{{ store.url }}" title="{{ store.name }}">{{ "Inicio" | translate }}</a> <span class="divider">></span> {% if template == 'page' %} <span class="crumb active">{{ page.name }}</span> {% elseif template == 'cart' %} <span class="crumb active">{{ "Carrito de compras" | translate }}</span> {% elseif template == 'search' %} <span class="crumb active">{{ "Resultados de búsqueda" | translate }}</span> {% elseif template == 'account.order' %} <span class="crumb active">{{ 'Orden {1}' | translate(order.number) }}</span> {% else %} {% for crumb in breadcrumbs %} {% if crumb.last %} <span class="crumb active">{{ crumb.name }}</span> {% else %} <a class="crumb" href="{{ crumb.url }}" title="{{ crumb.name }}">{{ crumb.name }}</a> <span class="divider">></span> {% endif %} {% endfor %} {% endif %} </div> {% endif %}
Incluimos este tpl en el snipplet page-header.tpl
whatsapp-chat.tpl
Como su nombre indica, este snipplet lo usamos para mostrar un botón de WhatsApp que permite al usuario chatear con el administrador de la tienda. Lo que hace es abrir el WhatsApp web si el usuario está navegando desde una computadora y la app si es desde el celular.
Para que este botón se muestre es necesario activar la funcionalidad desde el Administrador nube en la sección Configuraciones/WhatsApp
{% if store.whatsapp %} <a href="{{ store.whatsapp }}" target="_blank" class="btn-whatsapp" aria-label="{{ 'Comunicate por WhatsApp' | translate }}"> {% include "snipplets/svg/whatsapp.tpl" with {svg_custom_class: "icon-inline icon-2x"} %} </a> {% endif %}
Incluimos este tpl en el layout.tpl
contact-links.tpl
Estos son los links de contacto, configurados desde el Administrador nube en la sección Personalizar/Información de contacto.
Se muestran tanto en el footer como en la página de contacto, exhibiendo información sobre:
- Teléfono o WhatsApp
- Dirección del local
- URL a un blog
{# /*============================================================================ #Contact links ==============================================================================*/#} <ul class="contact-info text-center{% if columns %} row{% endif %}"> {% if store.whatsapp %} <li class="contact-item{% if columns %} col-6 col-md{% endif %}"> {% include "snipplets/svg/whatsapp.tpl" with {svg_custom_class: "icon-inline icon-lg icon-w mx-2 svg-icon-text"} %} <a href="{{ store.whatsapp }}" class="contact-link">{{ store.whatsapp |trim('https://wa.me/') }}</a> </li> {% elseif store.phone %} <li class="contact-item{% if columns %} col-6 col-md{% endif %}"> {% include "snipplets/svg/phone.tpl" with {svg_custom_class: "icon-inline icon-lg icon-w mx-2 svg-icon-text"} %} <a href="tel:{{ store.phone }}" class="contact-link">{{ store.phone }}</a> </li> {% endif %} {% if store.email %} <li class="contact-item{% if columns %} col-6 col-md{% endif %}"> {% include "snipplets/svg/envelope.tpl" with {svg_custom_class: "icon-inline icon-lg icon-w mx-2 svg-icon-text"} %} <a href="mailto:{{ store.email }}" class="contact-link">{{ store.email }}</a> </li> {% endif %} {% if store.address %} <li class="contact-item{% if columns %} col-6 col-md{% endif %}"> {% include "snipplets/svg/map-marker-alt.tpl" with {svg_custom_class: "icon-inline icon-lg icon-w mx-2 svg-icon-text"} %} {{ store.address }} </li> {% endif %} {% if store.blog %} <li class="contact-item{% if columns %} col-6 col-md{% endif %}"> {% include "snipplets/svg/comments.tpl" with {svg_custom_class: "icon-inline icon-lg icon-w mx-2 svg-icon-text"} %} <a target="_blank" href="{{ store.blog }}" class="contact-link">{{ "Visita nuestro Blog!" | translate }}</a> </li> {% endif %} </ul>
Incluimos este tpl en el snipplet footer.tpl y el tamplate contact.tpl
logo-icons.tpl
Incluye los logos de medios de pago y medios de envío que se muestran dentro del footer.
{% if payments %} {% for payment in settings.payments %} <img src="{{ 'images/empty-placeholder.png' | static_url }}" data-src="{{ payment | payment_new_logo }}" class="icon-logo lazyload"> {% endfor %} {% elseif shipping %} {% for shipping in settings.shipping %} <img src="{{ 'images/empty-placeholder.png' | static_url }}" data-src="{{ shipping | shipping_logo }}" class="icon-logo lazyload"> {% endfor %} {% endif %}
Incluimos este tpl en el snipplet footer.tpl
facebook-login.tpl
Incluye el botón para logearse o crear una cuenta usando Facebook.
{% if store_fb_app_id %} <a class="btn btn-primary btn-facebook d-block mb-4" onclick="loginFacebook();" > {% include "snipplets/svg/facebook.tpl" with {svg_custom_class: "icon-inline icon-2x mr-1 svg-fb-icon align-middle"} %} <span class="align-middle">{{ 'Facebook login' | translate }}</span> </a> {% if result.facebook and result.invalid %} <div class="alert alert-danger">{{ 'Hubo un problema con el login de Facebook.' | translate }}</div> {% endif %} {% endif %}
Incluimos este tpl en los templates login.tpl y register.tpl
card.tpl
Es un componente que consiste en una tarjeta, con head, body y footer; similar a los “panels” de Bootstrap.
{# /*============================================================================ #Card ==============================================================================*/ #Head // Block - card_head #Body // Block - card_body #Footer // Block - card_footer #} <div class="card"> <div class="card-header"> {% block card_head %}{% endblock %} </div> <div class="card-body"> {% block card_body %}{% endblock %} </div> {% if card_footer %} <div class="card-footer"> {% block card_foot %}{% endblock %} </div> {% endif %} </div>
Incluimos este tpl en el template orders.tpl
newsletter.tpl
Es el formulario de newsletter que usamos en el footer del theme.
Incluye un solo campo de email así como la posibilidad de agregar un título y un texto personalizado.
{% set newsletter_contact_error = contact.type == 'newsletter' and not contact.success %} <div class="row justify-content-md-center"> <div class="col-md-8 text-center"> <div class="js-newsletter newsletter section-footer"> {% if settings.news_title %} <h3>{{ settings.news_title }}</h3> {% endif %} {% if settings.news_text %} <p>{{ settings.news_text }}</p> {% endif %} {% if contact and contact.type == 'newsletter' %} {% if contact.success %} <div class="alert alert-success">{{ "¡Gracias por suscribirte! A partir de ahora vas a recibir nuestras novedades en tu email" | translate }}</div> {% else %} <div class="alert alert-danger">{{ "Necesitamos tu email para enviarte nuestras novedades." | translate }}</div> {% endif %} {% endif %} <form method="post" action="/winnie-pooh" onsubmit="$(this).attr('action', '');"> <div class="input-append"> {% embed "snipplets/forms/form-input.tpl" with{input_for: 'email', type_email: true, input_name: 'email', input_id: 'email', input_placeholder: 'Email' | translate, input_aria_label: 'Email' | translate } %} {% endembed %} <div class="winnie-pooh" style="display: none;"> <label for="winnie-pooh-newsletter">{{ "No completar este campo" | translate }}</label> <input id="winnie-pooh-newsletter" type="text" name="winnie-pooh"/> </div> <input type="hidden" name="name" value="{{ "Sin nombre" | translate }}" /> <input type="hidden" name="message" value="{{ "Pedido de inscripción a newsletter" | translate }}" /> <input type="hidden" name="type" value="newsletter" /> <input type="submit" name="contact" class="btn newsletter-btn" value='{{ "Enviar" | translate }}'> </div> </form> </div> </div> </div>
Incluimos este tpl en el snipplet footer.tpl
Banners services
Los tpls dentro de esta carpeta mostramos el código para los banners que muestran información de:
- Pagos
- Envíos
- Seguridad
- Cambios y devoluciones
- Promociones
- Pago en efectivo
banner-services.tpl
Es el archivo que incluye el for para cada item dentro de estos banners.
{% macro for_each_banner_include(template) %} {% set num_banners_services = 0 %} {% set available_banners = []%} {% for banner in ['banner_services_01', 'banner_services_02', 'banner_services_03'] %} {% set banner_services_icon = attribute(settings,"#{banner}_icon") %} {% set banner_services_title = attribute(settings,"#{banner}_title") %} {% set banner_services_description = attribute(settings,"#{banner}_description") %} {% set banner_services_url = attribute(settings,"#{banner}_url") %} {% set has_banner_services = banner_services_title or banner_services_description %} {% if has_banner_services %} {% set num_banners_services = num_banners_services + 1 %} {% set available_banners = available_banners | merge([banner]) %} {% endif %} {% endfor %} {% for banner in available_banners %} {% set banner_services_title = attribute(settings,"#{banner}_title") %} {% set banner_services_icon = attribute(settings,"#{banner}_icon") %} {% set banner_services_description = attribute(settings,"#{banner}_description") %} {% set banner_services_url = attribute(settings,"#{banner}_url") %} {% include template %} {% endfor %} {% endmacro %} {% import _self as banner_services %} {% if settings.banner_services %} <section class="section-informative-banners" data-store="banner-services"> <div class="container"> <div class="row"> <div class="js-informative-banners swiper-container"> <div class="swiper-wrapper"> {{ banner_services.for_each_banner_include('snipplets/banner-services/banner-services-item.tpl') }} </div> <div class="js-informative-banners-pagination service-pagination swiper-pagination swiper-pagination-black"></div> </div> </div> </div> </section> {% endif %}
Incluimos este tpl en el template home.tpl
banner-services-item.tpl
Este tpl es para cada item dentro del grupo de banners informativos.
<div class="service-item-container col-md swiper-slide p-0 px-md-3"> <div class="service-item row justify-content-md-center text-md-left"> <div class="col-md-auto"> {% if banner_services_icon == 'shipping' %} {% include "snipplets/svg/truck.tpl" with {svg_custom_class: "icon-inline icon-w-20 icon-2x service-icon"} %} {% elseif banner_services_icon == 'card' %} {% include "snipplets/svg/credit-card-blank.tpl" with {svg_custom_class: "icon-inline icon-w-18 icon-2x service-icon"} %} {% elseif banner_services_icon == 'security' %} {% include "snipplets/svg/lock.tpl" with {svg_custom_class: "icon-inline icon-w-14 icon-2x service-icon"} %} {% elseif banner_services_icon == 'returns' %} {% include "snipplets/svg/sync-alt.tpl" with {svg_custom_class: "icon-inline icon-w-16 icon-2x service-icon"} %} {% elseif banner_services_icon == 'whatsapp' %} {% include "snipplets/svg/whatsapp.tpl" with {svg_custom_class: "icon-inline icon-w-16 icon-2x service-icon service-icon-big"} %} {% elseif banner_services_icon == 'promotions' %} {% include "snipplets/svg/tag.tpl" with {svg_custom_class: "icon-inline icon-w-16 icon-2x service-icon"} %} {% elseif banner_services_icon == 'cash' %} {% include "snipplets/svg/dollar-sign.tpl" with {svg_custom_class: "icon-inline icon-w-9 icon-2x service-icon"} %} {% endif %} </div> <div class="col"> {% if banner_services_url %} <a href="{{ banner_services_url }}"> {% endif %} <h3 class="service-title">{{ banner_services_title }}</h3> <p>{{ banner_services_description }}</p> {% if banner_services_url %} </a> {% endif %} </div> </div> </div>
Incluimos este tpl en el snipplet banner-services.tpl
video-item.tpl
Este tpl contiene al componente de video usado tanto en la página dei inicio como en los productos.
{% if product_modal %} {# Product video modal wrapper #} <div id="product-video-modal" class="js-product-video-modal product-video" style="display: none;"> {% endif %} <div class="js-video {% if product_video %}js-video-product{% endif %} embed-responsive embed-responsive-16by9 visible-when-content-ready"> {% if product_modal_trigger %} {# Open modal in mobile with product video inside #} <a href="#product-video-modal" data-fancybox="product-gallery" class="js-play-button video-player d-block d-md-none"> <div class="video-player-icon">{% include "snipplets/svg/play-circle.tpl" with {svg_custom_class: "icon-inline svg-icon-invert"} %}</div> </a> {% endif %} <a href="#" class="js-play-button video-player {% if product_modal_trigger %}d-none d-md-block{% endif %}"> <div class="video-player-icon"> {% include "snipplets/svg/play-circle.tpl" with {svg_custom_class: "icon-inline svg-icon-invert"} %} </div> </a> <div class="js-video-image"> <img src="{{ 'images/empty-placeholder.png' | static_url }}" data-src="" class="lazyload video-image fade-in" alt="{{ 'Video de' | translate }} {% if template != 'product' %}{{ product.name }}{% else %}{{ store.name }}{% endif %}" style="display: none;"> <div class="placeholder-fade"> </div> </div> </div> {# Empty iframe component: will be filled with JS on play button click #} <div class="js-video-iframe embed-responsive embed-responsive-16by9" style="display: none;" data-video-color="{{ settings.primary_color | trim('#') }}"> </div> {% if product_modal %} </div> {% endif %}
Incluimos este tpl en los snipplets home-video.tpl y product-video.tpl