Entendendo o .liquid

Liquid é a linguagem de programação responsável por implementar os templates desenvolvidos e utilizados pela Vnda.

Filtros

filtros para alterar a saída de uma váriavel:

subtração  {{ 4 | minus:2 }} #=> 2
      

minus


adição {{ '1' | plus:'1' }} #=> '11', {{ 1 | plus:1 }} #=> 2
      

plus


multiplicação {{ 5 | times:4 }} #=> 20
      

times


divisão {{ 10 | divided_by:2 }} #=> 5
      

divided_by


retorna o elemento da primeira posição do array
      

first


retorna o elemento da última posição do array
      

last


Junta-se aos elementos de uma matriz com o caráter passado como o parâmetro. O resultado é uma única cadeia.

{{ product.tags | join: ',' }}

Retorno:
tag1, tag2, tag3
      

join


sort elements of the array
      

sort


retorna o tamanho de array ou string

{{ "esta é uma sequência de 38  caracteres" | split: " " | size }}

Retorno:
38

size


Codifica uma string

{{ "<p>Loren Ipsum</p>" | escape }}

Retorno
<p>Loren Ipsum</p>
  

escape


Remove elementos html da string

{{ "<h1>Olá Mundo</h1> World" | strip_html }}

Olá Mundo

strip_html


Remove todas as novas linhas (\n) da string

strip_newlines


Substitui each newline (\n) with html break(<br>)

newline_to_br


Substitui todas as ocorrências de uma cadeia com uma substring.
{{ 'Sites Ecommerce' | replace:'Sites','Sites para' }}

Retorno:
Sites para Ecommerce

replace


Substitui as primeira ocorrência de uma cadeia com uma substring.

{{ 'barbar' | replace_first:'bar','foo' }}

Retorno:
foobar

replace_first


Remove as ocorrência de uma cadeia com uma substring.

{{ 'foobarfoobar' | remove:'foo' }}

Retorno:barbar

remove


Remove a primeira ocorrência de uma cadeia com uma substring.

{{ 'barbar' | remove_first:'bar' }}

Retorno:
bar

remove_first


Trunca uma cadeia de caracteres para x caracteres. Ele também aceita um segundo parâmetro que será anexada à cadeia
{{ 'foobarfoobar' | truncate: 5, '.' }}

Retorno:
foob.

truncate


Trunca uma cadeia de caracteres para x palavras. Ele também aceita um segundo parâmetro que será anexada à cadeia

{{ 'truncar uma cadeia de caracteres para x palavras' | truncatewords: 2 }}

Retorno:
truncar uma

truncatewords


Adiciona uma string no final da cadeia de caracteres

{{ 'bar' | prepend:'foo' }}

Retorno:
foobar

prepend


Adiciona uma string no inicio da cadeia de caracteres

{{ 'foo' | append:'bar' }}

Retorno:
foobar

append


Transforma um string em array de acordo com o valor informado no parâmetro

{{ "a~b" | split:"~" }}

Retorno:
['a','b']

{% assign array = "a~b" | split:"~" %}

{{ array[0] }}
Retorno:
a

{{ array[1] }}
Retorno:
b

split


Resto da divisão

{{ 3 | modulo:2 }}

Retorno:
1

modulo

Formata a date

{{ order.received_at | date: '%m/%d/%Y') }}

date


Transforma a primeira letra da cadeia de caracteres em caixa alta

{{ 'loren ipsum loren ipsun' | capitalize }}

Retorno:
Loren ipsum loren ipsun

capitalize


Transforma a cadeia de caracteres em caixa baixa

{{ 'LOREN IPSUN LOREN IPSUN' | downcase }}

Retorno:
loren ipsun loren ipsun

downcase


Transforma a cadeia de caracteres em caixa alta

{{ 'loren ipsun loren ipsun' | upcase }}

Retorno:
LOREN IPSUN LOREN IPSUN

upcase


Tags Nativas do .liquid

Tags são usadas para a lógica, criar variáveis, includes dentre outras técnicas e recurso nativos da linguagem para auxiliar a implementação do seu template. Abaixo está uma lista de tag atualmente suportadas.

{% assign %} - Criando Variáveis

// Criando uma variável

{% assign name = 'freestyle' %}

{{ name }}

Retorno:
freestyle

Criando Variáveis - {% assign %}


{% capture %} - Armazenando conteudo

// Atualizando o conteúdo em lista_produto
{% capture lista_produto %}
  
Item 1
{% endcapture %} {% capture lista_produto %}
Item 2
{% endcapture %} {% capture lista_produto %}
Item 3
{% endcapture %} {{ lista_produto }} retorno: Item 3

O {% capture %} é usado para atualizar conteúdo dentro da variável "nome_da_variavel", contrário da tag {% contentfor %} que atualiza o conteúdo.


{% case %} - Condicional


{% assign condition = 2 %}
{% case condition %}
{% when 1 %}
 caso 1
{% when 2 or 3 %}
 caso 2 ou 3
{% else %}
 nenhum dos casos
{% endcase %}

Retorno:
caso 2 ou 3

A instrução case é usada para executar ações diferentes com base em diferentes condições.


{% comment %} - Comentários

Fizemos 1 milhão de dólares {% comment %} em perdas {% endcomment %} este ano

Retorno:
Fizemos 1 milhão de dólares este ano

Bloco de código que não será carregado na renderização do html na página


{% cycle %} - Interações no loop

Lista de Produtos com 5 interações
<li class="{% cycle 'one', 'two', 'three' %}">Item {{ forloop.index }}</li>

Retorno:
<li class="one">Item 1</li>
<li class="two">Item 2</li>
<li class="three">Item 3</li>
<li class="one">Item 4</li>
<li class="two">Item 5</li>

Cycle é usado geralmente dentro de um loop para alternar entre os valores, como cores ou classes css.


{% for %} - laço

{% for item in array %}
{{ item }}
{% endfor %}

// When iterating a hash, item[0] contains the key, and item[1] contains the value:

{% for item in hash %}
{{ item[0] }}: {{ item[1] }}
{% endfor %}

Durante cada loop for, as seguintes variáveis auxiliares estão disponíveis para necessidade adicionais

forloop.length      # => tamanho de todo o ciclo for
forloop.index       # => índice da iteração atual
forloop.index0      # => índice da iteração atual (iniciando em zero)
forloop.rindex      # => quantos itens ainda são deixados?
forloop.rindex0     # => quantos itens ainda são deixados?? (iniciando em zero)
forloop.first       # => É esta a primeira iteração
forloop.last        # => É esta a última iteração

Há vários atributos que você pode usar para influenciar os itens que você recebe em seu loop

limite: int permite restringir quantos itens que você recebe. offset: int permite que você comece a coleção com o item enésimo.

# array = [1,2,3,4,5,6]
{% for item in array limit:2 offset:2 %}
{{ item }}
{% endfor %}

Retorno:
3,4

Invertendo o loop
{% for item in collection reversed %} {{item}} {% endfor %}

Em vez de um loop sobre uma coleção existente, você pode definir um intervalo de números para percorrer. O intervalo pode ser definido por ambos os números literais e variáveis:

# if item.quantity is 4...
{% for i in (1..item.quantity) %}
{{ i }}
{% endfor %}

Retorno:
1,2,3,4

Loops em coleções de arrays:


{% break %} sair da interação

A linguagem aceita a tag {% break %} para sair d laço de interações, por exemplo: {% for %} nativo da linguagem e até mesmo das tags personalizadas {% tags %}, {% products %}, {% banner %} {% menus %} etc ...

{% continue %} ir para próxima interação

Pula para o próximo laço da interação use a tag {% continue %}.

{% if %} - Condicional

{% if "online" == "online" %}
  online
{% endif %}

Retorno:
online

{% if "online" == "ofline" %}
  ofline
{% elsif "online" == "online" %}
  online
{% endif %}

Retorno:
online

{% if "online" == "ofline" %}
  ofline
{% elsif "online" == "ofline" %}
  online
{% else %}
  ofline
{% endif %}

Retorno:
ofline


If's são usadas para executar ações diferentes com base em diferentes condições.


{% unless %} - Condicional

Criando um array com 5 posições apartir de um string
{% assign array = "1,2,3,4,5" | split: ',' %}

Percorrendo a variável array

{% for i in array %}
  {% unless i == "3" %}
    {{ i }}
  {% endunless %}
{% endfor %}

Retorno:
1,2,4,5

Executação as interação ao menos que o resultado da condição não seja verdadeiro


Templates

Os templates ficam na raiz da pasta compartilhada no Dropbox onde todos os arquivo são salvos na extensão .liquid

Dentro da raiz ficam os diretórios "images" "stylesheets" "javascript" e "page"

Dentro do diretórios page/ é gerado os arquivos de extensão .html das página que são criados no admin.

O conteúdo do LAYOUT.LIQUID estará presente em todos os templates abaixo. Por este motivo, é o local indicado para colocar o cabeçalho e rodapé da loja. Todos os templates abaixo serão incluídos dentro deste layout, usando na variável {{ yield }}.

Segue abaixo os templates disponíveis na plataforma:

Home.liquid

Este é o template corresponde a página inicial da loja. Aqui podem ser exibidos produtos, banners, menus e qualquer informação que atraia o cliente a navegar no resto do site. -- A variável {{ tag }} conterá a tag de nome "capa", quando esta estiver cadastrada no Painel Administrativo.

Tag.liquid

Página que exibe produtos relacionados a uma tag. Tag é a forma de categorização utilizada pela Vnda para organizar os produtos.

        {{ tag.id }}
        {{ tag.name }}
        {{ tag.title }}
        {{ tag.subtitle }}
        {{ tag.description }}
        {{ tag.type }}
        {{ tag.image_url }}
      

Dentro do template tag.liquid é possível exibir informações da tag em questão, que foram cadastradas no admin usando as seguintes variáveis:

Nesse template é muito comun usar banners de posição de tag.
basta criar uma variável com o nome da tag e chamar esta posição de banner.

      {% assign banner_tag_name = "categoria-" | append: tag.name %}
      {% banners position:banner_tag_name %}
      ....
      {% endbanners %}
      

Listar produtos

{% products tag:tag.name parent_tag:parent_tag.name page:params.page %}
  ...
{% endproducts %}
      

Listagem de produtos com a tag {% products %}



Configurando a páginação na tag

{% products tag:tag.name page:params.page per_page:20 %}
// executa no primeiro loop
{% if forloop.first %}
<ul class="products">
{% endif %}
// include com o arquivo com a listagem de produtos
{% include "list_product" %}
// executa no último loop
{% if forloop.last %}
</ul>
{% contentfor paginacao %}
// armazena o conteúdo de "<nav>" para dentro variável paginacao
{% if pagination.total_pages > 1 %}
<nav class="pagination">
<span class="pagination">
    Página {{ pagination.current_page }} de {{ pagination.total_pages }}
    {% if pagination.prev_page %}
        <a href="{{ pagination.prev_url }}" class="previous"></a>
    {% endif %}
    {% for page in pagination.pages %}
      <li class="{% if current_url == page.url %} active {% endif %}"><a href="{{ page.url }}">{{ page.number }} {% if current_url == page.url %}{% endif %}
    {% endfor %}
    {% if pagination.next_page %}
        <a href="{{ pagination.next_url }}" class="next"></a>
    {% endif %}
</span>
</nav>
{% endif %}
{% endcontentfor %}
{% endif %}
{% endproducts %}

// exibe o conteúdo da variável paginacaos
<div>{{ paginacao }}</div>

O código da paginação deve ser executado pelo menos 1 vez dentro do loop de % products, o mesmo que é usado para carregar uma listagem de produtos.
ex: {% products %} {% if forloop.last %} // código {% endif %}{% endproducts %}

Use o {% if forloop.first %} ou {% if forloop.last %} incluir o script de paginação.

**importante: use o {% contentfor %} para armazenar o bloco da páginação e chamar em outra parte do código:

**a páginação funcionára só para páginas de tag.

**Por padrão, a tag é páginada em 25 itens. Para alterar, basta inserir um valor diferente para o parâmentro [ per_page: ] conforme mostrado no exemplo.

