diff --git a/.env.development b/.env.development index 66dca1b..dfc65f4 100644 --- a/.env.development +++ b/.env.development @@ -2,3 +2,5 @@ VITE_KEYCLOAK_URL= VITE_KEYCLOAK_REALM= VITE_KEYCLOAK_CLIENT_ID= VITE_API_URL= +# Ativar mock: copie esta linha para .env.development.local e defina como true +VITE_USE_MOCK=false diff --git a/docs/api-backend.md b/docs/api-backend.md new file mode 100644 index 0000000..67bf6ea --- /dev/null +++ b/docs/api-backend.md @@ -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: ` +- `X-Dominio: ` + +--- + +### 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 ` + +--- + +### 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 | — | diff --git a/src/main.js b/src/main.js index da09e22..92c0c1a 100644 --- a/src/main.js +++ b/src/main.js @@ -21,7 +21,12 @@ async function startApp() { pinia.use(piniaPluginPersistedstate) 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(PrimeVue, primeVueConfig) diff --git a/src/mocks/mockInterceptor.js b/src/mocks/mockInterceptor.js new file mode 100644 index 0000000..48534ec --- /dev/null +++ b/src/mocks/mockInterceptor.js @@ -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 400–800ms 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<>endobj\n2 0 obj<>endobj\n3 0 obj<>endobj\nxref\n0 4\n0000000000 65535 f\n0000000009 00000 n\n0000000068 00000 n\n0000000125 00000 n\ntrailer<>\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.') +}