sass-imobiliaria/specs/028-trabalhe-conosco/contracts/jobs-api.md
MatheusAlves96 cf5603243c
Some checks failed
CI/CD → Deploy via SSH / Build & Push Docker Images (push) Successful in 1m0s
CI/CD → Deploy via SSH / Deploy via SSH (push) Successful in 4m35s
CI/CD → Deploy via SSH / Validate HTTPS & Endpoints (push) Failing after 46s
feat: features 025-032 - favoritos, contatos, trabalhe-conosco, area-cliente, navbar, hero-light-dark, performance-homepage
- feat(025): favoritos locais com FavoritesContext, HeartButton, PublicFavoritesPage
- feat(026): central de contatos admin (leads/contatos unificados)
- feat(027): configuração da página de contato via admin
- feat(028): trabalhe conosco - candidaturas com upload e admin
- feat(029): UX área do cliente - visitas, comparação, perfil
- feat(030): navbar UX - menu mobile, ThemeToggle, useFavorites
- feat(031): hero light/dark - imagens separadas por tema, upload, preview, seed
- feat(032): performance homepage - Promise.all parallel fetches, sessionStorage cache,
  preload hero image, loading=lazy nos cards, useInView hook, will-change carrossel,
  keyframes em index.css, AgentsCarousel e HomeScrollScene via props
- fix: light mode HomeScrollScene - gradiente, cores de texto, scroll hint

migrations: g1h2i3j4k5l6 (source em leads), h1i2j3k4l5m6 (contact_config),
            i1j2k3l4m5n6 (job_applications), j2k3l4m5n6o7 (hero theme images)
2026-04-22 22:35:17 -03:00

210 lines
6.5 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.

