sass-imobiliaria/.specify/features/006-client-area/spec.md

17 KiB

Feature Specification: Área do Cliente

Feature Branch: 006-client-area Created: 2026-04-13 Status: Draft Depends On: Feature 005 (Autenticação de Clientes — ClientUser model e JWT middleware)


User Scenarios & Testing (mandatory)

User Story 1 — Favoritar e Desfavoritar Imóvel (Priority: P1)

Um cliente autenticado pode favoritar um imóvel a partir do card ou da página de detalhes. O estado do botão de coração é persistido no backend e permanece entre sessões.

Why this priority: Favoritos é o recurso mais imediato de retenção do usuário na plataforma. Incentiva o retorno e aumenta o tempo de sessão.

Independent Test: Pode ser testado de forma isolada ao verificar que um cliente autenticado consegue adicionar e remover um imóvel de favoritos, e que ao recarregar a página o estado é mantido.

Acceptance Scenarios:

  1. Given o cliente está autenticado e navega pelo catálogo, When clica no botão de coração em um PropertyCard, Then o imóvel é adicionado aos favoritos, o coração fica preenchido e a ação é persistida na API.
  2. Given o imóvel já está favoritado, When o cliente clica no coração novamente, Then o imóvel é removido dos favoritos, o coração fica vazio e o backend reflete a remoção.
  3. Given o cliente não está autenticado, When clica no coração, Then é redirecionado para a página de login e, após autenticar, retorna ao imóvel.
  4. Given o cliente já favoritou o mesmo imóvel, When uma segunda requisição de adição é enviada, Then o sistema retorna 409 sem duplicar o registro.

User Story 2 — Página de Favoritos (Priority: P2)

Um cliente autenticado acessa /area-do-cliente/favoritos e visualiza todos os imóveis que marcou como favoritos, podendo desfavoritar diretamente da lista.

Why this priority: Sem a página de favoritos o botão de coração não tem destino, tornando o recurso incompleto do ponto de vista do usuário.

Independent Test: Pode ser testado verificando que a página exibe corretamente todos os imóveis favoritados e permite removê-los da lista.

Acceptance Scenarios:

  1. Given o cliente possui imóveis favoritados, When acessa /area-do-cliente/favoritos, Then vê uma grade de PropertyCards com botão de desfavoritar em cada um.
  2. Given o cliente não possui nenhum favorito, When acessa a página, Then vê o estado vazio: "Nenhum favorito ainda".
  3. Given o cliente clica para desfavoritar na página de favoritos, When a remoção é confirmada, Then o card desaparece da lista sem recarregar a página inteira.
  4. Given o cliente não está autenticado, When acessa a rota diretamente, Then é redirecionado para a página de login.

User Story 3 — Painel Principal (Dashboard) (Priority: P3)

Um cliente autenticado acessa /area-do-cliente e vê um painel de resumo com contadores e atalhos para as seções da área do cliente.

Why this priority: É o ponto de entrada da área do cliente; sem ele o usuário não tem orientação após o login.

Independent Test: Pode ser testado verificando que os contadores exibidos refletem dados reais do cliente (favoritos, visitas pendentes, boletos ativos).

Acceptance Scenarios:

  1. Given o cliente está autenticado, When acessa /area-do-cliente, Then vê cards de resumo mostrando: total de favoritos, visitas pendentes e boletos ativos.
  2. Given o cliente clica em um card de resumo (ex.: "Favoritos"), Then é navegado para a subseção correspondente.
  3. Given todos os contadores estão zerados, When o cliente acessa o painel, Then os cards exibem "0" sem mensagens de erro.

User Story 4 — Comparar Imóveis (Priority: P4)

Um cliente pode adicionar até 3 imóveis a uma lista de comparação e visualizar uma tabela lado a lado em /area-do-cliente/comparar. A seleção é mantida localmente durante a sessão.

Why this priority: Recurso diferencial que ajuda na decisão de compra; não requer autenticação de dados no backend, sendo implementável de forma autônoma.

Independent Test: Pode ser testado verificando a barra flutuante de comparação, adição/remoção de imóveis e a renderização correta da tabela comparativa.