** É possível limitar a quantidade de itens de uma tag, usando o parâmetro [ limit:Valor_desejado]. Este limite desativa a paginação. É útil na página de busca, caso alguma busca possa retornar muitos resultados, para evitar perda de perorma

Product.liquid

Template para exibição do produto, suas imagens, produtos relacionados. Aqui também fica o formulário que permite adicionar o item ao carrinho, neste formulário o cliente pode selecionar qual atributo do produto (cor, tamanho, etc) ele realmente quer.

Para exibir o nome, preço, remarcações, disponibilidade, parcelas utilize tags dentro do loop {% product %}

{{ product.name }}
// Nome do produto

{{ product.slug }}
// Slug da url do produto

{{ product.url }}
// Url absoluta do produto

{{ product.updated_at }}
// Data da última atualização

{{ product.id }}
// ID numeral do produto

{{ product.reference }}
// Referência do produto

{{ product.sku }}
// SKU principal - Para exibir demais sku(s), usar tag variants

{{ product.available }}
// Disponibilidade (true/false)

{{ product.stock }}
// Quantidade em estoque

{{ product.min_quantity }}
// Quantidade mínima para compra

{{ product.price | money_format }}
// Preço normal

{{ product.on_sale }}
// Se esta em promoção (true/false)

{{ product.sale_price money_format }}
// Preço promocional

{{ product.discount_id }}
// ID de desconto, para exibir texto diferenciado

{{ product.installments }}
// Opções de parcelamento

{{ product.description }}
// Descrição do produto

{{ product.rating }}
// Avaliação - Em fase de testes

{{ product.variants }}
// Variantes do produto - Obsoleto

{{ product.image_url }}
// Imagem principal do produto

{{ product.tag_names }}
// Exibe o slug das tags presentes no produto

{{ product.category_tags }}
// Exibe um loop contendo informações das tags tipadas.

Exemplo:
{% for category in product.category_tags %}
  {% if category.tag_type == "nome-do-tipo-da-tag" %}
    {{ category.name }}
    {{ category.title }}
  {% endif %}
{% endfor %}

Enviar produto ao carrinho

**As configurações de atributo devem estar ativadas no admin.

***quando o atributo nao é marcado como "opcional de compra" nao é obrigatório montar o sku apartir do valor contido neste atributo.

<form action="{{ cart_item_url }}" method="post">
  <input type="hidden" name="sku">
  <input type="number" name="quantity" value="1">
</form>
      

Enviar um formulário com action {{ cart_item_url }} e os campos sku e quantidade obrigatórios.


Para acessar os atributos é preciso usar a tag {% variants %} ou carregá-los via javascript injetando os elementos de seleção no html, para isto basta iniciar um variável no seu arquivo.js como por ex:
variant = vnda.variants.all().

** Importante ter noções de programação orientada a objetos
Exemplo de uso para um produto com 2 atributos

// No .liquid
{% variants product_id:product.id %}
{{variant.properties["property1"]["value"]}}
{{variant.properties["property2"]["value"]}}
{% endvariants %}

// Arquivo .js
$(function(){
  // variável que recebe os objetos com as informaçoes de cada variante
  variants = vnda.variants.all()
})
      
Listar variantes de um atributo
// Variantes do produtos
camiseta tamanho M #000
camiseta tamanho M #fff
camiseta tamanho M #333
camiseta tamanho G #000
camiseta tamanho G #333

// Quais as variantes com tamanho M?
$(function(){
  variants = vnda.variants.all()
  var array = vnda.variants.find({Tamanho: "M" })
  // variável array recebe o objeto json com todas as informações
  // de variante com tamanho M
  // apartir desses dados é possível montar o html
  // com todas as cores desse atributo
})

      

Para exibir as variantes que pertencem ao um mesmo atributo usamos o javascript.

**só para os casos de produtos com mais de 1 atributo

Como montar o sku?

Como já dito antes, para que um item seja adicionado ao carrinho é preciso enviar um campo com o valor do sku da variante(há casos que é preciso montar um sku) e enviar para o url "/carrinho/adicionar".

$(function(){
  // filtro que retorna variantes com o rgb #000
  vnda.variants.find({Cor: "#000" })
})
      

Para montar e/ou enviar o sku para /carrinho/adicionar vai depender da quantidade de atributos do produto ou escopo do projeto, sendo viável carregar via js ou tag por .liquid.

LISTANDO O SKU

** Para produtos com 1 variante


{% variants product_id:product.id %}
  {{ variant.sku }}
{% endvariants %}

ou

$(function(){
  // variável que recebe os objetos com as informaçoes de cada variante
  var variants = vnda.variants.all()
  // acessando a propriedade sku do objeto
  variants.sku
})
      

Método mais simples, é recomendável usar a tag {{ variant.sku }} do .liquid no loop {% variants %}.


**Para produtos com mais de uma variante

Podemos dividir em 2 casos: os produtos com 1 ou 2 atributos.

// montar o sku com 1 atributos
variantes do produto:
camiseta tamanho M
camiseta tamanho G
camiseta tamanho GG

Qual o sku da variante com tamanho G?

$(function(){
  variants = vnda.variants.all()
  // lista o valor de sku para a variante com o tamanho G
  vnda.variants.find({Tamanho: "G"})[0].sku
})

// montar o sku com 2 atributos
camiseta tamanho M  #000
camiseta tamanho M  #fff
camiseta tamanho M  #333

Qual o sku da variante com tamanho M  e cor #000 ?

$(function(){
  variants = vnda.variants.all()
  // lista o valor de sku para a variante com o tamanho G
  vnda.variants.find({Tamanho: "G", Cor:"#000" })[0].sku
})
      

Mensagem de Erro

{% if flash.cart.error  %}{{ flash.cart.error }}{% endif %}
      

Quando existir algum erro no formulário de compra »

Listar imagens do produto

{% images %}
  {{ image.url }}
{% endimages %}
      ou
{% images sku:sku %}
  {{ image.url }}
{% endimages %}
    

Para exibir todas imagens cadastradas de um produto deve-se utilizar o tag {% imagens %}. Se prefirir listar apenas as imagens de uma variante x, utilize o parâmetro "sku" {% images sku:sku %} mais detalhes aqui.

Produtos Também Vistos

{% also_viewed id:product.id key:session_id %}
{{ product }}
{% endalso_viewed %}
      

* geralmente utilizado na página de produto

Cart.liquid

1ª etapa do processo de compra.
Neste template é listados os produtos do carrinho que ficam armazenados na váriavel {{ cart }}, o resumo do carrinho(padrão no layout), o campo input para o cálcudo do CEP e o formulário de frete(métodos de entrega).

{% if cart.items_count > 0 %}
  {{ cartitems }}
{% else %}
  Carrinho Vazio
{% endif %}
      

* O campo do CEP só deve aparecer quando o carrinho possuir ao menos 1 item adicionado. Para verificar use {% if cart.items_count > 0 %} no .liquid.


Atualizar item do carrinho

<form action="{{ cart_url }}" method="post">
  <input type="hidden" name="_method" value="patch" />
  <input type="hidden" name="item_id" value="999" />
  <input type="hidden" name="quantity" value="1" />
  <input type="submit" value="Alterar"/>
</form>
      

Para alterar a quantidade de item do carrinho, você precisa fazer um POST para a URL definida na variável {{ cart_url }}

Remover item do carrinho

<form action="{{ cart_url }}" method="post">
  <input type="hidden" name="_method" value="Delete" />
  <input type="hidden" name="item_id" value="999" />
  <input type="submit" value="Excluir"/>
</form>
      

Para Remover um produto do carrinho, você precisa fazer um POST para a URL definida na variável {{ cart_url }}, passando dois campos:
* item_id: Id do item selecionado pelo usuário
* _method: Informar o value "Delete"


  $.post("{{ cart_url }}", { item_id: 999, cart_method: "Delete" }, function() {
    alert("Produto Removido com sucesso");
  });
      

Ou via AJAX:

Calcular frete

<form action="{{ shipping_zip_url }}" method="post">
  <fieldset>
    <p>
      <input  type="text" name="zip" value="{{ shipping_address.zip }}">
      <button type="submit" name="submit-cep">Calcule seu frete</button>
    </p>
  </fieldset>
 </form>
      

Utilizar o {{ shipping_zip_url }} na action * validar cep pode ser utilizado o flash.zip.error


{{ flash.zip.error }} // exibe mensagem de erro padrão
  ou
{% if flash.zip.error %}
  ....
{% endif %}
      

Quando existir algum erro no calculo do frete >>

Método de entrega

// se o frete já foi calculado
// exibe no html o formulário com os métodos de
{% if shipping_address.zip %}
  <form action="{{ shipping_method_url }}" method="POST">
    {% shippingmethods %}
      <input type="radio" name="type" value="{{ shipping_method.value }}"">
      {{ shipping_method.name }}
      {{ shipping_method.price | money_format }}
      {{ shipping_method.description }}
      //valor que falta para o frete grátis
      {{  shipping_method.value_needed_to_discount }}
    {% endshippingmethods %}
{% endif %}
  <form>
      

Faça um form com action "{{ shipping_method_url }} e com input type radio com name="type".

Não avançar nas etapas do carrinho sem ao menos um método escolhido

Cupom de Desconto

{% if template == "cart" %}
  {% unless shipping_address.zip %}
    <form action="{{ coupon_url }}" method="post" class="coupon-form">
        <fieldset>
            <label for="coupon-code" class="coupon-label">INSIRA SEU CUPOM DE DESCONTO</label>
            <input type="text" name="code" id="coupon-code" value="{{ cart.coupon_code }}"<span>>
            <button type="submit" class="ir">OK</button>
        </fieldset>
    </form>
{% endunless %}
{% endif %}
      

* O cupom de desconto só deve aparecer neste template, umas das formas é verificando se a tag "template" é igual a "cart" e se o cep ainda não foi informado.

O form de cupom de desconto deve usar a action {{ coupon_url }}


Formulário de Cupom de Desconto

  <form action="{{ coupon_url }}" method="post" class="coupon-form">
      <fieldset>
          <label for="coupon-code" class="coupon-label">INSIRA SEU CUPOM DE DESCONTO</label>
          <input type="text" name="code" id="coupon-code" value="{{ cart.coupon_code }}"<span>>
          <button type="submit" class="ir">OK</button>
      </fieldset>
  </form>
      

O form de cupom de desconto deve usar a action {{ coupon_url }}


{% if flash.coupon.error %}
  {{ flash.coupon.error }}
{% endif %}
      

Quando existir algum erro no formulário de cupom de desconto »


Ápós o preechimento do CEP a url da loja é redirecionada para /frete. Também é listado o conteúdo da variável {{ shipping_method }} que contém os dados para montar o html do formulário de frete.
* use no action {{ shipping_method_url }} » ver implementação no .liquid

Após validar e enviar o formulário do frete o site é redirencionado para a próxima etapa.

Address.liquid

3ª etapa do processo de compra

Template onde é exibido o formulário responsável por capturar as informações para entrega do pedido, assim como dados pessoais do cliente, como e-mail e CPF.

Salvar dados do Endereço

A plataforma Vnda permite que pedidos sejam feitos tanto para pessoas físicas, quanto jurídicas. O que define quando é um ou outro é o campo documents, se ele possuir a chave cpf e for um CPF válido, então é pessoa física, se ele possuir a chave cnpj e for um CNPJ válido, é pessoa júridica.

<form action="{{ shipping_address_url }}" method="POST">
  ...
</form>
      

O formulário deve ser enviado para a URL definida na variável {{ shipping_address_url }}. Ao enviar dados para entrega e cobrança utilize a váriavel {{ addresses_url }} mais detalhes aqui


Pessoa Física

O cadastro de pessoa fisica, exige os seguinte campos:

Antes de confirir a tabela abaixo, é importante para seu conhecimento compreender que os campos zip e state são sempre extraídos do CEP que foi informado pelo cliente e não podem ser alterado sem que o CEP seja informado novamente e o frete seja recalculado.

