sass-imobiliaria/.specify/features/004-property-detail-page/research.md

7.6 KiB
Raw Permalink Blame History

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.


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:

  • useState para índice ativo
  • onKeyDown no container (tabIndex=0)
  • onTouchStart/onTouchEnd para detectar swipe horizontal
  • CSS transition para 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
  • OpenStreetMap via iframe Nominatim: 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:

  • code e description sã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/description a PropertyOut polui 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, 2150 chars
  • email: obrigatório, EmailStr
  • phone: opcional, max 20 chars
  • message: obrigatório, 102000 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