Escolha de brinde no carrinho (Storefront 2.0).

Como adaptar o template da loja para que o consumidor escolha, dentro do carrinho, qual brinde quer receber entre as opções cadastradas na promoção.

Até aqui, uma promoção com brinde adicionava o produto ao carrinho sem possibilidade de escolha: no máximo o consumidor trocava a variação (cor, tamanho) do mesmo produto. Com a evolução do módulo de Promoções, o lojista pode cadastrar vários produtos diferentes como opção de brinde e definir quantos deles o consumidor pode escolher — o clássico "escolha 1 de 5". A vitrine dessa escolha é renderizada pelo template, a partir dos dados que o Storefront API passa a expor no carrinho.

Comportamento

O módulo de Promoções continua adicionando o brinde ao carrinho automaticamente: não existe brinde pendente fora do carrinho. O que muda é que cada linha de brinde passa a carregar a lista de produtos que podem ocupar aquela posição, e o consumidor pode trocar o que está ali.

EtapaResponsávelO que acontece
1Módulo de PromoçõesO carrinho atinge as regras da promoção e o brinde é adicionado, com 100% de desconto, já com uma opção padrão vinculada.
2Storefront APICada linha de brinde expõe cartGiftId e giftOptions — as opções declaradas pela promoção, com catálogo resolvido e indisponíveis filtradas.
3TemplateRenderiza a vitrine de opções e permite ao consumidor escolher outra.
4TemplateEfetiva a escolha com a mutation checkoutGiftVariantSelection.
5Módulo de PromoçõesValida a escolha, reprocessa o carrinho e vincula o produto escolhido à linha de brinde.

A escolha é sempre uma troca, nunca uma adição: a linha de brinde já existe e passa a apontar para outro produto. O total do carrinho não muda, porque o brinde continua com 100% de desconto.

Os dois eixos da escolha

Existem dois seletores diferentes, independentes, que podem aparecer na mesma linha de brinde:

EixoCampoPergunta que respondeAutoridade
ProdutogiftOptions"Qual produto o consumidor recebe?"A promoção, que declara as opções
VariaçãoattributeSelections"Qual variação do produto que já está na linha?"O catálogo do produto

attributeSelections é o comportamento que já existia e não mudou. giftOptions é o campo novo. Quando o consumidor troca o produto pelo eixo 1, o attributeSelections daquela linha passa a oferecer as variações do produto novo.

Escolha de mais de um brinde

Quando a promoção dá direito a mais de um brinde ("escolha 2 de 5"), o módulo de Promoções adiciona uma linha de brinde por unidade e todas as linhas da mesma promoção trazem o mesmo conjunto de opções.

Isso significa que não há contagem a validar no template. A cardinalidade já está satisfeita pela quantidade de linhas: duas linhas de brinde = dois brindes a escolher. Para exibir "Escolha 2 de 5", agrupe visualmente as linhas de brinde e conte-as. Para permitir que o consumidor leve duas unidades do mesmo produto, basta que ele escolha a mesma opção nas duas linhas — desde que a promoção permita repetição.

Pré-requisitos

  • Loja em Storefront 2.0.
  • Promoção cadastrada no Admin com mais de um produto contemplado como brinde e com a escolha habilitada.
  • Nenhuma alteração é obrigatória para promoções de brinde já existentes: com um único produto contemplado, giftOptions retorna vazio e o comportamento atual do carrinho é preservado.

Alterações necessárias no template

1. Consultar as linhas de brinde e as opções

Na query do carrinho, acrescente gift, cartGiftId e giftOptions aos campos de products. Os dados de catálogo de cada opção (nome, imagem, SKU e URL) já vêm resolvidos: não é necessária nenhuma chamada extra para montar a vitrine.

query Carrinho($checkoutId: String!) {
  checkout(checkoutId: $checkoutId) {
    checkoutId
    total
    products {
      productId
      productVariantId
      name
      quantity
      imageUrl
      gift
      cartGiftId

      giftOptions(width: 300, height: 300) {
        productId
        productVariantId
        selected
        name
        imageUrl
        sku
        url
      }

      attributeSelections {
        selections {
          attributeId
          name
          values {
            value
            selected
            available
          }
        }
      }
    }
  }
}

Referência completa dos campos: Checkout.

2. Renderizar a vitrine de brinde

Para cada item de products, a decisão de renderização é:

CondiçãoO que renderizar
gift: falseLinha normal do carrinho. giftOptions sempre vem vazio.
gift: true e giftOptions vazioLinha de brinde sem escolha de produto, como hoje.
gift: true e giftOptions com 1 opçãoLinha de brinde sem seletor de produto — não há troca possível.
gift: true e giftOptions com 2 ou mais opçõesVitrine de opções, destacando a opção com selected: true.

Recomendações de interface, alinhadas ao protótipo aprovado:

  • Sinalize explicitamente que se trata de brinde — um título antes do bloco ("Escolha seu brinde") ou uma etiqueta em cada card. Sem isso, o consumidor tende a interpretar a vitrine como produtos que está comprando.
  • Exiba a quantidade de brindes a escolher quando houver mais de uma linha de brinde da mesma promoção ("Escolha 2 de 5").
  • A opção com selected: true é a que está no carrinho neste momento. Sempre haja exatamente uma destacada — a promoção nunca deixa a linha vazia.
  • Não exiba preço nos cards de opção: o brinde tem 100% de desconto.
  • Não é preciso tratar disponibilidade: opções sem estoque não são retornadas pela API. O único caso em que uma opção indisponível aparece é a que já está no carrinho, que nunca é filtrada.

