Na mesma conversa, pedimos à IA o desenho do banco de dados a partir do PRD. É aqui que você entende o que o sistema guarda e como as informações se relacionam.
O que acontece
O PRD diz o que o sistema faz. Agora precisamos definir o que ele guarda.
Usamos o Prompt 2 na mesma conversa. A IA devolve o schema do banco acompanhado de uma explicação em português.
Vemos as quatro coisas que o qualificador precisa guardar: as perguntas, os leads, as respostas e o diagnóstico, e como elas se ligam.
Conferimos o schema contra o PRD: existe alguma tabela que o PRD não pede? Existe algum dado que o PRD mostra e ninguém guarda?
Na tela
A mesma conversa da aula anterior, o schema e a explicação em português.
Do Lovable
Ainda a IA de conversa. Esta é a primeira vez que o conceito de banco de dados aparece no curso.
Você sai com
O schema do banco e a noção de tabela e relação, sem precisar ler código.
Com base nesse PRD, crie o schema Prisma do banco de dados do sistema.
- Uma model para cada coisa que o sistema precisa guardar, incluindo
o conteúdo pronto da seção 6 do PRD (por exemplo, as perguntas e
suas opções vivem no banco, não no código).
- As relações entre as models explícitas.
- Cada campo com tipo, se é obrigatório ou não, e valor padrão
quando fizer sentido.
- id, createdAt e updatedAt em toda model.
- Enum para todo campo que tem uma lista fixa de valores (status,
tipo, temperatura, etc.).
- Nomes em inglês nas models e nos campos, como é o padrão.
O login é feito pela própria plataforma (Lovable Cloud). Não crie
tabela de usuário, de sessão nem de senha. Não pergunte sobre
autenticação, infraestrutura ou tecnologia: essas decisões já estão
tomadas.
Entregue o schema como um arquivo para download chamado
`schema.prisma`, só o código. Se não conseguir criar arquivo, entregue
num bloco de código.
Depois do arquivo, escreva em português, em poucas linhas, o que cada
model guarda e como elas se ligam.
Não pergunte nada. Se algo no PRD estiver ambíguo ou contraditório,
escolha a opção mais simples, siga com ela, e liste no fim o que você
decidiu. Não invente tabela para algo que o PRD não pede.
Depois que a IA responder: baixe o arquivo. A explicação em português, logo abaixo dele, é a parte que importa para quem não programa. Se ela não corresponder ao que você imaginava, o schema está errado, e não você. Ajuste na conversa.
Documentos
schema-qualificador.prisma
// Gerado pelo Prompt 2 na mesma conversa do PRD, sem correção.
// Sete tabelas: Question, QuestionOption, Lead, LeadAnswer, Diagnosis, DiagnosisStep, Settings.
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
// ---------------------------------------------------------------------------
// Enums
// ---------------------------------------------------------------------------
/// Identidade estável de cada uma das 6 perguntas.
/// O texto é editável no painel; a chave nunca muda, porque a lógica de
/// pontuação e a leitura das respostas dependem dela.
enum QuestionKey {
SELLING_MODE // 1. Como você vende hoje?
REVENUE // 2. Faturamento médio dos últimos 3 meses
ACQUISITION_CHANNEL // 3. De onde vêm os clientes
MAIN_PROBLEM // 4. Maior problema hoje
TIME_INVESTED // 5. Tempo por semana atraindo clientes
URGENCY // 6. Quando quer resolver
}
/// Faixas fixas do PRD 7.3: 70-100 HOT, 40-69 WARM, 0-39 COLD.
enum Temperature {
HOT
WARM
COLD
}
/// NO_DIAGNOSIS é o quarto rótulo do painel: o lead foi gravado, mas a IA
/// falhou ou estourou o tempo. Esses leads vão para o topo da lista.
enum LeadStatus {
NO_DIAGNOSIS
DIAGNOSED
}
enum ContactStatus {
NOT_CONTACTED
CONTACTED
}
// ---------------------------------------------------------------------------
// Conteúdo editável
// ---------------------------------------------------------------------------
model Question {
id String @id @default(cuid())
key QuestionKey @unique
position Int @unique // 1 a 6, fixo
text String // enunciado atual, editável no painel (máx. 120)
defaultText String // texto de fábrica, usado em "Restaurar textos originais"
maxPoints Int // teto do bloco desta pergunta na tabela de nota
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
options QuestionOption[]
answers LeadAnswer[]
@@index([position])
}
model QuestionOption {
id String @id @default(cuid())
questionId String
code String // identidade estável da opção (ex.: "ABOVE_50K")
position Int // ordem de exibição dentro da pergunta
text String // texto atual, editável no painel (máx. 60)
defaultText String // texto de fábrica
points Int @default(0) // pontos que esta opção soma na nota
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
question Question @relation(fields: [questionId], references: [id], onDelete: Cascade)
answers LeadAnswer[]
@@unique([questionId, code])
@@unique([questionId, position])
}
// ---------------------------------------------------------------------------
// Leads
// ---------------------------------------------------------------------------
model Lead {
id String @id @default(cuid())
name String
email String
company String
whatsapp String // só dígitos, 10 ou 11 com DDD
/// Identificador do navegador que enviou. Usado só para o limite de
/// 5 envios por hora (PRD 8.18). Não identifica pessoa.
browserId String
score Int? // nulo enquanto não houver diagnóstico
temperature Temperature? // nulo enquanto não houver diagnóstico
status LeadStatus @default(NO_DIAGNOSIS)
contactStatus ContactStatus @default(NOT_CONTACTED)
contactedAt DateTime?
/// Segundo envio ou mais do mesmo e-mail (PRD 8.17).
isRepeat Boolean @default(false)
/// Lead de demonstração; some junto com os outros em "Remover exemplos".
isExample Boolean @default(false)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
answers LeadAnswer[]
diagnosis Diagnosis?
@@index([temperature, score, createdAt])
@@index([status])
@@index([contactStatus])
@@index([email])
@@index([browserId, createdAt])
@@index([isExample])
}
/// Uma linha por pergunta respondida. Guarda o texto da pergunta e da opção
/// como estavam no momento do envio (PRD 8.5): editar as perguntas depois
/// não altera leads antigos.
model LeadAnswer {
id String @id @default(cuid())
leadId String
questionId String
optionId String
position Int // 1 a 6, copiado da pergunta no envio
questionText String
optionText String
points Int
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
lead Lead @relation(fields: [leadId], references: [id], onDelete: Cascade)
question Question @relation(fields: [questionId], references: [id], onDelete: Restrict)
option QuestionOption @relation(fields: [optionId], references: [id], onDelete: Restrict)
@@unique([leadId, questionId])
@@index([leadId, position])
}
// ---------------------------------------------------------------------------
// Saída da IA
// ---------------------------------------------------------------------------
/// Resultado da única chamada de IA. Gerar de novo substitui o registro
/// inteiro (PRD 8.9), por isso é 1-para-1 com o lead.
model Diagnosis {
id String @id @default(cuid())
leadId String @unique
// Parte pública (T5)
title String // máx. 70 caracteres
whereYouAre String @db.Text
whatIsBlocking String @db.Text
// Parte interna (T9) — nunca exibida ao lead
internalContext String @db.Text
approachPoints String[] // exatamente 3 frases
firstMessage String @db.Text // máx. 400 caracteres
likelyObjection String
whatToAvoid String
/// Quantas vezes a IA foi executada para este lead.
generationCount Int @default(1)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
lead Lead @relation(fields: [leadId], references: [id], onDelete: Cascade)
steps DiagnosisStep[]
}
/// Os três passos do bloco "Por onde começar".
model DiagnosisStep {
id String @id @default(cuid())
diagnosisId String
position Int // 1, 2, 3
title String // máx. 40 caracteres
text String @db.Text
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
diagnosis Diagnosis @relation(fields: [diagnosisId], references: [id], onDelete: Cascade)
@@unique([diagnosisId, position])
}
// ---------------------------------------------------------------------------
// Configuração
// ---------------------------------------------------------------------------
/// Linha única. Guarda o que o PRD trata como "definido na montagem":
/// textos da capa, WhatsApp do dono e o contexto de negócio do prompt.
model Settings {
id String @id @default(cuid())
singleton Boolean @unique @default(true)
companyName String
ownerWhatsapp String // destino do botão "Falar no WhatsApp"
whatsappPrefill String // mensagem já escrita no link do lead
publicTitle String
publicSubtitle String
publicBenefits String[] // 3 itens abaixo do botão da capa
publicFooter String
/// Descrição fixa de quem é o dono e o que ele vende, enviada à IA.
aiBusinessContext String @db.Text
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}