87 lines
5.5 KiB
Markdown
87 lines
5.5 KiB
Markdown
# Research: Área do Cliente (Feature 006)
|
|
|
|
**Date**: 2026-04-13
|
|
**Status**: Complete — todos os NEEDS CLARIFICATION resolvidos
|
|
|
|
---
|
|
|
|
## 1. Padrão de favoritos: backend vs. localStorage
|
|
|
|
**Decisão**: Favoritos persistidos no **backend** (tabela `saved_properties`).
|
|
**Rationale**: Requisito explícito no spec (FR-003, SC-001): estado mantido entre sessões/dispositivos. localStorage não atende porque o usuário espera ver seus favoritos ao fazer login em outro dispositivo.
|
|
**Alternativas consideradas**: localStorage com sync eventual — rejeitado; adiciona complexidade de sincronização sem benefício para o MVP.
|
|
|
|
---
|
|
|
|
## 2. Comparação: backend vs. localStorage
|
|
|
|
**Decisão**: Comparação persistida apenas em **localStorage** (chave `imob_comparison`).
|
|
**Rationale**: Spec é explícito (FR-006, Assumptions): "usar armazenamento local do navegador é suficiente para os requisitos do MVP". Comparação é sessão temporária de decisão de compra, não dado de longo prazo.
|
|
**Alternativas consideradas**: Persistir no backend com `comparison_lists` — rejeitado (YAGNI: nenhum requisito exige cross-device para comparação).
|
|
|
|
---
|
|
|
|
## 3. VisitRequest: criação automática via formulário de contato
|
|
|
|
**Decisão**: Integração **opcional no MVP** — apenas se o JWT válido estiver presente no header da requisição de contato. O endpoint `POST /api/v1/properties/<slug>/contact` cria VisitRequest quando autenticado; caso contrário, cria apenas ContactLead.
|
|
**Rationale**: FR-012 e User Story 5 exigem a integração, mas é marcada como "opcional para MVP" no design. Evitar bloqueio da feature se Feature 005 não estiver completamente integrada. Abordagem: verificar `Authorization` header sem obrigar — se ausente ou inválido, seguir fluxo normal de ContactLead.
|
|
**Alternativas consideradas**: Novo endpoint separado para criação de VisitRequest — rejeitado; duplica a UX de submissão do formulário de contato.
|
|
|
|
---
|
|
|
|
## 4. Admin: verificação de role vs. require_auth apenas
|
|
|
|
**Decisão**: MVP usa apenas **`require_auth`** nas rotas admin, sem verificação de role.
|
|
**Rationale**: Único usuário que acessa a API admin no MVP é o proprietário da aplicação. Implementar RBAC completo sem tabela de roles seria over-engineering. Dívida técnica documentada no Constitution Check.
|
|
**Alternativas consideradas**: `require_admin_role` via campo `is_admin` no ClientUser — possível futuro; não implementar agora (YAGNI).
|
|
|
|
---
|
|
|
|
## 5. Carregamento do FavoritesContext
|
|
|
|
**Decisão**: `FavoritesContext` carrega a lista de favoritos via `GET /api/v1/me/favorites` na inicialização do `AuthContext` (quando `user !== null`). Armazena apenas os `UUIDs` dos imóveis favoritados em um `Set<string>`, não os objetos completos de Property.
|
|
**Rationale**: O contexto precisa responder rapidamente ao estado "favoritado ou não" para cada card. Armazenar apenas IDs é O(1) para lookup. Os dados completos são carregados pela `FavoritesPage` sob demanda. Evita duplicar grande payload de imóveis no contexto global.
|
|
**Alternativas consideradas**: Armazenar objetos Property completos no contexto — rejeitado; memory footprint desnecessário para o caso de uso principal (mostrar coração preenchido/vazio).
|
|
|
|
---
|
|
|
|
## 6. Estrutura do ClientLayout
|
|
|
|
**Decisão**: `ClientLayout.tsx` com sidebar lateral fixa em desktop, colapsável em mobile.
|
|
**Rationale**: Padrão de dashboard consistente com DESIGN.md (fundo `#08090a`, painéis `#0f1011`). Sidebar com links: Dashboard, Favoritos, Comparar, Visitas, Boletos. User info no topo da sidebar.
|
|
**Alternativas consideradas**: Tabs horizontais no topo — rejeitado; não escala bem com 5+ seções e viola o padrão de dashboard visual estabelecido.
|
|
|
|
---
|
|
|
|
## 7. Unicidade de SavedProperty
|
|
|
|
**Decisão**: Unique constraint na combinação `(user_id, property_id)` no banco de dados (além da validação no código).
|
|
**Rationale**: FR-001 exige 409 para duplicata. A constraint no banco garante integridade mesmo em race conditions. O código verifica antes de inserir e retorna 409 em `IntegrityError` do SQLAlchemy.
|
|
**Alternativas consideradas**: Verificar apenas em código — rejeitado; sujeito a race condition em inserts paralelos.
|
|
|
|
---
|
|
|
|
## 8. Tipo monetário para `Boleto.amount`
|
|
|
|
**Decisão**: `Numeric(12, 2)` — mesmo padrão de `Property.price`.
|
|
**Rationale**: Princípio IV da constituição: "Sensitive fields (e.g., prices, area measurements) MUST use appropriate numeric types — no float for money".
|
|
**Alternativas consideradas**: `Float` — rejeitado explicitamente pela constituição.
|
|
|
|
---
|
|
|
|
## 9. Ordenação das listas
|
|
|
|
**Decisão**:
|
|
- `GET /api/v1/me/visits` → ordenado por `created_at DESC`
|
|
- `GET /api/v1/me/boletos` → ordenado por `due_date ASC` (vencimentos próximos primeiro)
|
|
- `GET /api/v1/me/favorites` → ordenado por `created_at DESC` (mais recentes primeiro)
|
|
|
|
**Rationale**: Visitas: mais recentes primeiro é padrão de log/histórico. Boletos: vencimentos próximos primeiro é mais útil financeiramente. Favoritos: mais recentes primeiro é o comportamento esperado numa lista de "wishlist".
|
|
|
|
---
|
|
|
|
## 10. Dependência de Feature 005
|
|
|
|
**Decisão**: Assumir que `ClientUser`, `require_auth` e JWT middleware estão disponíveis.
|
|
**Rationale**: Declarado explicitamente como pré-requisito no spec e nas instruções. Se Feature 005 ainda não estiver implementada, as rotas retornarão 500 até que o middleware seja injetado — identificável rapidamente em teste.
|
|
**Alternativas consideradas**: Mock de `require_auth` para desenvolvimento paralelo — possível, mas não necessário; o plano assume sequência correta de implementação.
|