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

90 lines
5.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Implementation Plan: Property Detail Page
**Branch**: `004-property-detail-page` | **Date**: 2026-04-13 | **Spec**: [spec.md](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)
```text
.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
```text
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 estabelecido no projeto.
## Complexity Tracking
> Nenhuma violação identificada. Todos os princípios passam sem justificativa de exceção.