Lead e WhatsApp
Só o modelo captura tem formulário de lead. Os outros modelos não recebem dados pelo site: o contato é pelo WhatsApp e, nos modelos que vendem, a compra vai para o checkout. Para conhecer os modelos, veja Modelos.
Formulário de lead
Seção intitulada “Formulário de lead”O formulário fica na primeira dobra, no lugar do Hero com a variante split-form. Ele tem:
- Nome e Telefone com DDD, obrigatórios. O telefone precisa ter pelo menos 10 dígitos, sem contar o
+55(ui/lead/phone.ts). - E-mail, só quando o formulário recebe
fields: ["email"]. O modelocapturanão recebe, então o campo não aparece. - Consentimento, obrigatório, com o link «Política de privacidade» para
consent.privacyHref, dentro dehero.form.consentemsrc/data/content.ts. - Botão de envio, com o texto de
submitLabelemsrc/data/content.ts.
O envio com JavaScript é um POST em JSON para lead.endpoint, com 10 segundos de limite. Só conta como enviado uma resposta 2xx. Uma resposta de redirecionamento, uma falha de rede ou o limite estourado mostram o erro.
Quando o envio dá certo, o formulário some e aparece a mensagem de lead.successMessage, com o foco nela. Quando dá errado, aparece «Não foi possível enviar agora.» com o botão «Chamar no WhatsApp». O visitante pode tentar de novo.
O corpo do POST tem os campos name, phone e page (o caminho da página). email só vai quando o campo existe e foi preenchido, e os parâmetros utm_* só quando a URL os traz. O consentimento não vai no JSON.
Sem JavaScript, o navegador envia um POST comum, no formato de formulário, com os campos do formulário: name, phone, email (se existir), consent e company. O endpoint precisa aceitar os dois formatos.
Quando o endpoint fica em outro domínio, o navegador faz uma pré-requisição OPTIONS antes do POST, porque o envio usa content-type: application/json. O endpoint precisa responder a ela e liberar o domínio do site e o cabeçalho content-type. O endpoint também precisa responder 2xx direto: um redirecionamento 3xx conta como erro.
Um campo oculto chamado company serve de isca para robôs. Com JavaScript, se ele vier preenchido, o envio finge sucesso e nada sai do navegador. Sem JavaScript, ele vai no POST, então o destino deve descartar qualquer lead com company preenchido.
A medição envia o evento generate_lead, com method: "form", só quando o envio dá certo. O evento não leva nome, telefone nem e-mail.
Endpoint
Seção intitulada “Endpoint”O endereço fica em lead.endpoint, em src/data/site.ts. Ele precisa começar com https://: um endereço sem isso faz o build parar com a mensagem de correção. Um pending(...) nesse campo também para o build, porque o plain o transforma em undefined.
Enquanto o endereço tiver o marcador fictitious(...), o slash check pede slash approve. Um endereço sem marcador, que é o que a agência escreve, não entra na lista do approve.
import { fictitious } from "./origin";import type { FooterBusiness, SeoInput } from "../ui";import { theme } from "../theme";
/** Site data, read by the CLI and by the pages. Marked values go through the content gate; use `plain(site)` to consume them. */export const site = { name: "Aurora Odonto", baseUrl: "https://exemplo.com.br", lang: "pt-BR", whatsapp: fictitious({ number: "11999999999", message: "Olá! Quero marcar minha avaliação gratuita na Aurora Odonto." }), lead: { endpoint: "https://leads.exemplo.com.br/aurora", successMessage: "Recebemos seus dados. Nossa equipe chama você no WhatsApp para marcar a avaliação.", }, gtmId: fictitious("GTM-TEST000"), business: fictitious<FooterBusiness>({ name: "Aurora Odontologia Ltda.", legalId: "12.345.678/0001-90", email: "contato@exemplo.com.br", hours: "Seg a Sáb, 8h às 19h", }),} as const;
/** SEO and share data of the page (`jsonLd` and `extra` are added by routes.ts). */export const seo: SeoInput = { title: "Aurora Odonto: avaliação odontológica gratuita com dentista", description: "Deixe seu contato e marque uma avaliação gratuita com dentista: diagnóstico, plano de tratamento e orçamento sem compromisso.", share: { image: "hero.jpg", alt: "Dentista da Aurora Odonto conversando com uma paciente na clínica" }, favicon: "/favicon.svg", themeColor: theme.tones.light.bg,};O exemplo usa fictitious de src/data/origin.ts nos campos que ainda são de exemplo, e os tipos FooterBusiness e SeoInput de src/ui/ (exportados por src/ui/index.ts). O tema vem de src/theme.ts. Este exemplo troca o endereço de exemplo pelo da agência, sem marcador, e tira o marcador do texto de sucesso.
O número e a mensagem ficam em site.whatsapp, em src/data/site.ts. O número precisa ter de 10 a 13 dígitos, com DDD. Com 10 ou 11 dígitos, o código do país 55 é acrescentado; com 12 ou 13, o número é usado como está. Fora dessa faixa, a geração do link lança o erro «WhatsApp inválido», e o build para. A mensagem entra no link já codificada.
Onde o WhatsApp aparece:
- Botão do cabeçalho, nos modelos que têm cabeçalho (todos, menos
vendas). O texto do botão muda por modelo: nocapturaé o WhatsApp; emproduto, «Testar grátis», que leva ao checkout. Abaixo de 768 px, com o menu funcionando (com JS), o botão do cabeçalho sai da barra. A regra é do componente Header, vale para todo modelo e não depende do tipo do botão. - Botão flutuante «Falar no WhatsApp», no canto inferior direito. Ele está em
vendas,produto,eventoelancamento. O modelocapturanão tem botão flutuante. - Botões de CTA de tipo WhatsApp, como a faixa final e o cartão de contato.
Os botões de WhatsApp registram o evento generate_lead, usado para medir contatos.