sass-imobiliaria/specs/033-video-apresentacao-imovel/data-model.md
MatheusAlves96 e1a1f71fbd
Some checks failed
CI/CD → Deploy via SSH / Build & Push Docker Images (push) Successful in 1m6s
CI/CD → Deploy via SSH / Deploy via SSH (push) Successful in 4m18s
CI/CD → Deploy via SSH / Validate HTTPS & Endpoints (push) Has been cancelled
chore: seed sample video data, add spec 033 and update instructions
2026-04-22 23:57:50 -03:00

95 lines
2.9 KiB
Markdown

# Data Model: Vídeo de Apresentação do Imóvel (033)
**Gerado por**: /speckit.plan
**Data**: 2026-04-22
---
## Entidade Afetada: `Property` (tabela `properties`)
### Novos Campos
| Coluna | Tipo SQL | Nullable | Default | Restrições |
|--------|----------|----------|---------|-----------|
| `video_url` | `VARCHAR(512)` | ✅ NULL | `NULL` | — |
| `video_position` | `VARCHAR(20)` | ❌ NOT NULL | `'section'` | valores válidos: `'carousel'` \| `'section'` (enforced no Pydantic) |
### Justificativa de tipos
- `VARCHAR(512)` para `video_url`: URLs YouTube/Vimeo raramente ultrapassam 100 chars, mas URLs diretas de CDN podem ser longas; 512 é conservador sem overhead relevante.
- `VARCHAR(20)` para `video_position`: valores `'carousel'` (8 chars) e `'section'` (7 chars) cabem confortavelmente; sem ENUM SQL para simplificar migrations.
---
## Migration Alembic
**Arquivo**: `backend/migrations/versions/k3l4m5n6o7p8_add_video_to_properties.py`
**Antecessora**: `j2k3l4m5n6o7_add_homepage_hero_theme_images.py`
```python
# upgrade
op.add_column('properties', sa.Column('video_url', sa.String(512), nullable=True))
op.add_column('properties', sa.Column('video_position', sa.String(20), nullable=False, server_default='section'))
# downgrade
op.drop_column('properties', 'video_position')
op.drop_column('properties', 'video_url')
```
---
## Schema Pydantic — Campos Adicionados
### `PropertyDetailOut` (backend/app/schemas/property.py)
```python
video_url: str | None = None
video_position: Literal['carousel', 'section'] = 'section'
```
### `PropertyAdminOut` (backend/app/routes/admin.py)
```python
video_url: str | None = None
video_position: Literal['carousel', 'section'] = 'section'
```
### `_SCALAR_FIELDS` no `admin_update_property`
Adicionar `'video_url'` e `'video_position'` à tupla `_SCALAR_FIELDS`.
**Sanitização**: se `'video_url'` estiver presente no body, aplicar `.strip()` e tratar string vazia como `None`.
---
## Tipos TypeScript
### `PropertyDetail` (frontend/src/types/property.ts)
```ts
video_url: string | null
video_position: 'carousel' | 'section'
```
---
## Validação de Regras
| Regra | Onde Validado |
|-------|--------------|
| `video_position` só aceita `'carousel'` ou `'section'` | Pydantic `Literal` (backend) |
| `video_url` string vazia → `None` | handler `admin_update_property` (backend) |
| `video_url` sem espaços | `.strip()` antes de persistir (backend) |
| URL inválida (domínio não suportado) → sem exibição | `getEmbedUrl` retorna `type: 'unknown'` (frontend) |
---
## Estado de Transição
```
Property sem vídeo (video_url = NULL)
→ Nenhuma alteração visual na página de detalhe
Property com video_position = 'section'
→ Seção "Vídeo de Apresentação" renderizada após carrossel
Property com video_position = 'carousel'
→ VideoPlayer como índice 0 no PhotoCarousel
Remover vídeo (admin)
→ video_url = NULL, video_position retorna ao default 'section'
```