Acceptance Scenarios:

  1. Given o cliente visualiza um imóvel no catálogo, When clica em "Comparar", Then o imóvel é adicionado à barra flutuante de comparação no rodapé da tela.
  2. Given o cliente já tem 3 imóveis na comparação, When tenta adicionar um quarto, Then recebe uma mensagem informando que o limite de 3 imóveis foi atingido e não ocorre adição.
  3. Given o cliente tem ao menos 1 imóvel na barra, When clica em "Ver Comparação" ou acessa /area-do-cliente/comparar, Then vê uma tabela com colunas por imóvel e linhas para: preço, área, quartos, banheiros, vagas, condomínio, tipo, bairro e comodidades.
  4. Given o cliente clica em "Remover" em uma coluna da tabela, Then o imóvel é removido e a tabela é atualizada.
  5. Given o cliente recarrega a página, Then os imóveis selecionados para comparação são restaurados do armazenamento local.
  6. Given a lista de comparação está vazia e o cliente acessa /area-do-cliente/comparar, Then vê estado vazio com sugestão de selecionar imóveis do catálogo.

User Story 5 — Histórico de Visitas (Priority: P5)

Um cliente autenticado acessa /area-do-cliente/visitas e visualiza o histórico de solicitações de visita com status atual.

Why this priority: Permite ao cliente acompanhar suas solicitações, reduzindo contato direto e dúvidas recorrentes para a equipe de vendas.

Independent Test: Pode ser testado verificando que as visitas criadas ao submeter o formulário de contato (como usuário logado) aparecem listadas com os status corretos.

Acceptance Scenarios:

  1. Given o cliente possui visitas cadastradas, When acessa /area-do-cliente/visitas, Then vê uma lista cronológica com: imóvel vinculado, mensagem enviada, status atual (badge colorido) e data agendada (quando confirmada).
  2. Given o status de uma visita é alterado pelo admin, When o cliente recarrega a página, Then o novo status é refletido.
  3. Given o cliente não tem nenhuma visita, When acessa a página, Then vê "Nenhuma visita agendada".
  4. Given o cliente está autenticado e submete o formulário de contato na página de um imóvel, When a solicitação é enviada, Then uma VisitRequest é criada com status "pending" e aparece no histórico.

User Story 6 — Boletos (Priority: P6)

Um cliente autenticado acessa /area-do-cliente/boletos e visualiza os boletos criados pelo admin, podendo acessar o link de pagamento ou baixar o PDF.

Why this priority: Acesso a boletos é funcionalidade financeira crítica para clientes em processo de locação ou compra.

Independent Test: Pode ser testado com boletos criados diretamente via API de admin, verificando listagem e acesso ao link.

Acceptance Scenarios:

  1. Given o cliente possui boletos vinculados, When acessa /area-do-cliente/boletos, Then vê uma tabela com: imóvel (quando vinculado), descrição, valor, vencimento, badge de status e botão para acessar o boleto.
  2. Given o cliente clica no botão de acesso ao boleto, Then é aberto o link/URL do boleto em nova aba.
  3. Given o boleto está com status "paid", Then o badge exibe "Pago" em cor distinta dos demais status.
  4. Given o cliente não possui boletos, When acessa a página, Then vê "Nenhum boleto disponível".

User Story 7 — Admin cria Boleto via API (Priority: P7)

Um administrador autentica na API e cria um boleto para um cliente, opcionalmente vinculado a um imóvel.

Why this priority: Backend necessário para suportar a P6; não há UI de admin no MVP.

Independent Test: Pode ser testado diretamente via chamada à API POST /api/v1/admin/boletos com token de admin.

Acceptance Scenarios:

  1. Given o admin envia POST /api/v1/admin/boletos com os campos obrigatórios, Then o boleto é criado com status "pending" e retorna 201 com os dados do boleto.
  2. Given o campo user_id não corresponde a um ClientUser existente, Then a API retorna 404.
  3. Given campos obrigatórios estão ausentes (user_id, description, amount, due_date), Then a API retorna 422.

User Story 8 — Admin atualiza status de Visita via API (Priority: P8)

Um administrador atualiza o status de uma solicitação de visita e opcionalmente define a data/hora agendada.

Why this priority: Necessário para o ciclo completo de visitas; sem esta operação o cliente nunca vê status diferente de "pending".

Independent Test: Pode ser testado via PUT /api/v1/admin/visits/<id>/status e verificando a mudança no retorno de GET /api/v1/me/visits.

