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

5.1 KiB
Raw Blame History

Implementation Plan: Property Detail Page

Branch: 004-property-detail-page | Date: 2026-04-13 | Spec: spec.md Input: Feature specification from .specify/features/004-property-detail-page/spec.md

Summary

Página pública /imoveis/<slug> que exibe todas as informações de um imóvel em detalhe: galeria de fotos em carrossel (teclado + swipe), estatísticas, caixa de preço sticky, description, amenidades agrupadas e seção de contato (formulário + WhatsApp). O backend adiciona dois novos endpoints — GET /api/v1/properties/<slug> e POST /api/v1/properties/<slug>/contact — além de uma nova tabela contact_leads e os campos code / description no modelo Property exigidos pelo contrato da spec.

Technical Context

Language/Version: Python 3.12 / TypeScript 5.5 Primary Dependencies: Flask 3.x, SQLAlchemy 2.x, Pydantic v2 (backend) · React 18, Tailwind CSS 3.4, react-router-dom v6, Axios (frontend) Storage: PostgreSQL 16 via Docker Testing: pytest (backend) · Vite build + verificação visual (frontend) Target Platform: Container Linux Docker (backend) / browser SPA (frontend) Project Type: REST web-service + single-page application Performance Goals: Página renderizada em < 3 s em conexão padrão (SC-001) Constraints: Rotas públicas sem autenticação; CORS explícito via Flask-CORS; sem rate limiting no MVP (assumption doc spec); nenhum secret no bundle frontend Scale/Scope: MVP — única imobiliária, único número WhatsApp via env

Constitution Check

GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.

Princípio Status Evidência
I. Design-First PASS Todos os componentes usam tokens do DESIGN.md: canvas #08090a, panel #0f1011, accent #7170ff, border rgba(255,255,255,0.050.08). Nenhum inline style fora do sistema.
II. Separation of Concerns PASS Flask retorna JSON puro; React consome a API. Nenhum SSR. CORS explícito configurado.
III. Spec-Driven PASS spec.md finalizado; plan → tasks → implement.
IV. Data Integrity PASS ContactLeadIn valida com Pydantic + EmailStr; migration Alembic para contact_leads e novos campos de Property; ORM exclusivo; property_id resolvido via slug no backend (FR-B04).
V. Security PASS Rotas públicas sem auth (especificado na spec). VITE_WHATSAPP_NUMBER via env (nunca hardcoded). property_id nunca aceito do cliente.
VI. Simplicity First PASS Carousel implementado com React state + event handlers nativos (sem nova lib). Google Maps Embed como iframe simples (US3 P3). Zero novas dependências npm.

Re-check pós-design: confirmar que PropertyDetailOut herda de PropertyOut sem duplicação e que a migration é um único arquivo cobrindo contact_leads + colunas novas.

Project Structure

Documentation (this feature)

.specify/features/004-property-detail-page/
├── plan.md              ← este arquivo
├── research.md          ← Phase 0 output
├── data-model.md        ← Phase 1 output
├── quickstart.md        ← Phase 1 output
├── contracts/
│   └── rest.md          ← Phase 1 output
└── tasks.md             ← Phase 2 output (/speckit.tasks — NOT criado aqui)

Source Code

backend/
├── app/
│   ├── models/
│   │   └── property.py           ← adicionar ContactLead; adicionar code/description em Property
│   ├── schemas/
│   │   └── property.py           ← PropertyDetailOut(PropertyOut), ContactLeadIn, ContactLeadCreatedOut
│   └── routes/
│       └── properties.py         ← GET /properties/<slug>, POST /properties/<slug>/contact
└── migrations/versions/
    └── <hash>_add_contact_leads_and_property_detail_fields.py  ← migration única

frontend/
└── src/
    ├── types/
    │   └── property.ts           ← PropertyDetail (extends Property), ContactFormData
    ├── services/
    │   └── properties.ts         ← getProperty(slug), submitContactForm(slug, data)
    ├── components/
    │   ├── PropertyCarousel.tsx       ← novo
    │   ├── PropertyStatsStrip.tsx     ← novo
    │   ├── Breadcrumb.tsx             ← novo
    │   ├── PriceBox.tsx               ← novo
    │   ├── AmenitiesSection.tsx       ← novo
    │   ├── ContactSection.tsx         ← novo
    │   ├── PropertyDetailSkeleton.tsx ← novo
    │   └── PropertyCard.tsx           ← wrap com Link para /imoveis/<slug>
    ├── pages/
    │   └── PropertyDetailPage.tsx    ← novo
    └── App.tsx                       ← adicionar rota /imoveis/:slug

Structure Decision: Web application (backend + frontend). Sem novo diretório top-level; componentes separados por responsabilidade seguindo o padrão já estabelecido no projeto.

Complexity Tracking

Nenhuma violação identificada. Todos os princípios passam sem justificativa de exceção.