feat: mock system + documentação de API

- `src/mocks/mockInterceptor.js` — interceptor Axios que substitui chamadas HTTP
  por dados falsos (400–800ms de latência simulada). Cobre todos os endpoints do
  portal: certidão, IPTU, primeiro acesso, credenciamento, painel, débitos,
  certidões, alvarás, pagamentos e dados cadastrais.
- `src/main.js` — carrega mock condicionalmente via `VITE_USE_MOCK=true`; em modo
  mock, pula `bootstrapPrefeitura` e injeta sessão fake no authStore.
- `.env.development` — documenta a variável `VITE_USE_MOCK` como template.
- `docs/api-backend.md` — especificação completa de todos os endpoints necessários
  (request/response, tipos, status possíveis, tabela de implementação).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Gabriel Bezerra 2026-05-18 01:14:00 -03:00
parent f687e650b8
commit 6635030d53
4 changed files with 988 additions and 1 deletions

View File

@ -2,3 +2,5 @@ VITE_KEYCLOAK_URL=
VITE_KEYCLOAK_REALM= VITE_KEYCLOAK_REALM=
VITE_KEYCLOAK_CLIENT_ID= VITE_KEYCLOAK_CLIENT_ID=
VITE_API_URL= VITE_API_URL=
# Ativar mock: copie esta linha para .env.development.local e defina como true
VITE_USE_MOCK=false

699
docs/api-backend.md Normal file
View File