Acceptance Scenarios:

  1. Given o admin envia PUT /api/v1/admin/visits/<id>/status com {"status": "confirmed", "scheduled_at": "2026-05-01T10:00:00"}, Then a VisitRequest é atualizada e retorna 200.
  2. Given o id não existe, Then retorna 404.
  3. Given o valor de status não é um dos permitidos (pending/confirmed/cancelled/completed), Then retorna 422.

Edge Cases

  • O que acontece quando o cliente tenta favoritar um imóvel que foi removido/desativado do catálogo? O registro SavedProperty permanece; o imóvel é exibido com indicação "Imóvel indisponível" ou omitido da listagem de favoritos.
  • O que acontece com a comparação se um dos imóveis armazenados no localStorage deixar de existir? A aplicação ignora silenciosamente o id inválido ao carregar e exibe apenas os imóveis válidos.
  • O que acontece quando a VisitRequest é criada e o cliente posteriormente envia o contato como anônimo (para o mesmo imóvel)? O ContactLead é criado normalmente (usuário anônimo) sem afetar a VisitRequest existente.
  • O que acontece com boletos cujo url é nulo? O botão de acesso é desabilitado ou ocultado; o boleto ainda aparece na listagem.
  • O que acontece se o token JWT expirar durante a navegação na área do cliente? O Axios interceptor (feature 005) redireciona para o login; a rota protegida bloqueia o acesso.

Requirements (mandatory)

Functional Requirements

Favoritos

  • FR-001: O sistema DEVE permitir que clientes autenticados adicionem imóveis à lista de favoritos; tentativas de adicionar um imóvel já favoritado DEVEM retornar 409.
  • FR-002: O sistema DEVE permitir que clientes autenticados removam imóveis da lista de favoritos; tentativas de remover um imóvel não favoritado DEVEM retornar 404.
  • FR-003: A lista de favoritos de um cliente DEVE ser persistida no backend e recuperada entre sessões.
  • FR-004: O botão de coração DEVE refletir o estado de favorito do imóvel para o cliente autenticado corrente.

Comparação

  • FR-005: A aplicação DEVE permitir que o usuário adicione até 3 imóveis à lista de comparação; a adição de um quarto imóvel DEVE ser bloqueada com mensagem de feedback.
  • FR-006: A lista de comparação DEVE ser persistida no armazenamento local do navegador e restaurada ao recarregar a página.
  • FR-007: A página de comparação DEVE exibir uma tabela lado a lado com as seguintes características: preço, área, quartos, banheiros, vagas, condomínio, tipo, bairro e comodidades.
  • FR-008: Uma barra flutuante de comparação DEVE ser exibida no rodapé sempre que houver ao menos 1 imóvel na lista.

Painel do Cliente

  • FR-009: A rota /area-do-cliente DEVE exibir cards de resumo com o total de favoritos, o número de visitas com status "pending" e o número de boletos com status "pending".
  • FR-010: Todas as rotas sob /area-do-cliente DEVEM ser protegidas; clientes não autenticados DEVEM ser redirecionados para o login.

Visitas

  • FR-011: A rota /area-do-cliente/visitas DEVE exibir todas as VisitRequests do cliente autenticado, ordenadas por data de criação decrescente.
  • FR-012: Ao submeter o formulário de contato na página de detalhes de um imóvel, se o usuário estiver autenticado, o sistema DEVE criar uma VisitRequest vinculada ao cliente além do ContactLead.
  • FR-013: O admin DEVE poder atualizar o status de uma VisitRequest via API, incluindo opcionalmente a data/hora agendada.

Boletos

  • FR-014: A rota /area-do-cliente/boletos DEVE exibir todos os boletos do cliente autenticado, ordenados por data de vencimento decrescente.
  • FR-015: O admin DEVE poder criar boletos para qualquer cliente via API, com ou sem vínculo a um imóvel.
  • FR-016: Boletos com url preenchida DEVEM exibir um botão de acesso; boletos sem url DEVEM exibir o botão desabilitado.

API

  • FR-017: Todas as rotas sob /api/v1/me/ DEVEM exigir token JWT válido de ClientUser; ausência ou token inválido DEVE retornar 401.
  • FR-018: Todas as rotas sob /api/v1/admin/ DEVEM exigir autenticação de admin; acesso com token de ClientUser DEVE retornar 403.

