- 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)
201 lines
19 KiB
Markdown
201 lines
19 KiB
Markdown
# 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**:
|
|
|
|
1. **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.
|
|
2. **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.
|
|
3. **Given** um visitante que submeteu o formulário com sucesso, **When** o lead é registrado no sistema, **Then** o campo `source` do registro é `"contato"` e `source_detail` fica vazio.
|
|
4. **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.
|
|
5. **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**:
|
|
|
|
1. **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.
|
|
2. **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.
|
|
3. **Given** um proprietário que submeteu o formulário com sucesso, **When** o lead é registrado, **Then** `source` é `"cadastro_residencia"` e `source_detail` contém informação identificável do imóvel (ex.: tipo + finalidade ou endereço).
|
|
4. **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.
|
|
5. **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**:
|
|
|
|
1. **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"` e `source_detail` contendo o título do imóvel.
|
|
2. **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_detail` com o título do imóvel está visível.
|
|
3. **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**:
|
|
|
|
1. **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.
|
|
2. **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.
|
|
3. **Given** a listagem de leads, **When** o administrador seleciona o filtro "Contato", **Then** apenas leads com `source = "contato"` são exibidos.
|
|
4. **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.
|
|
5. **Given** a listagem de leads com filtro "Imóvel" ativo, **When** há leads com `source_detail` preenchido, **Then** o título do imóvel é exibido como detalhe do badge ou em coluna/tooltip separado.
|
|
6. **Given** a listagem de leads, **When** o administrador seleciona "Todos", **Then** todos os leads de todas as origens são exibidos sem filtro.
|
|
7. **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.
|
|
8. **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**:
|
|
|
|
1. **Given** qualquer página do site com a navbar visível, **When** o visitante clica em "Contato", **Then** é navegado para `/contato` sem recarregar a página inteira.
|
|
2. **Given** qualquer página do site com a navbar visível, **When** o visitante clica em "Sobre", **Then** é navegado para `/sobre` sem recarregar a página inteira.
|
|
3. **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 `/contato` mais 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_detail` está 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-residencia` receber 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 `/contato` ou `/cadastro-residencia` em 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 `source` que 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_detail` opcional 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 `source` preenchido automaticamente de acordo com a origem do formulário que os gerou.
|
|
- **FR-004**: Leads existentes criados antes desta feature (campo `source` ausente) 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 `/contato` com 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-residencia` com 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"`, com `source_detail` contendo 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_detail` com 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/leads` acessí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"` e `source_detail` preenchido, 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 `source` está 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 `/contato` em 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-residencia` em menos de 3 minutos.
|
|
- **SC-003**: 100% dos leads criados a partir das páginas `/contato`, `/cadastro-residencia` e do formulário de imóvel contêm o campo `source` preenchido 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 `/contato` e `/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 `ContactLead` já existe no banco de dados com as colunas `id`, `property_id`, `name`, `email`, `phone`, `message` e `created_at`; as colunas `source` e `source_detail` serão adicionadas via migration Alembic.
|
|
- A coluna `source` aceita `NULL` para registros existentes criados antes da migration (compatibilidade retroativa sem valor padrão obrigatório).
|
|
- O assunto selecionado no formulário `/contato` é incluído no campo `message` (compondo a mensagem) ou em um campo auxiliar — não requer nova coluna no modelo.
|
|
- Os dados do formulário `/cadastro-residencia` são registrados como um único lead usando os campos existentes (`name`, `email`, `phone`, `message`) com detalhes do imóvel concatenados na mensagem e `source_detail` com identificação resumida.
|
|
- A autenticação de administrador já existe no sistema; `/admin/leads` usa o mesmo mecanismo de proteção das demais rotas admin.
|
|
- A rota `GET /admin/leads` existente será estendida para aceitar o parâmetro de filtro `source` e 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 `/contato` e `/cadastro-residencia` sã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.
|