feat: add full project - backend, frontend, docker, specs and configs
This commit is contained in:
parent
b77c7d5a01
commit
e6cb06255b
24489 changed files with 61341 additions and 36 deletions
116
specs/023-ux-melhorias-imoveis/data-model.md
Normal file
116
specs/023-ux-melhorias-imoveis/data-model.md
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
# Data Model — Melhorias UX/UI (023)
|
||||
|
||||
> Nenhuma migration de banco necessária. Todos os campos utilizados já existem no modelo `Property`.
|
||||
|
||||
---
|
||||
|
||||
## Entidades Existentes (campos utilizados nesta feature)
|
||||
|
||||
### `Property` (`backend/app/models/property.py`)
|
||||
|
||||
| Campo | Tipo SQLAlchemy | Tipo Python | Sprint | Utilização |
|
||||
|---|---|---|---|---|
|
||||
| `title` | `VARCHAR(200)` | `str` | 1 | Busca textual `q` (ILIKE) |
|
||||
| `address` | `VARCHAR(300)` | `str \| None` | 1 | Busca textual `q` (ILIKE) |
|
||||
| `code` | `VARCHAR(30)` | `str \| None` | 1 | Busca textual `q` (ILIKE) |
|
||||
| `neighborhood_id` | `INTEGER FK → neighborhoods.id` | `int \| None` | 1 | Join para busca `q` em `Neighborhood.name` |
|
||||
| `price` | `NUMERIC(12,2)` | `Decimal` | 2 | Ordenação `price_asc` / `price_desc` |
|
||||
| `area_m2` | `INTEGER` | `int` | 2 | Ordenação `area_desc` |
|
||||
| `created_at` | `DATETIME` | `datetime` | 2/3 | Ordenação `newest`; badge "Novo" (frontend) |
|
||||
| `is_featured` | `BOOLEAN` | `bool` | 3 | Badge "Destaque" no card |
|
||||
|
||||
### `Neighborhood` (`backend/app/models/location.py`)
|
||||
|
||||
| Campo | Tipo SQLAlchemy | Utilização |
|
||||
|---|---|---|
|
||||
| `id` | `INTEGER PK` | Join com `Property.neighborhood_id` |
|
||||
| `name` | `VARCHAR` | Busca textual `q` (ILIKE) |
|
||||
|
||||
---
|
||||
|
||||
## Tipos Frontend Adicionados (`frontend/src/services/properties.ts`)
|
||||
|
||||
### `PropertyFilters` — campos novos
|
||||
|
||||
```ts
|
||||
// Adição aos campos existentes:
|
||||
q?: string // busca textual livre
|
||||
sort?: SortOption
|
||||
```
|
||||
|
||||
### `SortOption` (novo tipo)
|
||||
|
||||
```ts
|
||||
type SortOption =
|
||||
| 'relevance' // default — equivale a created_at DESC no backend
|
||||
| 'price_asc'
|
||||
| 'price_desc'
|
||||
| 'area_desc'
|
||||
| 'newest'
|
||||
```
|
||||
|
||||
### `ViewMode` (novo tipo local — apenas frontend)
|
||||
|
||||
```ts
|
||||
type ViewMode = 'list' | 'grid'
|
||||
// Persiste em localStorage com key 'imoveis_view_mode'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Entidades de UI (apenas frontend — sem persistência no banco)
|
||||
|
||||
### `ActiveFilterChip`
|
||||
|
||||
Tipo derivado calculado a partir de `PropertyFilters` + dados do catálogo:
|
||||
|
||||
```ts
|
||||
interface ActiveFilterChip {
|
||||
key: string // identificador único (ex: 'city_id', 'q', 'bedrooms_min')
|
||||
label: string // texto exibido no chip (ex: 'São Paulo', 'Busca: "Jardins"')
|
||||
onRemove: () => void // callback que remove este filtro específico
|
||||
}
|
||||
```
|
||||
|
||||
### `EmptyStateSuggestion`
|
||||
|
||||
```ts
|
||||
interface EmptyStateSuggestion {
|
||||
label: string // ex: 'Remover filtro de bairro'
|
||||
relaxedFilters: PropertyFilters
|
||||
count: number // total de imóveis com o filtro relaxado
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Validação de Entrada — Backend
|
||||
|
||||
O parâmetro `q` não é validado via Pydantic (é query param de GET, sem body).
|
||||
Sanitização aplicada diretamente na rota:
|
||||
|
||||
```python
|
||||
q = args.get("q", "").strip()
|
||||
# Comprimento máximo razoável (evitar payloads abusivos)
|
||||
if len(q) > 200:
|
||||
q = q[:200]
|
||||
```
|
||||
|
||||
O parâmetro `sort` é validado via whitelist implícita no `sort_map.get(sort, default)`.
|
||||
|
||||
---
|
||||
|
||||
## Diagrama de Relacionamentos (campos relevantes)
|
||||
|
||||
```
|
||||
Property
|
||||
├── title (VARCHAR 200) ─── ILIKE com q
|
||||
├── address (VARCHAR 300) ─── ILIKE com q
|
||||
├── code (VARCHAR 30) ─── ILIKE com q
|
||||
├── neighborhood_id (FK) ─┐
|
||||
│ ├── JOIN → Neighborhood.name ─── ILIKE com q
|
||||
├── price (NUMERIC 12,2) ─── ORDER BY price_asc/desc
|
||||
├── area_m2 (INTEGER) ─── ORDER BY area_desc
|
||||
├── created_at (DATETIME) ─── ORDER BY newest; badge "Novo" no frontend
|
||||
└── is_featured (BOOLEAN) ─── badge "Destaque" no frontend
|
||||
```
|
||||
Loading…
Add table
Add a link
Reference in a new issue