material da comunidade 8 prompts na ordem o seu Typeform, com o seu nome use Opus 4.8 ou Opus 5
material da comunidade 8 prompts na ordem o seu Typeform, com o seu nome use Opus 4.8 ou Opus 5
crie o seu formulário
passo a passo
8 prompts, 1 dia
Crie o seu formulário conversacional e pare de alugar o Typeform.
Uma pergunta por tela, o lead respondendo no impulso, e no fim ele já cai classificado em quente, morno ou frio. Você não vai construir um formulário: vai construir a plataforma onde você cria quantos quiser, arrasta as perguntas num construtor visual, publica um link e vê as respostas num painel que é seu. Oito prompts, na ordem.
8 prompts prontospeça Opus 4.8 ou Opus 5construtor visuallead já qualificado
/painel/[id] · o construtor
O destino. Print de verdade da plataforma rodando: telas à esquerda, prévia no meio, inspetor à direita. Repare no chip laranja empresa dentro da pergunta e no vai para embaixo de cada opção. Esses dois detalhes são o que fazem parecer ferramenta paga.
O modelo certo, e por que aqui não é arquivo único
No CRM dava pra fazer tudo num index.html. Aqui não dá, e o motivo é o que separa esse produto de um formulário de brinquedo: quem responde não pode ter acesso ao seu banco. A resposta sai do navegador, bate numa rota do seu servidor, e é o servidor que grava. A chave do banco nunca chega no computador de quem respondeu.
Então a stack é Next.js com App Router, React, TypeScript, Tailwind e Supabase. São umas 20 arquivos no fim, e é a IA que escreve todos.
Use no mínimo o Opus 4.8. Se tiver Opus 5, use o Opus 5.
Esse projeto tem um motor de 700 linhas, um construtor de 700 e quatro rotas de servidor que precisam concordar entre si. Modelo leve constrói uma parte e quebra a outra sem perceber. Escolha Opus no seletor antes do primeiro prompt e não troque no meio.
Onde rodar: Claude Code, com folga. Ele cria os arquivos nas pastas certas, roda o servidor local e você só olha o navegador. Pelo claude.ai também dá, mas você vai copiar arquivo por arquivo na mão e a chance de errar caminho é grande. A regra continua: uma conversa só do primeiro ao último prompt.
Modelo em Opus, confirmado no seletor.
Conta no Supabase criada, com um projeto novo.
Guardadas a Project URL e a service role key (Settings, API). Essa segunda é secreta de verdade: ela só existe no servidor.
Uma pasta vazia chamada meu-form e uma conversa só do começo ao fim.
02
Prompt 01: o formulário é dado, não código
Essa é a decisão que decide o produto inteiro, e é onde 90% das pessoas erram. A tentação é pedir "faz um formulário conversacional com essas perguntas". Aí as perguntas viram código, e no dia que você quiser o segundo formulário, você tem que chamar a IA de novo.
O jeito certo: toda a estrutura do formulário vive num objeto JSON. O motor lê esse objeto e desenha as telas. Trocar o JSON troca o formulário. É isso que permite, mais pra frente, ter um construtor visual sem reescrever uma linha do motor.
Guarde essa frase pro resto da sua vida construindo
Se uma coisa muda por cliente, por campanha ou por dia, ela é dado, e mora no banco. Se ela é igual pra todo mundo, ela é código. Confundir os dois é o que faz uma ferramenta virar dez ferramentas quebradas.
Prompt 01 · esqueleto e o formato do formulário
Você é um engenheiro full-stack sênior. Vamos construir um clone do Typeform/Respondi: uma plataforma onde EU crio formulários conversacionais (uma pergunta por tela), publico um link e recebo as respostas já classificadas por interesse.
STACK, sem discussão
- Next.js (App Router) + React + TypeScript + Tailwind. Sem biblioteca de formulário, sem biblioteca de animação, sem UI kit.
- Banco: Supabase (Postgres), acessado SÓ pelo servidor, com a service role key. Essa chave nunca pode aparecer no navegador.
- CSS em globals.css com variáveis de cor. Ícones em SVG inline, traço 1.8. Nada de emoji na interface.
- Deploy final na Vercel.
POR QUE NÃO É UM ARQUIVO SÓ: quem responde o formulário não pode ter acesso ao banco. O navegador manda a resposta pra uma rota do meu servidor e o servidor grava. Isso não é negociável.
A DECISÃO MAIS IMPORTANTE DO PROJETO
Um formulário é DADO, não código. Toda a estrutura de um formulário vive num objeto JSON chamado FormConfig, guardado numa coluna jsonb. O motor lê esse objeto e desenha as telas. Nenhuma pergunta minha pode estar escrita dentro de um componente.
Comece criando src/lib/form-config.ts com exatamente estes tipos:
FieldType, uma união de: "name" | "company" | "email" | "short_text" | "long_text" | "phone" | "date" | "url" | "cpf" | "cnpj" | "number" | "single_choice" | "multiple_choice" | "rating" | "file" | "calendar" | "statement"
Choice = { label, value?, score?, goTo? }
score é o peso daquela opção na qualificação.
goTo é o id da próxima pergunta quando a pessoa escolhe essa opção, ou "__end__" pra finalizar.
Question = { id, type, title, description?, required?, placeholder?, mapTo?, choices?, maxSelect?, ratingMax?, ratingLabels?, accept?, calendarUrl?, asButton?, buttonLabel?, buttonUrl? }
mapTo aceita "nome" | "email" | "whatsapp" | "empresa" e diz em qual coluna do lead aquela resposta é gravada, além do jsonb.
Tier = { name, min, label }
EndingScreen = { title, subtitle, cta?: { label, url } }
WelcomeScreen = { eyebrow?, title, subtitle?, buttonLabel, minutes? }
FormTheme = { accent?, mode?: "dark" | "light" }
FormConfig = { meta?, welcome, questions[], scoring?: { tiers[] }, ending?: registro de tier para EndingScreen, theme? }
No mesmo arquivo, três helpers que RECEBEM a config (nunca leiam de variável global):
- computeScore(config, answers): soma o score das opções escolhidas, contando cada escolha de multipla.
- tierForScore(config, score): devolve o tier de maior "min" que caiba no score.
- endingForTier(config, tier): devolve a tela final daquele tier, caindo pro "default" quando não houver.
Crie também um defaultConfig de exemplo com 6 perguntas (nome, empresa, uma escolha única com pesos, uma escolha única, uma escala de 0 a 10 e telefone), três tiers (quente min 7, morno min 4, frio min 0) e três telas finais diferentes. Ele serve de referência e de teste.
ENTREGA
- Só o esqueleto do projeto e esse arquivo de tipos, funcionando e compilando. Ainda não construa telas.
- Nada de TODO, nada de função vazia, nada de "aqui você implementa".
- Me diga em 5 linhas o que existe agora e o que vem no próximo passo.
03
O banco, com a trava fechada por padrão
Duas tabelas e uma view resolvem a plataforma inteira: uma guarda os formulários (cada linha é um formulário, com o JSON inteiro numa coluna) e outra guarda as respostas. A view só conta quantas respostas cada formulário tem, pra o painel não ter que contar na mão.
A trava aqui é diferente da do CRM, e é mais simples: a RLS fica ligada e sem nenhuma política. Isso significa que ninguém, com nenhuma chave pública, lê ou escreve nada. Só a service role, que só existe dentro do seu servidor, passa. Cole no SQL Editor do Supabase e rode uma vez.
SQL · cole no editor do Supabase
-- 1. OS FORMULÁRIOS (cada linha é um formulário inteiro)
create table if not exists form_formularios (
id uuid primary key default gen_random_uuid(),
created_at timestamptz not null default now(),
updated_at timestamptz not null default now(),
owner text not null default 'eu',
titulo text not null default 'Formulário sem título',
slug text not null unique,
privacidade text not null default 'privado',
status text not null default 'rascunho',
config jsonb not null default '{}'::jsonb,
thumb text
);
-- 2. AS RESPOSTAS
create table if not exists form_respostas (
id uuid primary key default gen_random_uuid(),
created_at timestamptz not null default now(),
form_id text not null default 'default',
nome text,
empresa text,
email text,
whatsapp text,
respostas jsonb not null default '{}'::jsonb,
score int not null default 0,
tier text,
utm jsonb not null default '{}'::jsonb,
referer text,
user_agent text
);
create index if not exists form_respostas_form on form_respostas(form_id);
create index if not exists form_respostas_data on form_respostas(created_at desc);
-- 3. QUANTAS RESPOSTAS CADA FORMULÁRIO TEM
create or replace view form_response_counts as
select form_id, count(*)::int as total
from form_respostas
group by form_id;
-- 4. A TRAVA: RLS LIGADA E SEM NENHUMA POLÍTICA
-- Ninguém com chave pública lê nem escreve. Só a service role (que vive
-- no servidor) passa. É a proteção mais simples e mais forte pra esse caso.
alter table form_formularios enable row level security;
alter table form_respostas enable row level security;
-- 5. CARIMBO DE ATUALIZAÇÃO
create or replace function form_touch() returns trigger
language plpgsql set search_path = public as $$
begin new.updated_at := now(); return new; end $$;
drop trigger if exists form_formularios_touch on form_formularios;
create trigger form_formularios_touch before update on form_formularios
for each row execute function form_touch();
Ligar RLS sem política não é preguiça, é a escolha certa aqui
No CRM cada pessoa entra com a conta dela, então a política precisa existir pra dizer quem vê o quê. Aqui quem responde o formulário não tem conta. Então a regra é: o navegador nunca fala com o banco. Ele fala com o seu servidor, e o seu servidor decide. Fechar tudo e liberar só pelo servidor é mais seguro do que tentar escrever uma política pra um usuário anônimo.
04
Prompt 02: o motor conversacional
Aqui nasce a sensação de conversa. Uma pergunta por tela, transição suave, teclado funcionando, e a barra andando. Parece simples e é o componente mais delicado do projeto, porque ele precisa dar conta de dezessete tipos de campo sem virar uma bagunça de "se for isso, faz aquilo".
Prompt 02 · o motor
Agora construa o motor: o componente que recebe uma FormConfig e vira o formulário conversacional na tela. Ele é a cara do produto, então capricha.
COMPORTAMENTO
- Uma pergunta por tela cheia, centralizada, com muito respiro. Nunca duas perguntas juntas.
- Transição entre telas: desliza de baixo pra cima com um fade curto, 0.35s, saída e entrada. Nada de piscar.
- Barra de progresso fina no topo, e o contador "3 / 6" acima da pergunta.
- Botão de voltar discreto no canto. Voltar tem que devolver a resposta anterior já preenchida.
- Teclado: Enter avança, Shift+Enter quebra linha no texto longo, as letras A, B, C, D escolhem as opções, e escolha única e escala AVANÇAM SOZINHAS 0.4s depois do clique. Essa auto-avanço é metade da mágica.
- Tela de boas-vindas com título, subtítulo, o tempo estimado e um botão. Tela final no fim.
- Se um campo obrigatório estiver vazio, mostra a mensagem embaixo do campo, em vermelho, e NÃO avança.
OS 17 TIPOS DE CAMPO, cada um com o seu comportamento
- name e company: texto simples, mas já marcados pra virar coluna do lead.
- email: valida formato de verdade antes de avançar.
- short_text e long_text: o longo é textarea que cresce sozinha.
- phone: seletor de país com bandeira e código, lista de uns 50 países, busca dentro do seletor, Brasil como padrão. Grava sempre no formato "+DDI numero". Máscara de celular brasileiro só quando o país for o Brasil.
- date: campo de data nativo, formatado em dd/mm/aaaa na exibição.
- url: aceita com ou sem https e normaliza.
- cpf e cnpj: máscara enquanto digita e validação de dígito verificador de verdade. Documento inválido não passa.
- number: só número, com mínimo e máximo opcionais.
- single_choice: cartões grandes clicáveis, com a letra do atalho num quadradinho à esquerda.
- multiple_choice: mesmos cartões, com marcação, respeitando o limite de seleção quando existir, e um botão de avançar (esse não auto-avança).
- rating: escala de 1 até o máximo definido, com os dois rótulos das pontas.
- file: envia o arquivo pro storage por uma rota do servidor e guarda a URL. Mostra o nome do arquivo e o progresso.
- calendar: mostra a agenda embutida num iframe a partir do link que eu colar (Calendly ou outra), e pré-preenche nome e e-mail se já tiverem sido respondidos.
- statement: só uma mensagem, sem resposta, com um botão. Se o botão tiver link, ele abre em outra aba e ENCERRA o formulário.
REGRAS
- Nenhuma pergunta pode estar escrita no componente. Tudo sai da config recebida por props.
- O componente precisa funcionar igual no celular: campo com 16px pra o iPhone não dar zoom, área de toque de 44px, teclado certo por tipo de campo (email abre teclado de e-mail, telefone abre teclado numérico).
- Nada de biblioteca de animação. CSS puro.
- Me entregue o arquivo completo e a lista do que testar tipo por tipo.
/f/[slug] · boas-vindas
/f/[slug] · escolha única
/f/[slug] · escala
390px
05
Prompt 03: a qualificação que muda o final
Formulário comum devolve uma lista de respostas e você que se vire pra ler. O seu vai fazer diferente: cada opção tem um peso, a soma vira um tier, e o tier muda a tela final. Quem pontuou alto vê o botão do WhatsApp pra falar agora. Quem pontuou baixo vê um obrigado com um material. O mesmo formulário, dois destinos.
E tem uma regra de segurança escondida nesse prompt que quase ninguém pensa: o score é recalculado no servidor. Se você confiar no número que o navegador mandou, qualquer pessoa esperta manda "score 10" e entra na sua lista de quentes.
Prompt 03 · qualificação e gravação
Agora ligue a qualificação e a gravação no banco.
A LÓGICA DE QUALIFICAÇÃO
- Cada opção de escolha tem um peso (score). A soma dos pesos das respostas é a pontuação do lead.
- Os tiers vêm da config: quente a partir de 7, morno a partir de 4, frio a partir de 0. Isso é dado, não código: outro formulário pode ter outros cortes.
- A tela final muda por tier: título, subtítulo e botão diferentes. Se o tier não tiver tela própria, cai na "default".
- Na tela final, mostre uma etiqueta discreta com o nome do tier.
A ROTA QUE GRAVA (POST /api/submit)
- Recebe o slug do formulário, as respostas, os utm e o referer.
- Carrega a config daquele formulário DO BANCO e RECALCULA o score e o tier no servidor. Nunca confie no score que o navegador mandar: ele pode ter sido alterado. Isso é obrigatório.
- Grava uma linha na tabela de respostas com: form_id, o jsonb inteiro das respostas, score, tier, utm, referer e user agent.
- Além do jsonb, joga nas colunas próprias o que tiver mapTo: nome, email, whatsapp e empresa. É isso que deixa a lista de leads legível depois.
- Devolve o tier calculado, pra o formulário mostrar a tela final certa.
- Valida: sem respostas, devolve erro 400. Formulário que não existe, 404. Erro do banco, 500 com mensagem curta.
- Nunca devolva detalhe interno de erro pro navegador.
CAPTURA DE ORIGEM
- Leia os utm_source, utm_medium, utm_campaign e o sck da URL quando a pessoa abre o formulário, guarde e mande junto no envio. É assim que você vai saber de qual anúncio veio o lead bom.
REGRAS
- O cálculo do score tem que existir em UMA função só, usada pelo servidor. Se o navegador quiser mostrar uma prévia, ele chama a mesma função, mas quem manda é o servidor.
- Teste comigo: responda escolhendo as opções de peso alto e confirme que caiu como quente; depois de peso baixo e confirme frio.
- Me entregue os arquivos completos e o que testar.
/f/[slug] · tela final do tier quente
Mesma pessoa, outra resposta, outra tela. Quem pontua alto sai com o botão de falar agora. Quem pontua baixo nem vê esse botão.
06
Prompt 04: o painel de formulários
Até agora você tem um motor que lê um JSON. A partir daqui vira produto: a tela onde você vê todos os seus formulários, cria um novo e entra pra editar. É a primeira coisa que você vê quando abre o sistema.
Prompt 04 · o painel
Agora construa o painel: a tela em /painel onde eu vejo e crio os meus formulários.
ENTRADA
- Uma senha só, minha, guardada numa variável de ambiente do servidor. A tela pede a senha, e cada chamada de API manda ela num cabeçalho. Se bater, passa; se não, 401.
- A senha fica lembrada no navegador pra eu não digitar toda hora, e tem um "Sair" que esquece.
- Não é login de usuário, é cadeado de dono. Deixe simples de trocar depois por login de verdade.
A TELA
- Topo com a marca à esquerda, busca no meio e o meu avatar com iniciais à direita.
- Botão principal "Criar novo".
- Lista de cards, um por formulário, cada um com: miniatura, título, etiqueta de Privado ou Público, etiqueta de Rascunho quando não estiver publicado, e a contagem de respostas ("28 respostas") que é um link direto pra tela de respostas daquele formulário.
- Menu de três pontinhos em cada card: Editar, Ver respostas, Excluir. Excluir pede confirmação.
- Clicar no card abre o construtor daquele formulário.
- Busca filtra por título enquanto digito.
- Estado de carregando é esqueleto cinza, nunca a palavra "carregando". Estado vazio tem uma frase e o botão de criar.
AS ROTAS
- GET lista os formulários com a contagem vindo da view de contagem, ordenados pelo último editado.
- POST cria um formulário com título padrão, gera um slug único a partir do título (sem acento, sem espaço, com um sufixo curto aleatório pra nunca colidir) e uma config inicial com uma pergunta só.
- DELETE apaga o formulário.
- Todas exigem a senha no cabeçalho.
O QUE EU NÃO QUERO
- Nada de "Faça upgrade", nada de bolha de ajuda, nada de banner de novidade. É a minha ferramenta, não um SaaS tentando me vender.
- Nada de emoji na interface.
Me entregue os arquivos completos e o que testar.
/painel
Teste antes de seguir: criar, aparecer na lista, entrar, voltar, excluir. E abrir a mesma página numa aba anônima pra confirmar que sem senha não passa.
07
Prompt 05: o construtor visual
Esse é o prompt mais pesado da página e o que mais vale. É a tela que transforma o seu projeto de "formulário que eu configurei no código" em ferramenta que qualquer pessoa usa. Três colunas: as telas à esquerda, a prévia ao vivo no meio, o inspetor à direita.
Note uma exigência que parece detalhe e não é: a prévia é o motor de verdade, não um desenho parecido. Se a prévia for uma imitação, ela vai divergir do formulário publicado e você vai passar a vida caçando diferença entre as duas.
Prompt 05 · o construtor de 3 colunas
Agora o construtor visual, em /painel/[id]. É a tela mais importante do produto. Três colunas.
TOPO
- Seta de voltar fininha, sem caixa.
- Título do formulário editável ali mesmo, clicando.
- À direita: "Salvar" (com indicador de "salvo" discreto), "Publicar" ou "Despublicar" conforme o estado, um botão que abre a tela de respostas, um que abre o formulário publicado em outra aba e um que copia o link público.
COLUNA 1, AS TELAS
- Lista na ordem: Boas-vindas, cada pergunta numerada, e Tela final. As duas pontas são fixas e editáveis, mas não podem ser apagadas.
- Cada pergunta mostra o começo do título e o tipo embaixo, em cinza.
- Setas de subir e descer pra reordenar, e uma lixeira pra excluir com confirmação.
- Um bloco pontilhado "Adicionar pergunta" no fim.
- A pergunta selecionada fica destacada.
COLUNA 2, A PRÉVIA AO VIVO
- Mostra o passo selecionado usando O MOTOR DE VERDADE, o mesmo componente do formulário publicado, num quadro com cara de tela de celular. Nada de recriar uma prévia parecida: é o motor, em modo prévia (sem gravar no banco).
- Muda na hora enquanto eu digito no inspetor.
COLUNA 3, O INSPETOR
- Muda conforme o tipo do campo selecionado.
- Sempre: seletor de "Tipo de campo" (os 17), título, descrição opcional, obrigatória sim ou não, e o campo "grava em" (nome, e-mail, WhatsApp, empresa ou nenhum).
- Escolha única e múltipla: lista de opções, cada uma com o texto à esquerda e o PESO num campinho à direita, botão de remover e um "adicionar opção".
- Escala: valor máximo e os dois rótulos das pontas.
- Anexo: tipos de arquivo aceitos.
- Calendário: campo pro link da agenda.
- Mensagem: texto do botão e link opcional.
- Tela de boas-vindas e tela final têm o seu próprio inspetor, com os campos delas (incluindo, na final, o texto e o link do botão por tier).
SALVAR
- Uma rota que recebe o formulário inteiro e grava a config no banco, mais o título, o status e a privacidade.
- Salvar não pode perder nada: quem salva é o objeto inteiro, não campo por campo.
- Publicar muda o status. Um formulário em rascunho, se alguém abrir o link público, mostra uma tela dizendo que não está publicado.
REGRAS
- Nada de biblioteca de arrastar. Reordenar com as setas já resolve, e não quebra no celular.
- Toda edição atualiza a prévia na hora, sem recarregar.
- Me entregue os arquivos completos e uma lista de 10 coisas pra eu testar clicando.
/painel/[id] · construtor
Esquerda: as telas na ordem. Meio: o motor de verdade rodando em modo prévia. Direita: o inspetor da pergunta selecionada.
08
Prompt 06: os três truques que fazem parecer Typeform
Até aqui você tem um produto honesto. Esses três recursos são o que fazem alguém olhar e perguntar qual ferramenta você usa.
Puxar resposta: a pergunta 3 usa o nome que a pessoa deu na 1. Lógica condicional: cada opção decide pra onde a pessoa vai. Cor por formulário: cada formulário com a cor do cliente, sem tocar em código.
Prompt 06 · recall, lógica e cores
Três recursos, um de cada vez, sem quebrar o que já funciona.
1) PUXAR RESPOSTA (o recall)
- Em qualquer título, descrição ou texto da tela final, eu posso escrever um marcador com o id de uma pergunta anterior entre chaves duplas. Na hora de mostrar, o motor troca pelo que a pessoa respondeu.
- Exemplo: "Quanto a {{empresa}} investe por mês?" e "{{nome}}, qual é o seu maior gargalo?".
- Se a resposta ainda não existe, o marcador some do texto e a frase continua fazendo sentido, sem sobrar espaço duplo.
- Quando o marcador for usado dentro do link do botão da tela final, o valor precisa ser codificado pra URL (pra funcionar no link do WhatsApp).
- No inspetor, um seletor "puxar resposta de outra pergunta" que insere o marcador no cursor. Só oferece perguntas ANTERIORES à atual.
- Na prévia, o marcador aparece como um chip colorido com o nome do campo, não como texto cru.
2) LÓGICA CONDICIONAL (o pulo)
- Cada opção de uma pergunta de escolha ÚNICA ganha um destino: a próxima pergunta, uma pergunta específica ou "finalizar agora".
- No inspetor, embaixo de cada opção, um seletor "vai para" com a lista das perguntas.
- A navegação passa a ser por pilha de histórico: o botão de voltar tem que respeitar o caminho que a pessoa fez, não a ordem da lista.
- Depois de pular pra uma pergunta, o fluxo segue linear a partir dali.
- Exemplo que precisa funcionar: "Já investe em anúncio? Não" pula direto pra pergunta de contato e encerra; "Sim" segue o caminho completo.
3) COR POR FORMULÁRIO
- Cada formulário guarda na config uma cor de destaque e o modo claro ou escuro.
- No topo do construtor, um botão "Cores" abre um seletor com uns 10 tons prontos, um seletor de cor livre e a escolha entre fundo claro e escuro.
- A cor entra como variável de CSS aplicada no formulário e na prévia. Botões, seleção, barra de progresso e brilhos usam essa variável.
- Calcule automaticamente se o texto em cima da cor deve ser preto ou branco, pela luminância. Cor clara com texto branco é o erro clássico aqui.
REGRAS
- Faça um recurso, me mostre funcionando, e só então vá pro próximo.
- Nenhum dos três pode quebrar formulários antigos que não usam o recurso.
- Me diga, pra cada um, exatamente o que clicar pra ver funcionando.
/painel/[id] · inspetor com peso e pulo
Cada opção com o peso à direita e o vai para embaixo. À esquerda da prévia, o marcador virou chip. É esse print que faz a pessoa achar que você paga caro por uma ferramenta.
09
Prompt 07: a tela de respostas
De nada adianta captar bem e ler mal. Essa tela é onde o lead vira ação: você bate o olho, vê quem é quente e chama no WhatsApp sem copiar número na mão.
Prompt 07 · leads
Agora a tela de respostas de um formulário, em /admin com o slug do formulário na URL. Mesma senha do painel.
TOPO
- Nome do formulário e a contagem de leads embaixo.
- Botões pra trocar entre duas visões: cartões e lista. A escolha fica lembrada.
- Botão de recarregar, com o ícone virando um spinner enquanto carrega.
FAIXA DE NÚMEROS
Quatro caixas: total, quentes, mornos e frios. Cada número na cor do seu tier.
VISÃO EM CARTÕES
- Um cartão por lead: nome grande, etiqueta do tier com a pontuação ("quente · 7pts"), data e hora, e uma lixeira.
- Linha de contato: empresa, WhatsApp e e-mail, todos clicáveis. O WhatsApp abre a conversa já com o número certo, respeitando o código do país. O e-mail abre o cliente de e-mail.
- Se veio de anúncio, mostre a origem em cinza.
- Embaixo, todas as respostas em duas colunas: a pergunta em cinza pequeno e a resposta em branco. Resposta que for link vira link clicável, e anexo aparece como "ver arquivo".
VISÃO EM LISTA
Tabela com nome, empresa, contato, classificação e quando. Serve pra bater o olho em 50 leads de uma vez.
EXCLUIR
Lixeira abre um modal de confirmação com o nome do lead. Só apaga depois do confirmar, e some da tela na hora.
A ROTA
- GET devolve os leads daquele formulário, do mais novo pro mais antigo, exigindo a senha no cabeçalho. Sem senha, 401.
- DELETE apaga um lead pelo id, também com senha.
- Nenhuma dessas rotas pode ser aberta sem a senha. Teste isso e me mostre o 401.
REGRAS
- Estado vazio com uma frase, nunca uma tela em branco.
- Nada de paginar por enquanto: carregue os últimos 500 e pronto.
- Me entregue os arquivos completos e o que testar.
/admin · cartões
/admin · lista
Os leads dos prints são inventados só pra ilustrar. Quando for o seu, os nomes ali são de gente de verdade: trate essa tela como dado sensível, não saia mandando print dela por aí.
10
Prompt 08: colocar no ar
Três variáveis de ambiente e um deploy. A parte que mais trava gente aqui é esquecer que a chave secreta precisa ser cadastrada na Vercel, não só no arquivo local.
Prompt 08 · deploy e teste de ponta a ponta
Meu projeto está funcionando na minha máquina. Quero ele no ar, de graça, com um link que eu possa mandar hoje.
Me guie pelo caminho mais curto na Vercel, clicado, assumindo que eu não sei o que é terminal:
1. O que eu clico pra criar a conta e subir o projeto.
2. Onde eu cadastro as três variáveis de ambiente (a URL do Supabase, a service role key e a senha do painel) e por que elas NÃO podem estar no código.
3. Como eu confiro que subiu certo e como atualizo depois de mudar alguma coisa.
4. Como eu ligo um domínio próprio.
Depois de no ar, faça a checagem de produção comigo:
- Abra o formulário publicado numa aba anônima e responda inteiro. A resposta apareceu na tela de respostas?
- O score e o tier bateram com o que eu esperava?
- A tela de respostas sem senha devolve 401?
- A chave secreta aparece em algum lugar no código que chega no navegador? Procure de verdade e me prove que não.
- O formulário abre certo no celular, com o teclado adequado em cada tipo de campo?
- O título da aba, o ícone e a imagem de compartilhamento estão com a cara do meu formulário ou ainda genéricos?
- Se o upload de arquivo estiver ligado, o arquivo sobe e o link abre?
Me responda em passos numerados e curtos, sem termo técnico sem explicação.
com a plataforma de pé você montacaptação de leaddiagnóstico de clientebriefing de projetolista de esperapesquisa pós-vendatriagem de candidatocom a plataforma de pé você montacaptação de leaddiagnóstico de clientebriefing de projetolista de esperapesquisa pós-vendatriagem de candidato
11
Os 3 loops, agora com os erros típicos de formulário
Mesma ideia do CRM, mas mirando onde esse produto quebra. Rode na mesma conversa, em ordem, e repita sempre que mexer em algo grande.
Loop 01 · caça-problema
Pare de construir. Agora ataque o próprio trabalho como o usuário mais desastrado possível.
Vá atrás de tudo que quebra, especificamente:
- Campo obrigatório: dá pra avançar sem responder apertando Enter duas vezes rápido? E clicando no botão duas vezes?
- Voltar: depois de um pulo condicional, o botão de voltar leva pra tela certa ou pra ordem da lista? Voltar e mudar a resposta refaz o caminho?
- Puxar resposta: o que aparece quando o marcador aponta pra uma pergunta que ainda não foi respondida, ou pra uma pergunta apagada?
- Envio duplo: clicar em enviar duas vezes cria dois leads? A internet caindo no meio do envio deixa a pessoa numa tela morta?
- Máscaras: CPF e CNPJ inválidos passam? Telefone de outro país grava com o código certo? E-mail com espaço no fim?
- Escolha múltipla com limite: dá pra marcar além do limite?
- Escala: o zero funciona ou some por ser valor falso em JavaScript?
- Arquivo: arquivo de 40MB, arquivo de tipo não permitido, e cancelar no meio do upload.
- Formulário em rascunho: o link público mostra o quê?
- Formulário sem nenhuma pergunta: o construtor e o motor aguentam?
- Texto com acento, emoji e aspas nas respostas: chega inteiro no banco e na tela de leads?
- A prévia do construtor grava alguma coisa no banco? Ela NÃO pode gravar.
Escreva a lista numerada, do mais grave pro mais bobo, cada item com o que acontece na prática e em que arquivo está. Não conserte ainda.
Depois conserte um por um, na ordem, dizendo o que mudou.
No fim, rode a varredura inteira de novo. Repita até uma passagem inteira não achar nada novo.
Loop 02 · refino visual
Compare o meu formulário com o Typeform, o Respondi e o Tally, e o meu construtor com o editor do Notion.
Liste 10 diferenças concretas de acabamento. Nada de "melhorar o design", quero item objetivo:
- Tempo e curva das transições entre perguntas.
- Onde o cursor está quando a tela troca (o campo já vem focado?).
- Respiro entre a pergunta e as opções, e largura máxima da linha de texto.
- Peso e tamanho da fonte na hierarquia pergunta, descrição, opção.
- Estado de hover, de selecionado e de foco por teclado nas opções.
- Como o erro aparece e como ele some.
- O que a pessoa vê no meio segundo entre apertar enviar e a tela final.
- Consistência de raio de canto e de borda entre motor, painel e construtor.
- Contraste do texto cinza no fundo claro e no escuro.
- Como fica em 390px de largura.
Regras: não invente cor nova nem fonte nova, nada de emoji na interface, nada de sombra pesada. Estado de carregando é esqueleto, não a palavra "carregando".
Aplique uma de cada vez e me diga o que mudou. No fim, descreva o antes e o depois em texto.
Loop 03 · o invasor
Esqueça que você construiu isso. Você é uma pessoa mal intencionada que abriu o meu formulário publicado e apertou F12. Seu objetivo é roubar os leads.
Liste tudo que você tentaria e o resultado real, considerando o meu código:
- Achar alguma chave do banco no que chegou no navegador. Vasculhe o bundle e me prove se tem ou não tem.
- Chamar a rota que lista os leads sem a senha, e com senha errada.
- Chamar a rota que grava passando um score altíssimo, pra entrar como lead quente.
- Gravar resposta num formulário que não é meu, ou num slug inventado.
- Mandar um payload gigante ou um jsonb com 5MB de texto pra derrubar ou encher o banco.
- Enviar mil respostas em um minuto (tem algum limite?).
- Injetar HTML ou script dentro de uma resposta e ver se ele executa na minha tela de leads. Esse é o ataque mais provável de todos.
- Subir um arquivo executável pelo campo de anexo.
- Abrir a tela do construtor de um formulário que não é meu.
Para cada item: o que eu tentei, o que aconteceu, e se é um buraco.
Se alguma coisa passar, me entregue a correção pronta e a explicação em português do que ela bloqueia.
No fim, diga em uma frase qual é o meu risco real que sobra mesmo com tudo certo.
12
Quando travar
Mesma regra do outro material: nunca diga só "não funciona". Cole isso, preenchido.
SOS · destravar sem piorar
Alguma coisa quebrou. Antes de mexer em qualquer linha, investigue.
O que eu fiz e o que aconteceu: DESCREVA AQUI
O que eu esperava: DESCREVA AQUI
Onde quebrou: no formulário público, no painel, no construtor ou na tela de respostas?
Console do navegador (F12, aba Console): COLE A MENSAGEM VERMELHA, OU ESCREVA "nada"
Terminal onde o projeto está rodando: COLE O ERRO, OU ESCREVA "nada"
Siga exatamente esta ordem:
1. Diga em uma frase a sua hipótese da causa, apontando o arquivo.
2. Liste outras duas causas possíveis.
3. Diga o teste mais rápido pra saber qual das três é a verdadeira, e o que eu devo ver em cada caso.
4. Só com a causa confirmada, faça a correção mínima. Não reescreva arquivo inteiro, não aproveite pra melhorar outra coisa, não mude o visual.
5. Diga o que mudou e o que eu clico pra confirmar.
Se não tiver certeza, diga que não tem certeza e me peça o que falta. Chutar correção é proibido.
o que aparece
o que é, na real
Invalid API key / supabaseKey is required
A variável de ambiente não existe onde o código está rodando. No seu computador é o arquivo local, no ar é o painel da Vercel. Cadastrou e não subiu de novo? Precisa refazer o deploy.
401 na tela de respostas
A senha do cabeçalho não bate com a do servidor. Quase sempre a variável da senha está diferente entre local e produção.
A consulta volta vazia, sem erro
É a RLS fazendo o trabalho dela. Alguma parte do código está tentando falar com o banco pelo navegador ou com a chave pública. Só a service role, no servidor, enxerga.
Esse formulário não está publicado
Faltou clicar em Publicar no construtor. Rascunho não abre pelo link público, e isso é de propósito.
duplicate key value violates unique constraint
Dois formulários com o mesmo slug. O gerador de slug precisa do sufixo aleatório no fim.
A resposta some ao voltar uma pergunta
O estado das respostas está sendo recriado a cada troca de tela em vez de viver num nível acima do passo atual.
O marcador aparece cru como texto
A troca do marcador só foi aplicada no título. Ela precisa valer na descrição e no texto da tela final também.
O zero da escala não conta
Clássico: zero é tratado como resposta vazia. A checagem precisa ser por ausência de valor, não por valor falso.
O WhatsApp abre sem o número
O link está sendo montado com a máscara. Tire tudo que não é dígito e mantenha o código do país.
O iframe da agenda fica com rolagem dupla
A altura do iframe está fixa. Ele precisa receber a altura real do conteúdo, ou ficar alto o suficiente com a etapa alinhada no topo.
As oito regras que fazem esse caminho funcionar
Nenhuma é sobre código. São sobre como você comanda. Quem segue termina com a plataforma no ar. Quem ignora termina com um monte de arquivo que quase funciona.
Opus, sempre
No mínimo Opus 4.8, de preferência Opus 5. São três telas grandes que precisam concordar entre si. Modelo leve conserta uma e quebra a outra.
Uma conversa só
Do prompt 01 ao 08 no mesmo chat. Chat novo é IA sem memória do que construiu, reinventando o que já existia.
O formulário é dado
Nenhuma pergunta sua pode estar escrita dentro de um componente. Se está, você fez um formulário. Se não está, você fez uma plataforma.
Responda o próprio formulário
Depois de cada prompt, abra e responda até o fim. É o único teste que importa, e leva 40 segundos.
A chave secreta nunca vai pro navegador
Ela só existe em variável de ambiente do servidor. Se em algum momento a IA colocar a chave num arquivo de tela, mande refazer na hora.
Quem decide é o servidor
Score, tier e permissão são recalculados no servidor. Tudo que vem do navegador é palpite até o servidor conferir.
Proibido TODO
Repita em todo prompt: nada de função vazia, nada de "aqui você implementa". Sem isso, você recebe meia entrega bonita.
Salve versão que funciona
Antes de um prompt grande, faça uma cópia da pasta ou um commit. É o seu ponto de retorno quando algo der errado.
o mesmo caminho
Um formulário é a porta. Repare no que ele abre.
Com a plataforma de pé, cada formulário novo custa cinco minutos e nenhum código. E o pulo do gato é o que dá pra plugar na saída: o lead quente cai no seu WhatsApp, entra no seu CRM, dispara a mensagem de agradecimento sozinha.
Diagnóstico de cliente: as mesmas perguntas que você faz na call, respondidas antes dela
Briefing de projeto: o cliente monta o escopo sozinho, no lugar de três áudios
Triagem: quem não se qualifica sai por uma porta diferente, sem tomar o seu tempo
O lead qualificado sai daqui e entra no seu CRM, no seu disparo, na sua agenda
Copia o primeiro e vai.
Oito prompts. Uma conversa. No fim do dia você tem um link pra mandar, e a primeira resposta cai já dizendo se vale a sua ligação de amanhã.