Input "name" Descrição
first_name Primeiro nome do cliente.
last_name Sobrenome do cliente.
email Um e-mail válido.
documents[cpf] CPF válido.
first_phone_area Código de área do um telefone do cliente.
first_phone Número de um telefone do cliente.
second_phone_area Código de área do um telefone do cliente.
second_phone Número de um telefone do cliente.
street_name Nome da rua que o pedido será enviado.
street_number Número da casa/prédio que o pedido será enviado.
neighborhood Nome do bairro que o pedido será enviado.
**Campos opcionais Descrição
cellphone_area Código de área de um celular do cliente.
cellphone Número de um telefone celular do cliente.
complement Informação como número apartamento/casa/lote do cliente. Ou qualquer outra informação necessária para a entrega do pedido.

tabela 1.1

Pessoa Júridica

O cadastro de pessoa júridica necessita dos seguintes campos:

Esse cadastro ainda pode ter os mesmos campos opcionais que o cadastro de pessoa fisica tem, além de seguir a mesma regra para os campos city e state.

O campo documents permite que você envie até cinco tipos de documentos diferentes. Você pode solicitar no cadastro documentos como, por exemplo, RG e/ou Inscrição Estadual.

Input "name" Descrição
first_name Primeiro nome da pessoa que está realizando a compra.
last_name Sobrenome da pessoa que está realizando a compra.
company_name Nome da empresa.
email Um e-mail válido.
documents[cnpj] CNPJ válido.
first_phone_area Código de área do telefone da empresa.
first_phone Número do telefone da empresa.
street_name Nome da rua que o pedido será enviado.
street_number Número da casa/prédio que o pedido será enviado.
neighborhood Nome do bairro que o pedido será enviado.

tabela 1.2

Dados para cobrança/entrega

Formulários com dados diferentes para cobrança e entrega, respectivamente serão diferenciados pelas variáveis "billing_address" ou "shipping_address".

Atenção: neste caso o action do formulário da página de endereço deve mudar para "/enderecos" = {{ addresses_url }}

Dados para Cobrança

Atributo "name" Atributo "value"
billing_address[first_name] {{ billing_address.first_name }}
billing_address[last_name] {{ billing_address.last_name }}
...

Dados para entrega

Atributo "name" Atributo "value"
shipping_address[first_name] {{ shipping_address.first_name }}
shipping_address[last_name] {{ shipping_address.last_name }}
...

Dados para Entrega e Pagamentos são os mesmo?

Para que os campos de entrega e cobrança sejam o mesmo não é necessário usar a variável "billing_address", utilize no value "shipping_address.[nome_do_campo]", automaticamente o sistema usará os mesmos dados de entrega para cobrança.

Atributo "name" Atributo "value"
first_name {{ shipping_address.first_name }}
last_name {{ shipping_address.last_name }}
...

tabela 1.3






Payment.liquid

3ª etapa do processo de compra. Aqui o cliente informa os dados do cartão de crédito ou dá inicio a geração do boleto.

Enviar dados para pagamento

<form action="{{ payment_url }}" method="POST"></form>
      

O form usado para enviar esses dados deve estar com o atributo action definido para o valor da variável {{ payment_url }}, dessa forma

Cartão Crédito

Para enviar dados de pagamento usando cartão de crédito, os seguintes campos são obrigatórios:

O form usado para enviar esses dados deve estar com o atributo action definido para o valor da variável {{ payment_url }}, dessa forma:

Caso a transação seja efetuada com sucesso, o cliente é automaticamente redirecionado para a página de conclusão da compra.

Input "name" Descrição
payment_method O valor desse campo deve ser sempre credit para compras com cartão de crédito.
card_flag É bandeira do cartão do cliente, ele deve selecionar isso em campos do tipo select ou radio.
card_number Campo onde o cliente informa o número do cartão.
card_code Campo para o código de segurança do cartão.
card_owner_name Campo onde o cliente informa o nome impresso no cartão.
card_month Número mês que vence o cartão do cliente.
card_year Número do ano, começando no ano atual, que vence o cartão do cliente.
installments Número de parcelas que o cliente deseja fazer a compra.
Listar as parcelas
{% installments %}
  <input type="radio" name="installments" value="{{ installment.number }}" />
  {{ installment.number }}x de
  {{ installment.price | money_format }}
  {% if installment.interest %}
    juros {{ installment.interest_rate }} a.m. ({{ installment.total | money_format}})
  {% else %}
    sem juros
  {% endif %}
{% endinstallments %}
      

Para listar as parcelas utilize a variável "installments".

*Dentro do loop "installments" verificar se há juros.

Dados para teste

Visa
  • Número: 4012001038443335
  • Código de Segurança: 123
  • Vencimento: 05/2018

Para efetuar testes no ambiente de testes da Vnda, você pode usar os seguintes números de cartão de crédito (esses números são os mesmos fornecidos pela Cielo no manual do desenvolvedor):

Para que as transações sejam autorizadas pela Cielo no ambiente de testes, o total do pedido não deve conter centavos (R$ 100,23, por exemplo). Caso contrário, todas as transações serão negadas.


Mastercard
  
  • Número: 5453010000066167
  • Código de Segurança: 123
  • Vencimento: 05/2018

Diners Club
  • Número: 36490102462661
  • Código de Segurança: 123
  • Vencimento: 05/2018

Amex
  • Número: 376449047333005
  • Código de Segurança: 1234
  • Vencimento: 05/2018
Discover
  • Número: 6011020000245045
  • Código de Segurança: 123
  • Vencimento: 05/2018

Elo
  • Número: 6362970000457013
  • Código de Segurança: 123
  • Vencimento: 05/2018
Boleto
payment_method: O valor desse campo deve ser sempre slip para compras com no boleto.

      

O pagamento do boleto necessita apenas o campo:

Finished.liquid

Esta é a última etapa do processo de compra. Aqui são exibidas as informações do pedido, como o número e/ou o link para impressão do boleto, junto com a mensagem de conclusão da compra. Para exibir as informaóes do pedido, podemos usar estas duas variáveis: {{ client }} | {{ order }

Account.liquid

Template usado para exibir os dados da conta do cliente, os pedidos efetuados, assim como a situação deles. Nesta página o cliente também pode alterar os dados pessoais, como nome, e-mail e senha.

Os dados do pedido ficam armezados na variável {{ order }}
» ver implementação no .liquid

<form action="{{ account_url }}" method="post">
   <input type="text" name="email" value="{{ client.email }}">
  <input type="text" name="password" >
  <input type="text" name="password_confirmation" >
  <input type="checkbox" name="terms">
</form>
      

Para alterar os dados pessoais crie um form com o action {{ account_url }} e envie os dados nome, email, senha.

Formulário para Atualizar Senha

<form action="{{ account_url }}" method="post"">
  <input type="text" name="{{ client.email }}" >
  <input type="text" name="password" >
  <input type="text" name="password_confirmation" >
  <input type="submit" value="Atualizar Senha">
</form>
      

O formulário para atualizar a senha a senha deve usar action {{account_url}}


{% if flash.client.error %}
      

Mensagem de sucesso e de erro para o form de recuperar senha.

Login.liquid

Template usado tanto para novos cadastros quanto para logins de clientes já cadastrados. Desta página parte também o login via Facebook, que permite que clientes participem de promoções específicas.

» ver implementação no .liquid

Formulário de Cadastro

<form action="{{ login_url }}" method="post">
  <input id="new_client_terms" name="new_client[terms]" type="hidden" value="on">
  <input type="text" name="email" >
  <input type="text" name="password" >
  <input type="text" name="password_confirmation" >
  <input type="checkbox" name="terms">
</form>
      

O form de cadastro deve usar a action {{ login_url }}


{% if flash.signup.error %}
  {{ flash.signup.error }}
{% endif %}}
      

Quando existir algum erro no formulário de cadastro »

Cadastro por api
* Primeiro monte o html do formulário
* não precisa colocar nenhum campo como obrigatório
<form action="{{ login_url }}" method="POST" id="form_register">
    <input type="hidden" name="new_client[terms]" value="on">
    <input type="hidden" name="terms" value="true">
    <input type="hidden" name="_method" value="put">
    // input "redirect_to" definir para qual template 
    // o usuário é redirecionado se tudo ocorrer certo;
    // valores aceitos são "home", "login", "account";
    <input type="hidden" name="redirect_to" value="account" />
    // importante div "empty-login" para tratar os erros no callback api
    <div class="empty-register"></div>
    <div class="form-group">
      <input type="email" placeholder="Email" name="email">
    <</div>
    <div class="form-group">
      <input type="password" placeholder="Senha" name="password">
    </div>
    <div class="form-group">
      <button type="submit">Entrar</button>
    </div>

  </form>

 * Agora monte a lógica de programação;
 * Carregue uma biblioteca jquery.js 
 * antes do script abaixo;

  $("#form_register").submit( function(e) {
    // não vai executa o evento do submit
    e.preventDefault();

    // Abaixo tratamos o login por api por ajax
    var form = $(this).serialize();
    $.post("/entrar", form, function(data) {
      // Se tudo ocorrer certo,
      // o usuário é redirecionado para 
      // o template definido no campo "redirect_to"
      // do formulário;
      ( data.redirect_to == undefined ) ? '' : window.location.href = data.redirect_to;
      ( data.error != undefined ) ? $(".empty-register").html(data.error) : '';
    },"json")
  });

A plataforma permite que o login seja feito por requisição à api no qual retorna um objeto json com erro ou com um **redirect. Para quem já possui conhecimento sólidos de javascript e lógica de programação, com base no exemplo ao lado é simples fazer um teste no console de qualquer uma das nossas lojas, basta fazer um script com $.post informando os mesmos campos input do formulário html, desde que a mesma utilize jquery(é comun).

**Há duas situações diferente no retorno das requisições por api, ou o retorno é uma string com erro ou é uma string com um link de redirecionamento, esse é o mesmo que foi informado no formulário com no input "redirect_to"

Formulário de Login

<form action="{{ login_url }}" method="post">
  <input type="hidden" name="_method" value="put">
  <input type="text" name="password" >
  <input type="text" name="password_confirmation" >
 </form>
      

O form de login deve usar a action {{ login_url }}


{% if flash.login.error %}
  {{ flash.login.error }}
{% endif %}
      

Quando existir algum erro no formulário de login »

Login por api

* Primeiro monte o html do formulário
* não precisa colocar nenhum campo como obrigatório
<form action="{{ login_url }}" method="POST" id="form_login">
    <input type="hidden" name="_method" value="put">
    // input "redirect_to" definir para qual template 
    // o usuário é redirecionado se tudo ocorrer certo;
    // valores aceitos são "home", "login", "account";
    <input type="hidden" name="redirect_to" value="account" />
    // importante div "empty-login" para tratar os erros no callback api
    <div class="empty-login"></div>
    <div class="form-group">
      <input type="email" placeholder="Email" name="email">
    <</div>
    <div class="form-group">
      <input type="password" placeholder="Senha" name="password">
    </div>
    <div class="form-group">
      <button type="submit">Entrar</button>
    </div>

  </form>

 * Agora monte a lógica de programação;
 * Carregue uma biblioteca jquery.js 
 * antes do script abaixo;

  $("#form_login").submit( function(e) {
    // não vai executa o evento do submit
    e.preventDefault();

    // Abaixo tratamos o login por api por ajax
    var form = $(this).serialize();
    $.post("/entrar", form, function(data) {
      // Se tudo ocorrer certo,
      // o usuário é redirecionado para 
      // o template definido no campo "redirect_to"
      // do formulário;
      ( data.redirect_to == undefined ) ? '' : window.location.href = data.redirect_to;
      ( data.error != undefined ) ? $(".empty-login").html(data.error) : '';
    },"json")
  });

A plataforma permite que o login seja feito por requisição à api no qual retorna um objeto json com erro ou com um **redirect. Para quem já possui conhecimento sólidos de javascript e lógica de programação, com base no exemplo ao lado é simples fazer um teste no console de qualquer uma das nossas lojas, basta fazer um script com $.post informando os mesmos campos input do formulário html, desde que a mesma utilize jquery(é comun).

**Há duas situações diferente no retorno das requisições por api, ou o retorno é uma string com erro ou é uma string com um link de redirecionamento, esse é o mesmo que foi informado no formulário com no input "redirect_to"