@ -0,0 +1,699 @@
# API Backend — portal-modumfiscal-web
Documentação dos endpoints que o portal consome. Base URL: `https://sistema.modumfiscal.com.br/api/v1`.
**Envelope padrão de resposta (sucesso):**
```json
{
"timestamp": "2025-05-18T10:00:00Z",
"statusCode": 200,
"responseType": "SUCCESS",
"message": "OK",
"data": { }
}
```
**Envelope de erro:**
```json
{
"statusCode": 422,
"message": "Unprocessable Entity",
"internalCode": "119",
"description": "Registro não foi encontrado."
}
```
Respostas binárias (PDF) retornam `Content-Type: application/pdf` direto — sem envelope.
---
## Módulo Público — sem autenticação
Headers obrigatórios em toda requisição:
- `X-Municipio: <id_municipio>`
- `X-Dominio: <subdominio>`
---
### Prefeitura (bootstrap)
#### `GET /publico/prefeitura/{dominio}`
Retorna a configuração visual e institucional da prefeitura.
**Response `data`:**
```json
{
"idMunicipio": 1,
"nomePrefeitura": "Prefeitura Municipal de Tutóia",
"nomeMunicipio": "Tutóia",
"uf": "MA",
"template": "tutoia",
"pathLogo": "/logos/tutoia.png",
"pathBackground": "/backgrounds/tutoia.jpeg",
"corPrimaria": "#F59E0B",
"cnpj": "12.345.678/0001-90",
"site": "https://tutoia.ma.gov.br",
"email": "atendimento@tutoia.ma.gov.br",
"telefone": "(98) 3478-1234"
}
```
> `pathLogo` e `pathBackground` são paths relativos — o frontend resolve para URL absoluta via `resolverUrl()`.
---
### Certidão Pública
#### `GET /publico/certidao/consultar`
Consulta a situação fiscal do contribuinte.
**Query params:**
| Param | Tipo | Obrigatório | Descrição |
|---|---|---|---|
| `documento` | string | sim | CPF (11 dígitos) ou CNPJ (14 dígitos) sem formatação |
**Response `data`:**
```json
{
"situacao": "NEGATIVA",
"nomeContribuinte": "João da Silva Santos"
}
```
`situacao` possíveis: `NEGATIVA` · `POSITIVA` · `POSITIVA_EFEITOS_NEGATIVA`
---
#### `GET /publico/certidao/emitir`
Emite a certidão em PDF.
**Query params:**
| Param | Tipo | Obrigatório | Descrição |
|---|---|---|---|
| `documento` | string | sim | CPF/CNPJ sem formatação |
| `tipoCertidao` | string | sim | `NEGATIVA` · `POSITIVA` · `POSITIVA_EFEITOS_NEGATIVA` |
**Response:** `application/pdf` (binário direto, sem envelope)
---
### IPTU Público
#### `GET /publico/iptu/consultar`
Retorna todos os imóveis e débitos de IPTU do contribuinte ou de uma inscrição específica.
**Query params:**
| Param | Tipo | Obrigatório | Descrição |
|---|---|---|---|
| `documento` | string | condicional | CPF/CNPJ sem formatação (informar `documento` **ou** `inscricao`) |
| `inscricao` | string | condicional | Inscrição imobiliária (ex: `0001.001.0001.001`) |
**Response `data`:**
```json
{
"content": [
{
"inscricaoImobiliaria": "0001.001.0001.001",
"enderecoCompleto": "Rua das Flores, 100 — Centro",
"debitos": [
{
"id": "d1",
"descricao": "IPTU 2025 — Cota 4/10",
"vencimento": "30/04/2025",
"valor": 125.90,
"valorAtualizado": 138.49,
"status": "VENCIDO"
}
]
}
]
}
```
`status` possíveis: `VENCIDO` · `A_VENCER` · `PAGO` · `PARCELADO`
---
#### `GET /publico/iptu/carne`
Emite o carnê completo de IPTU em PDF.
**Query params:**
| Param | Tipo | Obrigatório | Descrição |
|---|---|---|---|
| `inscricao` | string | sim | Inscrição imobiliária |
**Response:** `application/pdf`
---
#### `GET /publico/iptu/boleto`
Emite o boleto avulso de uma parcela.
**Query params:**
| Param | Tipo | Obrigatório | Descrição |
|---|---|---|---|
| `idDebito` | string | sim | ID do débito retornado por `/consultar` |
**Response:** `application/pdf`
---
### Primeiro Acesso
Fluxo de 4 etapas para o contribuinte criar senha de acesso ao portal.
#### `GET /publico/primeiro-acesso/verificar`
Valida se o documento tem cadastro e retorna os canais disponíveis para envio do código.
**Query params:**
| Param | Tipo | Obrigatório |
|---|---|---|
| `documento` | string | sim |
**Response `data`:**
```json
{
"nome": "Maria Aparecida Santos",
"canais": [
{ "tipo": "EMAIL", "valor": "ma***@gmail.com" },
{ "tipo": "WHATSAPP", "valor": "(98) *****-8901" },
{ "tipo": "SMS", "valor": "(98) *****-8901" }
]
}
```
`tipo` possíveis: `EMAIL` · `WHATSAPP` · `SMS`
---
#### `POST /publico/primeiro-acesso/codigo`
Envia o código de 6 dígitos para o canal escolhido.
**Request body:**
```json
{
"documento": "12345678900",
"canal": "EMAIL"
}
```
**Response `data`:**
```json
{ "enviado": true }
```
---
#### `POST /publico/primeiro-acesso/validar`
Valida o código digitado e retorna um token temporário para definição de senha.
**Request body:**
```json
{
"documento": "12345678900",
"codigo": "123456"
}
```
**Response `data`:**
```json
{ "token": "eyJhb..." }
```
Erros: `422` se código inválido ou expirado.
---
#### `POST /publico/primeiro-acesso/senha`
Define a nova senha usando o token temporário.
**Request body:**
```json
{
"token": "eyJhb...",
"senha": "MinhaS3nha!"
}
```
**Response `data`:**
```json
{ "sucesso": true }
```
---
### Credenciamento
Fluxo para novo contribuinte solicitar cadastro no sistema.
#### `GET /publico/credenciamento/verificar`
Verifica se o documento já tem cadastro ou pode ser credenciado.
**Query params:**
| Param | Tipo | Obrigatório |
|---|---|---|
| `documento` | string | sim |
**Response `data`:**
```json
{ "situacao": "APTO" }
```
`situacao` possíveis:
- `APTO` — pode prosseguir com o credenciamento
- `JA_CREDENCIADO` — já tem acesso ao sistema
- `BLOQUEADO` — documento bloqueado (contatar prefeitura)
---
#### `GET /publico/cep/{cep}`
Busca endereço por CEP para auto-preenchimento no formulário de credenciamento.
**Path param:** `cep` — 8 dígitos sem hífen
**Response `data`:**
```json
{
"logradouro": "Rua das Acácias",
"bairro": "Centro",
"localidade": "Tutóia",
"uf": "MA"
}
```
---
#### `POST /publico/credenciamento/solicitar`
Envia a solicitação de credenciamento para análise da prefeitura.
**Request body (Pessoa Física):**
```json
{
"documento": "12345678900",
"tipoPessoa": "FISICA",
"nomeCompleto": "João da Silva Santos",
"dataNascimento": "1990-05-15",
"email": "joao@email.com",
"emailConfirmacao": "joao@email.com",
"telefone": "98991234567",
"whatsapp": true,
"endereco": {
"cep": "65900000",
"logradouro": "Rua das Flores",
"numero": "100",
"complemento": "Apto 201",
"bairro": "Centro",
"cidade": "Tutóia",
"uf": "MA"
}
}
```
**Request body adicional (Pessoa Jurídica):**
```json
{
"tipoPessoa": "JURIDICA",
"razaoSocial": "Empresa LTDA",
"nomeFantasia": "Empresa",
"inscricaoEstadual": "12345678",
"representanteLegal": {
"nomeCompleto": "João da Silva",
"cpf": "12345678900",
"cargo": "Diretor"
}
}
```
**Response `data`:**
```json
{ "protocolo": "CRED-2025-00123" }
```
---
## Módulo Portal — requer autenticação
Header obrigatório além dos de tenant:
- `Authorization: Bearer <jwt_keycloak>`
---
### Painel
#### `GET /contribuinte/painel/resumo`
Dados do card de resumo do dashboard.
**Response `data`:**
```json
{
"totalDebitos": 1250.90,
"certidoesAtivas": 2,
"alvarasAndamento": 2,
"ultimoPagamento": 430.00,
"debitosVencidos": 1
}
```
---
#### `GET /contribuinte/painel/atividades`
Lista das últimas atividades do contribuinte.
**Response `data`:**
```json
{
"content": [
{
"tipo": "PAGAMENTO",
"descricao": "IPTU 2025 — Parcela 3/10 paga",
"data": "15/05/2025"
},
{
"tipo": "CERTIDAO",
"descricao": "Certidão Negativa emitida",
"data": "10/05/2025"
}
]
}
```
`tipo` possíveis: `PAGAMENTO` · `CERTIDAO` · `DEBITO` · `ALVARA` · `CADASTRO`
---
### Débitos
#### `GET /contribuinte/debitos`
Lista os débitos do contribuinte com filtros opcionais.
**Query params:**
| Param | Tipo | Obrigatório | Descrição |
|---|---|---|---|
| `tipo` | string | não | `IPTU` · `ISS` · `TAXA` · `MULTA` |
| `status` | string | não | `VENCIDO` · `A_VENCER` · `PARCELADO` |
**Response `data`:**
```json
{
"content": [
{
"id": "deb1",
"descricao": "IPTU 2025 — Cota 4/10",
"tipo": "IPTU",
"referencia": "ABR/2025",
"vencimento": "30/04/2025",
"valor": 125.90,
"valorAtualizado": 138.49,
"status": "VENCIDO"
}
]
}
```
`valorAtualizado` inclui juros e multa (pode ser igual a `valor` se em dia).
---
#### `GET /contribuinte/debitos/{id}/guia`
Emite a guia de pagamento (boleto/DAM) de um débito específico em PDF.
**Path param:** `id` — ID do débito
**Response:** `application/pdf`
---
### Certidões
#### `GET /contribuinte/certidoes`
Lista todas as certidões emitidas pelo contribuinte.
**Response `data`:**
```json
{
"content": [
{
"id": "cert1",
"tipo": "Certidão Negativa de Débitos",
"numero": "CN-2025-00481",
"dataEmissao": "10/05/2025",
"dataValidade": "07/11/2025",
"status": "ATIVA"
}
]
}
```
`status` possíveis: `ATIVA` · `VENCIDA` · `CANCELADA`
---
#### `GET /contribuinte/certidoes/{id}/pdf`
Reemite uma certidão em PDF.
**Path param:** `id` — ID da certidão
**Response:** `application/pdf`
> Retorna `422` se a certidão estiver com status `CANCELADA`.
---
### Alvarás
#### `GET /contribuinte/alvaras`
Lista os processos de alvará do contribuinte.
**Query params:**
| Param | Tipo | Obrigatório | Descrição |
|---|---|---|---|
| `status` | string | não | Filtro por status do processo |
**Response `data`:**
```json
{
"content": [
{
"id": "alv1",
"tipo": "Alvará de Funcionamento — Comércio Varejista",
"numeroProcesso": "ALV-2025-00342",
"status": "EM_ANALISE",
"ultimaAtualizacao": "14/05/2025",
"etapas": [
{ "nome": "Protocolo", "concluida": true, "atual": false },
{ "nome": "Análise", "concluida": false, "atual": true },
{ "nome": "Vistoria", "concluida": false, "atual": false },
{ "nome": "Emissão", "concluida": false, "atual": false }
]
}
]
}
```
`status` possíveis: `EM_ANALISE` · `AGUARDANDO_DOCUMENTOS` · `DEFERIDO` · `INDEFERIDO` · `CANCELADO`
---
### Pagamentos
#### `GET /contribuinte/pagamentos`
Histórico de pagamentos do contribuinte, filtrável por ano.
**Query params:**
| Param | Tipo | Obrigatório | Descrição |
|---|---|---|---|
| `ano` | integer | não | Ano de referência (padrão: ano atual) |
**Response `data`:**
```json
{
"content": [
{
"id": "pag1",
"descricao": "IPTU 2025 — Cota 3/10",
"referencia": "MAR/2025",
"dataPagamento": "28/03/2025",
"formaPagamento": "PIX",
"valor": 125.90
}
]
}
```
`formaPagamento` possíveis: `BOLETO` · `PIX` · `CARTAO` · `TRANSFERENCIA` · `ESPECIE`
---
#### `GET /contribuinte/pagamentos/{id}/comprovante`
Baixa o comprovante de um pagamento em PDF.
**Path param:** `id` — ID do pagamento
**Response:** `application/pdf`
---
### Dados Cadastrais
#### `GET /contribuinte/dados`
Retorna os dados cadastrais completos do contribuinte autenticado.
**Response `data` (Pessoa Física):**
```json
{
"tipoPessoa": "FISICA",
"documento": "123.456.789-00",
"nomeCompleto": "João da Silva Santos",
"dataNascimento": "15/06/1975",
"email": "joao.santos@email.com",
"telefone": "(98) 99123-4567",
"whatsapp": true,
"endereco": {
"cep": "65900-000",
"logradouro": "Rua das Flores",
"numero": "100",
"complemento": "Apto 201",
"bairro": "Centro",
"cidade": "Tutóia",
"uf": "MA"
}
}
```
**Campos adicionais (Pessoa Jurídica):**
```json
{
"tipoPessoa": "JURIDICA",
"documento": "12.345.678/0001-90",
"razaoSocial": "Empresa LTDA",
"nomeFantasia": "Empresa",
"inscricaoEstadual": "12345678"
}
```
---
#### `PUT /contribuinte/dados/contato`
Atualiza os dados de contato editáveis (email, telefone, WhatsApp).
**Request body:**
```json
{
"email": "novo@email.com",
"telefone": "98991234567",
"whatsapp": true
}
```
`telefone` deve ser enviado **sem formatação** (apenas dígitos).
**Response `data`:**
```json
{ "atualizado": true }
```
---
## Avisos (pendente no backend)
#### `GET /publico/avisos/{dominio}`
Retorna os avisos/banners exibidos no carousel da Home.
> **Status:** endpoint ainda não implementado no backend. O frontend usa dados estáticos hardcoded em `HomeView.vue`.
**Response `data` esperada:**
```json
{
"content": [
{
"id": "av1",
"titulo": "IPTU 2025 — Prazo final",
"descricao": "Últimas parcelas com desconto até 30 de junho.",
"tipo": "ALERTA",
"ativo": true
}
]
}
```
`tipo` sugeridos: `INFO` · `ALERTA` · `URGENTE`
---
## Fluxo de Autenticação
O portal usa **Keycloak PKCE** — nenhuma credencial trafega pelo frontend.
```
1. Usuário informa CPF/CNPJ na Home
2. LoginView monta — exibe doc mascarado + campo senha
3. entrar() → redireciona para Keycloak (PKCE code challenge)
4. Keycloak autentica e redireciona de volta com ?code=
5. authService troca code por access_token + refresh_token
6. authStore.setSession(token, userInfo) persiste sessão
7. router.push({ name: 'painel' })
```
**Realm:** `modumfiscal-dev`
**Client ID:** `portal-modumfiscal-web`
**Grant type:** `authorization_code` com PKCE (S256)
**Token endpoint:** `https://keycloakprod.modumfiscal.com.br/realms/modumfiscal-dev/protocol/openid-connect/token`
O `preferred_username` do JWT é o CPF/CNPJ sem formatação — usado como identificador do contribuinte nas chamadas ao backend.
---
## Status de Implementação dos Endpoints
| Endpoint | Frontend pronto | Backend pronto | Mock |
|---|---|---|---|
| `GET /publico/prefeitura/{dominio}` | ✓ | ✓ | — |
| `GET /publico/certidao/consultar` | ✓ | pendente | ✓ |
| `GET /publico/certidao/emitir` | ✓ | pendente | ✓ |
| `GET /publico/iptu/consultar` | ✓ | pendente | ✓ |
| `GET /publico/iptu/carne` | ✓ | pendente | ✓ |
| `GET /publico/iptu/boleto` | ✓ | pendente | ✓ |
| `GET /publico/primeiro-acesso/verificar` | ✓ | pendente | ✓ |
| `POST /publico/primeiro-acesso/codigo` | ✓ | pendente | ✓ |
| `POST /publico/primeiro-acesso/validar` | ✓ | pendente | ✓ |
| `POST /publico/primeiro-acesso/senha` | ✓ | pendente | ✓ |
| `GET /publico/credenciamento/verificar` | ✓ | pendente | ✓ |
| `GET /publico/cep/{cep}` | ✓ | pendente | ✓ |
| `POST /publico/credenciamento/solicitar` | ✓ | pendente | ✓ |
| `GET /contribuinte/painel/resumo` | ✓ | pendente | ✓ |
| `GET /contribuinte/painel/atividades` | ✓ | pendente | ✓ |
| `GET /contribuinte/debitos` | ✓ | pendente | ✓ |
| `GET /contribuinte/debitos/{id}/guia` | ✓ | pendente | ✓ |
| `GET /contribuinte/certidoes` | ✓ | pendente | ✓ |
| `GET /contribuinte/certidoes/{id}/pdf` | ✓ | pendente | ✓ |
| `GET /contribuinte/alvaras` | ✓ | pendente | ✓ |
| `GET /contribuinte/pagamentos` | ✓ | pendente | ✓ |
| `GET /contribuinte/pagamentos/{id}/comprovante` | ✓ | pendente | ✓ |
| `GET /contribuinte/dados` | ✓ | pendente | ✓ |
| `PUT /contribuinte/dados/contato` | ✓ | pendente | ✓ |
| `GET /publico/avisos/{dominio}` | parcial | pendente | — |

