# Feature Specification: Listagem de Imóveis com Filtros **Feature Branch**: `003-property-listing` **Created**: 2026-04-13 **Status**: Approved ## Contexto Página pública `/imoveis` onde visitantes navegam e filtram o catálogo completo de imóveis. Os filtros utilizam dados dinâmicos cadastrados pela área administrativa (tipos, características, lazer, condomínio, segurança). ## User Stories ### US1 — Visitante navega no catálogo (P1) **Given** o visitante acessa `/imoveis`, **When** a página carrega, **Then** vê a grade de imóveis ativos com paginação, foto, preço, tipo, dormitórios, banheiros, área e vagas. ### US2 — Visitante filtra por tipo de imóvel (P1) **Given** o painel de filtros está visível, **When** seleciona categoria "Residencial" e subtipo "Apartamento", **Then** a lista atualiza mostrando apenas apartamentos residenciais. ### US3 — Visitante filtra por preço (P1) **Given** o filtro de preço está disponível, **When** define min R$ 500.000 e max R$ 1.500.000, **Then** apenas imóveis nesse intervalo aparecem. Com a opção "incluir condomínio" marcada, o backend soma `price + condo_fee` antes de comparar. ### US4 — Visitante filtra por quartos/banheiros/vagas (P1) **Given** os sliders numéricos estão disponíveis, **When** define mínimo 2 quartos, **Then** apenas imóveis com ≥ 2 quartos aparecem. ### US5 — Visitante filtra por área (P1) **Given** o filtro de área em m² está disponível, **When** define 50–150 m², **Then** apenas imóveis nesse intervalo aparecem. ### US6 — Visitante filtra por características (P2) **Given** a lista de características está visível (Aceita animais, Ar-condicionado, etc.), **When** seleciona múltiplas, **Then** apenas imóveis que possuem TODAS as características selecionadas aparecem. ### US7 — Visitante filtra lazer, condomínio e segurança (P2) Mesmo comportamento do US6 para os grupos adicionais. ## API Contract ### Endpoints necessários **GET /api/v1/property-types** → lista hierárquica de categorias e subtipos ```json [ { "id": 1, "name": "Residencial", "slug": "residencial", "subtypes": [{"id": 10, "name": "Apartamento", "slug": "apartamento"}, ...] } ] ``` **GET /api/v1/amenities** → lista agrupada de amenidades ```json [ {"id": 1, "name": "Aceita animais", "group": "caracteristica", "count": 37}, ... ] ``` **GET /api/v1/properties** (atualizar) — aceitar query params: | Param | Tipo | Descrição | |---|---|---| | `subtype_id` | int | ID do subtipo | | `listing_type` | venda\|aluguel | tipo da transação | | `price_min` | number | preço mínimo | | `price_max` | number | preço máximo | | `include_condo` | bool | somar condomínio ao preço | | `bedrooms_min` | int | quartos mínimo | | `bedrooms_max` | int | quartos máximo | | `bathrooms_min` | int | banheiros mínimo | | `bathrooms_max` | int | banheiros máximo | | `parking_min` | int | vagas mínimo | | `parking_max` | int | vagas máximo | | `area_min` | int | área m² mínimo | | `area_max` | int | área m² máximo | | `amenity_ids` | int[] (CSV) | IDs de amenidades (AND) | | `page` | int | página (default 1) | | `per_page` | int | itens por página (default 24, max 48) | | `featured` | bool | retorna destaques (comportamento existente) | Resposta paginada: ```json { "items": [...], "total": 120, "page": 1, "per_page": 24, "pages": 5 } ``` ## Modelos necessários ### PropertyType (novo) - `id`, `name`, `slug`, `parent_id` (nullable → hierarquia) ### Amenity (novo) - `id`, `name`, `slug`, `group` (enum: caracteristica | lazer | condominio | seguranca), `count` (denormalizado) ### PropertyAmenity (nova tabela N:N) - `property_id`, `amenity_id` ### Atualizar Property (novo campo) - `subtype_id` FK → PropertyType - `parking_spots` INT - `condo_fee` NUMERIC(10,2) nullable ## Out of Scope - Detalhe do imóvel (feature 004) - Mapa/geolocalização - Ordenação customizada (entregue com default created_at desc) - Favoritos / comparador