Formulário de Recuperar Senha

<form action="{{recover_password_url}}" method="post"">
  <input type="text" name="email" >
  <input type="submit" value="RECUPERAR SENHA">
</form>
      

O formulário de recuperar a senha deve usar action {{recover_password_url}}

O usuário vai receber um email com a nova senha

{{ flash.recover_password.ok }}
{{ flash.recover_password.error }}
      

Mensagem de sucesso e de erro para o form de recuperar senha.

Recuperar senha por api
* Primeiro monte o html do formulário
* não precisa colocar nenhum campo como obrigatório
<form class="forget-form hidden" action="{{ recover_password_url }}" method="POST" id="form_recover">
  // importante para tratar as mensagens do callback
  <div class="empty-recover-success"></div>
  <div class="empty-recover-danger"></div>
  <div class="form-group">
    <input type="email" placeholder="Email Cadastrado" name="email">
  </div>
  <div class="form-group">
    <button type="submit">Enviar</button>
  </div>
  <div class="form-group">
  </div>
</form>

 * Agora monte a lógica de programação;
 * Carregue uma biblioteca jquery.js 
 * antes do script abaixo;

 $("#form_recover").submit( function(e) {
    e.preventDefault();
    var form = $(this).serialize();

    $.post("/recuperar_senha", form, function(data) {
       ( data.ok == undefined ) ? $(".empty-recover-success").html("") : $(".empty-recover-success").html(data.ok);
       ( data.error == undefined ) ? $(".empty-recover-danger").html("") : $(".empty-recover-danger").html(data.error);
    }, "json")
  });

A plataforma permite que o recuperar senha também seja feito por requisição à api no qual retorna um objeto json com **erro ou **sucesso. Para quem já possui conhecimento sólidos de javascript e lógica de programação, com base no exemplo ao lado é simples fazer um teste no console de qualquer uma das nossas lojas, basta fazer um script com $.post informando os mesmos campos input do formulário html, desde que a mesma utilize jquery(é comun).

O retorno da api para recuperar a senha é um pouco diferente do login e criar conta, os dois tipos de retorno são data.error ou data.ok, veja no exemplo ao lado.

Search.liquid

<form action="{{ search_url }}" method="post"">
  <input type="search" name="q" >
  <input type="submit" value="Pesquisar">
</form>

Template para a exibição dos resultados de uma busca.

Para realizar uma busca use um formulário com action="{{ search_url }}", método GET e com o campo type="search" name="q".

**Url final: https://www.nome_da_loja/busca?q=termo.


{% for product in products %}
  {{ product.image_url | resize: '234x' }}
    {{ product.url }}
    {{ product.tag_names }}
    {{ product.reference }}
    {{ product.description }}
    {% if product .available %}
      {% if product.on_sale %}
        // preço
        {{product.price | money_format}}
        // preço com desconto
        {{ product | apply_discount | money_format }}
      {% else %}
        preço: {{ product.price | money_format }}
      {% endif %}
    {% else %}
     Produto Esgotado
    {% endif %}
{% endfor

O resultado da busca é armazenado na tag {{ products }}, use a tag {% for %} para percorrer e exibir as informações de produto deste array.

**Mostra o termo da busca: {{ params.q | strip_html }}

**Não é necessário informar parâmetros no {% for product in products %}.


Busca padrão

// lojas que não usam o elasticsearch
{% if products.size > 0  %}
 Encontramos {{ products.size }} resultados(s) por: {{ params.q | strip_html }}.
{% endif %}

**Verifica se a busca retornou algum resultado: {{ products.size }}.

Busca com Elasticsearch

{% if pagination.total_hits > 0  %}
 Encontramos {{ pagination.total_hits }} resultados(s) por: {{ params.q | strip_html }}.
{% endif %}

**Verifica se a busca retornou algum resultado: {{ pagination.total_hits }}.

Páginação na busca com Elasticsearch
{% for product in products %}
  // include com o arquivo com a listagem de produtos
  {% include "list_product" %}
{% endfor %}

{% if pagination.total_pages > 1 %}
<nav class="pagination">
<span class="pagination">
    Página {{ pagination.current_page }} de {{ pagination.total_pages }}
    {% if pagination.prev_page %}
        <a href="{{ pagination.prev_url }}" class="previous"></a>
    {% endif %}
    {% for page in pagination.pages %}
      <li class="{% if current_url == page.url %} active {% endif %}"><a href="{{ page.url }}">{{ page.number }} {% if current_url == page.url %}{% endif %}
    {% endfor %}
    {% if pagination.next_page %}
        <a href="{{ pagination.next_url }}" class="next"></a>
    {% endif %}
</span>
</nav>
{% endif %}

Páginação disponível para as lojas com Elasticsearch ativado.

*** O código da paginação não precisa ser incluído em algum dos loop das interações de {% for product in products %}.

*** Quantidade máxima de 20 produtos por página.

Page.liquid

Vnda permite que sejam criadas e alteradas páginas pelo Painel Administrativo. Isso é feito pelo próprio gestor da loja pois dispensa conhecimentos de programação. Neste template serão exibidas as páginas, de acordo com a URL digitada.

Tags personalizadas da Vnda

A plataforma atende com um conjunto de tags responsável por tornar a aplicação dinâmica.

{% orders %}

{% orders client_id:client.id %}
  {{ order.code }}
  {{ order.email }}
  {{ order.received_at | date: '%m/%d/%Y') }}
  {{ order | order_status }}
  {{ order.total | money_format }}
{% endorders  %}
      

Listar pedidos é utilizado no template account.liquid.

* No template account.liquid é possível enviar e exibir dados do usuário:


<form action="{{acount_url}}" method="post">
  <input type="text" name="email" />
  <input type="password" name="password" />
  <input type="password" name="password_confirmation" />
</form>
      

* editar senha