View File

@ -21,7 +21,12 @@ async function startApp() {
pinia.use(piniaPluginPersistedstate) pinia.use(piniaPluginPersistedstate)
app.use(pinia) app.use(pinia)
await bootstrapPrefeitura(pinia) if (import.meta.env.VITE_USE_MOCK === 'true') {
const { setupMocks } = await import('@/mocks/mockInterceptor')
setupMocks(pinia)
} else {
await bootstrapPrefeitura(pinia)
}
app.use(router) app.use(router)
app.use(PrimeVue, primeVueConfig) app.use(PrimeVue, primeVueConfig)

View File

@ -0,0 +1,281 @@
/**
* Mock interceptor para desenvolvimento sem backend.
* Ativado via VITE_USE_MOCK=true em .env.development.local
*
* Técnica: injeta config.adapter em cada request que bate em uma rota mockada.
* O adapter retorna dados falsos diretamente, sem fazer chamada HTTP.
* Simula latência aleatória de 400800ms para comportamento realista.
*/
import apiClient, { apiClientPublico } from '@/config/apiClient'
import { useAuthStore } from '@/stores/authStore'
// ─── Helpers ─────────────────────────────────────────────────────────────────
function envelope(data) {
return {
timestamp: new Date().toISOString(),
statusCode: 200,
responseType: 'SUCCESS',
message: 'OK',
data,
}
}
// PDF mínimo válido — suficiente para o browser abrir/baixar
const FAKE_PDF = '%PDF-1.4\n1 0 obj<</Type/Catalog/Pages 2 0 R>>endobj\n2 0 obj<</Type/Pages/Kids[3 0 R]/Count 1>>endobj\n3 0 obj<</Type/Page/Parent 2 0 R/MediaBox[0 0 612 792]>>endobj\nxref\n0 4\n0000000000 65535 f\n0000000009 00000 n\n0000000068 00000 n\n0000000125 00000 n\ntrailer<</Size 4/Root 1 0 R>>\nstartxref\n240\n%%EOF'
const fakePdf = () => new Blob([FAKE_PDF], { type: 'application/pdf' })
// ─── Rotas mockadas ───────────────────────────────────────────────────────────
const routes = [
// ── Certidão pública ──────────────────────────────────────────────────
{
test: (url, m) => /\/publico\/certidao\/consultar/.test(url) && m === 'get',
data: envelope({ situacao: 'NEGATIVA', nomeContribuinte: 'João da Silva Santos' }),
},
{
test: (url, m) => /\/publico\/certidao\/emitir/.test(url) && m === 'get',
blob: true,
},
// ── IPTU público ──────────────────────────────────────────────────────
{
test: (url, m) => /\/publico\/iptu\/consultar/.test(url) && m === 'get',
data: envelope({
content: [
{
inscricaoImobiliaria: '0001.001.0001.001',
enderecoCompleto: 'Rua das Flores, 100 — Centro',
debitos: [
{ id: 'd1', descricao: 'IPTU 2025 — Cota 4/10', vencimento: '30/04/2025', valor: 125.90, valorAtualizado: 138.49, status: 'VENCIDO' },
{ id: 'd2', descricao: 'IPTU 2025 — Cota 5/10', vencimento: '31/05/2025', valor: 125.90, valorAtualizado: 125.90, status: 'A_VENCER' },
],
},
{
inscricaoImobiliaria: '0001.001.0002.001',
enderecoCompleto: 'Av. Principal, 250 — Bairro Novo',
debitos: [],
},
],
}),
},
{ test: (url, m) => /\/publico\/iptu\/carne/.test(url) && m === 'get', blob: true },
{ test: (url, m) => /\/publico\/iptu\/boleto/.test(url) && m === 'get', blob: true },
// ── Primeiro Acesso ───────────────────────────────────────────────────
{
test: (url, m) => /\/publico\/primeiro-acesso\/verificar/.test(url) && m === 'get',
data: envelope({
nome: 'Maria Aparecida Santos',
canais: [
{ tipo: 'EMAIL', valor: 'ma***@gmail.com' },
{ tipo: 'WHATSAPP', valor: '(98) *****-8901' },
],
}),
},
{
test: (url, m) => /\/publico\/primeiro-acesso\/codigo/.test(url) && m === 'post',
data: envelope({ enviado: true }),
},
{
test: (url, m) => /\/publico\/primeiro-acesso\/validar/.test(url) && m === 'post',
data: envelope({ token: 'mock-reset-token-abc123' }),
},
{
test: (url, m) => /\/publico\/primeiro-acesso\/senha/.test(url) && m === 'post',
data: envelope({ sucesso: true }),
},
// ── Credenciamento ────────────────────────────────────────────────────
{
test: (url, m) => /\/publico\/credenciamento\/verificar/.test(url) && m === 'get',
data: envelope({ situacao: 'APTO' }),
},
{
test: (url, m) => /\/publico\/cep\//.test(url) && m === 'get',
data: envelope({ logradouro: 'Rua das Acácias', bairro: 'Centro', localidade: 'Tutóia', uf: 'MA' }),
},
{
test: (url, m) => /\/publico\/credenciamento\/solicitar/.test(url) && m === 'post',
data: envelope({ protocolo: 'CRED-2025-00123' }),
},
// ── Portal — Painel ───────────────────────────────────────────────────
{
test: (url, m) => /\/contribuinte\/painel\/resumo/.test(url) && m === 'get',
data: envelope({
totalDebitos: 1250.90,
certidoesAtivas: 2,
alvarasAndamento: 2,
ultimoPagamento: 430.00,
debitosVencidos: 1,
}),
},
{
test: (url, m) => /\/contribuinte\/painel\/atividades/.test(url) && m === 'get',
data: envelope({
content: [
{ tipo: 'PAGAMENTO', descricao: 'IPTU 2025 — Parcela 3/10 paga', data: '15/05/2025' },
{ tipo: 'CERTIDAO', descricao: 'Certidão Negativa emitida', data: '10/05/2025' },
{ tipo: 'DEBITO', descricao: 'ISS 1º Trimestre 2025 lançado', data: '01/04/2025' },
{ tipo: 'ALVARA', descricao: 'Alvará de Funcionamento — documento solicitado', data: '28/03/2025' },
],
}),
},
// ── Portal — Débitos ──────────────────────────────────────────────────
{
test: (url, m) => /\/contribuinte\/debitos$/.test(url) && m === 'get',
data: envelope({
content: [
{ id: 'deb1', descricao: 'IPTU 2025 — Cota 4/10', tipo: 'IPTU', referencia: 'ABR/2025', vencimento: '30/04/2025', valor: 125.90, valorAtualizado: 138.49, status: 'VENCIDO' },
{ id: 'deb2', descricao: 'IPTU 2025 — Cota 5/10', tipo: 'IPTU', referencia: 'MAI/2025', vencimento: '31/05/2025', valor: 125.90, valorAtualizado: 125.90, status: 'A_VENCER' },
{ id: 'deb3', descricao: 'ISS — 1º Trimestre 2025', tipo: 'ISS', referencia: '1T/2025', vencimento: '28/02/2025', valor: 520.00, valorAtualizado: 572.00, status: 'VENCIDO' },
{ id: 'deb4', descricao: 'Taxa de Licença 2025', tipo: 'TAXA', referencia: '2025', vencimento: '30/06/2025', valor: 180.00, valorAtualizado: 180.00, status: 'A_VENCER' },
{ id: 'deb5', descricao: 'IPTU 2024 — Parcelamento', tipo: 'IPTU', referencia: '2024', vencimento: '15/06/2025', valor: 307.10, valorAtualizado: 307.10, status: 'PARCELADO' },
],
}),
},
{ test: (url, m) => /\/contribuinte\/debitos\/.+\/guia/.test(url) && m === 'get', blob: true },
// ── Portal — Certidões ────────────────────────────────────────────────
{
test: (url, m) => /\/contribuinte\/certidoes$/.test(url) && m === 'get',
data: envelope({
content: [
{ id: 'cert1', tipo: 'Certidão Negativa de Débitos', numero: 'CN-2025-00481', dataEmissao: '10/05/2025', dataValidade: '07/11/2025', status: 'ATIVA' },
{ id: 'cert2', tipo: 'Positiva com Efeitos de Negativa', numero: 'CPN-2025-00219', dataEmissao: '03/03/2025', dataValidade: '29/08/2025', status: 'ATIVA' },
{ id: 'cert3', tipo: 'Certidão Negativa de Débitos', numero: 'CN-2024-01102', dataEmissao: '15/10/2024', dataValidade: '13/04/2025', status: 'VENCIDA' },
],
}),
},
{ test: (url, m) => /\/contribuinte\/certidoes\/.+\/pdf/.test(url) && m === 'get', blob: true },
// ── Portal — Alvarás ──────────────────────────────────────────────────
{
test: (url, m) => /\/contribuinte\/alvaras/.test(url) && m === 'get',
data: envelope({
content: [
{
id: 'alv1', tipo: 'Alvará de Funcionamento — Comércio Varejista',
numeroProcesso: 'ALV-2025-00342', status: 'EM_ANALISE', ultimaAtualizacao: '14/05/2025',
etapas: [
{ nome: 'Protocolo', concluida: true, atual: false },
{ nome: 'Análise', concluida: false, atual: true },
{ nome: 'Vistoria', concluida: false, atual: false },
{ nome: 'Emissão', concluida: false, atual: false },
],
},
{
id: 'alv2', tipo: 'Alvará de Construção — Reforma Residencial',
numeroProcesso: 'ALV-2024-00891', status: 'AGUARDANDO_DOCUMENTOS', ultimaAtualizacao: '02/04/2025',
etapas: [
{ nome: 'Protocolo', concluida: true, atual: false },
{ nome: 'Docs', concluida: false, atual: true },
{ nome: 'Análise', concluida: false, atual: false },
{ nome: 'Emissão', concluida: false, atual: false },
],
},
{
id: 'alv3', tipo: 'Alvará de Funcionamento — Bares e Restaurantes',
numeroProcesso: 'ALV-2023-00114', status: 'DEFERIDO', ultimaAtualizacao: '10/01/2024',
etapas: [
{ nome: 'Protocolo', concluida: true, atual: false },
{ nome: 'Análise', concluida: true, atual: false },
{ nome: 'Vistoria', concluida: true, atual: false },
{ nome: 'Emissão', concluida: true, atual: false },
],
},
],
}),
},
// ── Portal — Pagamentos ───────────────────────────────────────────────
{
test: (url, m) => /\/contribuinte\/pagamentos/.test(url) && m === 'get',
data: envelope({
content: [
{ id: 'pag1', descricao: 'IPTU 2025 — Cota 3/10', referencia: 'MAR/2025', dataPagamento: '28/03/2025', formaPagamento: 'PIX', valor: 125.90 },
{ id: 'pag2', descricao: 'IPTU 2025 — Cota 2/10', referencia: 'FEV/2025', dataPagamento: '27/02/2025', formaPagamento: 'BOLETO', valor: 125.90 },
{ id: 'pag3', descricao: 'ISS — 4º Trimestre 2024', referencia: '4T/2024', dataPagamento: '15/01/2025', formaPagamento: 'BOLETO', valor: 480.00 },
{ id: 'pag4', descricao: 'Taxa Coleta Lixo 2024', referencia: '2024', dataPagamento: '10/12/2024', formaPagamento: 'CARTAO', valor: 98.50 },
{ id: 'pag5', descricao: 'IPTU 2025 — Cota 1/10', referencia: 'JAN/2025', dataPagamento: '30/01/2025', formaPagamento: 'TRANSFERENCIA', valor: 125.90 },
],
}),
},
{ test: (url, m) => /\/contribuinte\/pagamentos\/.+\/comprovante/.test(url) && m === 'get', blob: true },
// ── Portal — Dados Cadastrais ─────────────────────────────────────────
{
test: (url, m) => /\/contribuinte\/dados$/.test(url) && m === 'get',
data: envelope({
tipoPessoa: 'FISICA',
documento: '123.456.789-00',
nomeCompleto: 'João da Silva Santos',
dataNascimento: '15/06/1975',
email: 'joao.santos@email.com',
telefone: '(98) 99123-4567',
whatsapp: true,
endereco: {
cep: '65900-000',
logradouro: 'Rua das Flores',
numero: '100',
complemento: 'Apto 201',
bairro: 'Centro',
cidade: 'Tutóia',
uf: 'MA',
},
}),
},
{
test: (url, m) => /\/contribuinte\/dados\/contato/.test(url) && m === 'put',
data: envelope({ atualizado: true }),
},
]
// ─── Engine ───────────────────────────────────────────────────────────────────
function buildAdapter(route) {
return (config) => {
const ms = 400 + Math.random() * 400
return new Promise((resolve) =>
setTimeout(() => resolve({
data: route.blob ? fakePdf() : route.data,
status: 200,
statusText: 'OK',
headers: { 'content-type': route.blob ? 'application/pdf' : 'application/json' },
config,
request: {},
}), ms),
)
}
}
function applyInterceptor(client) {
client.interceptors.request.use((config) => {
const url = config.url ?? ''
const method = (config.method ?? 'get').toLowerCase()
const route = routes.find((r) => r.test(url, method))
if (route) config.adapter = buildAdapter(route)
return config
})
}
// ─── Entry point ─────────────────────────────────────────────────────────────
export function setupMocks(pinia) {
// Sessão fake para acessar rotas autenticadas do /portal/*
const auth = useAuthStore(pinia)
auth.setSession('mock-dev-token', {
name: 'João da Silva',
preferred_username: '12345678900',
email: 'joao@mock.dev',
})
applyInterceptor(apiClientPublico)
applyInterceptor(apiClient)
console.info('[mock] Interceptor ativo — todas as chamadas à API estão sendo mockadas.')
}