refactor(alvara): remover módulo de alvarás do portal
All checks were successful
Dev Build & Deploy Portal / build-deploy (push) Successful in 2m44s

Remove página, navegação e chamadas de API de alvarás do portal
do contribuinte.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Joao Guilherme de Mesquita Mendes 2026-05-21 20:30:46 -03:00
parent 222b098a35
commit 94d49e73a2
5 changed files with 2 additions and 223 deletions

View File

@ -15,7 +15,6 @@ const navItems = [
{ path: '/portal/debitos', label: 'Débitos', icon: 'pi-receipt' },
{ path: '/portal/taxas', label: 'Taxas', icon: 'pi-file-export' },
{ path: '/portal/certidoes', label: 'Certidões', icon: 'pi-file-check' },
{ path: '/portal/alvaras', label: 'Alvarás', icon: 'pi-briefcase' },
{ path: '/portal/pagamentos', label: 'Pagamentos', icon: 'pi-credit-card' },
{ path: '/portal/dados', label: 'Dados Cadastrais', icon: 'pi-user' },
]

View File

@ -121,7 +121,6 @@ const servicosPublicos = [
const servicosAutenticados = [
{ icon: 'pi-receipt', titulo: 'Débitos e Guias', descricao: 'Emita guias de pagamento de todos os seus tributos.', to: '/portal/debitos' },
{ icon: 'pi-file-edit', titulo: 'Certidões Ativas', descricao: 'Acesse e reemita certidões emitidas anteriormente.', to: '/portal/certidoes' },
{ icon: 'pi-briefcase', titulo: 'Alvarás', descricao: 'Acompanhe processos e solicite novos alvarás.', to: '/portal/alvaras' },
{ icon: 'pi-credit-card', titulo: 'Pagamentos', descricao: 'Histórico completo com comprovantes para download.', to: '/portal/pagamentos' },
{ icon: 'pi-user', titulo: 'Dados Cadastrais', descricao: 'Visualize e mantenha seus dados sempre atualizados.', to: '/portal/dados' },
]

View File

@ -1,195 +0,0 @@
<script setup>
import { ref, onMounted } from 'vue'
import { portalService } from '@/services/portalService'
definePageMeta({
layout: 'portal',
middleware: 'auth',
})
// MOCKS APRESENTAÇÃO remover antes do deploy
const MOCK_ATIVO = true
const ALVARAS_MOCK = [
{
id: 1,
tipo: 'Alvará de Funcionamento',
numero: 'ALV-2024/0051',
status: 'EM_ANALISE',
etapas: [
{ nome: 'Protocolo', concluida: true, atual: false },
{ nome: 'Pagamento', concluida: true, atual: false },
{ nome: 'Análise', concluida: false, atual: true },
{ nome: 'Emissão', concluida: false, atual: false },
],
},
{
id: 2,
tipo: 'Licença Sanitária',
numero: 'ALV-2023/0198',
status: 'DEFERIDO',
etapas: [
{ nome: 'Protocolo', concluida: true, atual: false },
{ nome: 'Pagamento', concluida: true, atual: false },
{ nome: 'Análise', concluida: true, atual: false },
{ nome: 'Emissão', concluida: true, atual: false },
],
},
]
//
const alvaras = ref([])
const carregando = ref(true)
const mensagemErro = ref('')
const filtroStatus = ref(null)
const situacaoMap = {
EM_ANALISE: 0,
AGUARDANDO_DOCUMENTOS: 1,
DEFERIDO: 2,
INDEFERIDO: 3,
CANCELADO: 4,
}
const statusOptions = [
{ value: 'EM_ANALISE', label: 'Em análise' },
{ value: 'AGUARDANDO_DOCUMENTOS', label: 'Aguardando documentos' },
{ value: 'DEFERIDO', label: 'Deferido' },
{ value: 'INDEFERIDO', label: 'Indeferido' },
{ value: 'CANCELADO', label: 'Cancelado' },
]
const statusMap = {
EM_ANALISE: { label: 'Em análise', classe: 'bg-blue-100 dark:bg-blue-900/30 text-blue-700 dark:text-blue-400', icone: 'pi-clock' },
AGUARDANDO_DOCUMENTOS: { label: 'Aguard. docs', classe: 'bg-amber-100 dark:bg-amber-900/30 text-amber-700 dark:text-amber-400', icone: 'pi-file-edit' },
DEFERIDO: { label: 'Deferido', classe: 'bg-emerald-100 dark:bg-emerald-900/30 text-emerald-700 dark:text-emerald-400', icone: 'pi-check-circle' },
INDEFERIDO: { label: 'Indeferido', classe: 'bg-red-100 dark:bg-red-900/30 text-red-700 dark:text-red-400', icone: 'pi-times-circle' },
CANCELADO: { label: 'Cancelado', classe: 'bg-slate-100 dark:bg-slate-700 text-slate-500 dark:text-slate-400', icone: 'pi-ban' },
}
onMounted(() => carregar())
async function carregar() {
carregando.value = true
mensagemErro.value = ''
try {
const params = filtroStatus.value
? { situacao: situacaoMap[filtroStatus.value] }
: {}
const res = await portalService.getAlvaras(params)
alvaras.value = res.data ?? []
if (MOCK_ATIVO && alvaras.value.length === 0) {
const mockFiltrado = filtroStatus.value
? ALVARAS_MOCK.filter(a => a.status === filtroStatus.value)
: ALVARAS_MOCK
alvaras.value = mockFiltrado
}
} catch (e) {
mensagemErro.value = e?.data?.description ?? 'Não foi possível carregar os alvarás.'
if (MOCK_ATIVO) {
const mockFiltrado = filtroStatus.value
? ALVARAS_MOCK.filter(a => a.status === filtroStatus.value)
: ALVARAS_MOCK
alvaras.value = mockFiltrado
mensagemErro.value = ''
}
} finally {
carregando.value = false
}
}
</script>
<template>
<div class="space-y-6">
<div>
<h1 class="text-2xl font-bold text-slate-800 dark:text-slate-100">Alvarás</h1>
<p class="text-sm text-slate-500 dark:text-slate-400 mt-0.5">Acompanhe o andamento dos seus processos de alvará.</p>
</div>
<div class="flex gap-3 flex-wrap">
<button
:class="['px-4 py-2 rounded-lg text-sm font-semibold border transition-colors', filtroStatus === null ? 'bg-primary text-white border-primary' : 'bg-white dark:bg-slate-800 text-slate-600 dark:text-slate-300 border-slate-200 dark:border-slate-700 hover:border-slate-300']"
@click="filtroStatus = null; carregar()"
>
Todos
</button>
<button
v-for="opt in statusOptions"
:key="opt.value"
:class="['px-4 py-2 rounded-lg text-sm font-semibold border transition-colors', filtroStatus === opt.value ? 'bg-primary text-white border-primary' : 'bg-white dark:bg-slate-800 text-slate-600 dark:text-slate-300 border-slate-200 dark:border-slate-700 hover:border-slate-300']"
@click="filtroStatus = opt.value; carregar()"
>
{{ opt.label }}
</button>
</div>
<div class="bg-white dark:bg-slate-800 rounded-xl border border-slate-200 dark:border-slate-700 overflow-hidden">
<div v-if="carregando" class="divide-y divide-slate-100 dark:divide-slate-700">
<div v-for="i in 3" :key="i" class="p-5 space-y-3">
<div class="flex items-center gap-3">
<div class="h-4 bg-slate-200 dark:bg-slate-700 rounded animate-pulse w-1/3" />
<div class="h-6 bg-slate-200 dark:bg-slate-700 rounded-full animate-pulse w-24 ml-auto" />
</div>
<div class="h-3 bg-slate-200 dark:bg-slate-700 rounded animate-pulse w-2/3" />
<div class="h-2.5 bg-slate-200 dark:bg-slate-700 rounded animate-pulse w-1/4" />
</div>
</div>
<div v-else-if="mensagemErro" class="p-8 text-center">
<p class="text-sm text-slate-600 dark:text-slate-300">{{ mensagemErro }}</p>
<Button label="Tentar novamente" severity="secondary" size="small" class="mt-4" @click="carregar" />
</div>
<div v-else-if="alvaras.length === 0" class="p-12 text-center">
<i class="pi pi-briefcase text-slate-300 dark:text-slate-600 text-4xl mb-3 block" aria-hidden="true" />
<p class="font-semibold text-slate-700 dark:text-slate-200">Nenhum alvará encontrado</p>
<p class="text-sm text-slate-400 dark:text-slate-500 mt-1">Solicitações de alvará aparecem aqui após o protocolo.</p>
</div>
<div v-else class="divide-y divide-slate-100 dark:divide-slate-700">
<div
v-for="alv in alvaras"
:key="alv.id"
class="p-5 hover:bg-slate-50 dark:hover:bg-slate-700/30 transition-colors"
>
<div class="flex items-start justify-between gap-4 mb-2">
<div>
<p class="font-semibold text-slate-800 dark:text-slate-100 text-sm">{{ alv.tipo }}</p>
<p class="text-xs text-slate-400 dark:text-slate-500 mt-0.5">Processo {{ alv.numero }}</p>
</div>
<div class="flex items-center gap-2 flex-shrink-0">
<i :class="['pi', statusMap[alv.status]?.icone ?? 'pi-circle', 'text-xs', statusMap[alv.status]?.classe?.split(' ').find(c => c.startsWith('text'))]" aria-hidden="true" />
<span :class="['text-xs font-semibold px-2 py-1 rounded-full whitespace-nowrap', statusMap[alv.status]?.classe ?? 'bg-slate-100 text-slate-500']">
{{ statusMap[alv.status]?.label ?? alv.status }}
</span>
</div>
</div>
<div v-if="alv.etapas?.length" class="mt-4 flex items-center gap-1 overflow-x-auto pb-1">
<template v-for="(etapa, idx) in alv.etapas" :key="idx">
<div class="flex flex-col items-center gap-1 min-w-[72px]">
<div
class="w-6 h-6 rounded-full flex items-center justify-center text-xs"
:class="etapa.concluida
? 'bg-primary text-white'
: etapa.atual
? 'bg-primary/20 dark:bg-primary/30 text-primary border-2 border-primary'
: 'bg-slate-100 dark:bg-slate-700 text-slate-400'"
>
<i v-if="etapa.concluida" class="pi pi-check text-xs" aria-hidden="true" />
<span v-else class="text-xs font-bold">{{ idx + 1 }}</span>
</div>
<span class="text-xs text-slate-500 dark:text-slate-400 text-center leading-tight w-16">{{ etapa.nome }}</span>
</div>
<div
v-if="idx < alv.etapas.length - 1"
class="flex-1 h-px min-w-[12px]"
:class="alv.etapas[idx + 1].concluida || alv.etapas[idx + 1].atual ? 'bg-primary/40' : 'bg-slate-200 dark:bg-slate-700'"
/>
</template>
</div>
</div>
</div>
</div>
</div>
</template>

View File

@ -18,7 +18,6 @@ const MOCK_ATIVO = true
const RESUMO_MOCK = {
totalDebitos: 1250.90,
certidoesAtivas: 2,
alvarasAndamento: 2,
ultimoPagamento: '2025-05-15',
valorUltimoPagamento: 430.00,
debitosVencidos: 1,
@ -27,7 +26,6 @@ const ATIVIDADES_MOCK = [
{ 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: 'Guia IPTU 2025 — Cota 4 emitida', data: '02/05/2025' },
{ tipo: 'ALVARA', descricao: 'Alvará de funcionamento em análise', data: '28/04/2025' },
{ tipo: 'CADASTRO', descricao: 'E-mail cadastral atualizado', data: '20/04/2025' },
]
//
@ -65,7 +63,7 @@ const acesRapidos = [
{ icon: 'pi-receipt', label: 'Emitir Guia', to: '/portal/debitos', cor: 'text-primary' },
{ icon: 'pi-file-export', label: 'Emitir Taxa', to: '/portal/taxas/emitir', cor: 'text-sky-600 dark:text-sky-400' },
{ icon: 'pi-file-check', label: 'Nova Certidão', to: '/portal/certidoes', cor: 'text-emerald-600 dark:text-emerald-400' },
{ icon: 'pi-briefcase', label: 'Acompanhar Alvará', to: '/portal/alvaras', cor: 'text-amber-600 dark:text-amber-400' },
{ icon: 'pi-credit-card', label: 'Pagamentos', to: '/portal/pagamentos', cor: 'text-amber-600 dark:text-amber-400' },
{ icon: 'pi-user', label: 'Meus Dados', to: '/portal/dados', cor: 'text-violet-600 dark:text-violet-400' },
]
@ -82,7 +80,6 @@ function formatarData(data) {
const iconeAtividade = {
DEBITO: 'pi-receipt',
CERTIDAO: 'pi-file-check',
ALVARA: 'pi-briefcase',
PAGAMENTO: 'pi-credit-card',
CADASTRO: 'pi-user',
}
@ -109,7 +106,7 @@ const iconeAtividade = {
/>
</div>
<div class="grid grid-cols-2 lg:grid-cols-4 gap-4">
<div class="grid grid-cols-2 lg:grid-cols-3 gap-4">
<div class="bg-white dark:bg-slate-800 rounded-xl border border-slate-200 dark:border-slate-700 p-5 space-y-3">
<div class="w-10 h-10 bg-red-50 dark:bg-red-900/20 rounded-lg flex items-center justify-center">
@ -137,19 +134,6 @@ const iconeAtividade = {
</div>
</div>
<div class="bg-white dark:bg-slate-800 rounded-xl border border-slate-200 dark:border-slate-700 p-5 space-y-3">
<div class="w-10 h-10 bg-amber-50 dark:bg-amber-900/20 rounded-lg flex items-center justify-center">
<i class="pi pi-briefcase text-amber-600 dark:text-amber-400 text-sm" aria-hidden="true" />
</div>
<div>
<p class="text-2xl font-bold text-slate-800 dark:text-slate-100">
<span v-if="carregando" class="inline-block w-8 h-7 bg-slate-200 dark:bg-slate-700 rounded animate-pulse" />
<template v-else>{{ resumo?.alvarasAndamento ?? 0 }}</template>
</p>
<p class="text-xs text-slate-500 dark:text-slate-400 mt-0.5">Alvarás em andamento</p>
</div>
</div>
<div class="bg-white dark:bg-slate-800 rounded-xl border border-slate-200 dark:border-slate-700 p-5 space-y-3">
<div class="w-10 h-10 bg-primary/10 dark:bg-primary/20 rounded-lg flex items-center justify-center">
<i class="pi pi-credit-card text-primary text-sm" aria-hidden="true" />

View File

@ -48,14 +48,6 @@ export const portalService = {
})
},
// ─── Alvarás ─────────────────────────────────────────────────────────────
getAlvaras(params = {}) {
return $fetch(proxyUrl('/contribuinte/alvaras'), {
headers: FETCH_HEADERS,
query: params,
})
},
// ─── Pagamentos ──────────────────────────────────────────────────────────
getPagamentos(params = {}) {
return $fetch(proxyUrl('/contribuinte/pagamentos'), {