{{ client.first_name }}
{{ client.last_name}}
{{ client.email }
      

* exibir dados do usuário

{% cartitems %}

{% cartitems  %}
{{  item.image_url | resize: '234x' }}
  {{ item.product_name }}
  {{ item.id }}
  {{ item.quantity }}
  {{item.subtotal | money_format }}
  {{ item.variant_name | money_format }}
{% endcartitems %}
      

* Loop para exibir os itens do Carrinho

{% products %}

{% products tag:tag.name parent_tag:parent_tag.name page:params.page per_page:25 %}
  {{ product.image_url | resize: '234x' }}
  {{ product.url }}
  {{ product.reference }}
  {{ product.description }}
  {% if product.available %}
    {% if product.on_sale %}
     // preço
     {{product.price | money_format}}
     // preço com desconto
     {{ product | apply_discount | money_format }}
     // parcelas
     (ou em até {{ product | apply_discount | installment_number }}x de: {{  product | apply_discount | installment_number | money_format }})
    {% else %}
      // preço
      preço: {{ product.price | money_format }}
      // parcelas
      (ou em até {{ product.price | installment_number }}x de: {{ product.price | installment_number | money_format }})
    {% endif %}
  {% else %}
   Produto Esgotado
  {% endif %}
{% endproducts %}

*listar produtos por id sendo assim possível buscar determinado produto ou uma lista deles
ex:
{% products ids:"1,2,3,4,5" %}
  {{product}}
{% endproducts %}
OU
{% products ids:"5" %}
  {{product}}
{% endproducts %}
      

Listar produtos
* loop para exibir os produto
* aceita o paramentro "limit".

Ainda que não seja informado os parâmetros "limit" e "per_page" serão listados no máximo 25 produtos por página.

Caso o parametro "limit" seja informado, os parâmetros "page" e "per_page" serão desconsiderados.

A váriavel {{ params.page }} vem dos parâmetros da url ..produtos?page=2


{% products tag:tag.name filter:size:GG %}
{% products tag:tag.name filter:price:0-50 %}
{% products tag:tag.name filter:price:50-100 %}
{% products tag:tag.name filter:price:50-150 filter:size:GG %}
* esses filtros podem ser selecionados por um form e o valor pode ser capturado de acordo com o nome do input, podeno usar o nome que desejar.
{% products tag:tag.name filter:size:params.tamanho %} se o nome do input “tamanho”
{% products tag:tag.name filter:price:params.price %} se o nome do input “price”
* podem ser listadas todas as opções de tamanho disponíveis por tag
{% sizes tag:”nome-da-tag ”%}{{size}}{% endsizes %}
      

Filtrar produtos
* produtos podem ser filtrados pode e tamanho e faixa de preço.


{% products tag:tag.name sort:”price” %}
* da mesma forma que os filtros, a opção de ordenação do usuário pode ser enviada por um form e colocada na tag de produtos.
{% products tag:tag.name sort:params.order %}
 se o nome do field for “ordem”
      

Ordenar produtos

Por padrão, os produtos são ordenados de forma que os atualizados mais recentemente sejam exibidos primeiro. Caso o parâmetro "tag" esteja presente, os produtos serão ordenados pela importância dentro de cada tag.

Para usar outra ordenação, basta incluir o parãmetro sort na tag que lista os produtos.

Produtos podem ser ordenados por “price”, “price,desc”, “newest”, “newest,desc”
(newest é baseado na data de cadastro do produto), “name”, “name,desc”.


{% tags %}

{% tags product_id:product.id type:"marca" limit:1 %}
  {{ tag.name}}
  {{ tag.type}}
  {{ tag.image_url }}
{% endtags %}
      

Listar tags
* aceita o parâmetro "limit, type, product_id.

{% tag %}

{% tag id:"categoria" %}
 {{ tag.name}}
 {{ tag.title }}
 {{ tag.description}}
 {{ tag.type}}
 {{ tag.image_url }}
{% endtag %}
      

Listar tag pelo "id"

Assim que uma tag é cadastrado no admin se torna acessível desde que seja no .liquid informado o parêmentro id

{% product_count %}

{% product_count tag:tag.name parent_tag:parent_tag.name %}
  {{ product_count }}
{% endproduct_count %}

Lista quantidade de produtos encontrados na tag

{% menus position:"nome_da_posicao_do_menu_no_admin" %}
  {{ menu.url }}
  {{ menu.title }}
  {{ menu.subtitle }}
  {{ menu.items_count  }} // quantidade de sub-menus
  {{ menu.image_url }} // Tags com imagem
{% endmenus %}
      

Listar menus
* loop para exibir os itens de um menu

Listar sub menus
* menu com submenus (html opcional)

A quantidade de submenus é ilimitada.

Dica: ara fazer um menu de marcas com logos, basca cadastrar um menu para esta finalidade, cadastrar o logo das marcas em suas respectivas tags e chamar no menu a variável: [ {{ menu.image_url }} ]

{% menus position:"nome_da_posicao_do_menu_no_admin" %}
  {% if forloop.first %}<ul>{% endif %}
    <li>
      {% menus parent_id:menu.id %}
        {% if forloop.first %}<ul>{% endif %}
        <li>{{ menu.url }} {{ menu.title }}</li>
        {% if forloop.last %}</ul>{% endif %}
      {% endmenus %}
    </li>
  {% if forloop.last %}</ul>{% endif %}
{% endmenus %}
      

// Exemplo de menu com 1 filho(subcategoria)
 {% menus position:"nome-da-posicao", children_depth:"1" %}
    {{ menu.title }}
    {{ menu.url }}
    ....
    {% for menu in menu.children %}
      {{ menu.title }}
      {{ menu.url }}
      ...
    {% endof %}

 {% endmenus%}

Já esta disponível uma nova versão de filtro de tag {% menus %} que consome menos requisições para nossa api de menus.

Na chamada da tag foi adicionado o parâmentro "children_depth", nele é preciso informar a quantidade de filhos desse menu.

Agora usamos a tag {% for %} para percorrer as subcategorias do menu pai com auxílio de um váriavel local.

{% page %}

{% page id:'bloco' %}

Listar conteúdo de uma page

{% banners %}

{% banners tag:"nome_da_posicao_no_admin" %}
  {{ banner.title }}
  {{ banner.subtitle }}
  {{ banner.description }}
  {{ banner.file_url | resize: '990x' }}
  {{ banner.url }}
  {% if banner.external %} target="_blank"{% endif %}
{% endbanners %}
* aceita o parâmetro “limit:3”

Listar banners
* loop para exibir os banners

{% images %}

{% images %}
  {{image}}
{% endimages %}
      

Listar imagens do produto
* deve estar dentro de um loop {% products %}

{% shippingmethods %}

{% shippingmethods %}
  {% if forloop.first %}
    <fieldset class="shipping-method">
    <legend>Selecione o método de entrega:</legend>
  {% endif %}
  <label for="delivery-{{ shipping_method.name }}">
  <input type="radio" name="type" value="{{ shipping_method.value }}" >
    {{ shipping_method.name }} {{ shipping_method.price | money_format }} {{ shipping_method.description }}
   </label>
  //valor que falta para o frete grátis
  {{  shipping_method.value_needed_to_discount }}
 {% if forloop.last %}
  </fieldset>
 {% endif %}
{% endshippingmethods %}
      

{% variants %}

  * Exibir todas as variantes de um produto
  {% variants product_id:product.id %}
    {{ variant }}

  {% else %}
    nenhuma variante encontrada
  {% endvariants %}
      

Listar as variantes de um produto
* Um produto pode conter uma ou mais variantes cadastradas no admin, através do liquid é possível acessar todos os dados que contém uma variante como por exemplo preço, atributos, imagem, dispononibilidade...



{{ product | apply_discount, variant.price }}
      

Passando o preço da variante quando houver desconto.

{% variant %}

  {% variant sku:12001 %}
    {{ variant }}
  {% else %}
    variante não encontrada
  {% endvariant %}
                    

* Exibir uma variante, pelo SKU

{% videos %}

  {% videos %}
    {{video.embed_url}} // lista a url cadastrada no painel
    {{video.thumbnail_url}} // lista a imagem gerada pelo painel
  {% endvideos %}
                    

Listar vídeos de um produto

{% sizes %}

  {% sizes tag:’nome-da-tag’, parent_tag:’outra-tag’ %}
    {{ size.value }}
    {{ size.available }}
  {% endsizes %}
  

Listar atributos tamanhos (deprecate)

{% colors %}

  {% colors tag:'nome-da-tag' %}
    {{ color.value }}
    {{ color.available }}
  {% endcolors %}
                  

Listar atributos cor (deprecate)

{% opengraphfor %}

// Colar o código abaixo antes do fechamendo da tag <head>
{% if template == "home" %}
  {% tag id:"capa" %}
    {% assign meta_title = tag.title %}
    {% assign meta_description = tag.description | strip_html %}
    {% assign meta_image = tag.image_url | strip_html %}
  {% endtag %}
  <meta property="og:description" content="{{ meta_description }}">
  <meta property="og:image" content="{{ meta_image }}" />
  <meta property="og:url" content="{{ home_url }}" />
  <meta name="description" content="{{ meta_description }}">
  <meta name="image" content="{{ meta_image }}" />
  <meta meta="url" content="{{ home_url }}" />
{% else %}
  {% case template %}
  {% when 'tag' %}{% opengraphfor tag %}{{ opengraph }}
  {% when 'product' %}{% opengraphfor product %}{{ opengraph }}
  {% when 'page' %}{% opengraphfor page %}{{ opengraph }}
  {% when 'search' %}{% contentfor title %}{{ params.q }}{% endcontentfor %}
  {% when 'finished' %}{% assign pretitle = 'Concluído' %}
  {% endcase %}
{% endif %}
<title>
  {% if pretitle %}
    {{ pretitle }} | {{ store.name }}
  {% elsif title %}
    {{ title }} | {{ store.name }}
  {% else %}
    {{ meta_title }}
  {% endif %}
</title>

Configura as meta tags de SEO para facebook e google.
Carregue as informações informando o parâmetro {% opengraphfor nome_do_template %} e exiba as metatags usando o {{ opengraph }} antes do fechamento da tag <head>.

**obs:templates disponíveis são:
para o template de tag {% opengraphfor tag %}
para o template de produto {% opengraphfor product %}
para o template de página institucional {% opengraphfor page %}

Para exibir as metas da home, crie uma tag auxiliar como por exemplo "capa" e nela configure com os campos title, description e imagem para que possam ser carregadas apartir do código de exemplo ao lado.

{% contentfor %}

// armazendo o conteúdo em lista_produto
{% contentfor lista_produto %}
  <div> Item 1 </div>
{% endcontentfor %}
{% contentfor lista_produto %}
  <div> Item 2 </div>
{% endcontentfor %}
{% contentfor lista_produto %}
  <div> Item 3 </div>
{% endcontentfor %}

{{  lista_produto }}

retorno:
Item 1
Item 2
Item 3

// removendo o conteúdo em lista_produto
{% assign lista_produto = "" %}

// armazendo o conteúdo em lista_produto
{% contentfor lista_produto %}
  <div> Item 5 </div>
{% endcontentfor %}

{% contentfor lista_produto %}
  <div> Item 6 </div>
{% endcontentfor %}

{{  lista_produto }}

retorno:
Item 5
Item 6

{% assign lista_produto = "pense nele como uma váriavel" %}
{% contentfor lista_produto %}
  <div> Item 10 </div>
{% endcontentfor %}

{{  lista_produto }}

retorno:
pense nele como uma váriavel
Item 6

O {% contentfor nome_da_variavel %} é usado para adicionar conteúdo dentro da variável "nome_da_variavel" sem remover o conteúdo atual, contrário da tag {% capture %} que remove o conteúdo anterior.

{% aggregations %}

{% aggregations tag:tag.name parent_tag:parent_tag.name %}
{% endaggregations %}

Filtros podem ser usados tanto no template de busca, quanto no de tag, com uma pequena diferença na sintaxe.
Para usar os filtros em um mesmo arquivo e diferenciar o template em que estão sendo usados, use a seguinte sintaxe para identificar o template {{ template }}.
Para ocultar os filtros, caso não queira usar em páginas que possuam pouco resultado e dos quais não seja interessante filtrar, verifique a quantidade de produtos resultante da busca ou da tag, com a seguinte sintaxe: Tag: {{ tag.products_count }} Busca: {{ products.size }}.

- Filtro de Preço

{
  "min"=>99.0,
  "max"=>999.0
}

O filtro de preço retorna apenas o menor e o maior valor e podem ser usados para criar um slice de preço, para filtrar produtos entre a faixa de preço desejado.

- Filtro de Tags

{
  "name"  =>"marca",
  "count" =>30,
  "values"=>[{
    "name"      =>"mosaico",
    "title"     =>"Mosaico",
    "count"     =>10,
    "selected"  =>false,
    "url"       =>"/feminino?typedtags__marca=mosaico"
  }]
}

O filtro de tags oferece o seguinte retorno:

- Filtro de Atributos

{
  "name"  =>"Tamanho",
  "count" =>59,
  "values"=>[{
     "name"     =>"G",
     "count"    =>19,
     "selected" =>false,
     "url"      =>"/feminino?property__Tamanho=G"
  }]
}

O filtro de atributos oferece o seguinte retorno:

- Filtro nas tags

{% aggregations tag:tag.name parent_tag:parent_tag.name %}
  {{ price }}
  {{ properties }}
  {{ typed_tags }}
{% endaggregations %}

Para exibis filtros em uma tag, usamos a seguinte sintaxe:

- Filtro na busca

{% aggregations q:params.q %}
  {{ price }}
  {{ properties }}
  {{ typed_tags }}
{% endaggregations %}

Para exibis filtros na busca, usamos a seguinte sintaxe:

- Filtros Específicos

{% aggregations tag:tag.name parent_tag:parent_tag.name only_typed_tags:"marca" %}
  {{ typed_tags }}
{% endaggregations %}

{% aggregations q:params.q only_typed_tags:"marca" %}
  {{ typed_tags }}
{% endaggregations %}

{% aggregations tag:tag.name parent_tag:parent_tag.name only_properties:"Tamanho" %}
  {{ properties }}
{% endaggregations %}

{% aggregations q:params.q only_properties:"Tamanho" %}
  {{ properties }}
{% endaggregations %}

Podemos usar também filtros específicos, conforme o exemplo:

- Filtros Unitários

{% aggregations tag:tag.name parent_tag:parent_tag.name single_option_typed_tags:"marca" %}
  {{ typed_tags }}
{% endaggregations %}

{% aggregations q:params.q single_option_typed_tags:"marca" %}
  {{ typed_tags }}
{% endaggregations %}

{% aggregations tag:tag.name parent_tag:parent_tag.name single_option_properties:"Tamanho" %}
  {{ typed_tags }}
{% endaggregations %}

{% aggregations q:params.q single_option_properties:"Tamanho" %}
  {{ typed_tags }}
{% endaggregations %}

Por padrão, todos os filtros permitem múltiplas seleções, mas casos eja necessário, podemos fazer com que ele permita que apenas um item seja selecionado por vez.

Caso ainda tenha dúvidas de como aplicar os códigos no .liquid:
acesse esse link.

{% search_options %}

{% search_options %}
{{ search_option.label }}
{% endsearch_options %}
-------------------------
{
"name"=>"small_price",
"label"=>"Menor Preço",
"active"=>false,
"url"=>"/mobilidade-acessibilidade?order=price"
}
-------------------------
{% products tag:tag.name parent_tag:parent_tag.name page:params.page sort:params.order   %}
{% include "_commons/list_products" %}
{% endproducts %}

Para obter um filtro de ordenação de produtos dentro de tags, usamos a seguinte sintaxe:
OBS: O parâmetro de ordenamento deve ser passado para a tag, [ sort:params.order ] para que a ordem seja aplicada.
Os parâmetros de ordenamento não se aplicam a busca, que mostra os produtos em ordem de relevância em relação ao que foi pesquisado.

{% include %}

// vnda_stg_demo/template/_header.liquid
// vnda_stg_demo/template/_footer.liquid
// vnda_stg_demo/layout.liquid
// vnda_stg_demo/home.liquid
.....

Incluindo topo e rodape no arquivo layout.liquid
{% include "template/header" %}
{% include "template/footer" %}

Para reutilizar as parte de códigos que se repetem ao longo do desenvolvimento da loja usaremos a tag "{% include %}".

Obs: é obrigatório para qualquer arquivo que não seja de template, como por exemplo: home.liquid, tag.liquid, product.liquid etc.., usar o underscore antes do nome do arquivo: "_nomedoarquivo.liquid", caso contrário seu include não vai funcionar.

Ao passar o caminho do arquivo na tag include não o use undescore antes do nome.

A regra do undescore não se aplica a diretórios.

Recursos

Novos recursos são inseridos na Vnda a cada semana garantindo um software sempre atual.

Adicionar Produto Ajax com $.post()

$.post( "/carrinho/adicionar",{ sku:'1004-4',quantity:'1' }, function( data ) {
  console.log(data)
},"json"

Para adicionar um produto no carrinho via ajax é necessário enviar para a url "/adicionar/carrinho" e os campos sku e quantidade.

Com jquery, recomendamos que use o $.post() para enviar os dados para url acima que o server esta configurado para retornar um objeto com os item(s) adicionado(s) no formato json como resposta, assim se torna fácil manipular o objeto com jquery.

Adicionado o(s) item(s) do carrinho no html (VIA JS)

** incluir biblioteca jquery.js
** ter noções básicas de objetos

$.post( "/carrinho/adicionar",{ sku:'1004-4',quantity:'1' }, function( data ) {
  console.log(data)
},"json")

Resposta: [objeto json]
Object {id: 29, subtotal: 80, total: 80, shipping_method: null, shipping_price: 0…}

Sempre for feito um $.post() para url "adicionar/carrinho" com os dados do sku e quantidade do produto, a resposta do server esta configurado para retornar um objeto com os item(s) adicionado(s) no formato json.

**os dados desse objeto json são os mesmo encontrados no loop de {% cartitemns %}.


$.post( "/carrinho/adicionar",{ sku:'1004-4',quantity:'1' }, function( data ) {
 $( cart.items ).each(function(index, item){
  console.log( item )
 });
},"json")

Object {id: 78, quantity: 1, price: 80, subtotal: 80, total: 80…}
Object {id: 79, quantity: 1, price: 80, subtotal: 80, total: 80…}
...

Você pode percorrer os indíces desse objeto usando each do jquery:


function montaCarrinho( item ) {
 // deixe uma li de marcação no html
 // variavel cart_preview clona a li de marcação que será alimentada com o conteúdo do item
 var cart_preview = $( "ul.cart-items li" ).clone();
 // coloca para dentro da lista
 cart_preview.appendTo( "ul.cart-items" );
 // pesquisa dentro da li pela tag image e inclui o atributo src
 cart_preview.find( "img" ).attr("src",item.image_url)
 // pesquisa dentro da li  pela classe .name e inclui o nome
 cart_preview.find( ".name" ).text(item.product_name)
 // pesquisa dentro da li pela classe .price e inclui o preço
 cart_preview.find( ".price" ).html(item.price );
 .....
}

$.post( "/carrinho/adicionar",{ sku:'1004-4',quantity:'1' }, function( data ) {
 $( cart.items ).each(function(index, item){
  // vai montando o html com o(s) item(s)
  montaCarrinho(item)
 });
},"json")

Existem várias formas de montar o dropdown e aba de carrinho via javascript, o exemplo ao lado temos função que serve de modelo para carregar o html com uma lista de items a cada interação do for de items.

publicado no github

Avaliação de Produto

Com votações que podem ser de 1 a 5 pontos é possível enviar um formulário por método post os campos "rating"(pontuação) e "product_id"(id do produto) para url "/avaliacao".


// Situação atual
// média atual = 4
// votos = 6

// dados coletado na avaliação com pontuação 5
$.ajax({
  url: "/avaliacao",
  type: "POST",
  data: {rate: 5, product_id: id },
  success: function(response) {
    console.log("Média "+response.rating)
    console.log("Votos " +response.votes)
  }
});

Retorno:
"Média 4.2"
"Votos 7"

Com jquery, recomendamos que use o $.ajax() para enviar os dados para url acima que o server esta configurado para retornar um objeto json com a média da pontuação e a quantidade total de votos.

Existem vários plugins js pronto com as aplicações de estrelinhas, dica,com boostrap.

Ver exemplo usado na http://www.cadence.com.br

Os dados da pontuação também podem ser gerenciados via admin na edição do produto.

Para zerar a pontuação do produto é necessário acessar a página de edição do produto no admin e selecionar a opção limpar

API lista de Espera(Avise-me quando chegar)

<form action="/lista_de_espera" method="post">
<p>Produto Indisponível</p>
<strong>Avise-me quando chegar</strong>
<input type="hidden" name="sku" value="{{ variant.sku }}">
<input type="hidden" name="nomeproduto" value="{{ product.name }}">
<input type="hidden" name="imagemvariante" value="{{ product.image_url }}">
<input type="hidden" name="referencia" value="{{ product.referente }}">
<input type="hidden" name="preco" value="{{ variant.price | money_format }}">
<input type="email" name="email" value='usuario@email.com.br'>
<input type="submit" value="Enviar">
</form>

Api de lista de espera é um recurso da plataforma que notificará o cliente sempre que um produto esgotado voltar ao estoque. O cadastro é feito através do formulário por método post para url "/lista_de_espera" com os campos obrigatórios: "email" e "sku"(da variante).

Campos opcionais: "referencia"(referencia do produto), preço, imagemvariante, nomeproduto.

Email do avise-me:

DNS

- Criar uma entrada (ou alterar uma já existente) do tipo A, apontando para o IP 52.4.198.23

- Criar uma entrada www (ou alterar uma já existente) do tipo CNAME, apontando para o domínio vnda.com.br.

- Criar uma entrada "redirect" do tipo CNAME, apontando para o domínio www.nome-da-loja.com.br.opts-uri.redirect.vnda.com.br (substituir nome-da-loja.com.br pelo dompinio real da sua loja)

Etiqueta da Promoção

* deve estar dentro de um loop {% products %}
{% if product.discount_id > 0 %}
{% get promotion from '/discount' [name: product.discount_id] %}
{% if promotion %}
<img src="{{ promotion.seal_url }}" />
{% endif %}
{% endif %}

Listar a etiqueta da promoção que foi cadastrada no admin.

Formulários WebForms

<form action="/webform" method="post" onsubmit="document.getElementsByName('reply_to')[0].value = document.getElementsByName('email')[0].value">
<input type="hidden" name="key" value="nome-da-loja-nome-do-form">
<iinput type="hidden" name="reply_to" id="reply_to" value="">
<input type="hidden" name="email" value="">
<input type="submit">
</form>

Configuração admin:

- Criando um novo form no admin: confira aqui

- Informando o email de destino e assunto: confira aqui

- Ao salvar será gerado um chave: confira aqui

Configuração .liquid:

- Form action= "/webform"

- Incluir o campo hidden com name="key" e com o valor da key do form da configuração anterior.

Incluir o campo hidden com name="reply_to", este campo deve pegar o valor do campo email antes do evento submit do formulário. Use um evento jquery onsubmit para pegar o valor do email antes do submit ou apenas informe o evento onsubmit direto na tag form como o exemplo ao lado

Montando um carrinho pela url

// adicionando 2 itens na loja demo
// sku 1007-1 e 1009-4
// a quantidade do item 1007-1 = 10
// a quantidade do item 11009-4 = 5
// desconto de 50% no subtotal do pedido
// código do cupom de desconto "DESCONTO50"

https://demo.vnda.com.br/carrinho?itens=1007-1:10,1009-4:5&cupom=DESCONTO50

Quando um carrinho ainda não tiver nenhum dado de cliente ou para ser feito na mão, é possível incluir na URL os parâmetros "sku","quantidade" e opicional "cupom".

Basicamente, deve entrar no final da URL SKU:QNT+SKU:QNT...

Esse link leva direto para o carrinho montado e pode ter algum CUPOM ASSOCIADO

Url /carrinho?itens=SKU:quantidade,SKU:quantidade&cupom=CUPOM

Descrição do produto com múltiplas seções

{% if product.description %}
  {% assign descParts = product.description | split:"linhadecorte" %}
  {% for desc in descParts %}
    {% if forloop.index == 1 %}{% assign desc1 = desc %}{% endif %}
    {% if forloop.index == 2 %}{% assign desc2 = desc %}{% endif %}
    {% if forloop.index == 3 %}{% assign desc3 = desc %}{% endif %}
  {% endfor %}
{% endif %}
...
{{ desc1 }}
...
{{ desc2 }}
...
{{ desc3 }}

Para determinar o o local onde a descrição será dividida, deve-se colocar o texto “linhadecorte” na descrição do produto

Notificações de e-mails

A plataforma possui integração com Madmimi, ferramenta que gerencia as campanhas de e-mails dos nossos clientes.

Atualmente disponibilizamos as seguintes notificações:

exemplo-pedido-recebido
exemplo-pedido-recebido-deposito
exemplo-pedido-recebido-boleto
exemplo-pedido-entregue
exemplo-pedido-enviado
exemplo-pedido-confirmado
exemplo-pedido-cancelado
exemplo-novo-cliente
exemplo-senha-cliente
      

O nome das campanhas »













"nome",
"sobrenome",
"pedido",
"paginadopedido",
"experienciadocliente",
"valordodesconto",
"valordoenvio",
"valordopedido",
"formadepagamento",
"formadeenvio",
"itensdopedido",
"enderecoentrega",
"bairroentrega",
"cidadeentrega",
"estadoentrega",
"enderecocobranca",
"bairrocobranca",
"cidadecobranca",
"estadocobranca",
"linkdoboleto"
      

Os campos que podem ser enviados nos emails de noviticação »

Percentual de desconto no Boleto

// Preço total do carrinho R$ 100,00

{{ cart.total_for_slip | money_format }}
Retorno "R$ 70,00"

{{ store.settings.slip_discount }}
Retorno "30"
      

Em configurações da loja, no item Boleto, informe o % de desconto que o pagamento por boleto terá.confira aqui

A tag {{ cart.total_for_slip }} exibe o total do carrinho com o desconto aplicado.

É possível pegar o % de desconto na store pelo settings da loja com {{ store.settings.slip_discount }}.

Url's Destino após Login

Os valores aceitos são:

=> Index
<input type="hidden" name="redirect_to" value="home" />

=> /conta
<input type="hidden" name="redirect_to" value="account" />

=> /produto/slug-id
<input type="hidden" name="redirect_to" value="product:{{ product.id }}" />

=> /nomedatag
<input type="hidden" name="redirect_to" value="tag:{{ tag.name }}" />

=> //p/keydapages
<input type="hidden" name="redirect_to" value="page:{{ page.id }}" />

Por default a plataforma redireciona formulários de login ou cadastro para Home da loja. Visando melhorar a usabilidade é possível configurar para qual url você deseja redirecionar seu cliente, basta incluir um campo hidden com name "rediret_to" em ambos formulários e passar um dos valores suportados.

Esse tipo de ação é mais recorrente nos formulários dentro de modals, visto que pode ser acessados em qualquer página, por exemplo na página de detalhe do produto.

Wishlist(lista de desejos)

<form action="{{ wishlist_url }}" method="POST">
  <input type="hidden" name="_method" value="DELETE"/>
  <input type="hidden" name="client_id" value="{{client.id}}"/>
  <input type="hidden" name="product_id" value="{{wish.product_id}}"/>
  <button type="submit">Remover</button>
</form>
      

* Deletar produto da lista (também pode ser por JS)


{% wishlist client_id:client.id %}
    {{wish}}
{% else %}
    nada
{% endwishlist %}

* para a listagem dos produtos utilize a tag {% products %} » ver código
    

* Mostrar lista do cliente


{% wishlist client_id:client.id product_id:2 %}
    {{wish}}
{% endwishlist %}}
      

* Consultar se produto está na lista


<form action="{{ wishlist_url }}" method="POST">
  <input type="text" name="client_id" value="{{client.id}}"/>
  <input type="text" name="product_id"/>
  <button type="submit" value="Enviar">Enviar</button>
</form>
      

* Adicionar na lista com html


$.post(
  "/lista_de_desejos",
  { client_id: "15", product_id: "4" },
  function( data ) {
    console.log("success");;
  },
  "json"
)
      

* Adicionar via JS

Dicas Setup/Admin

Seção de dicas para integrar tanto no setup da loja ou para o admin.

Biblioteca Jquery para validar cpf e cnpj

Clique aqui para ver os filtros disponíveis para usar no .liquid.

Veja exemplos de como fazer o breadcrumb na tag e na página de produto.

{% comment %} Exemplo Tag {% endcomment %}
<a href="{{ home_url }}">Home</a>
{% if params.parent %}
  {% if parent_tag.title != "" %}
  <a href="{{ home_url }}{{ params.parent }}">{{ parent_tag.title }}</a>
  {% endif %}
{% endif %}
<a href="{{ tag.image_url }}">{{ tag.title}}</a>

-----------------------------------------
{% comment %} Exemplo Produto {% endcomment %}
<a href="{{ home_url }}">Home</a>
{% tags product_id:product.id %}
  {% if tag.type != "capa"%}
    <a href="{{ home_url }}{{ tag.name }}">{{ tag.title }}</a>
  {% break %}
  {% endif%}
{% endtags %}
<a href="{{ product.url }}" title="{{ product.name }}">{{ product.name }}</a>

Listar Percentual de Desconto no produto em promoção

//variável que armazena o preço original
{% assign valor_produto = product.price %}

//variável que armazena o preço c/ desconto
{% assign valor_com_desconto = product | apply_discount %}

//variável que armazena o percentual de desconto
{% assign desconto =  valor_produto | minus:valor_com_desconto | times:100 | divided_by:valor_produto | remove: '.0' %}

// lista o percentual de desconto
{{ desconto }}
      

Para listar o % (percentual) de desconto de um produto em promoção é necessário é criar variáveis e auxiliar na criação dos filtros das operações.

Calcular frete no detalhe do produto

$.ajax({
  url: "/frete_produto",
  cache: false,
  type: "POST",
  dataType: "json",
  data: {
    product_id:99999, // informe um id válido de produto
    zip: "93265340"
  },
  success: function(json) {
    //console.log(json.error);
    if(json.error == "Invalid zip or product_id"){
        alert("Cep nao cadastrado no correios")
    } else {
        var methods = $.parseJSON(json.methods);
        console.log(methods)
    }
  }
});

Retorno:
[Object, Object, Object]

Para calcular o frente de um produto envie um formulário de dados via ajax passando os parâmetros product_id(id do produto ) e zip(cep) para url "/frete_produto". Api de frete esta configurada para retornar um objeto no formato json com as informações do local do cep digitado e dados de entrega: nome do método(sedex,pac etc..), prazo e preço.

Rode o código do preview ao lado no console do navegador e em umas das página da sua loja, altere apenas o campo product_id com uma valor válido.

Código publicado no github, o mesmo usado no lojadamoto.com.br

Como Listar Parcelas

Para listar parcelas, podemos usar um dos seguintes exemplos de código:

// produto com desconto
{{ product | apply_discount | installment_number }}x de {{  product | apply_discount | installments | money_format }}
ou
{% assign preco =  product | apply_discount %}
{{ preco | installment_number }}x de {{ preco | money_format }}

// produto sem desconto
{{ product.price | installment_number }}x de {{  product.price | installments | money_format }}
ou
{% assign preco =  product.price %}
{{ preco | installment_number }}x de {{ preco | money_format }}

Como Listar Juros das Parcelas

// Juros das parcelas do produto
{% product_installments product_id:product.id %}
  OU {{ max_without_interest.number }}X DE
  {{ max_without_interest.price | money_format }}
  {% if max_with_interest.number %}
    OU {{ max_with_interest.number }}X DE
    {{ max_with_interest.price | money_format }} com {{ max_with_interest.interest_rate }}% de juros a.m.
  {% endif %}
{% endproduct_installments %}

Listando os juros das parcelas do produto.


// Juros das parcela nas variantes
{% product_installments variant_sku:variant.sku %}
  OU {{ max_without_interest.number }}X DE
  {{ max_without_interest.price | money_format }}
  {% if max_with_interest.number %}
    OU {{ max_with_interest.number }}X DE
    {{ max_with_interest.price | money_format }} com {{ max_with_interest.interest_rate }}% de juros a.m.
  {% endif %}
{% endproduct_installments %}

Listando os juros das parcelas da variantes

O que muda nesse caso são os parâmetros, de product_id para variant_sku: {% product_installments variant_sku:variant.sku %}

Não esquece que esse deve estar dentro do loop de {% variants %}

Como relacionar produtos

{% assign product_id = product.id %}
{% tags product_id:product.id type:"relacionado" limit:1 %}
  {% products tag:tag.name %}
    {% unless product_id == product.id %}
      {{product}}
    {% endunless %}
  {% endproducts %}
{% endtags %}
      

É necessário criar uma tag e colocar um tipo para ela (pois vamos buscar a tag por tipo na sequencia. no exemplo usamos o tipo "relacionado") Então usamos o código abaixo na tela de produtos. Ele percorre as tags do produto em busca de uma que tenha o tipo "relacionado". Caso encontre, serão listados os produtos que também tem essa tag, ou seja, que estão relacionados entre si.

Como exibir o menu para uma determinda tag

para url http://www.loja.com.br/masculino/luvas -->
{% if tag.name == "masculino" or params.parent == "masculino" %}
//carrega uma posição de menu %
{% menus position:"masculino" %}
...
{% endmenus %}
{% endif %}
entenda a tag {{ params }}
      

Para chamar um menu em uma determinada categoria na página de tag será necessário veficar o nome da tag, entretanto, se necessário incluir o menu em todas categorias filhas use params.parent

Note que no ex ao lado que é nessário chamar a posição de menu masculino em 2 cenários, um para quando a tag for masculino ou para quando a tag for "luvas". Leve em consideração que "luvas" é filha de "masculino".

Como incluir Favicon?

Chamando um favicon salvo na raiz da pasta "/public"
{{ "favicon.ico" | favicon_tag }}
// Retorno
<link href="nomedaloja/images/favicon.ico" rel="shortcut icon" type="image/vnd.microsoft.icon"/>


Chamando um favicon salvo na raiz da pasta "/public/images"
// Retorno
{{ "images/favicon.ico" | favicon_tag }}
<link href="nomedaloja/images/favicon.ico" rel="shortcut icon" type="image/vnd.microsoft.icon"/>

1. Para incluir uma favicon por uma tag de .liquid, é preciso informar o filtro "favicon_tag" {{ "favicon.ico" | favicon_tag }}.


2. Para incluir direto por html, informe o caminho relativo da pasta que esta salvo o favicon.ico.

2.1 Caminho relativo da pasta "/public"
<link href="/favicon" rel="shortcut icon" />

2.2 Caminho relativo na raiz da pasta "/images"
<link href="/images/favicon.ico" rel="shortcut icon" />

Como listar menor e maior preço

{% variants product_id: product.id %}
  {% if product.on_sale %}
    {% assign price = product | apply_discount, variant.price %}
  {% else %}
    {% assign price = variant.price %}
  {% endif %}

  {% if forloop.first %}
    {% assign min_price = price %}
    {% assign max_price = price %}
  {% else %}
    {% if price < min_price %}{% assign min_price = price %}{% endif %}
    {% if price > max_price %}{% assign max_price = price %}{% endif %}
  {% endif %}
{% endvariants %}
min: {{ min_price | money_format }}
max: {{ max_price | money_format }}

Na tela de tag, podemos exibir o preço menor e maior entre as variantes do produto.

Exibir imagens estáticas

* devem estar no diretório /public/images

  {{ “nome_do_arquivo.ext” | image_tag: “Texto alternativo” }}
  <img src="{{ “nome_do_arquivo.ext” | image_url }}" alt=“Texto alternativo”/>
      

Incluindo css e js pelo .liquid

// para o css
{{ "style.css" | stylesheet_tag }}

// para js
{{ "function.js" | javascript_tag }}
      

Para chamar os arquivos css e js salvos nas pastas "/stylesheets" e "/javascripts/" use os filtros "stylesheet_tag" e "javascript_tag".

Mapeamento da tag params

<pre>{{ params }}</pre>

// Retorno:

// na página da busca
http://www.bodystore.com.br/corpo?page=3
 <pre>{"q"=>"produto x"}</pre>

// na página da produto
par url http://www.loja.com.br/produto/produto-191
<pre>{"slug"=>"produto", "id"=>"191"}</pre>

// na página da tag
par url http://www.loja.com.br/masculino/bermudas
<pre>{"parent"=>"masculino", "tag"=>"bermudas"}</pre>

// na  páginação
par url http://www.loja.com.br/masculino/bermudas
<pre>{"page"=>"3", "parent"=>"doces", "tag"=>"bolos"}</pre>
      

É possível fazer o mapeamento dos parâmetros de cada template pela tag {{ params }}.

Para cada template é feito um mapeamento da url de acordo com o parâmetro disponível.

Veja um caso de uso para chamar uma posição de menu ou para tag "masculino" ou "feminino" mais detalhes aqui.

Métricas Google Analytics

Mensuração de banners

{% banners position: "capa-grande" %}
<li>
<a class="link-img" href="{{ banner.url }}">
<img src="{{ banner.file_url }}" alt="{{ banner.title }}" data-banner-title="{{ banner.title }}" data-content-type="banner" data-banner-position="capa-grande">
</a>
<div class="info">
<h3>{{ banner.subtitle }}</h3>
{{ banner.description }}
</div>
</li>
{% endbanners %}
      

É possível mensurar os cliques dos banners no Google Anatylics através do .liquid informando os atributos data-banner-title, data-content-type e data-banner-position dentro do loop {% banners %}.

Os atributos data-banner-title e data-content-type deve ser os mesmo cadastrados no admin nos campos Title e Position;

Resumo do Pedido

Segue um modelo de resumo do carrinho.

<table class="table">
    <tbody>
        <tr>
            <td>Subtotal
            <td>{{ cart.subtotal | money_format }}
        </tr>
        {% if cart.shipping_price > 0 %}
        <tr>
          <td>Frete
          <td>{{ cart.shipping_price | money_format }}
        </tr>
        {%  endif %}
        {% if cart.discount_price > 0 %}
        <tr>
          <td>Desconto
          <td>{{ cart.discount_price | money_format }}
        </tr>
        {% endif %}
        <tr>
            <td>Total de pedidos
            <td>{{ cart.total | money_format }}
         </tr>
    </tbody>
</table>
      

Outras tags do .liquid

Outras tags que pode ser chamados no .liquid

{{ home_url }}     -  retorna a url da home
{{ current_url  }} -  retorna a url atual
{{ store_name }}   -  retorna o nome da loja/admin
      

Verificar usuário logado

  {% if logged_in %}
    <a href="{{ account_url }}" title="minha conta">minha conta</a>
    <a href="{{ logout_url }}" title="sair">sair</a>
  {% else %}
    <a href="{{ login_url }}" title="">minha conta</a>
  {% endif %}
      

A váriavel "logged_in" retorna valor verdadeiro se o usuário estive logado.

Use a váriável "logout_url" para deslogar.

Tag - Valor que falta para o Frete Grátis

{% shippingmethods %}
 ...
{{ shipping_method.value_needed_to_discount }}
...
{% endshippingmethods %}
     

É possível listar o valor que ainda falta pra ter desconto no frete associado a um tipo de entrega(dentro do loop {% shippingmethods %}), pois o desconto é relativo ao método em questão.

Tag store (configurações admin)

// atribuindo endereço de forma dinâmica
{{ store.street_name }}..{{ store.street_number }}..
{{ store.neighborhood }}.
      

A tag { store } retorna um array com todas as propriedades da seção "admin/store/edit".

Umas das vantagens do uso dessa tag é deixar o site com a logo e endereços gerenciavéis pelo cliente.

Constantemente um novo recurso é adicionado ao admin gerando um novo atributo para a tag "store", caso necessite chame a tag { store } em qualquer parte do arquivo .liquid e veja você mesmo os atributos suportados por esta.

Trocar imagem principal por variante selecionada

 passando o parâmetro "sku" na chamada da tag {% images %}
// execute loop de 'variants' antes de 'images'
{% variants product_id:product.id %}

// passe o parametro sku
{% images sku:variant.sku %}

// passe um data-sku para saber de qual variante pertence essa imagem
<img src="{{ image.url }}" data-sku="{{ variant.sku }}">

{% endimages %}
{% endvariants %}

Para trocar a imagem principal façar uma função javascript que compare valor do input selecionado com o data-sku da imagem:

O valor do input das variantes sempre deve ser o sku.
      

Configurar produto no admin "admin > products > midias > images": ao clicar nas fotos do produto ficará disponível no modal com as variantes que podem ser selecionadas

Novo Carrinho/ Novo Checkout

A partir da implantação do novo carrinho e do novo checkout, não são mais necessárias as edição dos arquivos cart.liquid e checkout.liqud.

Toda a customização deve ser realizada em um arquivo chamado custom.css. Nele, basta sobrescrever os estilos padrões do carrinho e checkout pelas cores padrões do layout da loja.

Exemplo de custom.css: custom.css





Pedido Finalizado por Boleto Moip e Cobre Grátis

Moip

<a target="_blank" href="#" id="print-button" style="display:none">IMPRIMIR</a>

<script type="text/javascript">
  $(function(){
    $("#moip-script").on("success", function() {
       // função para retirar algum efeito de loader da página
    });
  });
</script>
        

A integração com o moip faz o mapeamento na página de pedido concluído para exibir e inserir o link do botao de imprimir. A validação do link é feita através da tag html <a> de atributo id definido como "print-button". Este processo pode levar alguns segundos por isso usamos um loader e um script padrão que será executado sempre que uma requisão for conclúida.

Cobre Grátis

Para o Cobre Grátis o link é carregado no .liquid pela tag {{ order.slip_url }}


 {% if store.settings.slip_gateway == 'cobre_gratis' %}
 {% else %}
   // moip
 {% endif %}
        

obs: validar no front qual a integração que a loja usa.

Exemplo de página de pedido finalizado aqui

Produtos c/ 2 Atributos - via Liquid

Partindo da premissa em que os atributos de um produtos são Cor(atributo_1) e Tamanho(atributo_2) respectivamente, e que ao abrir a página de produtos é listado apenas as cores e ao selecionar uma cor é listados as combinações de tamanho compátiveis com este atributo.

Para isso criaremos um arquivo _variants.liquid com as seguintes manipulações de dados: _variants_example.liquid



Produtos c/ 2 Atributos - via js

Partindo da premissa em que os atributos de um produtos são Cor(atributo_1) e Tamanho(atributo_2) respectivamente, e que ao abrir a página de produtos é listado apenas as cores e ao selecionar uma cor é listados as combinações de tamanho compátiveis com este atributo.

Para isso criaremos um arquivo atributos.js com as seguintes funções e manipulações de dado.

$(function(){

  // variável que recebe os objetos com as informaçoes de cada variante
  variants = vnda.variants.all()

  var cor = variants[0].properties.property1;
  var tamanho = variants[0].properties.property2;

  // verifica a quantidade de atributos do produto
  properties_count = quantity_of_properties()

  // produto com 2 atritubos
  if(properties_count >= 2) {
   $(".cores").html(
    '<label>Cor:</label><ul>'+create_color_radios(variants)+'</ul>'
        );
    // chamada da função que lista os tamanhos de uma cor selecionada
    enable_sizes_per_color()
    }
  } else if(properties_count == 1){   // produto com 1 atritubo

     // verifica qual html atributo deve ser criado no html
      if ( cor.value != null ) {
        $(".cor").html(
        '<label>Cor:<ul>'+create_absolute_color_radios(variants)+'</ul>'
        );
      } else {
        $('.tamanho').html(create_absolute_size_radios(variants))
      }
    } else {
      fill_sku_with_variant();
  }

  $( "form" ).on('submit',function(){
    if( quantity_of_properties() >= 2 ) {
      var atributo_1 =  $( ".cor input[type=radio]:checked").val();
      var atributo_2 = $( ".tamanho option:checked" ).val();
      variant = vnda.variants.find({Cor: atributo_1, Tamanho: atributo_2})[0]
      if(variant){
        sku = variant.sku
        $( "form" ).find( "#sku" ).val( sku );
      } else{
        return false
      }
    }
  })
}
      

Etapa 1 - onde são chamadas as funções para criar as combinações, sku e enviar o formulário de compra:


// função que monta o html das cores
function create_color_radios(variants){
  colors = [];
  radios = ""
  for(variant in variants) {
    if( variants[variant].available == true ) {
      if(variants[variant].properties) {
        colors.push(variants[variant].properties.property1.value)
      }
    }
  }
  unique_colors = colors.filter(onlyUnique);
  for(color in unique_colors){
    desired_color = unique_colors[color]
    radios += '<li style="background-color:'+desired_color+'">'
    radios += ' <a href="#">'
    radios += '<input type="radio" value="'+desired_color+'">'
    radios += ' '
  }
  return radios
}

// se existir apenas o atributo cor
function create_absolute_color_radios(variants){
  radios = ""
  for(variant in variants) {
    if( variants[variant].available == false ) {
      var desired_color = variants[variant].properties.property1.value
      radios += '<li style="background-color:'+desired_color+'">'
      // use como name "sku"
      radios += '<input name="sku" type="radio>'
     }
  }
  return radios
}

function enable_sizes_per_color(){
  $('.cor input').click(function(){
    var elm = $( this );
    // add a variável apenas os tamanhos da cor selecionada
    variants = vnda.variants.find({Cor: elm.val()})
    // insere o html de tamanhos
    $('.tamanho').html(create_size_select(variants))
  });
}

// função que monta o html do tamanho
function create_size_select(variants){
  sizes = [];
  for(variant in variants) {
    if( variants[variant].available == true ) {
     if(variants[variant].properties) sizes.push(variants[variant].properties.property2.value);
    }
  }
  unique_sizes = sizes.filter(onlyUnique);
  select = '<label>Tamanho:</label>'
  select += '<select>'
  for(size in unique_sizes){
    if ( variants[variant].available == true ) {
     desired_size = unique_sizes[size]
     select += '<option value="'+desired_size+'">'
     select += desired_size
     select += '</option>'
   }
  }
  select += '</select>'
  return select
}

// se existir apenas o atributo tamanho
function create_absolute_size_radios(variants){
  select = '<label>Tamanho:'
  // use como name "sku"
  select += '<select name="sku" required>'
  for(variant in variants) {
    if( variants[variant].available == true ) {
      var desired_size = variants[variant].properties.property2.value
       select += '<option id="size_'+desired_size+'"  data-name="tamamho" data-size="'+desired_size+'"  value="'+variants[variant].sku+'">'
       select += desired_size
      select += '</option>'
     }
    }
  select += '</select>'
  return select
}

function fill_sku_with_variant(){
  variant = vnda.variants.all()[0]
  $( "form" ).find("#sku").val(variant.sku)
}

function quantity_of_properties(){
  property_count = 0
  variants = vnda.variants.all()
  if(exists(variants[0].properties.property1)) property_count++
  if(exists(variants[0].properties.property2)) property_count++
  if(exists(variants[0].properties.property3)) property_count++
  return property_count
}

function exists(property){
  if(!property) return false;
  value = property.value
  if(value && property.defining && (value !== null || value !== ""))
    return true;
  else
    return false;
}

function onlyUnique(value, index, self) {
  return self.indexOf(value) === index;
}
      

Etapa 2 - Nessa etapa estão as funções:

Integrações

Dicas de integrações com api's ou softwares de terceiros que agregram valor ou otimizam operações na loja.

Contentful - script para o front da loja

1)
function product_for_reference(reference, callback){

  var client = contentful.createClient({
    space: '{{ store.settings.contentful_space }}',
    accessToken: '{{ store.settings.contentful_token }}',
    secure: true,
    host: 'cdn.contentful.com'
  });

  client.contentTypes({name: "Product"}, function(err, content_type) {
    var product_id = content_type[0].sys.id;
    client.entries({content_type: product_id, "fields.reference[match]": reference}, function(err, entries) {

      if(typeof entries[0] === 'undefined'){
       callback(null);
      } else {
        callback(entries[0].fields);
      }
    });
  });
}

