- Updated various Svelte components to improve code readability and maintainability. - Standardized button classes across components for a consistent user interface. - Enhanced error handling and user feedback in modals and forms. - Cleaned up unnecessary imports and optimized component structure for better performance.
585 lines
18 KiB
Svelte
585 lines
18 KiB
Svelte
<script lang="ts">
|
|
import jsPDF from 'jspdf';
|
|
import autoTable from 'jspdf-autotable';
|
|
import { maskCPF, maskCEP, maskPhone } from '$lib/utils/masks';
|
|
import {
|
|
SEXO_OPTIONS,
|
|
ESTADO_CIVIL_OPTIONS,
|
|
GRAU_INSTRUCAO_OPTIONS,
|
|
GRUPO_SANGUINEO_OPTIONS,
|
|
FATOR_RH_OPTIONS,
|
|
APOSENTADO_OPTIONS
|
|
} from '$lib/utils/constants';
|
|
import logoGovPE from '$lib/assets/logo_governo_PE.png';
|
|
import { CheckCircle2, X, Printer } from 'lucide-svelte';
|
|
|
|
interface Props {
|
|
funcionario: any;
|
|
onClose: () => void;
|
|
}
|
|
|
|
let { funcionario, onClose }: Props = $props();
|
|
|
|
let modalRef: HTMLDialogElement;
|
|
let generating = $state(false);
|
|
|
|
// Seções selecionáveis
|
|
let sections = $state({
|
|
dadosPessoais: true,
|
|
filiacao: true,
|
|
naturalidade: true,
|
|
documentos: true,
|
|
formacao: true,
|
|
saude: true,
|
|
endereco: true,
|
|
contato: true,
|
|
cargo: true,
|
|
financeiro: true,
|
|
bancario: true
|
|
});
|
|
|
|
const REGIME_LABELS: Record<string, string> = {
|
|
clt: 'CLT',
|
|
estatutario_municipal: 'Estatutário Municipal',
|
|
estatutario_pe: 'Estatutário PE',
|
|
estatutario_federal: 'Estatutário Federal'
|
|
};
|
|
|
|
function getLabelFromOptions(
|
|
value: string | undefined,
|
|
options: Array<{ value: string; label: string }>
|
|
): string {
|
|
if (!value) return '-';
|
|
return options.find((opt) => opt.value === value)?.label || value;
|
|
}
|
|
|
|
function getRegimeLabel(value?: string) {
|
|
if (!value) return '-';
|
|
return REGIME_LABELS[value] ?? value;
|
|
}
|
|
|
|
function selectAll() {
|
|
Object.keys(sections).forEach((key) => {
|
|
sections[key as keyof typeof sections] = true;
|
|
});
|
|
}
|
|
|
|
function deselectAll() {
|
|
Object.keys(sections).forEach((key) => {
|
|
sections[key as keyof typeof sections] = false;
|
|
});
|
|
}
|
|
|
|
async function gerarPDF() {
|
|
try {
|
|
generating = true;
|
|
|
|
const doc = new jsPDF();
|
|
|
|
// Logo no canto superior esquerdo (proporcional)
|
|
let yPosition = 20;
|
|
try {
|
|
const logoImg = new Image();
|
|
logoImg.src = logoGovPE;
|
|
await new Promise<void>((resolve, reject) => {
|
|
logoImg.onload = () => resolve();
|
|
logoImg.onerror = () => reject();
|
|
setTimeout(() => reject(), 3000); // timeout após 3s
|
|
});
|
|
|
|
// Logo proporcional: largura 25mm, altura ajustada automaticamente
|
|
const logoWidth = 25;
|
|
const aspectRatio = logoImg.height / logoImg.width;
|
|
const logoHeight = logoWidth * aspectRatio;
|
|
|
|
doc.addImage(logoImg, 'PNG', 15, 10, logoWidth, logoHeight);
|
|
|
|
// Ajustar posição inicial do texto para ficar ao lado da logo
|
|
yPosition = Math.max(20, 10 + logoHeight / 2);
|
|
} catch (err) {
|
|
console.warn('Não foi possível carregar a logo:', err);
|
|
}
|
|
|
|
// Cabeçalho (alinhado com a logo)
|
|
doc.setFontSize(16);
|
|
doc.setFont('helvetica', 'bold');
|
|
doc.text('Secretaria de Esportes', 50, yPosition);
|
|
doc.setFontSize(12);
|
|
doc.setFont('helvetica', 'normal');
|
|
doc.text('Governo de Pernambuco', 50, yPosition + 7);
|
|
|
|
yPosition = Math.max(45, yPosition + 25);
|
|
|
|
// Título da ficha
|
|
doc.setFontSize(18);
|
|
doc.setFont('helvetica', 'bold');
|
|
doc.text('FICHA CADASTRAL DE FUNCIONÁRIO', 105, yPosition, { align: 'center' });
|
|
|
|
yPosition += 8;
|
|
doc.setFontSize(10);
|
|
doc.setFont('helvetica', 'normal');
|
|
doc.text(`Gerado em: ${new Date().toLocaleString('pt-BR')}`, 105, yPosition, {
|
|
align: 'center'
|
|
});
|
|
|
|
yPosition += 12;
|
|
|
|
// Dados Pessoais
|
|
if (sections.dadosPessoais) {
|
|
const dadosPessoais: any[] = [
|
|
['Nome', funcionario.nome],
|
|
['Matrícula', funcionario.matricula],
|
|
['CPF', maskCPF(funcionario.cpf)],
|
|
['RG', funcionario.rg],
|
|
['Data Nascimento', funcionario.nascimento]
|
|
];
|
|
|
|
if (funcionario.rgOrgaoExpedidor)
|
|
dadosPessoais.push(['Órgão Expedidor RG', funcionario.rgOrgaoExpedidor]);
|
|
if (funcionario.rgDataEmissao)
|
|
dadosPessoais.push(['Data Emissão RG', funcionario.rgDataEmissao]);
|
|
if (funcionario.sexo)
|
|
dadosPessoais.push(['Sexo', getLabelFromOptions(funcionario.sexo, SEXO_OPTIONS)]);
|
|
if (funcionario.estadoCivil)
|
|
dadosPessoais.push([
|
|
'Estado Civil',
|
|
getLabelFromOptions(funcionario.estadoCivil, ESTADO_CIVIL_OPTIONS)
|
|
]);
|
|
if (funcionario.nacionalidade)
|
|
dadosPessoais.push(['Nacionalidade', funcionario.nacionalidade]);
|
|
|
|
autoTable(doc, {
|
|
startY: yPosition,
|
|
head: [['DADOS PESSOAIS', '']],
|
|
body: dadosPessoais,
|
|
theme: 'grid',
|
|
headStyles: { fillColor: [41, 128, 185], fontStyle: 'bold' },
|
|
styles: { fontSize: 9 }
|
|
});
|
|
|
|
yPosition = (doc as any).lastAutoTable.finalY + 10;
|
|
}
|
|
|
|
// Filiação
|
|
if (sections.filiacao && (funcionario.nomePai || funcionario.nomeMae)) {
|
|
const filiacao: any[] = [];
|
|
if (funcionario.nomePai) filiacao.push(['Nome do Pai', funcionario.nomePai]);
|
|
if (funcionario.nomeMae) filiacao.push(['Nome da Mãe', funcionario.nomeMae]);
|
|
|
|
autoTable(doc, {
|
|
startY: yPosition,
|
|
head: [['FILIAÇÃO', '']],
|
|
body: filiacao,
|
|
theme: 'grid',
|
|
headStyles: { fillColor: [41, 128, 185], fontStyle: 'bold' },
|
|
styles: { fontSize: 9 }
|
|
});
|
|
|
|
yPosition = (doc as any).lastAutoTable.finalY + 10;
|
|
}
|
|
|
|
// Naturalidade
|
|
if (sections.naturalidade && (funcionario.naturalidade || funcionario.naturalidadeUF)) {
|
|
const naturalidade: any[] = [];
|
|
if (funcionario.naturalidade) naturalidade.push(['Cidade', funcionario.naturalidade]);
|
|
if (funcionario.naturalidadeUF) naturalidade.push(['UF', funcionario.naturalidadeUF]);
|
|
|
|
autoTable(doc, {
|
|
startY: yPosition,
|
|
head: [['NATURALIDADE', '']],
|
|
body: naturalidade,
|
|
theme: 'grid',
|
|
headStyles: { fillColor: [41, 128, 185], fontStyle: 'bold' },
|
|
styles: { fontSize: 9 }
|
|
});
|
|
|
|
yPosition = (doc as any).lastAutoTable.finalY + 10;
|
|
}
|
|
|
|
// Documentos
|
|
if (sections.documentos) {
|
|
const documentosData: any[] = [];
|
|
|
|
if (funcionario.carteiraProfissionalNumero) {
|
|
documentosData.push([
|
|
'Cart. Profissional',
|
|
`Nº ${funcionario.carteiraProfissionalNumero}${funcionario.carteiraProfissionalSerie ? ' - Série: ' + funcionario.carteiraProfissionalSerie : ''}`
|
|
]);
|
|
}
|
|
if (funcionario.carteiraProfissionalDataEmissao) {
|
|
documentosData.push([
|
|
'Emissão Cart. Profissional',
|
|
funcionario.carteiraProfissionalDataEmissao
|
|
]);
|
|
}
|
|
if (funcionario.reservistaNumero) {
|
|
documentosData.push([
|
|
'Reservista',
|
|
`Nº ${funcionario.reservistaNumero}${funcionario.reservistaSerie ? ' - Série: ' + funcionario.reservistaSerie : ''}`
|
|
]);
|
|
}
|
|
if (funcionario.tituloEleitorNumero) {
|
|
let titulo = `Nº ${funcionario.tituloEleitorNumero}`;
|
|
if (funcionario.tituloEleitorZona) titulo += ` - Zona: ${funcionario.tituloEleitorZona}`;
|
|
if (funcionario.tituloEleitorSecao)
|
|
titulo += ` - Seção: ${funcionario.tituloEleitorSecao}`;
|
|
documentosData.push(['Título Eleitor', titulo]);
|
|
}
|
|
if (funcionario.pisNumero) documentosData.push(['PIS/PASEP', funcionario.pisNumero]);
|
|
|
|
if (documentosData.length > 0) {
|
|
autoTable(doc, {
|
|
startY: yPosition,
|
|
head: [['DOCUMENTOS', '']],
|
|
body: documentosData,
|
|
theme: 'grid',
|
|
headStyles: { fillColor: [41, 128, 185], fontStyle: 'bold' },
|
|
styles: { fontSize: 9 }
|
|
});
|
|
|
|
yPosition = (doc as any).lastAutoTable.finalY + 10;
|
|
}
|
|
}
|
|
|
|
// Formação
|
|
if (sections.formacao && (funcionario.grauInstrucao || funcionario.formacao)) {
|
|
const formacaoData: any[] = [];
|
|
if (funcionario.grauInstrucao)
|
|
formacaoData.push([
|
|
'Grau Instrução',
|
|
getLabelFromOptions(funcionario.grauInstrucao, GRAU_INSTRUCAO_OPTIONS)
|
|
]);
|
|
if (funcionario.formacao) formacaoData.push(['Formação', funcionario.formacao]);
|
|
if (funcionario.formacaoRegistro)
|
|
formacaoData.push(['Registro Nº', funcionario.formacaoRegistro]);
|
|
|
|
autoTable(doc, {
|
|
startY: yPosition,
|
|
head: [['FORMAÇÃO', '']],
|
|
body: formacaoData,
|
|
theme: 'grid',
|
|
headStyles: { fillColor: [41, 128, 185], fontStyle: 'bold' },
|
|
styles: { fontSize: 9 }
|
|
});
|
|
|
|
yPosition = (doc as any).lastAutoTable.finalY + 10;
|
|
}
|
|
|
|
// Saúde
|
|
if (sections.saude && (funcionario.grupoSanguineo || funcionario.fatorRH)) {
|
|
const saudeData: any[] = [];
|
|
if (funcionario.grupoSanguineo)
|
|
saudeData.push(['Grupo Sanguíneo', funcionario.grupoSanguineo]);
|
|
if (funcionario.fatorRH)
|
|
saudeData.push(['Fator RH', getLabelFromOptions(funcionario.fatorRH, FATOR_RH_OPTIONS)]);
|
|
|
|
autoTable(doc, {
|
|
startY: yPosition,
|
|
head: [['SAÚDE', '']],
|
|
body: saudeData,
|
|
theme: 'grid',
|
|
headStyles: { fillColor: [41, 128, 185], fontStyle: 'bold' },
|
|
styles: { fontSize: 9 }
|
|
});
|
|
|
|
yPosition = (doc as any).lastAutoTable.finalY + 10;
|
|
}
|
|
|
|
// Endereço
|
|
if (sections.endereco) {
|
|
const enderecoData: any[] = [
|
|
['Endereço', funcionario.endereco],
|
|
['Cidade', funcionario.cidade],
|
|
['UF', funcionario.uf],
|
|
['CEP', maskCEP(funcionario.cep)]
|
|
];
|
|
|
|
autoTable(doc, {
|
|
startY: yPosition,
|
|
head: [['ENDEREÇO', '']],
|
|
body: enderecoData,
|
|
theme: 'grid',
|
|
headStyles: { fillColor: [41, 128, 185], fontStyle: 'bold' },
|
|
styles: { fontSize: 9 }
|
|
});
|
|
|
|
yPosition = (doc as any).lastAutoTable.finalY + 10;
|
|
}
|
|
|
|
// Contato
|
|
if (sections.contato) {
|
|
const contatoData: any[] = [
|
|
['E-mail', funcionario.email],
|
|
['Telefone', maskPhone(funcionario.telefone)]
|
|
];
|
|
|
|
autoTable(doc, {
|
|
startY: yPosition,
|
|
head: [['CONTATO', '']],
|
|
body: contatoData,
|
|
theme: 'grid',
|
|
headStyles: { fillColor: [41, 128, 185], fontStyle: 'bold' },
|
|
styles: { fontSize: 9 }
|
|
});
|
|
|
|
yPosition = (doc as any).lastAutoTable.finalY + 10;
|
|
}
|
|
|
|
// Nova página para cargo
|
|
if (yPosition > 200) {
|
|
doc.addPage();
|
|
yPosition = 20;
|
|
}
|
|
|
|
// Cargo e Vínculo
|
|
if (sections.cargo) {
|
|
const cargoData: any[] = [
|
|
[
|
|
'Tipo',
|
|
funcionario.simboloTipo === 'cargo_comissionado'
|
|
? 'Cargo Comissionado'
|
|
: 'Função Gratificada'
|
|
]
|
|
];
|
|
|
|
const simboloInfo =
|
|
funcionario.simbolo ?? funcionario.simboloDetalhes ?? funcionario.simboloDados;
|
|
if (simboloInfo) {
|
|
cargoData.push(['Símbolo', simboloInfo.nome]);
|
|
if (simboloInfo.descricao)
|
|
cargoData.push(['Descrição do Símbolo', simboloInfo.descricao]);
|
|
}
|
|
if (funcionario.descricaoCargo) cargoData.push(['Descrição', funcionario.descricaoCargo]);
|
|
if (funcionario.regimeTrabalho)
|
|
cargoData.push(['Regime do Funcionário', getRegimeLabel(funcionario.regimeTrabalho)]);
|
|
if (funcionario.admissaoData) cargoData.push(['Data Admissão', funcionario.admissaoData]);
|
|
if (funcionario.nomeacaoPortaria)
|
|
cargoData.push(['Portaria', funcionario.nomeacaoPortaria]);
|
|
if (funcionario.nomeacaoData) cargoData.push(['Data Nomeação', funcionario.nomeacaoData]);
|
|
if (funcionario.nomeacaoDOE) cargoData.push(['DOE', funcionario.nomeacaoDOE]);
|
|
cargoData.push([
|
|
'Pertence Órgão Público',
|
|
funcionario.pertenceOrgaoPublico ? 'Sim' : 'Não'
|
|
]);
|
|
if (funcionario.pertenceOrgaoPublico && funcionario.orgaoOrigem)
|
|
cargoData.push(['Órgão Origem', funcionario.orgaoOrigem]);
|
|
if (funcionario.aposentado && funcionario.aposentado !== 'nao') {
|
|
cargoData.push([
|
|
'Aposentado',
|
|
getLabelFromOptions(funcionario.aposentado, APOSENTADO_OPTIONS)
|
|
]);
|
|
}
|
|
|
|
autoTable(doc, {
|
|
startY: yPosition,
|
|
head: [['CARGO E VÍNCULO', '']],
|
|
body: cargoData,
|
|
theme: 'grid',
|
|
headStyles: { fillColor: [41, 128, 185], fontStyle: 'bold' },
|
|
styles: { fontSize: 9 }
|
|
});
|
|
|
|
yPosition = (doc as any).lastAutoTable.finalY + 10;
|
|
}
|
|
|
|
// Dados Financeiros
|
|
if (sections.financeiro && funcionario.simbolo) {
|
|
const simbolo = funcionario.simbolo;
|
|
const financeiroData: any[] = [
|
|
['Símbolo', simbolo.nome],
|
|
[
|
|
'Tipo',
|
|
simbolo.tipo === 'cargo_comissionado' ? 'Cargo Comissionado' : 'Função Gratificada'
|
|
],
|
|
['Remuneração Total', `R$ ${simbolo.valor}`]
|
|
];
|
|
|
|
if (funcionario.simboloTipo === 'cargo_comissionado') {
|
|
if (simbolo.vencValor) {
|
|
financeiroData.push(['Vencimento', `R$ ${simbolo.vencValor}`]);
|
|
}
|
|
if (simbolo.repValor) {
|
|
financeiroData.push(['Representação', `R$ ${simbolo.repValor}`]);
|
|
}
|
|
}
|
|
|
|
autoTable(doc, {
|
|
startY: yPosition,
|
|
head: [['DADOS FINANCEIROS', '']],
|
|
body: financeiroData,
|
|
theme: 'grid',
|
|
headStyles: { fillColor: [41, 128, 185], fontStyle: 'bold' },
|
|
styles: { fontSize: 9 }
|
|
});
|
|
|
|
yPosition = (doc as any).lastAutoTable.finalY + 10;
|
|
}
|
|
|
|
// Dados Bancários
|
|
if (sections.bancario && funcionario.contaBradescoNumero) {
|
|
const bancarioData: any[] = [
|
|
[
|
|
'Conta',
|
|
`${funcionario.contaBradescoNumero}${funcionario.contaBradescoDV ? '-' + funcionario.contaBradescoDV : ''}`
|
|
]
|
|
];
|
|
if (funcionario.contaBradescoAgencia)
|
|
bancarioData.push(['Agência', funcionario.contaBradescoAgencia]);
|
|
|
|
autoTable(doc, {
|
|
startY: yPosition,
|
|
head: [['DADOS BANCÁRIOS - BRADESCO', '']],
|
|
body: bancarioData,
|
|
theme: 'grid',
|
|
headStyles: { fillColor: [41, 128, 185], fontStyle: 'bold' },
|
|
styles: { fontSize: 9 }
|
|
});
|
|
|
|
yPosition = (doc as any).lastAutoTable.finalY + 10;
|
|
}
|
|
|
|
// Adicionar rodapé em todas as páginas
|
|
const pageCount = (doc as any).internal.getNumberOfPages();
|
|
for (let i = 1; i <= pageCount; i++) {
|
|
doc.setPage(i);
|
|
doc.setFontSize(9);
|
|
doc.setFont('helvetica', 'normal');
|
|
doc.setTextColor(128, 128, 128);
|
|
doc.text('SGSE - Sistema de Gerenciamento da Secretaria de Esportes', 105, 285, {
|
|
align: 'center'
|
|
});
|
|
doc.text(`Página ${i} de ${pageCount}`, 195, 285, { align: 'right' });
|
|
}
|
|
|
|
// Salvar PDF
|
|
doc.save(`Ficha_${funcionario.nome.replace(/ /g, '_')}_${new Date().getTime()}.pdf`);
|
|
|
|
onClose();
|
|
} catch (error) {
|
|
console.error('Erro ao gerar PDF:', error);
|
|
alert('Erro ao gerar PDF. Verifique o console para mais detalhes.');
|
|
} finally {
|
|
generating = false;
|
|
}
|
|
}
|
|
|
|
$effect(() => {
|
|
if (modalRef) {
|
|
modalRef.showModal();
|
|
}
|
|
});
|
|
</script>
|
|
|
|
<dialog bind:this={modalRef} class="modal">
|
|
<div class="modal-box max-w-4xl">
|
|
<h3 class="mb-4 text-2xl font-bold">Imprimir Ficha Cadastral</h3>
|
|
<p class="text-base-content/70 mb-6 text-sm">Selecione as seções que deseja incluir no PDF</p>
|
|
|
|
<!-- Botões de seleção -->
|
|
<div class="mb-6 flex gap-2">
|
|
<button type="button" class="btn btn-sm btn-outline" onclick={selectAll}>
|
|
<CheckCircle2 class="h-4 w-4" strokeWidth={2} />
|
|
Selecionar Todos
|
|
</button>
|
|
<button type="button" class="btn btn-sm btn-outline" onclick={deselectAll}>
|
|
<X class="h-4 w-4" strokeWidth={2} />
|
|
Desmarcar Todos
|
|
</button>
|
|
</div>
|
|
|
|
<!-- Grid de checkboxes -->
|
|
<div
|
|
class="bg-base-200 mb-6 grid max-h-96 grid-cols-2 gap-4 overflow-y-auto rounded-lg border p-2 md:grid-cols-3"
|
|
>
|
|
<label class="label cursor-pointer justify-start gap-3">
|
|
<input
|
|
type="checkbox"
|
|
class="checkbox checkbox-primary"
|
|
bind:checked={sections.dadosPessoais}
|
|
/>
|
|
<span class="label-text">Dados Pessoais</span>
|
|
</label>
|
|
|
|
<label class="label cursor-pointer justify-start gap-3">
|
|
<input type="checkbox" class="checkbox checkbox-primary" bind:checked={sections.filiacao} />
|
|
<span class="label-text">Filiação</span>
|
|
</label>
|
|
|
|
<label class="label cursor-pointer justify-start gap-3">
|
|
<input
|
|
type="checkbox"
|
|
class="checkbox checkbox-primary"
|
|
bind:checked={sections.naturalidade}
|
|
/>
|
|
<span class="label-text">Naturalidade</span>
|
|
</label>
|
|
|
|
<label class="label cursor-pointer justify-start gap-3">
|
|
<input
|
|
type="checkbox"
|
|
class="checkbox checkbox-primary"
|
|
bind:checked={sections.documentos}
|
|
/>
|
|
<span class="label-text">Documentos</span>
|
|
</label>
|
|
|
|
<label class="label cursor-pointer justify-start gap-3">
|
|
<input type="checkbox" class="checkbox checkbox-primary" bind:checked={sections.formacao} />
|
|
<span class="label-text">Formação</span>
|
|
</label>
|
|
|
|
<label class="label cursor-pointer justify-start gap-3">
|
|
<input type="checkbox" class="checkbox checkbox-primary" bind:checked={sections.saude} />
|
|
<span class="label-text">Saúde</span>
|
|
</label>
|
|
|
|
<label class="label cursor-pointer justify-start gap-3">
|
|
<input type="checkbox" class="checkbox checkbox-primary" bind:checked={sections.endereco} />
|
|
<span class="label-text">Endereço</span>
|
|
</label>
|
|
|
|
<label class="label cursor-pointer justify-start gap-3">
|
|
<input type="checkbox" class="checkbox checkbox-primary" bind:checked={sections.contato} />
|
|
<span class="label-text">Contato</span>
|
|
</label>
|
|
|
|
<label class="label cursor-pointer justify-start gap-3">
|
|
<input type="checkbox" class="checkbox checkbox-primary" bind:checked={sections.cargo} />
|
|
<span class="label-text">Cargo e Vínculo</span>
|
|
</label>
|
|
|
|
<label class="label cursor-pointer justify-start gap-3">
|
|
<input
|
|
type="checkbox"
|
|
class="checkbox checkbox-primary"
|
|
bind:checked={sections.financeiro}
|
|
/>
|
|
<span class="label-text">Dados Financeiros</span>
|
|
</label>
|
|
|
|
<label class="label cursor-pointer justify-start gap-3">
|
|
<input type="checkbox" class="checkbox checkbox-primary" bind:checked={sections.bancario} />
|
|
<span class="label-text">Dados Bancários</span>
|
|
</label>
|
|
</div>
|
|
|
|
<!-- Ações -->
|
|
<div class="modal-action">
|
|
<button type="button" class="btn" onclick={onClose} disabled={generating}> Cancelar </button>
|
|
<button type="button" class="btn btn-primary gap-2" onclick={gerarPDF} disabled={generating}>
|
|
{#if generating}
|
|
<span class="loading loading-spinner loading-sm"></span>
|
|
Gerando PDF...
|
|
{:else}
|
|
<Printer class="h-5 w-5" strokeWidth={2} />
|
|
Gerar PDF
|
|
{/if}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<form method="dialog" class="modal-backdrop">
|
|
<button type="button" onclick={onClose}>fechar</button>
|
|
</form>
|
|
</dialog>
|