# API Contracts: Trabalhe Conosco
**Feature**: 028-trabalhe-conosco
**Phase**: 1 — Design & Contracts
**Base URL**: `/api/v1`
---
## Endpoints
| Método | Path | Auth | Descrição |
|--------|-------------------------|--------------|------------------------------------------|
| POST | `/jobs/apply` | Nenhuma | Submeter candidatura (público) |
| GET | `/admin/jobs` | `@require_admin` (JWT) | Listar candidaturas paginadas (admin) |
---
## POST /api/v1/jobs/apply
Endpoint público. Recebe os dados textuais da candidatura e persiste na tabela `job_applications`.
### Request
**Headers**
```
Content-Type: application/json
```
**Body** (JSON)
| Campo | Tipo | Obrigatório | Validações |
|-----------------|----------|-------------|-------------------------------------------------------------------------|
| `name` | string | Sim | Não pode ser vazio ou apenas espaços; strip aplicado |
| `email` | string | Sim | Formato de e-mail válido (RFC-5321 via `pydantic.EmailStr`) |
| `phone` | string | Não | Qualquer string; sem validação de formato nesta versão |
| `role_interest` | string | Sim | Deve ser exatamente um de: `"Corretor(a)"`, `"Assistente Administrativo"`, `"Estagiário(a)"`, `"Outro"` |
| `message` | string | Sim | Não pode ser vazio; máximo 5000 caracteres |
| `file_name` | string | Não | Nome do arquivo de currículo; sem conteúdo binário |
**Exemplo de request**
```json
{
"name": "Ana Lima",
"email": "ana.lima@email.com",
"phone": "(11) 98765-4321",
"role_interest": "Corretor(a)",
"message": "Tenho 5 anos de experiência no mercado imobiliário e gostaria de integrar a equipe.",
"file_name": "curriculo-ana-lima.pdf"
}
```
### Responses
#### 201 Created — Candidatura registrada com sucesso
```json
{
"message": "Candidatura recebida com sucesso"
}
```
#### 422 Unprocessable Entity — Dados inválidos
```json
{
"error": "Dados inválidos",
"details": [
{
"type": "value_error",
"loc": ["role_interest"],
"msg": "Value error, role_interest deve ser um de: Assistente Administrativo, Corretor(a), Estagiário(a), Outro",
"input": "Diretor",
"url": "https://errors.pydantic.dev/..."
}
]
}
```
#### 400 Bad Request — Body ausente ou não é JSON válido
```json
{
"error": "Dados inválidos",
"details": [...]
}
```
---
## GET /api/v1/admin/jobs
Endpoint protegido. Retorna listagem paginada de todas as candidaturas em ordem decrescente de `created_at`.
### Request
**Headers**
```
Authorization: Bearer <jwt_token>
Content-Type: application/json
```
**Query Parameters**
| Parâmetro | Tipo | Default | Restrições | Descrição |
|------------|---------|---------|-----------------|------------------------|
| `page` | integer | `1` | ≥ 1 | Número da página |
| `per_page` | integer | `20` | 1 100 | Registros por página |
**Exemplo de request**
```
GET /api/v1/admin/jobs?page=1&per_page=20
```
### Responses
#### 200 OK — Lista retornada com sucesso
```json
{
"items": [
{
"id": 7,
"name": "Ana Lima",
"email": "ana.lima@email.com",
"phone": "(11) 98765-4321",
"role_interest": "Corretor(a)",
"message": "Tenho 5 anos de experiência no mercado imobiliário...",
"file_name": "curriculo-ana-lima.pdf",
"status": "pending",
"created_at": "2026-04-21T14:35:00"
},
{
"id": 6,
"name": "Carlos Souza",
"email": "carlos@email.com",
"phone": null,
"role_interest": "Estagiário(a)",
"message": "Estudante de Administração em busca do primeiro emprego.",
"file_name": null,
"status": "pending",
"created_at": "2026-04-20T09:12:00"
}
],
"total": 42,
"page": 1,
"per_page": 20,
"pages": 3
}
```
**Schema do item** (`JobApplicationOut`)
| Campo | Tipo | Nullable | Descrição |
|-----------------|------------------|----------|----------------------------------|
| `id` | integer | Não | Identificador único |
| `name` | string | Não | Nome completo do candidato |
| `email` | string | Não | E-mail do candidato |
| `phone` | string \| null | Sim | Telefone (opcional) |
| `role_interest` | string | Não | Cargo de interesse selecionado |
| `message` | string | Não | Mensagem/apresentação |
| `file_name` | string \| null | Sim | Nome do arquivo de currículo |
| `status` | string | Não | Estado: `"pending"` (padrão) |
| `created_at` | string (ISO 8601)| Não | Data/hora do envio |
**Schema de paginação**
| Campo | Tipo | Descrição |
|------------|---------|-------------------------------------|
| `total` | integer | Total de candidaturas no sistema |
| `page` | integer | Página atual |
| `per_page` | integer | Registros retornados nesta página |
| `pages` | integer | Total de páginas |
#### 200 OK — Nenhuma candidatura registrada
```json
{
"items": [],
"total": 0,
"page": 1,
"per_page": 20,
"pages": 0
}
```
#### 401 Unauthorized — Token ausente ou inválido
```json
{
"error": "Token inválido ou ausente"
}
```
#### 403 Forbidden — Usuário autenticado sem permissão de admin
```json
{
"error": "Acesso negado"
}
```
---
## Notas de Implementação
- O endpoint `POST /api/v1/jobs/apply` **não** possui autenticação — qualquer cliente pode submeter.
- O endpoint `GET /api/v1/admin/jobs` usa o decorator `@require_admin` já existente no projeto, que valida o JWT e verifica a flag de administrador.
- O campo `created_at` é serializado pelo Pydantic como ISO 8601 sem timezone (`TIMESTAMP WITHOUT TIME ZONE` no PostgreSQL).
- `per_page` deve ser limitado a 100 no backend para evitar queries excessivamente grandes.
- Campos ausentes no body do POST são tratados pelo Pydantic: obrigatórios geram erro 422, opcionais recebem `None`.