1- Função principal, responsável por filtrar as Entries com os parâmetros "reference" e "callback".product_for_reference(reference, callback)

Parâmetro reference = responsável por indentificar quais são as entries do produto

Parâmetro callback = esse parâmetro é uma função que carrega todos os campos de uma entries quando houver. callback(entries[0].fields);

2- Função para injetar no html o conteúdo de um entries. append_recipes(product)

Parâmetro product = é o callback da função product_for_reference(reference, callback), neste caso o callback deve carregar um objeto no formato json das entries.

3-Por último a chamada da função no .liquid através da referencia do produto {{ product.reference }}

2)
function append_recipes(product){
  recipes_quantity = product.recipes.length;

  if(recipes_quantity > 3){ recipes_quantity = 3; }

  for(i = 0;i < recipes_quantity; i++){
    console.log(product.recipes[i].fields.imagem.fields.file.url)
    console.log(product.recipes[i].fields.title)
    console.log(product.recipes[i].fields.description)
  }
}

3)
// via .liquid
<script type="text/javascript">
  var product_ref = '{{ product.reference }}';
  product_for_reference(product_ref, append_recipes);
</script>

Incluir busca swiftype

// mudanças no .liquid
1) {{ meta_tags }} no layout, dentro do head

2) em product
{% contentfor meta_tags %}a
<meta property="st:image" content="{{ product.image_url | resize: '185x' }}">
<meta property='st:type' content='products' />
<meta class="swiftype" name="name" data-type="string" content="{{ product.name }}" />
{% if product.on_sale%}
<meta class="swiftype" name="price" data-type="float" content="{{ product | apply_discount | money_format }}" />
<meta class="swiftype" name="original-price" data-type="float" content="{{ product.price  | money_format}}" />
{% else %}
<meta class="swiftype" name="price" data-type="float" content="{{ product.price | money_format }}" />
<meta class="swiftype" name="original-price" data-type="float" content="{{ product.price | money_format }}" />
{% endif %}
<meta class="swiftype" name="on_sale" data-type="enum" content="{{ product.on_sale }}" />
<meta class="swiftype" name="available" data-type="enum" content="{{ product.available }}" />
<meta class="swiftype" name="score_booster" data-type="float" content="{% if product.available %}1{% else %}0{% endif %}" />
{% endcontentfor %}

