7.6 KiB
Research: Property Detail Page (004)
Feature: 004-property-detail-page
Date: 2026-04-13
Status: Complete — todos os NEEDS CLARIFICATION resolvidos
R-01 — Campos code e description ausentes no modelo Property
Pergunta: Os campos code e description devem ser adicionados nesta feature ou numa feature separada?
Evidência: O contrato da API em spec.md inclui explicitamente "code": "AP-00042" e "description": "..." na resposta de GET /api/v1/properties/<slug>. A Constituição (Princípio III) define que a spec é a fonte de verdade.
Decisão: Adicionar ambos os campos ao modelo Property nesta feature. A migration que cria contact_leads também incluirá as novas colunas.
Campos a adicionar:
code:VARCHAR(30),UNIQUE,nullable=True(nullable para não quebrar registros existentes sem migration data)description:TEXT,nullable=True
Rationale: Agrupar numa única migration evita fragmentação de DDL. Os campos são necessários para o contrato da API desta feature.
Alternativa rejeitada: Feature separada só para code/description — overhead desnecessário para dois campos simples.
R-02 — Campo address ausente em PropertyOut
Pergunta: O campo address existe no modelo mas não está em PropertyOut. Como tratar?
Evidência: Property.address = db.Column(db.String(300), nullable=True) existe no modelo. PropertyOut não inclui address. O contrato da spec exige address na resposta de detalhe.
Decisão: Adicionar address: str | None ao PropertyOut existente. É um campo geral do imóvel (não exclusivo da tela de detalhe) e sua ausência no schema era uma omissão.
Impacto nos consumers existentes: O endpoint GET /api/v1/properties (list) passará a incluir address na resposta. Isso é retrocompatível — campos adicionais em JSON não quebram consumers que não os leem.
Alternativa rejeitada: PropertyDetailOut separado apenas para address — over-engineering para um campo que logicamente pertence ao schema base.
R-03 — type vs listing_type no contrato da API
Pergunta: O contrato da spec documenta listing_type mas o schema e o modelo usam type. O que usar na implementação?
Evidência:
- Model:
type = db.Column(db.Enum("venda", "aluguel", name="property_type")) PropertyOut:type: Literal["venda", "aluguel"]- Spec API contract:
"listing_type": "venda"
Decisão: Manter type no schema e na serialização JSON. O contrato da spec usa listing_type como nome descritivo na documentação, mas o campo JSON emitido pelo backend será type (consistente com o endpoint de listagem já em produção). A spec documenta o significado do campo, não exige renaming.
Rationale: Renomear para listing_type quebraria o endpoint de listagem que já retorna type. Backward compatibility supera a preferência de nomenclatura da spec, especialmente porque o frontend já consome type.
Alternativa rejeitada: Alias Pydantic listing_type via Field(alias="type") — introduziria inconsistência entre list e detail sem benefício real no MVP.
R-04 — Carousel: biblioteca externa vs handlers nativos
Pergunta: Usar Embla Carousel, Swiper.js ou implementar com React state + handlers nativos?
Análise:
| Opção | Tamanho bundle | Complexidade | Justificativa |
|---|---|---|---|
| Embla Carousel | ~7 KB gzip | baixa (API simples) | overkill para carousel básico |
| Swiper.js | ~35 KB gzip | média-alta | excesso de features desnecessárias |
| React state nativo | 0 KB extra | baixa-média | suficiente para os requisitos da spec |
Requisitos da spec: navegação por teclado (←/→), swipe touch, thumbnail strip com estado ativo. Tudo implementável com:
useStatepara índice ativoonKeyDownno container (tabIndex=0)onTouchStart/onTouchEndpara detectar swipe horizontal- CSS
transitionpara animação suave
Decisão: Implementar com React state + handlers nativos. Zero nova dependência npm (alinhamento com Princípio VI).
Rationale: Os requisitos são exatos e limitados. Uma lib traz overhead de API para aprender, bundle weight extra e potencial conflito com o design system customizado.
R-05 — Mapa (US3 P3): Google Maps Embed
Pergunta: Qual serviço de mapa usar para US3? Chave de API necessária?
Análise:
- US3 é P3 (prioridade mais baixa) — não bloqueia o MVP funcional
- Google Maps Embed API: iframe simples, sem SDK JS, sem package npm
- URL:
https://www.google.com/maps/embed/v1/place?key=API_KEY&q=ENDEREÇO_CODIFICADO - Requer chave de API com "Maps Embed API" habilitada
- URL:
- OpenStreetMap via
iframeNominatim: gratuito, sem chave, mas qualidade variável
Decisão: Google Maps Embed via <iframe> simples quando VITE_GOOGLE_MAPS_API_KEY estiver definido. Se a variável não existir ou o endereço for nulo, a seção de mapa é silenciosamente omitida (null render).
Configuração necessária:
- Env var frontend:
VITE_GOOGLE_MAPS_API_KEY(opcional) - Sem nova dependência npm
Alternativa rejeitada: Leaflet + react-leaflet — adiciona ~40 KB ao bundle para uma feature P3 que pode ser omitida no MVP.
R-06 — Schema de detalhe: PropertyDetailOut vs extensão de PropertyOut
Pergunta: Criar PropertyDetailOut(PropertyOut) ou adicionar campos diretamente a PropertyOut?
Evidência e raciocínio:
codeedescriptionsão campos de detalhe narrativo — não fazem sentido no card da listagem (espaço limitado)PropertyOuté usado por dois endpoints: list (GET /api/v1/properties) e futuramente featured- Adicionar
code/descriptionaPropertyOutpolui a resposta da listagem
Decisão: Criar PropertyDetailOut(PropertyOut) com os campos adicionais:
class PropertyDetailOut(PropertyOut):
address: str | None
code: str | None
description: str | None
address vai em PropertyDetailOut — em PropertyOut base o campo não é adicionado para não expor informação de endereço completo na listagem paginada.
Wait — revisão: O R-02 decidiu adicionar address a PropertyOut. Reconsiderando com R-06: manter address apenas em PropertyDetailOut é mais conservador e evita expor endereços na listagem. Decisão final: address, code, description em PropertyDetailOut somente.
R-07 — Validação de ContactLeadIn: comprimentos de campo
Pergunta: Quais validações Pydantic usar em ContactLeadIn?
Spec definiu:
name: obrigatório, 2–150 charsemail: obrigatório, EmailStrphone: opcional, max 20 charsmessage: obrigatório, 10–2000 chars
Decisão: Usar pydantic.EmailStr (requer email-validator no pyproject.toml — já presente como dependência do projeto). Usar Annotated[str, Field(min_length=..., max_length=...)] para os demais.
Verificar: email-validator já está em pyproject.toml antes de implementar. Se não estiver, adicionar com uv add email-validator.
Resumo das Decisões
| ID | Decisão |
|---|---|
| R-01 | code e description adicionados em Property + na migration desta feature |
| R-02 | address vai em PropertyDetailOut (não em PropertyOut base) |
| R-03 | Manter type no JSON (não renomear para listing_type) |
| R-04 | Carousel: React state + handlers nativos, zero nova lib |
| R-05 | Google Maps Embed via iframe, env var opcional, omissão silenciosa se ausente |
| R-06 | PropertyDetailOut(PropertyOut) com address, code, description |
| R-07 | Pydantic EmailStr + Field(min_length, max_length) para ContactLeadIn |