developer #3
16
src/services/certidaoService.js
Normal file
16
src/services/certidaoService.js
Normal file
@ -0,0 +1,16 @@
|
||||
import { apiClientPublico } from '@/config/apiClient'
|
||||
|
||||
export const certidaoService = {
|
||||
consultar(documento) {
|
||||
return apiClientPublico.get('/publico/certidao/consultar', {
|
||||
params: { documento },
|
||||
})
|
||||
},
|
||||
|
||||
emitir(documento, tipoCertidao) {
|
||||
return apiClientPublico.get('/publico/certidao/emitir', {
|
||||
params: { documento, tipoCertidao },
|
||||
responseType: 'blob',
|
||||
})
|
||||
},
|
||||
}
|
||||
17
src/services/credenciamentoService.js
Normal file
17
src/services/credenciamentoService.js
Normal file
@ -0,0 +1,17 @@
|
||||
import { apiClientPublico } from '@/config/apiClient'
|
||||
|
||||
export const credenciamentoService = {
|
||||
verificarDocumento(documento) {
|
||||
return apiClientPublico.get('/publico/credenciamento/verificar', {
|
||||
params: { documento },
|
||||
})
|
||||
},
|
||||
|
||||
buscarCep(cep) {
|
||||
return apiClientPublico.get(`/publico/cep/${cep}`)
|
||||
},
|
||||
|
||||
solicitar(payload) {
|
||||
return apiClientPublico.post('/publico/credenciamento/solicitar', payload)
|
||||
},
|
||||
}
|
||||
28
src/services/iptuService.js
Normal file
28
src/services/iptuService.js
Normal file
@ -0,0 +1,28 @@
|
||||
import { apiClientPublico } from '@/config/apiClient'
|
||||
|
||||
export const iptuService = {
|
||||
consultarPorDocumento(documento) {
|
||||
return apiClientPublico.get('/publico/iptu/consultar', {
|
||||
params: { documento },
|
||||
})
|
||||
},
|
||||
|
||||
consultarPorInscricao(inscricao) {
|
||||
return apiClientPublico.get('/publico/iptu/consultar', {
|
||||
params: { inscricaoImobiliaria: inscricao },
|
||||
})
|
||||
},
|
||||
|
||||
emitirCarne(inscricao, exercicio) {
|
||||
return apiClientPublico.get('/publico/iptu/carne', {
|
||||
params: { inscricaoImobiliaria: inscricao, exercicio },
|
||||
responseType: 'blob',
|
||||
})
|
||||
},
|
||||
|
||||
emitirBoleto(idDebito) {
|
||||
return apiClientPublico.get(`/publico/iptu/boleto/${idDebito}`, {
|
||||
responseType: 'blob',
|
||||
})
|
||||
},
|
||||
}
|
||||
21
src/services/primeiroAcessoService.js
Normal file
21
src/services/primeiroAcessoService.js
Normal file
@ -0,0 +1,21 @@
|
||||
import { apiClientPublico } from '@/config/apiClient'
|
||||
|
||||
export const primeiroAcessoService = {
|
||||
verificarDocumento(documento) {
|
||||
return apiClientPublico.get('/publico/primeiro-acesso/verificar', {
|
||||
params: { documento },
|
||||
})
|
||||
},
|
||||
|
||||
solicitarCodigo(documento, canal) {
|
||||
return apiClientPublico.post('/publico/primeiro-acesso/codigo', { documento, canal })
|
||||
},
|
||||
|
||||
validarCodigo(documento, codigo) {
|
||||
return apiClientPublico.post('/publico/primeiro-acesso/validar', { documento, codigo })
|
||||
},
|
||||
|
||||
definirSenha(token, senha) {
|
||||
return apiClientPublico.post('/publico/primeiro-acesso/senha', { token, senha })
|
||||
},
|
||||
}
|
||||
@ -1,17 +1,208 @@
|
||||
<script setup>
|
||||
import { ref, computed } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { certidaoService } from '@/services/certidaoService'
|
||||
import DocumentoInput from '@/components/auth/DocumentoInput.vue'
|
||||
|
||||
const router = useRouter()
|
||||
|
||||
const documento = ref('')
|
||||
const tipoCertidao = ref('negativa')
|
||||
const etapa = ref('formulario') // formulario | resultado | erro
|
||||
const carregandoConsulta = ref(false)
|
||||
const carregandoEmissao = ref(false)
|
||||
const resultado = ref(null)
|
||||
const mensagemErro = ref('')
|
||||
|
||||
const tiposCertidao = [
|
||||
{ value: 'negativa', label: 'Certidão Negativa', descricao: 'Confirma que não há débitos pendentes.' },
|
||||
{ value: 'positiva_efeitos_negativa', label: 'Positiva com Efeitos de Negativa', descricao: 'Débitos com parcelamento em dia ou com exigibilidade suspensa.' },
|
||||
{ value: 'positiva', label: 'Certidão Positiva', descricao: 'Confirma a existência de débitos.' },
|
||||
]
|
||||
|
||||
const docValido = computed(() => {
|
||||
const d = documento.value.replace(/\D/g, '')
|
||||
return d.length === 11 || d.length === 14
|
||||
})
|
||||
|
||||
async function consultar() {
|
||||
if (!docValido.value) return
|
||||
carregandoConsulta.value = true
|
||||
mensagemErro.value = ''
|
||||
try {
|
||||
const { data } = await certidaoService.consultar(documento.value)
|
||||
resultado.value = data.data
|
||||
etapa.value = 'resultado'
|
||||
} catch (e) {
|
||||
mensagemErro.value = e.response?.data?.description ?? 'Não foi possível consultar a situação fiscal. Tente novamente.'
|
||||
} finally {
|
||||
carregandoConsulta.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function emitir() {
|
||||
carregandoEmissao.value = true
|
||||
try {
|
||||
const { data } = await certidaoService.emitir(documento.value, tipoCertidao.value)
|
||||
const url = URL.createObjectURL(new Blob([data], { type: 'application/pdf' }))
|
||||
const a = document.createElement('a')
|
||||
a.href = url
|
||||
a.download = `certidao-${tipoCertidao.value}-${documento.value}.pdf`
|
||||
a.click()
|
||||
URL.revokeObjectURL(url)
|
||||
} catch {
|
||||
mensagemErro.value = 'Erro ao gerar o PDF. Tente novamente.'
|
||||
} finally {
|
||||
carregandoEmissao.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function reiniciar() {
|
||||
documento.value = ''
|
||||
resultado.value = null
|
||||
mensagemErro.value = ''
|
||||
etapa.value = 'formulario'
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="max-w-3xl mx-auto px-4 py-12 text-center">
|
||||
<div class="w-16 h-16 bg-blue-100 rounded-2xl flex items-center justify-center mx-auto mb-6">
|
||||
<i class="pi pi-file-check text-blue-700 text-2xl" />
|
||||
<div class="max-w-2xl mx-auto px-4 sm:px-6 py-12">
|
||||
|
||||
<!-- Voltar -->
|
||||
<button
|
||||
class="inline-flex items-center gap-2 text-sm text-slate-500 dark:text-slate-400 hover:text-slate-800 dark:hover:text-slate-200 transition-colors mb-8 py-1"
|
||||
@click="router.push({ name: 'servicos' })"
|
||||
>
|
||||
<i class="pi pi-arrow-left text-xs" aria-hidden="true" />
|
||||
Voltar aos serviços
|
||||
</button>
|
||||
|
||||
<!-- Cabeçalho -->
|
||||
<div class="flex items-center gap-4 mb-8">
|
||||
<div class="w-12 h-12 bg-primary/10 dark:bg-primary/20 rounded-xl flex items-center justify-center flex-shrink-0">
|
||||
<i class="pi pi-file-check text-primary text-xl" aria-hidden="true" />
|
||||
</div>
|
||||
<h1 class="text-2xl font-bold text-slate-800 mb-3">Emissão de Certidão</h1>
|
||||
<p class="text-slate-500 mb-8">
|
||||
Consulta de situação fiscal e emissão de certidão em PDF — a ser implementado.
|
||||
<div>
|
||||
<h1 class="text-2xl font-bold text-slate-800 dark:text-slate-100">Emissão de Certidão</h1>
|
||||
<p class="text-sm text-slate-500 dark:text-slate-400 mt-0.5">Consulte sua situação fiscal e emita a certidão em PDF.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ── FORMULÁRIO ─────────────────────────────────────────────── -->
|
||||
<div v-if="etapa === 'formulario'" class="bg-white dark:bg-slate-800 rounded-2xl border border-slate-200 dark:border-slate-700 p-8 space-y-6">
|
||||
|
||||
<div>
|
||||
<label class="block text-sm font-semibold text-slate-700 dark:text-slate-200 mb-1.5">
|
||||
CPF ou CNPJ do contribuinte
|
||||
</label>
|
||||
<DocumentoInput v-model="documento" @keyup.enter="consultar" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<p class="text-sm font-semibold text-slate-700 dark:text-slate-200 mb-3">Tipo de certidão</p>
|
||||
<div class="space-y-2">
|
||||
<label
|
||||
v-for="tipo in tiposCertidao"
|
||||
:key="tipo.value"
|
||||
class="flex items-start gap-3 p-4 rounded-xl border cursor-pointer transition-colors"
|
||||
:class="tipoCertidao === tipo.value
|
||||
? 'border-primary bg-primary/5 dark:bg-primary/10'
|
||||
: 'border-slate-200 dark:border-slate-600 hover:border-slate-300 dark:hover:border-slate-500'"
|
||||
>
|
||||
<RadioButton v-model="tipoCertidao" :value="tipo.value" :input-id="tipo.value" class="mt-0.5 flex-shrink-0" />
|
||||
<div>
|
||||
<p class="text-sm font-semibold text-slate-800 dark:text-slate-100">{{ tipo.label }}</p>
|
||||
<p class="text-xs text-slate-500 dark:text-slate-400 mt-0.5">{{ tipo.descricao }}</p>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p v-if="mensagemErro" role="alert" class="text-sm text-red-600 dark:text-red-400 flex items-center gap-1.5">
|
||||
<i class="pi pi-exclamation-circle" aria-hidden="true" />
|
||||
{{ mensagemErro }}
|
||||
</p>
|
||||
<RouterLink :to="{ name: 'servicos' }">
|
||||
<Button label="Voltar aos serviços" severity="secondary" icon="pi pi-arrow-left" outlined />
|
||||
</RouterLink>
|
||||
|
||||
<Button
|
||||
label="Consultar situação fiscal"
|
||||
icon="pi pi-search"
|
||||
class="w-full"
|
||||
size="large"
|
||||
:loading="carregandoConsulta"
|
||||
:disabled="!docValido"
|
||||
@click="consultar"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- ── RESULTADO ──────────────────────────────────────────────── -->
|
||||
<div v-else-if="etapa === 'resultado'" class="space-y-4">
|
||||
|
||||
<!-- Status fiscal -->
|
||||
<div
|
||||
class="bg-white dark:bg-slate-800 rounded-2xl border p-6"
|
||||
:class="resultado?.situacao === 'NEGATIVA'
|
||||
? 'border-emerald-200 dark:border-emerald-700/50'
|
||||
: 'border-amber-200 dark:border-amber-700/50'"
|
||||
>
|
||||
<div class="flex items-center gap-4">
|
||||
<div
|
||||
class="w-12 h-12 rounded-xl flex items-center justify-center flex-shrink-0"
|
||||
:class="resultado?.situacao === 'NEGATIVA'
|
||||
? 'bg-emerald-100 dark:bg-emerald-900/30'
|
||||
: 'bg-amber-100 dark:bg-amber-900/30'"
|
||||
>
|
||||
<i
|
||||
:class="resultado?.situacao === 'NEGATIVA'
|
||||
? 'pi pi-check-circle text-emerald-600 dark:text-emerald-400 text-xl'
|
||||
: 'pi pi-exclamation-triangle text-amber-600 dark:text-amber-400 text-xl'"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<p class="font-bold text-slate-800 dark:text-slate-100">
|
||||
{{ resultado?.situacao === 'NEGATIVA' ? 'Nada consta' : 'Existem pendências' }}
|
||||
</p>
|
||||
<p class="text-sm text-slate-500 dark:text-slate-400 mt-0.5">
|
||||
{{ resultado?.nomeContribuinte ?? documento }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Tipo selecionado -->
|
||||
<div class="bg-white dark:bg-slate-800 rounded-2xl border border-slate-200 dark:border-slate-700 p-6">
|
||||
<p class="text-xs font-semibold text-slate-500 dark:text-slate-400 uppercase tracking-wide mb-1">Certidão a emitir</p>
|
||||
<p class="font-semibold text-slate-800 dark:text-slate-100">
|
||||
{{ tiposCertidao.find(t => t.value === tipoCertidao)?.label }}
|
||||
</p>
|
||||
<p class="text-xs text-slate-500 dark:text-slate-400 mt-4">
|
||||
Validade: <span class="font-medium">180 dias a partir da emissão</span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<p v-if="mensagemErro" role="alert" class="text-sm text-red-600 dark:text-red-400 flex items-center gap-1.5">
|
||||
<i class="pi pi-exclamation-circle" aria-hidden="true" />
|
||||
{{ mensagemErro }}
|
||||
</p>
|
||||
|
||||
<div class="flex gap-3">
|
||||
<Button
|
||||
label="Nova consulta"
|
||||
severity="secondary"
|
||||
icon="pi pi-arrow-left"
|
||||
outlined
|
||||
class="flex-1"
|
||||
@click="reiniciar"
|
||||
/>
|
||||
<Button
|
||||
label="Emitir PDF"
|
||||
icon="pi pi-download"
|
||||
class="flex-1"
|
||||
:loading="carregandoEmissao"
|
||||
@click="emitir"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -1,17 +1,271 @@
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { iptuService } from '@/services/iptuService'
|
||||
import DocumentoInput from '@/components/auth/DocumentoInput.vue'
|
||||
|
||||
const router = useRouter()
|
||||
|
||||
const modoConsulta = ref('documento') // 'documento' | 'inscricao'
|
||||
const documento = ref('')
|
||||
const inscricao = ref('')
|
||||
const etapa = ref('formulario') // formulario | resultado
|
||||
const carregando = ref(false)
|
||||
const carregandoPdf = ref(null) // id do débito sendo gerado
|
||||
const imoveis = ref([])
|
||||
const imovelSelecionado = ref(null)
|
||||
const mensagemErro = ref('')
|
||||
|
||||
const exercicioAtual = new Date().getFullYear()
|
||||
|
||||
async function consultar() {
|
||||
carregando.value = true
|
||||
mensagemErro.value = ''
|
||||
imoveis.value = []
|
||||
imovelSelecionado.value = null
|
||||
|
||||
try {
|
||||
const { data } = modoConsulta.value === 'documento'
|
||||
? await iptuService.consultarPorDocumento(documento.value)
|
||||
: await iptuService.consultarPorInscricao(inscricao.value)
|
||||
|
||||
imoveis.value = data.data ?? []
|
||||
if (imoveis.value.length === 1) imovelSelecionado.value = imoveis.value[0]
|
||||
etapa.value = 'resultado'
|
||||
} catch (e) {
|
||||
mensagemErro.value = e.response?.data?.description ?? 'Não foi possível localizar os imóveis. Verifique os dados e tente novamente.'
|
||||
} finally {
|
||||
carregando.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function emitirCarne(imovel) {
|
||||
carregandoPdf.value = `carne-${imovel.inscricaoImobiliaria}`
|
||||
try {
|
||||
const { data } = await iptuService.emitirCarne(imovel.inscricaoImobiliaria, exercicioAtual)
|
||||
const url = URL.createObjectURL(new Blob([data], { type: 'application/pdf' }))
|
||||
const a = document.createElement('a')
|
||||
a.href = url
|
||||
a.download = `carne-iptu-${imovel.inscricaoImobiliaria}-${exercicioAtual}.pdf`
|
||||
a.click()
|
||||
URL.revokeObjectURL(url)
|
||||
} catch {
|
||||
mensagemErro.value = 'Erro ao gerar o carnê. Tente novamente.'
|
||||
} finally {
|
||||
carregandoPdf.value = null
|
||||
}
|
||||
}
|
||||
|
||||
async function emitirBoleto(debito) {
|
||||
carregandoPdf.value = `boleto-${debito.id}`
|
||||
try {
|
||||
const { data } = await iptuService.emitirBoleto(debito.id)
|
||||
const url = URL.createObjectURL(new Blob([data], { type: 'application/pdf' }))
|
||||
const a = document.createElement('a')
|
||||
a.href = url
|
||||
a.download = `boleto-iptu-${debito.id}.pdf`
|
||||
a.click()
|
||||
URL.revokeObjectURL(url)
|
||||
} catch {
|
||||
mensagemErro.value = 'Erro ao gerar o boleto. Tente novamente.'
|
||||
} finally {
|
||||
carregandoPdf.value = null
|
||||
}
|
||||
}
|
||||
|
||||
function reiniciar() {
|
||||
documento.value = ''
|
||||
inscricao.value = ''
|
||||
imoveis.value = []
|
||||
imovelSelecionado.value = null
|
||||
mensagemErro.value = ''
|
||||
etapa.value = 'formulario'
|
||||
}
|
||||
|
||||
function formatarMoeda(valor) {
|
||||
return new Intl.NumberFormat('pt-BR', { style: 'currency', currency: 'BRL' }).format(valor ?? 0)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="max-w-3xl mx-auto px-4 py-12 text-center">
|
||||
<div class="w-16 h-16 bg-blue-100 rounded-2xl flex items-center justify-center mx-auto mb-6">
|
||||
<i class="pi pi-home text-blue-700 text-2xl" />
|
||||
<div class="max-w-2xl mx-auto px-4 sm:px-6 py-12">
|
||||
|
||||
<!-- Voltar -->
|
||||
<button
|
||||
class="inline-flex items-center gap-2 text-sm text-slate-500 dark:text-slate-400 hover:text-slate-800 dark:hover:text-slate-200 transition-colors mb-8 py-1"
|
||||
@click="router.push({ name: 'servicos' })"
|
||||
>
|
||||
<i class="pi pi-arrow-left text-xs" aria-hidden="true" />
|
||||
Voltar aos serviços
|
||||
</button>
|
||||
|
||||
<!-- Cabeçalho -->
|
||||
<div class="flex items-center gap-4 mb-8">
|
||||
<div class="w-12 h-12 bg-primary/10 dark:bg-primary/20 rounded-xl flex items-center justify-center flex-shrink-0">
|
||||
<i class="pi pi-home text-primary text-xl" aria-hidden="true" />
|
||||
</div>
|
||||
<h1 class="text-2xl font-bold text-slate-800 mb-3">IPTU — Débitos e Carnê</h1>
|
||||
<p class="text-slate-500 mb-8">
|
||||
Consulta por inscrição imobiliária ou CPF/CNPJ e impressão do carnê — a ser implementado.
|
||||
<div>
|
||||
<h1 class="text-2xl font-bold text-slate-800 dark:text-slate-100">IPTU — Débitos e Carnê</h1>
|
||||
<p class="text-sm text-slate-500 dark:text-slate-400 mt-0.5">Consulte débitos do imóvel e imprima o carnê ou boleto avulso.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ── FORMULÁRIO ─────────────────────────────────────────────── -->
|
||||
<div v-if="etapa === 'formulario'" class="bg-white dark:bg-slate-800 rounded-2xl border border-slate-200 dark:border-slate-700 p-8 space-y-6">
|
||||
|
||||
<!-- Selector de modo -->
|
||||
<div>
|
||||
<p class="text-sm font-semibold text-slate-700 dark:text-slate-200 mb-3">Buscar por</p>
|
||||
<div class="flex gap-3">
|
||||
<button
|
||||
:class="[
|
||||
'flex-1 py-2.5 rounded-lg border text-sm font-semibold transition-colors',
|
||||
modoConsulta === 'documento'
|
||||
? 'bg-primary text-white border-primary'
|
||||
: 'bg-white dark:bg-slate-700 text-slate-600 dark:text-slate-300 border-slate-200 dark:border-slate-600 hover:border-primary/40'
|
||||
]"
|
||||
@click="modoConsulta = 'documento'"
|
||||
>
|
||||
CPF / CNPJ
|
||||
</button>
|
||||
<button
|
||||
:class="[
|
||||
'flex-1 py-2.5 rounded-lg border text-sm font-semibold transition-colors',
|
||||
modoConsulta === 'inscricao'
|
||||
? 'bg-primary text-white border-primary'
|
||||
: 'bg-white dark:bg-slate-700 text-slate-600 dark:text-slate-300 border-slate-200 dark:border-slate-600 hover:border-primary/40'
|
||||
]"
|
||||
@click="modoConsulta = 'inscricao'"
|
||||
>
|
||||
Inscrição Imobiliária
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Campo dinâmico -->
|
||||
<div>
|
||||
<label class="block text-sm font-semibold text-slate-700 dark:text-slate-200 mb-1.5">
|
||||
{{ modoConsulta === 'documento' ? 'CPF ou CNPJ do proprietário' : 'Número da inscrição imobiliária' }}
|
||||
</label>
|
||||
<DocumentoInput v-if="modoConsulta === 'documento'" v-model="documento" @keyup.enter="consultar" />
|
||||
<InputText
|
||||
v-else
|
||||
v-model="inscricao"
|
||||
placeholder="Ex.: 0001.001.0001.001"
|
||||
class="w-full"
|
||||
size="large"
|
||||
@keyup.enter="consultar"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<p v-if="mensagemErro" role="alert" class="text-sm text-red-600 dark:text-red-400 flex items-center gap-1.5">
|
||||
<i class="pi pi-exclamation-circle" aria-hidden="true" />
|
||||
{{ mensagemErro }}
|
||||
</p>
|
||||
|
||||
<Button
|
||||
label="Consultar imóveis"
|
||||
icon="pi pi-search"
|
||||
class="w-full"
|
||||
size="large"
|
||||
:loading="carregando"
|
||||
:disabled="modoConsulta === 'documento' ? documento.replace(/\D/g,'').length < 11 : !inscricao.trim()"
|
||||
@click="consultar"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- ── RESULTADO ──────────────────────────────────────────────── -->
|
||||
<div v-else-if="etapa === 'resultado'" class="space-y-4">
|
||||
|
||||
<!-- Selector de imóvel se houver mais de um -->
|
||||
<div v-if="imoveis.length > 1" class="bg-white dark:bg-slate-800 rounded-2xl border border-slate-200 dark:border-slate-700 p-6">
|
||||
<p class="text-sm font-semibold text-slate-700 dark:text-slate-200 mb-3">
|
||||
{{ imoveis.length }} imóveis encontrados — selecione um
|
||||
</p>
|
||||
<div class="space-y-2">
|
||||
<button
|
||||
v-for="imovel in imoveis"
|
||||
:key="imovel.inscricaoImobiliaria"
|
||||
class="w-full text-left p-4 rounded-xl border transition-colors"
|
||||
:class="imovelSelecionado?.inscricaoImobiliaria === imovel.inscricaoImobiliaria
|
||||
? 'border-primary bg-primary/5 dark:bg-primary/10'
|
||||
: 'border-slate-200 dark:border-slate-600 hover:border-slate-300 dark:hover:border-slate-500'"
|
||||
@click="imovelSelecionado = imovel"
|
||||
>
|
||||
<p class="font-semibold text-slate-800 dark:text-slate-100 text-sm">{{ imovel.enderecoCompleto }}</p>
|
||||
<p class="text-xs text-slate-500 dark:text-slate-400 mt-0.5">Inscrição: {{ imovel.inscricaoImobiliaria }}</p>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Detalhe do imóvel selecionado -->
|
||||
<template v-if="imovelSelecionado">
|
||||
<div class="bg-white dark:bg-slate-800 rounded-2xl border border-slate-200 dark:border-slate-700 p-6">
|
||||
<div class="flex items-start justify-between gap-4 mb-4">
|
||||
<div>
|
||||
<p class="font-bold text-slate-800 dark:text-slate-100">{{ imovelSelecionado.enderecoCompleto }}</p>
|
||||
<p class="text-xs text-slate-500 dark:text-slate-400 mt-0.5">Inscrição: {{ imovelSelecionado.inscricaoImobiliaria }}</p>
|
||||
</div>
|
||||
<Button
|
||||
:label="`Carnê ${exercicioAtual}`"
|
||||
icon="pi pi-download"
|
||||
size="small"
|
||||
outlined
|
||||
:loading="carregandoPdf === `carne-${imovelSelecionado.inscricaoImobiliaria}`"
|
||||
@click="emitirCarne(imovelSelecionado)"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Tabela de débitos -->
|
||||
<div v-if="imovelSelecionado.debitos?.length" class="mt-4">
|
||||
<p class="text-xs font-semibold text-slate-500 dark:text-slate-400 uppercase tracking-wide mb-3">Débitos em aberto</p>
|
||||
<div class="space-y-2">
|
||||
<div
|
||||
v-for="debito in imovelSelecionado.debitos"
|
||||
:key="debito.id"
|
||||
class="flex items-center justify-between gap-4 py-3 border-b border-slate-100 dark:border-slate-700 last:border-0"
|
||||
>
|
||||
<div>
|
||||
<p class="text-sm font-semibold text-slate-800 dark:text-slate-100">{{ debito.descricao }}</p>
|
||||
<p class="text-xs text-slate-500 dark:text-slate-400">Venc.: {{ debito.vencimento }}</p>
|
||||
</div>
|
||||
<div class="flex items-center gap-3">
|
||||
<p class="text-sm font-bold text-slate-800 dark:text-slate-100 whitespace-nowrap">
|
||||
{{ formatarMoeda(debito.valor) }}
|
||||
</p>
|
||||
<Button
|
||||
icon="pi pi-download"
|
||||
size="small"
|
||||
text
|
||||
aria-label="Emitir boleto"
|
||||
:loading="carregandoPdf === `boleto-${debito.id}`"
|
||||
@click="emitirBoleto(debito)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p v-else class="text-sm text-emerald-600 dark:text-emerald-400 font-medium flex items-center gap-2 mt-2">
|
||||
<i class="pi pi-check-circle" aria-hidden="true" />
|
||||
Nenhum débito em aberto para este imóvel.
|
||||
</p>
|
||||
<RouterLink :to="{ name: 'servicos' }">
|
||||
<Button label="Voltar aos serviços" severity="secondary" icon="pi pi-arrow-left" outlined />
|
||||
</RouterLink>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<p v-if="mensagemErro" role="alert" class="text-sm text-red-600 dark:text-red-400 flex items-center gap-1.5">
|
||||
<i class="pi pi-exclamation-circle" aria-hidden="true" />
|
||||
{{ mensagemErro }}
|
||||
</p>
|
||||
|
||||
<Button
|
||||
label="Nova consulta"
|
||||
severity="secondary"
|
||||
icon="pi pi-arrow-left"
|
||||
outlined
|
||||
class="w-full"
|
||||
@click="reiniciar"
|
||||
/>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user