Insights / Artigos técnicos
Projeto técnico para levar o contexto de um CTA de serviço ao formulário de contato
Projeto de implementação para levar ao formulário o contexto lido em uma página de serviço. Abrange mini CTAs em Astro, contrato de parâmetros de URL, seleção inicial da categoria, prefill do assunto, URLs multilíngues, medição com GA e verificação do HTML gerado.
Sumário
- Validar a inicialização do formulário a partir de um CTA
- O objetivo não é apenas reduzir o preenchimento
- Separar responsabilidades em um componente de CTA
- Transformar os parâmetros de URL no contrato com o formulário
- Manter uma tabela de classificação no formulário
- Adicionar atributos data aos option
- Fazer o prefill no cliente
- Ir ao formulário usando o hash
- Decidir não aumentar campos hidden
- Abordagem para sites multilíngues
- Verificar o HTML gerado
- Divisão de papéis com o chat de IA
- Resumo
- Atualização de 6 de outubro de 2026: do formulário de contato ao registro administrativo
Atualização de setembro de 2026: Os exemplos
ServiceSectionActionseservice=webregistram o projeto do site corporativo em junho de 2026. Hoje, os CTAs de serviço da Acecore Systems passamcategory,service,fromeentry; o formulário valida chaves curtas e pré-seleciona categoria e assunto. Ele também usa campos ocultos para medição e integração; não copie a antiga escolha de dispensá-los como contrato atual. Valores de URL ainda precisam ser mapeados para opções permitidas.
Quando alguém que lê uma página de serviço pensa “quero consultar sobre isto”, apenas enviar essa pessoa ao formulário faz perder parte do contexto.
O usuário precisa escolher novamente o tipo de serviço e escrever o assunto. A equipe receptora também não consegue saber facilmente se a consulta é sobre produção web, operação de servidores ou Aceserver antes de ler a mensagem.
No site da Acecore, melhoramos essa jornada no PR que leva o alvo do CTA de serviço ao formulário. Este artigo organiza a solução não só como registro de implementação em Astro, mas como projeto de jornada reutilizável em outros sites.
Validar a inicialização do formulário a partir de um CTA
Teste uma chave válida, outra desconhecida e voltar após escrever o assunto. Verifique opções permitidas e preservação do assunto, mantendo o mesmo contrato nas URLs por idioma. Não inclua dados pessoais nem texto livre na URL; passe contexto com identificadores curtos.
MDN URLSearchParams:Ler parâmetros de URL
O objetivo não é apenas reduzir o preenchimento
A finalidade não é simplesmente preencher campos automaticamente para o formulário parecer mais fácil.
O essencial é levar corretamente o contexto criado na página de serviço ao formulário e à operação receptora.
| Perspectiva | O que deve melhorar |
|---|---|
| Usuário | Evitar que selecione novamente o serviço que estava lendo |
| Formulário | Inicializar categoria e assunto de acordo com a consulta |
| Equipe receptora | Facilitar a classificação apenas pela categoria |
| Medição | Rastrear de qual CTA de serviço a consulta começou |
| Jornada multilíngue | Enviar para a URL de contato correspondente ao locale |
Embora pareça um pequeno mini CTA, o projeto abrange CTA, URL, formulário, tradução, medição e operação receptora.
Separar responsabilidades em um componente de CTA
Colocamos um CTA “Consultar sobre este serviço” ao final de cada seção.
É melhor evitar escrever diretamente em cada seção a mesma geração de link e os mesmos atributos de GA. Com sete serviços, a lógica se repete sete vezes e alguma cópia pode ficar desatualizada quando a redação ou a URL mudar.
Por isso criamos ServiceSectionActions para centralizar o CTA.
---
import Icon from "./Icon.astro";
import { t, getLocalizedUrl, type Locale } from "../i18n";
interface Props {
locale: Locale;
gaLabel: string;
gaLocation: string;
serviceKey: string;
}
const { locale, gaLabel, gaLocation, serviceKey } = Astro.props;
const u = (path: string) => getLocalizedUrl(path, locale);
const contactUrl = `${u("/contact/")}?category=service&service=${encodeURIComponent(serviceKey)}#contact-form`;
---
<a
href={contactUrl}
class="ac-btn-outline gap-2 text-sm sm:w-auto"
data-ga-event="cta_click"
data-ga-label={gaLabel}
data-ga-location={gaLocation}
data-ga-destination={contactUrl}
>
<Icon name="message-circle" class="text-sm" />
{t(locale, "pages.services.miniCta")}
</a>
O componente tem três responsabilidades:
- Gerar uma URL de contato de acordo com o locale
- Colocar o service key em um parâmetro de URL
- Manter label e location usados na medição do GA
O CTA é um ponto de ação e também um ponto de medição. Mantemos data-ga-label e data-ga-location para ver depois de qual serviço a consulta começou.
Transformar os parâmetros de URL no contrato com o formulário
Os valores são passados por parâmetros de URL.
/contact/?category=service&service=web#contact-form
O ponto importante é não usar o texto exibido na URL.
Um rótulo como Webサイト制作・運用について sofre efeitos de tradução, variações de redação e futuras mudanças de nome. Na URL, coloque apenas um service key curto como web ou server.
| Parâmetro | Papel |
|---|---|
category |
Indica a entrada para tratar uma consulta de serviço |
service |
Key estável que representa o serviço-alvo |
| hash | Usado para rolar até o formulário |
O usuário pode editar os parâmetros. Por isso o formulário não usa o valor da URL diretamente no envio; ele o mapeia para um option existente.
- Resolver o contexto do CTA Mapear uma service key estável para a URL localizada e uma opção permitida do formulário.
- Validar dados e permissões Preencher o assunto apenas quando estiver vazio; a API também verifica a identidade do usuário e as permissões de operação.
- Criar o registro de entrada Registrar a solicitação no histórico e nas tarefas seguintes; a conclusão do atendimento é outro estado.
Manter uma tabela de classificação no formulário
O formulário mantém as categorias por serviço em um array.
const serviceCategoryOptions = [
{
key: "server",
value: "サーバー構築・運用について",
label: t(locale, "pages.contact.formCategoryServiceServer"),
subject: t(locale, "pages.services.server.title"),
},
{
key: "web",
value: "Webサイト制作・運用について",
label: t(locale, "pages.contact.formCategoryServiceWeb"),
subject: t(locale, "pages.services.web.title"),
},
];
key, value, label e subject têm papéis diferentes.
| Campo | Papel |
|---|---|
key |
Identificador estável para procurar pelo parâmetro da URL |
value |
Categoria recebida pela equipe quando o formulário é enviado |
label |
Option traduzido mostrado na tela |
subject |
Nome do serviço usado para inicializar o assunto |
Em um site multilíngue, label é traduzido para o locale. Já value é usado na classificação da equipe receptora e permanece como valor japonês estável.
A decisão varia conforme o produto. Se o CRM ou formulário externo aceitar classificações multilíngues, value também pode variar por locale. Para simplificar a operação receptora, separar rótulo exibido e valor enviado é mais fácil de administrar.
Adicionar atributos data aos option
O select gera um option para cada serviço.
<select id="category" name="category" required>
<option value="" disabled selected>
{t(locale, "pages.contact.formCategoryPlaceholder")}
</option>
<option value="サービス全般について">
{t(locale, "pages.contact.formCategoryService")}
</option>
{
serviceCategoryOptions.map((option) => (
<option
value={option.value}
data-service-key={option.key}
data-service-subject={option.subject}
>
{option.label}
</option>
))
}
</select>
data-service-key é comparado com service na URL. data-service-subject é usado para criar o assunto.
Também aqui não colocamos o valor da URL diretamente em category.value. Escolher sempre um option do select evita que um service key desconhecido ou um valor inválido entre nos dados enviados.
Fazer o prefill no cliente
Um pequeno script inicializa o formulário após o carregamento.
function initContactServicePrefill() {
const form = document.getElementById("contact-form");
if (!form || form.dataset.servicePrefillInitialized === "true") return;
form.dataset.servicePrefillInitialized = "true";
const url = new URL(window.location.href);
const requestedCategory = url.searchParams.get("category");
const requestedService = url.searchParams.get("service") || "";
const category = document.getElementById("category");
const subject = document.getElementById("subject");
if (
requestedCategory === "service" &&
category instanceof HTMLSelectElement
) {
const serviceOption = Array.from(category.options).find((option) => {
return option.dataset.serviceKey === requestedService;
});
category.value = serviceOption?.value || "サービス全般について";
category.dispatchEvent(new Event("input", { bubbles: true }));
category.dispatchEvent(new Event("change", { bubbles: true }));
if (
serviceOption &&
subject instanceof HTMLInputElement &&
!subject.value.trim()
) {
const template = form.dataset.serviceSubjectTemplate || "{service}";
const serviceName =
serviceOption.dataset.serviceSubject ||
serviceOption.textContent?.trim() ||
"";
subject.value = template.replace("{service}", serviceName);
}
}
}
Há quatro pontos:
- Verificar
data-service-prefill-initializedpara evitar inicialização dupla - Processar somente quando
category=service - Fazer fallback para
サービス全般についてdiante de um service key desconhecido - Preencher o assunto somente quando estiver vazio
O último ponto é importante. Se a navegação de retorno ou o preenchimento do navegador conservar o assunto, sobrescrevê-lo piora a experiência.
Com Astro View Transitions ou navegação no cliente, também inicializamos em astro:page-load.
document.addEventListener("astro:page-load", initContactServicePrefill);
initContactServicePrefill();
Ir ao formulário usando o hash
A URL do CTA inclui #contact-form.
/contact/?category=service&service=web#contact-form
Como a página pode ter FAQ, LINE, explicações e outros meios de contato, é natural levar diretamente ao formulário quem veio de um CTA de serviço.
Quando o formulário também é inicializado, é preciso cuidar do momento da rolagem. Usamos requestAnimationFrame para rolar após o elemento ser renderizado.
if (window.location.hash === "#contact-form") {
window.requestAnimationFrame(() => {
form.scrollIntoView({ block: "start" });
});
}
É um comportamento pequeno, mas o usuário se perde quando a intenção do CTA e a posição visível não coincidem. URL, seleção inicial e posição de rolagem são tratadas em conjunto.
Decidir não aumentar campos hidden
Não adicionamos um campo hidden 相談対象サービス.
Queríamos identificar o serviço apenas pela categoria.
Adicionar campos também adiciona verificações:
- Se aparece no e-mail de notificação
- Se requer uma coluna no painel ou planilha
- Se afeta modelos de resposta automática
- Se é tratado por CRM ou Webhook
- Como separar nomes multilíngues e valores recebidos
Se os campos existentes expressam a informação, não adicionar outro estabiliza a operação. Dividimos お問い合わせ種別 entre consultas gerais e categorias específicas.
Um hidden pode ser adequado quando é necessário selecionar vários serviços, guardar um ID de campanha ou usar um campo separado no CRM.
Abordagem para sites multilíngues
Separar três valores evita confusão.
| Tipo | Exemplo | Depende do locale |
|---|---|---|
| URL key | web, server, aceserver |
Não |
| Rótulo exibido | About Website Design, etc. |
Sim |
| Valor enviado | Webサイト制作・運用について |
Depende da operação |
É mais estável não traduzir a URL key, usada ao compartilhar, medir e comparar no formulário.
O rótulo exibido sempre é traduzido porque é o texto visto pelo usuário.
O valor enviado segue a operação. Aqui usamos valores japoneses estáveis. Separar a exibição multilíngue da operação interna posterior facilita o gerenciamento.
O fluxo de tradução também é descrito em Como operar um blog multilíngue com Sveltia CMS.
Verificar o HTML gerado
Não basta olhar o componente. Após o build, confirme que os links e option foram gerados.
Verificamos:
- Sete CTAs específicos em
/services/ - Cada CTA contém
?category=service&service=...#contact-form - Sete option com
data-service-keyem/contact/ サービス全般についてe categorias específicas aparecem- Nenhum hidden
相談対象サービスaparece
Por exemplo, use rg.
rg -n "category=service&service=.*#contact-form" dist\services\index.html
rg -n "data-service-key" dist\contact\index.html
rg -n "相談対象サービス" dist\contact\index.html
A última verificação confirma a ausência do que decidimos não adicionar. Mudanças em formulários devem conferir tanto o que foi incluído quanto o que foi deliberadamente omitido.
Divisão de papéis com o chat de IA
Esta jornada combina com o projeto técnico do chat de atendimento com IA, mas os papéis são diferentes.
| Jornada | No que é boa |
|---|---|
| Chat de IA | Organiza por conversa qual serviço consultar |
| CTA de serviço | Leva ao formulário o contexto do serviço lido |
| Formulário | Recebe a consulta formal e mantém o registro |
O chat é forte quando o usuário ainda está indeciso. Para quem já terminou a página e decidiu consultar aquele serviço, ir diretamente ao formulário é mais natural.
Ao adicionar jornadas, não dê a todas o mesmo papel. Use conversa, CTA e formulário conforme o estado do usuário.
Resumo
Levar o contexto da página ao formulário tem mais efeito do que a pequena mudança visual sugere.
Os pontos importantes foram:
- Transformar o CTA em componente e centralizar URL e atributos de medição
- Usar service key estável na URL em vez do texto exibido
- Mapear service key para um option no formulário
- Separar valor enviado, rótulo e nome usado no assunto
- Fazer fallback para consultas gerais diante de service key desconhecido
- Preencher o assunto apenas quando vazio
- Classificar pela categoria sem adicionar hidden
- Verificar após o build quantidades de links e option e ausência de campos desnecessários
Melhorar um formulário não é apenas reduzir campos. Levar o contexto que o usuário estava lendo até a equipe receptora facilita o atendimento real.
Atualização de 6 de outubro de 2026: do formulário de contato ao registro administrativo
Em uma integração anonimizada, o recebimento de uma solicitação, sua inclusão no histórico e nas tarefas do CRM e a conclusão do atendimento pela equipe foram tratados como estados distintos. Os valores de URL e o caminho de contato são validados, e os valores do caminho são associados a um enum estável. Conhecer a URL da interface administrativa não concede permissão para alterar histórico ou tarefas.
A API também verifica a pessoa usuária e suas permissões, e não mistura dados de clientes com a busca pública ou respostas de IA pública. Mesmo com a implementação do recebimento e do registro concluída, não se afirma que o contato com clientes reais até a conclusão do atendimento foi testado de ponta a ponta.
O prefill do formulário é distinguido da informação inserida pela pessoa. Quando a solicitação exige retorno, o canal de contato preferido é obrigatório e escolhido em uma lista fixa, sendo encaminhado à tarefa subsequente. Os requisitos da interface e da API, bem como as permissões da equipe para leitura e acréscimo, são alinhados; após uma alteração no banco de dados, o schema e as constraints são verificados. As verificações necessárias em produção permanecem separadas da cópia de todo o banco de dados de clientes reais para um ambiente de desenvolvimento.
Fluxo para passar o contexto do CTA de serviço ao formulário
Service
Atribuir um service key ao CTA de cada seção de serviço.
URL
Passar pelo contrato de URL, como /contact/?category=service&service=web#contact-form.
Form
Inicializar no formulário a categoria e o assunto correspondentes.
Ops
A equipe receptora identifica o contexto do serviço apenas pela categoria.
Diferença ao conectar o CTA ao formulário
Apenas enviar ao formulário
- O usuário precisa selecionar novamente o mesmo serviço
- O assunto fica vazio e a natureza da consulta não fica clara
- A equipe receptora precisa ler a mensagem para identificar o serviço
- A medição de cada CTA de serviço fica ambígua
Levar o contexto adiante
- A categoria pode ser pré-selecionada pelo service key do CTA
- O nome do serviço pode preencher o assunto e organizar a consulta
- A equipe receptora classifica a consulta olhando a categoria
- O GA label e os parâmetros de URL facilitam a análise por CTA
Verificação de projeto para adoção
- Usar nos parâmetros de URL apenas service keys curtos e estáveis
- Usar valores operacionalmente estáveis no envio, não textos exibidos ao usuário
- Fazer fallback para consultas gerais diante de um service key desconhecido
- Preencher o assunto somente quando estiver vazio
- Antes de adicionar campos hidden, confirmar se a categoria existente já permite a classificação
- Gerar no servidor a URL de contato de cada locale
- Adicionar GA label e location ao CTA para medir seu desempenho
- Após o build, verificar no HTML as quantidades de CTA e option e a presença ou ausência de campos hidden
Páginas relacionadas
Perguntas frequentes
Por que não enviar o serviço-alvo em um campo hidden?
Em junho de 2026, a classificação usava apenas a categoria existente e evitava campos ocultos extras. O formulário atual da Systems também guarda service, from e entry em campos ocultos para integração.
É seguro se os parâmetros da URL forem alterados?
Um service key desconhecido faz fallback para consultas gerais. O valor enviado é escolhido entre os option do formulário, portanto o valor da URL não é usado diretamente.
Como tratar isso em um site multilíngue?
Gere o destino do CTA para cada locale e traduza os rótulos exibidos no formulário. Manter os valores enviados em classificações japonesas estáveis ajuda a evitar variação na operação receptora.