3. Efetivar a escolha

Ao consumidor escolher outra opção, chame a mutation CheckoutGiftVariantSelection informando o cartGiftId da linha e o productVariantId da opção.

mutation EscolherBrinde(
  $checkoutId: UUID!
  $cartGiftId: Long!
  $productVariantId: Long!
) {
  checkoutGiftVariantSelection(
    checkoutId: $checkoutId
    cartGiftId: $cartGiftId
    productVariantId: $productVariantId
  ) {
    checkoutId
    total
    products {
      productId
      productVariantId
      name
      gift
      cartGiftId
      giftOptions {
        productId
        productVariantId
        selected
        name
        imageUrl
      }
    }
  }
}

Pontos de atenção:

  • Informe sempre o cartGiftId. Sem ele, a plataforma tenta descobrir a linha de brinde pelo próprio productVariantId — comportamento histórico, que não distingue duas linhas de brinde no mesmo carrinho.
  • Envie apenas valores obtidos em giftOptions. Escolha fora da lista é recusada com PRM117.
  • A mutation devolve o carrinho já reprocessado. Peça no retorno os mesmos campos que a tela usa e re-renderize com essa resposta, em vez de disparar uma nova consulta do carrinho.

4. Resolver a variação quando productVariantId vier nulo

Em giftOptions, o campo productVariantId pode vir null. Isso significa que aquele produto tem duas ou mais variações disponíveis e ainda resta uma escolha antes de efetivar o brinde.

productVariantId da opçãoO que fazer
PreenchidoChamar a mutation direto com esse valor.
nullConsultar as variações do produto, deixar o consumidor escolher e só então chamar a mutation com a variação escolhida.

Para consultar as variações da opção, use a query products com includeParentIdVariants: false:

query VariacoesDaOpcao($productId: Long!) {
  product(productId: $productId) {
    productId
    productName
    attributeSelections(includeParentIdVariants: false) {
      selectedVariant {
        productVariantId
      }
      selections {
        attributeId
        name
        values {
          value
          selected
          available
        }
      }
    }
  }
}

includeParentIdVariants: false é obrigatório

O valor padrão do parâmetro é true, o que traz também os produtos irmãos (mesmo produto pai). Nesse contexto o escopo está errado: o universo de troca do brinde é o giftOptions declarado pela promoção, e aqui interessa apenas escolher a variação daquele produto.

Faça um único commit: escolha do produto e da variação resolvidas na interface, e só então uma chamada da mutation. Chamar a mutation com uma variação provisória grava no carrinho um brinde que o consumidor não escolheu.

5. Tratar os erros

A escolha pode ser recusada. Nesses casos, mantenha a seleção anterior na tela e exiba a mensagem retornada:

CódigoSignificadoTratamento no template
PRM117A opção escolhida não está entre as opções daquele brindeRevise se os valores enviados vêm de giftOptions. Re-consulte o carrinho: as opções podem ter mudado.
PRM109Linha de brinde não encontrada no carrinhoO cartGiftId enviado não existe mais. Re-consulte o carrinho e re-renderize a vitrine.
PRM104Falha ao gravar a escolha no módulo de PromoçõesInclui as validações da própria promoção. Exiba mensagem de falha e mantenha a escolha anterior.

Além disso, o carrinho pode deixar de atender à promoção enquanto o consumidor navega — ao remover o item que dava direito ao brinde, por exemplo. Nesse caso a linha de brinde deixa de existir e a vitrine simplesmente não é mais renderizada. Se o carrinho voltar a atender à promoção, uma nova linha é criada e a escolha é feita novamente. Por isso, sempre re-renderize o bloco de brindes a partir do retorno mais recente do carrinho, sem manter estado próprio de seleção no front.

Referências

RecursoOnde
Campo giftOptions e objeto GiftOptionCheckout
Mutation de escolha do brindeCheckoutGiftVariantSelection
Variações de uma opçãoProducts
Implementação de referência no template padrãohttps://git.fbits.net/stores/awake
Template padrão (visão geral e acesso ao repositório)Template Padrão

A implementação da vitrine de escolha de brinde está disponível no repositório da Awake, o template padrão a partir do qual as novas lojas da Wake são criadas. Use-a como referência de markup e de fluxo de chamadas. Todo usuário criado no GitLab recebe acesso de leitura a esse repositório.

Checklist

  • gift, cartGiftId e giftOptions incluídos na query do carrinho
  • Vitrine renderizada apenas quando giftOptions tem 2 ou mais opções
  • Bloco identificado como brinde na interface, com a quantidade a escolher quando houver mais de uma linha
  • Opção com selected: true destacada como escolha atual
  • Mutation chamada sempre com o cartGiftId da linha
  • Opção com productVariantId nulo resolvida com a query de variações antes da mutation, usando includeParentIdVariants: false
  • Carrinho re-renderizado a partir do retorno da mutation, sem estado de seleção próprio no front
  • PRM117, PRM109 e PRM104 tratados com mensagem e sem perder a seleção anterior
  • Fluxo validado em promoção com escolha de mais de um brinde e em opção com variações