- feat(025): favoritos locais com FavoritesContext, HeartButton, PublicFavoritesPage
- feat(026): central de contatos admin (leads/contatos unificados)
- feat(027): configuração da página de contato via admin
- feat(028): trabalhe conosco - candidaturas com upload e admin
- feat(029): UX área do cliente - visitas, comparação, perfil
- feat(030): navbar UX - menu mobile, ThemeToggle, useFavorites
- feat(031): hero light/dark - imagens separadas por tema, upload, preview, seed
- feat(032): performance homepage - Promise.all parallel fetches, sessionStorage cache,
preload hero image, loading=lazy nos cards, useInView hook, will-change carrossel,
keyframes em index.css, AgentsCarousel e HomeScrollScene via props
- fix: light mode HomeScrollScene - gradiente, cores de texto, scroll hint
migrations: g1h2i3j4k5l6 (source em leads), h1i2j3k4l5m6 (contact_config),
i1j2k3l4m5n6 (job_applications), j2k3l4m5n6o7 (hero theme images)
19 KiB
Feature Specification: Central de Contatos com Rastreamento de Origem
Feature Branch: 026-central-contatos
Created: 2026-04-21
Status: Draft
Contexto
O sistema já possui um mecanismo básico de captura de leads: visitantes podem enviar mensagens a partir da página de detalhes de um imóvel, e esses registros são armazenados como ContactLead. No entanto, a origem de cada contato não é rastreada, o formulário de contato geral na navbar aponta para uma âncora na homepage (sem página própria) e não existe uma página para proprietários interessados em anunciar seus imóveis.
Esta spec cobre a criação de uma Central de Contatos unificada com três origens rastreáveis: formulário público de contato geral (/contato), formulário de cadastro de residência para anúncio (/cadastro-residencia), e o formulário de contato de imóvel já existente. Inclui também a página de administração consolidada para visualizar e filtrar todos os leads por origem, e a correção dos links da navbar para destinos internos.
User Scenarios & Testing
User Story 1 — Visitante Envia Contato Geral via /contato (Priority: P1)
Um visitante do site que deseja tirar dúvidas, solicitar informações ou propor parceria quer enviar uma mensagem para a imobiliária sem precisar acessar a página de um imóvel específico.
Why this priority: É o ponto de contato principal do site. Atualmente a navbar aponta "Contato" para uma âncora que pode não estar visível, criando uma experiência frustrante. Disponibilizar uma página dedicada com formulário funcional é o requisito mínimo de contato da imobiliária.
Independent Test: Acessar /contato sem estar autenticado, preencher todos os campos obrigatórios (nome, e-mail, telefone, assunto, mensagem) e submeter. Verificar que a submissão é aceita com mensagem de confirmação e que o lead aparece na base com source = "contato".
Acceptance Scenarios:
- Given um visitante na página
/contato, When ele preenche nome, e-mail, telefone, assunto e mensagem e clica em enviar, Then o formulário é submetido com sucesso, uma mensagem de confirmação é exibida e o visitante permanece na página. - Given um visitante na página
/contato, When ele submete o formulário sem preencher um campo obrigatório, Then o campo em falta é destacado com uma mensagem de erro inline e o formulário não é enviado. - Given um visitante que submeteu o formulário com sucesso, When o lead é registrado no sistema, Then o campo
sourcedo registro é"contato"esource_detailfica vazio. - Given um visitante na página
/contato, When ele informa um e-mail com formato inválido, Then o campo e-mail é destacado com erro de validação antes do envio. - Given um visitante na página
/contato, When o assunto selecionado é "Anúncio", Then o formulário é submetido normalmente com o assunto incluído na mensagem registrada.
User Story 2 — Proprietário Cadastra Imóvel para Anúncio via /cadastro-residencia (Priority: P1)
Um proprietário de imóvel que deseja anunciá-lo através da imobiliária quer enviar os dados básicos do imóvel e suas informações de contato para que a equipe entre em contato e dê continuidade ao processo.
Why this priority: Representa uma fonte direta de captação de novos imóveis para o portfólio da imobiliária. Sem esse canal, proprietários interessados não têm caminho claro para iniciar o processo de anúncio.
Independent Test: Acessar /cadastro-residencia, preencher todos os campos (nome, e-mail, telefone, endereço, tipo de imóvel, área, finalidade, observações) e submeter. Verificar que o lead é criado com source = "cadastro_residencia" e que os detalhes do imóvel estão na mensagem ou no source_detail.
Acceptance Scenarios:
- Given um proprietário na página
/cadastro-residencia, When ele preenche todos os campos e clica em enviar, Then o formulário é submetido com sucesso e uma mensagem de confirmação é exibida informando que a equipe entrará em contato. - Given um proprietário na página
/cadastro-residencia, When ele submete sem preencher campos obrigatórios (nome, e-mail, telefone, endereço, tipo e finalidade), Then os campos em falta são destacados com erros inline e o envio é bloqueado. - Given um proprietário que submeteu o formulário com sucesso, When o lead é registrado, Then
sourceé"cadastro_residencia"esource_detailcontém informação identificável do imóvel (ex.: tipo + finalidade ou endereço). - Given um proprietário na página
/cadastro-residencia, When ele seleciona "Apartamento" como tipo e "Aluguel" como finalidade, Then esses valores são incluídos no registro enviado ao sistema. - Given um proprietário na página
/cadastro-residencia, When o campo "Área m²" recebe um valor não numérico, Then o campo é destacado com erro de validação e o envio é bloqueado.
User Story 3 — Visitante Envia Contato de Imóvel com Origem Rastreada (Priority: P2)
Um visitante interessado em um imóvel específico envia uma mensagem a partir da página de detalhes do imóvel. O sistema deve registrar automaticamente que o contato veio daquele imóvel específico, sem que o visitante precise fazer nada diferente.
Why this priority: O formulário de contato de imóvel já existe e funciona. A melhoria é transparente para o usuário e enriquece os dados para a equipe de vendas, que saberá exatamente qual imóvel gerou cada lead.
Independent Test: Acessar a página de detalhes de um imóvel (ex.: /imoveis/apartamento-centro), preencher e enviar o formulário de contato. Verificar que o lead criado possui source = "imovel" e source_detail com o título do imóvel.
Acceptance Scenarios:
- Given um visitante na página de detalhes de um imóvel, When ele preenche e envia o formulário de contato, Then o lead é criado com
source = "imovel"esource_detailcontendo o título do imóvel. - Given um lead de imóvel criado com sucesso, When o administrador visualiza esse lead na central de leads, Then a coluna de origem exibe um badge "Imóvel" e o
source_detailcom o título do imóvel está visível. - Given um visitante na página de detalhes, When o formulário de contato é exibido, Then a experiência visual e os campos do formulário permanecem idênticos — nenhuma mudança visível para o usuário.
User Story 4 — Administrador Visualiza e Filtra Leads na Central de Contatos (Priority: P1)
O administrador da imobiliária precisa de uma visão consolidada de todos os contatos recebidos — de qualquer origem — com a possibilidade de filtrar por tipo de origem para priorizar o atendimento.
Why this priority: Sem uma central unificada com filtros, a equipe não consegue distinguir leads de imóveis de propostas de parceria ou de proprietários querendo anunciar. O valor do rastreamento de origem só se materializa com uma interface de gestão adequada.
Independent Test: Acessar /admin/leads como administrador autenticado, verificar que leads das três origens aparecem na listagem. Aplicar filtro por "Cadastro de Residência" e verificar que apenas leads com source = "cadastro_residencia" são exibidos.
Acceptance Scenarios:
- Given um administrador autenticado acessando
/admin/leads, When a página carrega, Then todos os leads são listados em ordem cronológica decrescente com as colunas: origem (badge colorido), nome, e-mail, telefone, prévia da mensagem e data. - Given a listagem de leads, When o administrador seleciona o filtro "Imóvel", Then apenas leads com
source = "imovel"são exibidos e o badge "Imóvel" aparece na coluna de origem de cada linha. - Given a listagem de leads, When o administrador seleciona o filtro "Contato", Then apenas leads com
source = "contato"são exibidos. - Given a listagem de leads, When o administrador seleciona o filtro "Cadastro de Residência", Then apenas leads com
source = "cadastro_residencia"são exibidos. - Given a listagem de leads com filtro "Imóvel" ativo, When há leads com
source_detailpreenchido, Then o título do imóvel é exibido como detalhe do badge ou em coluna/tooltip separado. - Given a listagem de leads, When o administrador seleciona "Todos", Then todos os leads de todas as origens são exibidos sem filtro.
- Given um não administrador (sem autenticação ou sem permissão), When tenta acessar
/admin/leads, Then é redirecionado para a tela de login ou recebe resposta de acesso negado. - Given a listagem de leads com muitos registros, When o limite de exibição por página é atingido, Then a navegação entre páginas ou carregamento adicional está disponível para acessar registros anteriores.
User Story 5 — Visitante Navega para /contato e /sobre via Navbar (Priority: P3)
Um visitante que clica em "Contato" ou "Sobre" na barra de navegação é levado diretamente às páginas internas correspondentes, sem âncoras ou rolagem forçada.
Why this priority: Corrige um problema de usabilidade existente. A navbar já tem os links; é apenas necessário atualizar os destinos para rotas internas corretas.
Independent Test: Na homepage (ou em qualquer outra página), clicar em "Contato" na navbar e verificar que a rota muda para /contato. Clicar em "Sobre" e verificar que a rota muda para /sobre.
Acceptance Scenarios:
- Given qualquer página do site com a navbar visível, When o visitante clica em "Contato", Then é navegado para
/contatosem recarregar a página inteira. - Given qualquer página do site com a navbar visível, When o visitante clica em "Sobre", Then é navegado para
/sobresem recarregar a página inteira. - Given o visitante está em
/contato, When a navbar é exibida, Then o link "Contato" aparece destacado como item ativo de navegação.
Edge Cases
- O que acontece se o usuário submeter o formulário de
/contatomais de uma vez seguida (duplo clique acidental)? - O que acontece se o servidor retornar erro ao salvar o lead — o usuário recebe feedback adequado?
- O que acontece com leads criados antes da adição da coluna
source— eles aparecem na listagem admin com origem "Desconhecida" ou ficam ocultos? - Como o sistema se comporta quando
source_detailestá vazio para leads de imóvel (caso de falha na captura do título)? - O que acontece se o campo "área m²" no formulário
/cadastro-residenciareceber um valor negativo? - Como a paginação da listagem admin se comporta quando um filtro é aplicado e o número total de resultados muda?
- O que acontece se um visitante acessar
/contatoou/cadastro-residenciaem um dispositivo móvel — os formulários são responsivos?
Requirements
Functional Requirements
Grupo 1 — Rastreamento de Origem no Modelo de Lead
- FR-001: O modelo de lead DEVE ter um campo
sourceque identifica a origem do contato com os valores possíveis:"contato","imovel"e"cadastro_residencia". - FR-002: O modelo de lead DEVE ter um campo
source_detailopcional para registrar informação complementar da origem (ex.: título do imóvel, tipo + finalidade do imóvel anunciado). - FR-003: Todos os novos leads criados a partir desta feature DEVEM ter o campo
sourcepreenchido automaticamente de acordo com a origem do formulário que os gerou. - FR-004: Leads existentes criados antes desta feature (campo
sourceausente) NÃO DEVEM ser alterados retroativamente, mas DEVEM continuar acessíveis na listagem admin.
Grupo 2 — Página de Contato Geral (/contato)
- FR-005: O sistema DEVE disponibilizar a rota pública
/contatocom um formulário de contato geral acessível sem autenticação. - FR-006: O formulário de contato DEVE conter os campos: nome (obrigatório), e-mail (obrigatório, formato válido), telefone (obrigatório), assunto (obrigatório, seleção entre: Informações, Anúncio, Parceria, Outro) e mensagem (obrigatória).
- FR-007: A submissão bem-sucedida do formulário DEVE criar um lead com
source = "contato"e exibir uma mensagem de confirmação ao usuário. - FR-008: O formulário DEVE validar todos os campos obrigatórios antes do envio e exibir mensagens de erro inline sem recarregar a página.
- FR-009: Após uma submissão bem-sucedida, o botão de envio DEVE ser desabilitado ou o formulário resetado para evitar envio duplicado acidental.
Grupo 3 — Página de Cadastro de Residência (/cadastro-residencia)
- FR-010: O sistema DEVE disponibilizar a rota pública
/cadastro-residenciacom formulário para proprietários interessados em anunciar um imóvel, acessível sem autenticação. - FR-011: O formulário de cadastro DEVE conter os campos: nome (obrigatório), e-mail (obrigatório, formato válido), telefone (obrigatório), endereço (obrigatório), tipo de imóvel (obrigatório, seleção entre: Casa, Apartamento, Comercial), área em m² (opcional, numérico positivo), finalidade (obrigatório, seleção entre: Venda, Aluguel) e observações/mensagem (opcional).
- FR-012: A submissão bem-sucedida DEVE criar um lead com
source = "cadastro_residencia", comsource_detailcontendo identificação do imóvel (tipo, finalidade e/ou endereço), e exibir confirmação ao usuário. - FR-013: O formulário DEVE validar todos os campos obrigatórios e o formato numérico do campo área antes do envio, com erros inline sem recarregar a página.
Grupo 4 — Atualização do Formulário de Contato de Imóvel
- FR-014: O formulário de contato existente na página de detalhes de imóvel DEVE passar
source = "imovel"ao criar o lead. - FR-015: O formulário de contato de imóvel DEVE passar
source_detailcom o título do imóvel ao criar o lead. - FR-016: A aparência e os campos do formulário de contato de imóvel NÃO DEVEM ser alterados — a mudança é exclusivamente no dado enviado ao backend.
Grupo 5 — Central de Leads Admin (/admin/leads)
- FR-017: O sistema DEVE disponibilizar a rota protegida
/admin/leadsacessível apenas por usuários com perfil de administrador. - FR-018: A listagem DEVE exibir todos os leads em ordem cronológica decrescente com as colunas: origem (badge colorido), nome, e-mail, telefone, prévia da mensagem (truncada) e data de criação.
- FR-019: Os badges de origem DEVEM ter cores distintas para cada valor de
source: uma cor para"contato", outra para"imovel"e outra para"cadastro_residencia". - FR-020: Para leads com
source = "imovel"esource_detailpreenchido, o título do imóvel DEVE ser exibido de forma associada ao badge (ex.: tooltip, sublinha ou coluna adicional). - FR-021: A listagem DEVE oferecer um seletor de filtro por origem com as opções: Todos, Contato, Imóvel e Cadastro de Residência. A seleção de filtro DEVE atualizar a listagem sem recarregar a página.
- FR-022: Leads cujo campo
sourceestá vazio (criados antes desta feature) DEVEM ser exibidos na listagem com um badge ou label indicando origem "Desconhecida" ao selecionar filtro "Todos". - FR-023: A listagem DEVE suportar paginação ou carregamento incremental para listas com grande volume de leads.
Grupo 6 — Navbar
- FR-024: O link "Contato" na navbar DEVE navegar para a rota interna
/contato. - FR-025: O link "Sobre" na navbar DEVE navegar para a rota interna
/sobre. - FR-026: O link ativo na navbar DEVE ser destacado visualmente quando a rota atual corresponder ao destino do link.
Key Entities
- Lead de Contato (ContactLead): Registro de interesse ou comunicação de um visitante. Atributos relevantes: identificador único, origem (
source), detalhe de origem (source_detail), nome, e-mail, telefone, mensagem, data de criação. Pode ou não estar associado a um imóvel específico. - Origem (Source): Classificação do canal pelo qual o lead foi gerado. Valores fixos:
"contato"(formulário geral),"imovel"(página de detalhes de imóvel),"cadastro_residencia"(formulário de anúncio de proprietário). - Detalhe de Origem (Source Detail): Informação livre associada à origem do lead. Para
"imovel": título do imóvel. Para"cadastro_residencia": tipo, finalidade e/ou endereço do imóvel informado pelo proprietário.
Success Criteria
Measurable Outcomes
- SC-001: Visitantes conseguem enviar um contato geral através de
/contatoem menos de 2 minutos a partir do primeiro acesso à página. - SC-002: Proprietários conseguem submeter o formulário de cadastro de residência em
/cadastro-residenciaem menos de 3 minutos. - SC-003: 100% dos leads criados a partir das páginas
/contato,/cadastro-residenciae do formulário de imóvel contêm o camposourcepreenchido corretamente. - SC-004: O administrador consegue filtrar leads por origem e visualizar apenas os registros relevantes em menos de 5 segundos após selecionar o filtro.
- SC-005: Os links "Contato" e "Sobre" da navbar levam às rotas corretas em 100% das navegações, sem âncoras intermediárias.
- SC-006: Zero leads duplicados são criados por submissões acidentais de duplo clique nos formulários de
/contatoe/cadastro-residencia. - SC-007: A listagem de leads admin exibe corretamente leads de todas as origens, incluindo leads legados (sem
source) sem erros ou omissões.
Assumptions
- O modelo
ContactLeadjá existe no banco de dados com as colunasid,property_id,name,email,phone,messageecreated_at; as colunassourceesource_detailserão adicionadas via migration Alembic. - A coluna
sourceaceitaNULLpara registros existentes criados antes da migration (compatibilidade retroativa sem valor padrão obrigatório). - O assunto selecionado no formulário
/contatoé incluído no campomessage(compondo a mensagem) ou em um campo auxiliar — não requer nova coluna no modelo. - Os dados do formulário
/cadastro-residenciasão registrados como um único lead usando os campos existentes (name,email,phone,message) com detalhes do imóvel concatenados na mensagem esource_detailcom identificação resumida. - A autenticação de administrador já existe no sistema;
/admin/leadsusa o mesmo mecanismo de proteção das demais rotas admin. - A rota
GET /admin/leadsexistente será estendida para aceitar o parâmetro de filtrosourcee retornar leads com os novos campos; não é criada uma nova rota separada. - A paginação da listagem admin utiliza paginação simples por offset/limit ou infinite scroll — a escolha de implementação é deixada para a fase de planejamento.
- Não há envio de e-mail de notificação à equipe da imobiliária como parte desta feature (pode ser adicionado em feature futura).
- As páginas
/contatoe/cadastro-residenciasão acessíveis publicamente sem autenticação, assim como as demais páginas públicas do site. - A navbar já possui os itens "Sobre" e "Contato" renderizados; apenas os
href/destinos de roteamento serão atualizados.