Key Entities

  • SavedProperty: Associação entre um cliente e um imóvel favoritado. Atributos: identificador único, referência ao cliente, referência ao imóvel, data de criação. Unicidade garantida por par (cliente, imóvel).
  • VisitRequest: Solicitação de visita feita por um cliente para um imóvel. Atributos: identificador único, referência ao cliente, referência ao imóvel, mensagem livre, status do fluxo (pendente/confirmado/cancelado/concluído), data/hora agendada (opcional), data de criação.
  • Boleto: Documento de cobrança criado pelo admin para um cliente, opcionalmente vinculado a um imóvel. Atributos: identificador único, referência ao cliente, referência ao imóvel (opcional), descrição, valor monetário, data de vencimento, status (pendente/pago/vencido), URL de acesso (opcional), data de criação.
  • ComparisonList (frontend): Lista temporária de até 3 imóveis selecionados para comparação. Armazenada localmente no navegador; não persiste no backend.

API Contract

Todas as rotas requerem Authorization: Bearer <token> (client JWT, exceto rotas /admin/ que requerem token de admin).

Método Rota Corpo / Parâmetros Respostas
GET /api/v1/me/favorites 200: [{property completo}] · 401
POST /api/v1/me/favorites {property_id} 201 · 409 se já favoritado · 401 · 404 se imóvel não existe
DELETE /api/v1/me/favorites/<property_id> 204 · 404 · 401
GET /api/v1/me/visits 200: [{id, property:{id,title,slug}, message, status, scheduled_at, created_at}] · 401
GET /api/v1/me/boletos 200: [{id, description, amount, due_date, status, url}] · 401
POST /api/v1/admin/boletos {user_id, property_id?, description, amount, due_date, url?} 201: boleto criado · 404 cliente não existe · 422 campos inválidos · 401/403
PUT /api/v1/admin/visits/<id>/status {status, scheduled_at?} 200: visita atualizada · 404 · 422 status inválido · 401/403

Success Criteria (mandatory)

Measurable Outcomes

  • SC-001: Clientes autenticados conseguem favoritar e desfavoritar imóveis em menos de 2 segundos por ação, com estado persistido entre sessões.
  • SC-002: A tabela de comparação é renderizada em menos de 1 segundo para até 3 imóveis; a seleção é restaurada corretamente ao recarregar a página.
  • SC-003: 100% das rotas da área do cliente bloqueiam acesso não autenticado, redirecionando para o login sem expor dados.
  • SC-004: O painel exibe contadores precisos — favoritos, visitas pendentes e boletos ativos — sem discrepâncias em relação ao banco de dados.
  • SC-005: Clientes conseguem localizar e acessar um boleto em até 3 cliques a partir do painel principal.
  • SC-006: Todas as mudanças de status de visita feitas pelo admin refletem no painel do cliente na próxima atualização de página.
  • SC-007: O formulário de contato na página de detalhes, quando submetido por cliente autenticado, cria a VisitRequest e ela aparece imediatamente no histórico de visitas.

Assumptions

  • O modelo ClientUser e o middleware de autenticação JWT (Feature 005) são pré-requisitos e estarão disponíveis antes da implementação desta feature.
  • O admin não terá uma interface gráfica no MVP; operações de admin (criação de boletos e atualização de status de visita) são executadas diretamente via API.
  • Boletos são gerados externamente; o sistema apenas armazena e exibe o link/URL de acesso. Nenhuma integração com gateway de pagamento é necessária no MVP.
  • A comparação de imóveis não será persistida no backend; usar armazenamento local do navegador é suficiente para os requisitos do MVP.
  • Uma VisitRequest é criada somente quando o cliente está autenticado. Usuários anônimos continuam gerando ContactLeads sem criar VisitRequests.
  • O status de boletos pode ser atualizado manualmente pelo admin via chamada à API (implícito no PUT /api/v1/admin/boletos/) o que fica como extensão futura; no MVP o status só é definido na criação.
  • O design de todos os componentes segue o tema Linear dark definido em DESIGN.md (fundo #08090a, acento #5e6ad2/#7170ff).
  • A lista de favoritos retornada pela API inclui todos os detalhes do imóvel necessários para renderização do PropertyCard, sem chamadas adicionais.