3) ajustar meta property="st:image com o tamanho da imagem usada no liquid da busca

Os processos para implementar a busca do swiftype são:
1 - Acrescentar meta tags no .liquid

2 - Criar engine de busca

3- Nessa etapa, ele vai buscar o conteúdo em todo o site

4 - Depois que já tiverem sido encontradas todas as páginas, colocar o filtro

5 - As informações que precisam ser colocadas no admin

Flickr - criando galeria com jflickrfeed

<script src="jflickrfeed.min.js"></script>

<ul class="slides"></ul>

$('.slides').jflickrfeed({
  limit: 14,
  qstrings: {
    id: '136314260@N04', // user name
    tags: 'A-CASA'
  },
  itemTemplate: '<li><img src="{{ image }}" alt="{{title}}" class="img-responsive" /></li>'
});

Através do plugin jflickrfeed.js é possível criar galerias de fotos.

O modelo de cadastro no flickr deve ser organizado por tags, ou seja, ao executar a função do jflickrfeed preencha os atributos id e tags da conta e monte o html no atributo itemTemplate

Segue o github /jflickrfeed-js um exemplo pronto.

Google Maps - Personalizando o Mapa no site

Inserindo o mapa do google maps em uma página do site

Api de places carrega os dados cadastrados no admin/lojas em forma de um objeto json.
**obs: para ativar o recurso do places solicite via email para dev@vnda.com.br.

