gabrielb 5e528f234b
All checks were successful
Dev Build & Deploy Portal / build-deploy (push) Successful in 2m36s
feat(auth): adicionar tratamento de erro ao salvar estado PKCE
feat(proxy): melhorar tratamento de erro para backend inacessível
feat(certidao): adicionar validação de CPF e CNPJ
feat(iptu): ajustar manipulação de dados de imóveis retornados
feat(error): criar componente de erro para exibição de mensagens
2026-05-25 18:59:41 -03:00

226 lines
11 KiB
Vue

<script setup>
import { ref, onMounted } from 'vue'
import { useExtratoDebitosPortal } from '@/composables/useExtratoDebitosPortal'
import { formatCurrency, formatDate } from '@/utils/formatador'
import ModalTransacoesContaCorrente from '@/components/extrato/ModalTransacoesContaCorrente.vue'
definePageMeta({
layout: 'portal',
middleware: 'auth',
})
const modalTransacoes = ref(null)
const {
resultados,
isLoading,
isLoadingGuia,
isLoadingExtrato,
mensagemErro,
dataVencimento,
erros,
filtro,
opcoesEstadoConta,
temSelecionado,
totalizadores,
consultar,
limparFiltros,
gerarGuia,
gerarExtratoPdf,
getEstadoSeverity,
} = useExtratoDebitosPortal()
onMounted(() => consultar())
function getSegundoNome(texto) {
const palavras = (texto || '').trim().split(/\s+/)
return palavras.length > 1 ? palavras[1] : texto
}
</script>
<template>
<div class="space-y-6">
<div>
<h1 class="text-2xl font-bold text-slate-800 dark:text-slate-100">Extrato de Débitos</h1>
<p class="text-sm text-slate-500 dark:text-slate-400 mt-0.5">
Consulte seus débitos, selecione parcelas e emita guias de pagamento.
</p>
</div>
<div class="bg-white dark:bg-slate-800 rounded-xl border border-slate-200 dark:border-slate-700 p-4 space-y-4">
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
<div>
<label class="block text-xs font-semibold text-slate-500 dark:text-slate-400 uppercase tracking-wide mb-1.5">Período Início</label>
<DatePicker v-model="filtro.periodoIni" view="month" date-format="mm/yy" show-icon class="w-full" size="small" />
</div>
<div>
<label class="block text-xs font-semibold text-slate-500 dark:text-slate-400 uppercase tracking-wide mb-1.5">Período Fim</label>
<DatePicker v-model="filtro.periodoFim" view="month" date-format="mm/yy" show-icon class="w-full" size="small" />
</div>
<div>
<label class="block text-xs font-semibold text-slate-500 dark:text-slate-400 uppercase tracking-wide mb-1.5">Estado da Conta</label>
<Select
v-model="filtro.estadoConta"
:options="opcoesEstadoConta"
option-label="label"
option-value="value"
placeholder="Todos"
class="w-full"
size="small"
/>
</div>
</div>
<div class="flex gap-2 flex-wrap">
<Button label="Consultar" icon="pi pi-search" size="small" :loading="isLoading" @click="consultar" />
<Button label="Limpar" icon="pi pi-eraser" size="small" severity="secondary" outlined @click="limparFiltros" />
</div>
</div>
<Message v-if="mensagemErro" severity="warn" :closable="false">{{ mensagemErro }}</Message>
<div v-if="isLoading" class="space-y-3">
<div v-for="i in 3" :key="i" class="h-24 bg-slate-200 dark:bg-slate-700 rounded-xl animate-pulse" />
</div>
<div v-else-if="resultados.length === 0 && !mensagemErro" class="bg-white dark:bg-slate-800 rounded-xl border p-12 text-center">
<i class="pi pi-check-circle text-emerald-400 text-4xl mb-3 block" />
<p class="font-semibold text-slate-700 dark:text-slate-200">Nenhum débito encontrado</p>
</div>
<template v-else-if="resultados.length">
<Accordion :multiple="true" :active-index="[0]">
<AccordionPanel v-for="(tributo, index) in resultados" :key="tributo.idContaTributo" :value="String(index)">
<AccordionHeader>
<div class="flex items-center justify-between w-full gap-2 pr-2">
<span class="text-sm font-semibold truncate">
Conta {{ tributo.idContaTributo }} {{ tributo.descricaoContaTributo }} / {{ tributo.descricaoTributo }}
</span>
<span class="text-sm shrink-0">
Total: <strong>{{ formatCurrency(tributo.totalPagamentos) }}</strong>
</span>
</div>
</AccordionHeader>
<AccordionContent>
<DataTable
v-model:selection="tributo.selecionados"
:value="tributo.debitos"
data-key="id"
size="small"
show-gridlines
selection-mode="multiple"
:is-row-selectable="({ data }) => data.codigoEstadoConta === 1"
scrollable
class="mt-2"
>
<Column selection-mode="multiple" header-style="width: 3rem" />
<Column field="numDoc" header=" Doc" style="min-width: 90px" />
<Column field="estadoConta" header="Estado" style="min-width: 90px">
<template #body="{ data }">
<Tag :value="getSegundoNome(data.estadoConta)" :severity="getEstadoSeverity(data.codigoEstadoConta)" />
</template>
</Column>
<Column field="periodoRef" header="Período" style="min-width: 80px" />
<Column field="dataVencimento" header="Vencimento" style="min-width: 95px">
<template #body="{ data }">{{ formatDate(data.dataVencimento) }}</template>
</Column>
<Column field="valorPrincipal" header="Principal" body-class="text-right" style="min-width: 100px">
<template #body="{ data }">{{ formatCurrency(data.valorPrincipal) }}</template>
</Column>
<Column field="valorMulta" header="Multa" body-class="text-right" style="min-width: 90px">
<template #body="{ data }">{{ formatCurrency(data.valorMulta) }}</template>
</Column>
<Column field="valorJuros" header="Juros" body-class="text-right" style="min-width: 90px">
<template #body="{ data }">{{ formatCurrency(data.valorJuros) }}</template>
</Column>
<Column field="valorTotal" header="Total" body-class="text-right" style="min-width: 100px">
<template #body="{ data }">
<strong>{{ formatCurrency(data.valorTotal) }}</strong>
</template>
</Column>
<Column header="" style="width: 3rem">
<template #body="{ data }">
<Button
icon="pi pi-search"
text
rounded
size="small"
@click="modalTransacoes?.abrir(data.idContaCorrente)"
/>
</template>
</Column>
</DataTable>
</AccordionContent>
</AccordionPanel>
</Accordion>
<div class="bg-white dark:bg-slate-800 rounded-xl border border-slate-200 dark:border-slate-700 p-4 space-y-4">
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
<div>
<label class="block text-xs font-semibold text-slate-500 uppercase mb-1.5">Vencimento da Guia</label>
<DatePicker
v-model="dataVencimento"
show-icon
:min-date="new Date()"
date-format="dd/mm/yy"
class="w-full"
size="small"
/>
<p v-if="erros.dataVencimento" class="text-xs text-red-500 mt-1">{{ erros.dataVencimento }}</p>
</div>
<div class="sm:col-span-3 grid grid-cols-2 sm:grid-cols-4 gap-3 items-end">
<div>
<p class="text-xs text-slate-500">Principal</p>
<p class="font-bold">{{ formatCurrency(totalizadores.principal) }}</p>
</div>
<div>
<p class="text-xs text-slate-500">Multa</p>
<p class="font-bold">{{ formatCurrency(totalizadores.multa) }}</p>
</div>
<div>
<p class="text-xs text-slate-500">Juros</p>
<p class="font-bold">{{ formatCurrency(totalizadores.juros) }}</p>
</div>
<div>
<p class="text-xs text-slate-500">Total a Pagar</p>
<p class="font-bold text-primary">{{ formatCurrency(totalizadores.valorTotal) }}</p>
</div>
</div>
</div>
<div class="flex flex-wrap gap-2 justify-between">
<div class="flex flex-wrap gap-2">
<Button
label="Imprimir Extrato (todos)"
icon="pi pi-print"
size="small"
severity="secondary"
:loading="isLoadingExtrato"
@click="gerarExtratoPdf(false)"
/>
<Button
v-if="temSelecionado"
label="Imprimir Selecionados"
icon="pi pi-print"
size="small"
severity="secondary"
outlined
:loading="isLoadingExtrato"
@click="gerarExtratoPdf(true)"
/>
</div>
<Button
v-if="temSelecionado"
label="Gerar Guia de Pagamento"
icon="pi pi-file-pdf"
size="small"
:loading="isLoadingGuia"
@click="gerarGuia"
/>
</div>
</div>
</template>
<ModalTransacoesContaCorrente ref="modalTransacoes" />
</div>
</template>