# 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//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`, 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.