Passo 1 - após efetivado o cadastro será gerado um token que servirá como parâmetro na url abaixo:
http://vnda-places.herokuapp.com/places.js?token=[token]&callback=
**obs: o arquivo places.js da api está configurado para aceitar o parâmetro callback e executar, no exemplo a seguir vamos executar a função loadShops que carregará as lojas cadastradas.

Passo 2 - cadastrar uma loja no admin/lojas/nova
Após efetivar o cadastro confira a url do passo 1, o retorno será como este:
**obs: para facilitar a leitura do arquivo instale a extensão para chrome JSON FORMATER.

Passo 3 - criar função que vai iniciar o mapa na página do site.
**obs: o script do passo 4 é um exemplo que pode ser tomado com base para criar outros tipos de aplicações que vão variar conforme a necessidade do layout. Ao encontrar dúvidas procure estudar a documentação do google maps e como manipular objetos no javascript.

Passo 4 - Modelo usado na loja da Retex - ver cógido no github

Personalizando o Template Zendesk

Com seu usuário logado acesse o painel de personalização

Clique [mudar tema ] e selecione o template Temas personalizados

Utilize o editor de código do zendesk para atualizar as páginas, css e javascript do tema

Abra os links abaixo, copie os código e atualize-os arquivos usando o editor.

Template da página inicial - ver código

Template da página categoria - ver código

Template da página produto - ver código

CSS do Template Zendesk - ver código

JS do Template Zendesk - ver código

Selecione o template ou arquivo a ser atualizado



Vimeo

// Alterar os dados abaixo para sua conta
var vimeoUserName = '940516';

// Tipo de dados do callback
 var videosCallback = 'showThumbs';

// Url para fazer a requisão
var videosUrl = 'https://vimeo.com/api/v2/channel/' + vimeoUserName + '/videos.json?callback=' + videosCallback;

Faça o download: https://vimeo.com/api/v2/channel/940516/videos.json?callback=showThumbs

Função completa publicada no github

Vimeo disponibiliza em sua api de integrações recursos de callback com respostas no formato json que facilmente com ajuda de jquery você pode criar galerias e consequemente efeitos lightbox após o carramento na página.

Disponibilizamos no github um exemplo de galeria de vídeos.

Importante: formato json da resposta de requisação da api do vimeo