feat: add Excel and PDF report generation functionality for employee leave and certificate data, integrating XLSX and jsPDF libraries for enhanced reporting capabilities
This commit is contained in:
@@ -56,6 +56,7 @@
|
|||||||
"lucide-svelte": "^0.552.0",
|
"lucide-svelte": "^0.552.0",
|
||||||
"papaparse": "^5.4.1",
|
"papaparse": "^5.4.1",
|
||||||
"svelte-sonner": "^1.0.5",
|
"svelte-sonner": "^1.0.5",
|
||||||
|
"xlsx": "^0.18.5",
|
||||||
"zod": "^4.1.12"
|
"zod": "^4.1.12"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,138 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { useQuery } from 'convex-svelte';
|
||||||
|
import { api } from '@sgse-app/backend/convex/_generated/api';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
value?: string; // Matrícula do funcionário
|
||||||
|
placeholder?: string;
|
||||||
|
disabled?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
let {
|
||||||
|
value = $bindable(''),
|
||||||
|
placeholder = 'Digite a matrícula do funcionário',
|
||||||
|
disabled = false
|
||||||
|
}: Props = $props();
|
||||||
|
|
||||||
|
let busca = $state('');
|
||||||
|
let mostrarDropdown = $state(false);
|
||||||
|
|
||||||
|
// Buscar funcionários
|
||||||
|
const funcionariosQuery = useQuery(api.funcionarios.getAll, {});
|
||||||
|
|
||||||
|
const funcionarios = $derived(funcionariosQuery?.data?.filter((f) => !f.desligamentoData) || []);
|
||||||
|
|
||||||
|
// Filtrar funcionários baseado na busca (por matrícula ou nome)
|
||||||
|
const funcionariosFiltrados = $derived.by(() => {
|
||||||
|
if (!busca.trim()) return funcionarios.slice(0, 10); // Limitar a 10 quando vazio
|
||||||
|
|
||||||
|
const termo = busca.toLowerCase().trim();
|
||||||
|
return funcionarios.filter((f) => {
|
||||||
|
const matriculaMatch = f.matricula?.toLowerCase().includes(termo);
|
||||||
|
const nomeMatch = f.nome?.toLowerCase().includes(termo);
|
||||||
|
return matriculaMatch || nomeMatch;
|
||||||
|
}).slice(0, 20); // Limitar resultados
|
||||||
|
});
|
||||||
|
|
||||||
|
// Sincronizar busca com value externo
|
||||||
|
$effect(() => {
|
||||||
|
if (value !== undefined && value !== busca) {
|
||||||
|
busca = value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function selecionarFuncionario(matricula: string) {
|
||||||
|
busca = matricula;
|
||||||
|
value = matricula;
|
||||||
|
mostrarDropdown = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleFocus() {
|
||||||
|
if (!disabled) {
|
||||||
|
mostrarDropdown = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleBlur() {
|
||||||
|
// Delay para permitir click no dropdown
|
||||||
|
setTimeout(() => {
|
||||||
|
mostrarDropdown = false;
|
||||||
|
}, 200);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleInput() {
|
||||||
|
mostrarDropdown = true;
|
||||||
|
if (busca !== value) {
|
||||||
|
value = busca;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="relative w-full">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
bind:value={busca}
|
||||||
|
oninput={handleInput}
|
||||||
|
{placeholder}
|
||||||
|
{disabled}
|
||||||
|
onfocus={handleFocus}
|
||||||
|
onblur={handleBlur}
|
||||||
|
class="input input-bordered w-full pr-10"
|
||||||
|
autocomplete="off"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div class="pointer-events-none absolute top-1/2 right-3 -translate-y-1/2">
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
class="text-base-content/40 h-5 w-5"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
stroke="currentColor"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width="2"
|
||||||
|
d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{#if mostrarDropdown && funcionariosFiltrados.length > 0}
|
||||||
|
<div
|
||||||
|
class="bg-base-100 border-base-300 absolute z-50 mt-1 max-h-60 w-full overflow-auto rounded-lg border shadow-lg"
|
||||||
|
>
|
||||||
|
{#each funcionariosFiltrados as funcionario}
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onclick={() => selecionarFuncionario(funcionario.matricula || '')}
|
||||||
|
class="hover:bg-base-200 border-base-200 w-full border-b px-4 py-3 text-left transition-colors last:border-b-0"
|
||||||
|
>
|
||||||
|
<div class="font-medium">
|
||||||
|
{#if funcionario.matricula}
|
||||||
|
Matrícula: {funcionario.matricula}
|
||||||
|
{:else}
|
||||||
|
Sem matrícula
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
<div class="text-base-content/60 text-sm">
|
||||||
|
{funcionario.nome}
|
||||||
|
{#if funcionario.descricaoCargo}
|
||||||
|
{funcionario.nome ? ' • ' : ''}
|
||||||
|
{funcionario.descricaoCargo}
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
{#if mostrarDropdown && busca && funcionariosFiltrados.length === 0}
|
||||||
|
<div
|
||||||
|
class="bg-base-100 border-base-300 text-base-content/60 absolute z-50 mt-1 w-full rounded-lg border p-4 text-center shadow-lg"
|
||||||
|
>
|
||||||
|
Nenhum funcionário encontrado
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
134
apps/web/src/lib/components/FuncionarioNomeAutocomplete.svelte
Normal file
134
apps/web/src/lib/components/FuncionarioNomeAutocomplete.svelte
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { useQuery } from 'convex-svelte';
|
||||||
|
import { api } from '@sgse-app/backend/convex/_generated/api';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
value?: string; // Nome do funcionário
|
||||||
|
placeholder?: string;
|
||||||
|
disabled?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
let {
|
||||||
|
value = $bindable(''),
|
||||||
|
placeholder = 'Digite o nome do funcionário',
|
||||||
|
disabled = false
|
||||||
|
}: Props = $props();
|
||||||
|
|
||||||
|
let busca = $state('');
|
||||||
|
let mostrarDropdown = $state(false);
|
||||||
|
|
||||||
|
// Buscar funcionários
|
||||||
|
const funcionariosQuery = useQuery(api.funcionarios.getAll, {});
|
||||||
|
|
||||||
|
const funcionarios = $derived(funcionariosQuery?.data?.filter((f) => !f.desligamentoData) || []);
|
||||||
|
|
||||||
|
// Filtrar funcionários baseado na busca (por nome ou matrícula)
|
||||||
|
const funcionariosFiltrados = $derived.by(() => {
|
||||||
|
if (!busca.trim()) return funcionarios.slice(0, 10); // Limitar a 10 quando vazio
|
||||||
|
|
||||||
|
const termo = busca.toLowerCase().trim();
|
||||||
|
return funcionarios.filter((f) => {
|
||||||
|
const nomeMatch = f.nome?.toLowerCase().includes(termo);
|
||||||
|
const matriculaMatch = f.matricula?.toLowerCase().includes(termo);
|
||||||
|
return nomeMatch || matriculaMatch;
|
||||||
|
}).slice(0, 20); // Limitar resultados
|
||||||
|
});
|
||||||
|
|
||||||
|
// Sincronizar busca com value externo
|
||||||
|
$effect(() => {
|
||||||
|
if (value !== undefined && value !== busca) {
|
||||||
|
busca = value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function selecionarFuncionario(nome: string) {
|
||||||
|
busca = nome;
|
||||||
|
value = nome;
|
||||||
|
mostrarDropdown = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleFocus() {
|
||||||
|
if (!disabled) {
|
||||||
|
mostrarDropdown = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleBlur() {
|
||||||
|
// Delay para permitir click no dropdown
|
||||||
|
setTimeout(() => {
|
||||||
|
mostrarDropdown = false;
|
||||||
|
}, 200);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleInput() {
|
||||||
|
mostrarDropdown = true;
|
||||||
|
if (busca !== value) {
|
||||||
|
value = busca;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="relative w-full">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
bind:value={busca}
|
||||||
|
oninput={handleInput}
|
||||||
|
{placeholder}
|
||||||
|
{disabled}
|
||||||
|
onfocus={handleFocus}
|
||||||
|
onblur={handleBlur}
|
||||||
|
class="input input-bordered w-full pr-10"
|
||||||
|
autocomplete="off"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div class="pointer-events-none absolute top-1/2 right-3 -translate-y-1/2">
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
class="text-base-content/40 h-5 w-5"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
stroke="currentColor"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width="2"
|
||||||
|
d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{#if mostrarDropdown && funcionariosFiltrados.length > 0}
|
||||||
|
<div
|
||||||
|
class="bg-base-100 border-base-300 absolute z-50 mt-1 max-h-60 w-full overflow-auto rounded-lg border shadow-lg"
|
||||||
|
>
|
||||||
|
{#each funcionariosFiltrados as funcionario}
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onclick={() => selecionarFuncionario(funcionario.nome || '')}
|
||||||
|
class="hover:bg-base-200 border-base-200 w-full border-b px-4 py-3 text-left transition-colors last:border-b-0"
|
||||||
|
>
|
||||||
|
<div class="font-medium">{funcionario.nome}</div>
|
||||||
|
<div class="text-base-content/60 text-sm">
|
||||||
|
{#if funcionario.matricula}
|
||||||
|
Matrícula: {funcionario.matricula}
|
||||||
|
{/if}
|
||||||
|
{#if funcionario.descricaoCargo}
|
||||||
|
{funcionario.matricula ? ' • ' : ''}
|
||||||
|
{funcionario.descricaoCargo}
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
{#if mostrarDropdown && busca && funcionariosFiltrados.length === 0}
|
||||||
|
<div
|
||||||
|
class="bg-base-100 border-base-300 text-base-content/60 absolute z-50 mt-1 w-full rounded-lg border p-4 text-center shadow-lg"
|
||||||
|
>
|
||||||
|
Nenhum funcionário encontrado
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
@@ -5,15 +5,23 @@
|
|||||||
import { api } from '@sgse-app/backend/convex/_generated/api';
|
import { api } from '@sgse-app/backend/convex/_generated/api';
|
||||||
import { toast } from 'svelte-sonner';
|
import { toast } from 'svelte-sonner';
|
||||||
import FuncionarioSelect from '$lib/components/FuncionarioSelect.svelte';
|
import FuncionarioSelect from '$lib/components/FuncionarioSelect.svelte';
|
||||||
|
import FuncionarioNomeAutocomplete from '$lib/components/FuncionarioNomeAutocomplete.svelte';
|
||||||
|
import FuncionarioMatriculaAutocomplete from '$lib/components/FuncionarioMatriculaAutocomplete.svelte';
|
||||||
import FileUpload from '$lib/components/FileUpload.svelte';
|
import FileUpload from '$lib/components/FileUpload.svelte';
|
||||||
import ErrorModal from '$lib/components/ErrorModal.svelte';
|
import ErrorModal from '$lib/components/ErrorModal.svelte';
|
||||||
import CalendarioAfastamentos from '$lib/components/CalendarioAfastamentos.svelte';
|
import CalendarioAfastamentos from '$lib/components/CalendarioAfastamentos.svelte';
|
||||||
import type { Id } from '@sgse-app/backend/convex/_generated/dataModel';
|
import type { Id } from '@sgse-app/backend/convex/_generated/dataModel';
|
||||||
|
import jsPDF from 'jspdf';
|
||||||
|
import autoTable from 'jspdf-autotable';
|
||||||
|
import * as XLSX from 'xlsx';
|
||||||
|
import logoGovPE from '$lib/assets/logo_governo_PE.png';
|
||||||
|
import { format } from 'date-fns';
|
||||||
|
import { ptBR } from 'date-fns/locale';
|
||||||
|
|
||||||
const client = useConvexClient();
|
const client = useConvexClient();
|
||||||
|
|
||||||
// Estado da aba ativa
|
// Estado da aba ativa
|
||||||
let abaAtiva = $state<'dashboard' | 'atestado' | 'declaracao' | 'maternidade' | 'paternidade'>(
|
let abaAtiva = $state<'dashboard' | 'atestado' | 'declaracao' | 'maternidade' | 'paternidade' | 'relatorios'>(
|
||||||
'dashboard'
|
'dashboard'
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -489,6 +497,389 @@
|
|||||||
filtroDataInicio = '';
|
filtroDataInicio = '';
|
||||||
filtroDataFim = '';
|
filtroDataFim = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Estados para relatórios
|
||||||
|
let relatorioPeriodoInicio = $state('');
|
||||||
|
let relatorioPeriodoFim = $state('');
|
||||||
|
let relatorioFuncionarioNome = $state('');
|
||||||
|
let relatorioFuncionarioMatricula = $state('');
|
||||||
|
let relatorioCampos = $state({
|
||||||
|
atestados: false,
|
||||||
|
licencaPaternidade: false,
|
||||||
|
licencaMaternidade: false,
|
||||||
|
declaracao: false,
|
||||||
|
todos: false
|
||||||
|
});
|
||||||
|
let gerandoRelatorio = $state(false);
|
||||||
|
|
||||||
|
// Função para obter dados filtrados para relatório
|
||||||
|
async function obterDadosRelatorio() {
|
||||||
|
const dados = dadosQuery?.data;
|
||||||
|
if (!dados) return { atestados: [], licencas: [] };
|
||||||
|
|
||||||
|
let atestados = dados.atestados;
|
||||||
|
let licencas = dados.licencas;
|
||||||
|
|
||||||
|
// Filtro por período
|
||||||
|
if (relatorioPeriodoInicio && relatorioPeriodoFim) {
|
||||||
|
const inicio = new Date(relatorioPeriodoInicio);
|
||||||
|
const fim = new Date(relatorioPeriodoFim);
|
||||||
|
atestados = atestados.filter((a) => {
|
||||||
|
const aInicio = new Date(a.dataInicio);
|
||||||
|
const aFim = new Date(a.dataFim);
|
||||||
|
return (
|
||||||
|
(aInicio >= inicio && aInicio <= fim) ||
|
||||||
|
(aFim >= inicio && aFim <= fim) ||
|
||||||
|
(aInicio <= inicio && aFim >= fim)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
licencas = licencas.filter((l) => {
|
||||||
|
const lInicio = new Date(l.dataInicio);
|
||||||
|
const lFim = new Date(l.dataFim);
|
||||||
|
return (
|
||||||
|
(lInicio >= inicio && lInicio <= fim) ||
|
||||||
|
(lFim >= inicio && lFim <= fim) ||
|
||||||
|
(lInicio <= inicio && lFim >= fim)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filtro por funcionário (nome ou matrícula)
|
||||||
|
if (relatorioFuncionarioNome || relatorioFuncionarioMatricula) {
|
||||||
|
const termoNome = relatorioFuncionarioNome.toLowerCase();
|
||||||
|
const termoMatricula = relatorioFuncionarioMatricula.toLowerCase();
|
||||||
|
atestados = atestados.filter((a) => {
|
||||||
|
const nomeMatch = !termoNome || a.funcionario?.nome?.toLowerCase().includes(termoNome);
|
||||||
|
const matriculaMatch = !termoMatricula || a.funcionario?.matricula?.toLowerCase().includes(termoMatricula);
|
||||||
|
return nomeMatch && matriculaMatch;
|
||||||
|
});
|
||||||
|
licencas = licencas.filter((l) => {
|
||||||
|
const nomeMatch = !termoNome || l.funcionario?.nome?.toLowerCase().includes(termoNome);
|
||||||
|
const matriculaMatch = !termoMatricula || l.funcionario?.matricula?.toLowerCase().includes(termoMatricula);
|
||||||
|
return nomeMatch && matriculaMatch;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filtro por campos selecionados
|
||||||
|
if (!relatorioCampos.todos) {
|
||||||
|
if (!relatorioCampos.atestados) {
|
||||||
|
atestados = atestados.filter((a) => a.tipo !== 'atestado_medico');
|
||||||
|
}
|
||||||
|
if (!relatorioCampos.declaracao) {
|
||||||
|
atestados = atestados.filter((a) => a.tipo !== 'declaracao_comparecimento');
|
||||||
|
}
|
||||||
|
if (!relatorioCampos.licencaMaternidade) {
|
||||||
|
licencas = licencas.filter((l) => l.tipo !== 'maternidade');
|
||||||
|
}
|
||||||
|
if (!relatorioCampos.licencaPaternidade) {
|
||||||
|
licencas = licencas.filter((l) => l.tipo !== 'paternidade');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { atestados, licencas };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Função para gerar PDF
|
||||||
|
async function gerarPDF() {
|
||||||
|
if (!relatorioPeriodoInicio || !relatorioPeriodoFim) {
|
||||||
|
toast.error('Selecione o período para gerar o relatório');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!relatorioCampos.todos && !relatorioCampos.atestados && !relatorioCampos.declaracao && !relatorioCampos.licencaMaternidade && !relatorioCampos.licencaPaternidade) {
|
||||||
|
toast.error('Selecione pelo menos um tipo de campo para incluir no relatório');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
gerandoRelatorio = true;
|
||||||
|
try {
|
||||||
|
const { atestados, licencas } = await obterDadosRelatorio();
|
||||||
|
const doc = new jsPDF();
|
||||||
|
|
||||||
|
// Logo
|
||||||
|
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);
|
||||||
|
});
|
||||||
|
|
||||||
|
const logoWidth = 30;
|
||||||
|
const aspectRatio = logoImg.height / logoImg.width;
|
||||||
|
const logoHeight = logoWidth * aspectRatio;
|
||||||
|
|
||||||
|
doc.addImage(logoImg, 'PNG', 15, 10, logoWidth, logoHeight);
|
||||||
|
yPosition = Math.max(25, 10 + logoHeight / 2);
|
||||||
|
} catch (err) {
|
||||||
|
console.warn('Não foi possível carregar a logo:', err);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Título
|
||||||
|
doc.setFontSize(18);
|
||||||
|
doc.setTextColor(41, 128, 185);
|
||||||
|
doc.text('RELATÓRIO DE ATESTADOS E LICENÇAS', 105, yPosition, { align: 'center' });
|
||||||
|
|
||||||
|
yPosition += 10;
|
||||||
|
|
||||||
|
// Período
|
||||||
|
doc.setFontSize(11);
|
||||||
|
doc.setTextColor(0, 0, 0);
|
||||||
|
doc.text(
|
||||||
|
`Período: ${format(new Date(relatorioPeriodoInicio), 'dd/MM/yyyy', { locale: ptBR })} até ${format(new Date(relatorioPeriodoFim), 'dd/MM/yyyy', { locale: ptBR })}`,
|
||||||
|
105,
|
||||||
|
yPosition,
|
||||||
|
{ align: 'center' }
|
||||||
|
);
|
||||||
|
|
||||||
|
yPosition += 8;
|
||||||
|
|
||||||
|
// Filtros aplicados
|
||||||
|
doc.setFontSize(9);
|
||||||
|
doc.setTextColor(100, 100, 100);
|
||||||
|
let filtrosTexto = 'Filtros: ';
|
||||||
|
if (relatorioFuncionarioNome) filtrosTexto += `Nome: ${relatorioFuncionarioNome}; `;
|
||||||
|
if (relatorioFuncionarioMatricula) filtrosTexto += `Matrícula: ${relatorioFuncionarioMatricula}; `;
|
||||||
|
if (relatorioCampos.todos) filtrosTexto += 'Todos os tipos';
|
||||||
|
else {
|
||||||
|
const tipos = [];
|
||||||
|
if (relatorioCampos.atestados) tipos.push('Atestados');
|
||||||
|
if (relatorioCampos.declaracao) tipos.push('Declarações');
|
||||||
|
if (relatorioCampos.licencaMaternidade) tipos.push('Licença Maternidade');
|
||||||
|
if (relatorioCampos.licencaPaternidade) tipos.push('Licença Paternidade');
|
||||||
|
filtrosTexto += tipos.join(', ');
|
||||||
|
}
|
||||||
|
doc.text(filtrosTexto, 105, yPosition, { align: 'center', maxWidth: 180 });
|
||||||
|
|
||||||
|
yPosition += 10;
|
||||||
|
|
||||||
|
// Data de geração
|
||||||
|
doc.setFontSize(9);
|
||||||
|
doc.text(`Gerado em: ${format(new Date(), 'dd/MM/yyyy HH:mm', { locale: ptBR })}`, 15, yPosition);
|
||||||
|
|
||||||
|
yPosition += 12;
|
||||||
|
|
||||||
|
// Preparar dados para tabela
|
||||||
|
const dadosTabela: string[][] = [];
|
||||||
|
|
||||||
|
// Adicionar atestados
|
||||||
|
if (relatorioCampos.todos || relatorioCampos.atestados || relatorioCampos.declaracao) {
|
||||||
|
atestados.forEach((a) => {
|
||||||
|
const tipo = a.tipo === 'atestado_medico' ? 'Atestado Médico' : 'Declaração';
|
||||||
|
dadosTabela.push([
|
||||||
|
a.funcionario?.nome || '-',
|
||||||
|
a.funcionario?.matricula || '-',
|
||||||
|
tipo,
|
||||||
|
formatarData(a.dataInicio),
|
||||||
|
formatarData(a.dataFim),
|
||||||
|
a.dias.toString(),
|
||||||
|
a.cid || '-',
|
||||||
|
a.status === 'ativo' ? 'Ativo' : 'Finalizado'
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adicionar licenças
|
||||||
|
if (relatorioCampos.todos || relatorioCampos.licencaMaternidade || relatorioCampos.licencaPaternidade) {
|
||||||
|
licencas.forEach((l) => {
|
||||||
|
const tipo = l.tipo === 'maternidade' ? 'Licença Maternidade' : 'Licença Paternidade';
|
||||||
|
dadosTabela.push([
|
||||||
|
l.funcionario?.nome || '-',
|
||||||
|
l.funcionario?.matricula || '-',
|
||||||
|
tipo + (l.ehProrrogacao ? ' (Prorrogação)' : ''),
|
||||||
|
formatarData(l.dataInicio),
|
||||||
|
formatarData(l.dataFim),
|
||||||
|
l.dias.toString(),
|
||||||
|
'-',
|
||||||
|
l.status === 'ativo' ? 'Ativo' : 'Finalizado'
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ordenar por data de início
|
||||||
|
dadosTabela.sort((a, b) => {
|
||||||
|
const dataA = new Date(a[3].split('/').reverse().join('-'));
|
||||||
|
const dataB = new Date(b[3].split('/').reverse().join('-'));
|
||||||
|
return dataA.getTime() - dataB.getTime();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Tabela
|
||||||
|
if (dadosTabela.length > 0) {
|
||||||
|
autoTable(doc, {
|
||||||
|
startY: yPosition,
|
||||||
|
head: [['Funcionário', 'Matrícula', 'Tipo', 'Data Início', 'Data Fim', 'Dias', 'CID', 'Status']],
|
||||||
|
body: dadosTabela,
|
||||||
|
theme: 'striped',
|
||||||
|
headStyles: {
|
||||||
|
fillColor: [41, 128, 185],
|
||||||
|
textColor: [255, 255, 255],
|
||||||
|
fontStyle: 'bold'
|
||||||
|
},
|
||||||
|
styles: { fontSize: 8 },
|
||||||
|
columnStyles: {
|
||||||
|
0: { cellWidth: 50 },
|
||||||
|
1: { cellWidth: 25 },
|
||||||
|
2: { cellWidth: 40 },
|
||||||
|
3: { cellWidth: 25 },
|
||||||
|
4: { cellWidth: 25 },
|
||||||
|
5: { cellWidth: 15 },
|
||||||
|
6: { cellWidth: 20 },
|
||||||
|
7: { cellWidth: 20 }
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Rodapé
|
||||||
|
const pageCount = doc.getNumberOfPages();
|
||||||
|
for (let i = 1; i <= pageCount; i++) {
|
||||||
|
doc.setPage(i);
|
||||||
|
doc.setFontSize(8);
|
||||||
|
doc.setTextColor(128, 128, 128);
|
||||||
|
doc.text(
|
||||||
|
`SGSE - Sistema de Gerenciamento de Secretaria | Página ${i} de ${pageCount}`,
|
||||||
|
doc.internal.pageSize.getWidth() / 2,
|
||||||
|
doc.internal.pageSize.getHeight() - 10,
|
||||||
|
{ align: 'center' }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
doc.setFontSize(12);
|
||||||
|
doc.setTextColor(150, 150, 150);
|
||||||
|
doc.text('Nenhum registro encontrado para os filtros selecionados', 105, yPosition + 20, {
|
||||||
|
align: 'center'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Salvar
|
||||||
|
const nomeArquivo = `relatorio-atestados-licencas-${format(new Date(), 'yyyy-MM-dd-HHmm')}.pdf`;
|
||||||
|
doc.save(nomeArquivo);
|
||||||
|
toast.success('Relatório PDF gerado com sucesso!');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Erro ao gerar PDF:', error);
|
||||||
|
toast.error('Erro ao gerar relatório PDF. Tente novamente.');
|
||||||
|
} finally {
|
||||||
|
gerandoRelatorio = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Função para gerar Excel
|
||||||
|
async function gerarExcel() {
|
||||||
|
if (!relatorioPeriodoInicio || !relatorioPeriodoFim) {
|
||||||
|
toast.error('Selecione o período para gerar o relatório');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!relatorioCampos.todos && !relatorioCampos.atestados && !relatorioCampos.declaracao && !relatorioCampos.licencaMaternidade && !relatorioCampos.licencaPaternidade) {
|
||||||
|
toast.error('Selecione pelo menos um tipo de campo para incluir no relatório');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
gerandoRelatorio = true;
|
||||||
|
try {
|
||||||
|
const { atestados, licencas } = await obterDadosRelatorio();
|
||||||
|
|
||||||
|
// Preparar dados
|
||||||
|
const dados: Array<Record<string, string | number>> = [];
|
||||||
|
|
||||||
|
// Adicionar atestados
|
||||||
|
if (relatorioCampos.todos || relatorioCampos.atestados || relatorioCampos.declaracao) {
|
||||||
|
atestados.forEach((a) => {
|
||||||
|
const tipo = a.tipo === 'atestado_medico' ? 'Atestado Médico' : 'Declaração';
|
||||||
|
dados.push({
|
||||||
|
'Funcionário': a.funcionario?.nome || '-',
|
||||||
|
'Matrícula': a.funcionario?.matricula || '-',
|
||||||
|
'Tipo': tipo,
|
||||||
|
'Data Início': formatarData(a.dataInicio),
|
||||||
|
'Data Fim': formatarData(a.dataFim),
|
||||||
|
'Dias': a.dias,
|
||||||
|
'CID': a.cid || '-',
|
||||||
|
'Status': a.status === 'ativo' ? 'Ativo' : 'Finalizado'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adicionar licenças
|
||||||
|
if (relatorioCampos.todos || relatorioCampos.licencaMaternidade || relatorioCampos.licencaPaternidade) {
|
||||||
|
licencas.forEach((l) => {
|
||||||
|
const tipo = l.tipo === 'maternidade' ? 'Licença Maternidade' : 'Licença Paternidade';
|
||||||
|
dados.push({
|
||||||
|
'Funcionário': l.funcionario?.nome || '-',
|
||||||
|
'Matrícula': l.funcionario?.matricula || '-',
|
||||||
|
'Tipo': tipo + (l.ehProrrogacao ? ' (Prorrogação)' : ''),
|
||||||
|
'Data Início': formatarData(l.dataInicio),
|
||||||
|
'Data Fim': formatarData(l.dataFim),
|
||||||
|
'Dias': l.dias,
|
||||||
|
'CID': '-',
|
||||||
|
'Status': l.status === 'ativo' ? 'Ativo' : 'Finalizado'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ordenar por data de início
|
||||||
|
dados.sort((a, b) => {
|
||||||
|
const dataAStr = a['Data Início'] as string;
|
||||||
|
const dataBStr = b['Data Início'] as string;
|
||||||
|
const dataA = new Date(dataAStr.split('/').reverse().join('-'));
|
||||||
|
const dataB = new Date(dataBStr.split('/').reverse().join('-'));
|
||||||
|
return dataA.getTime() - dataB.getTime();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Criar workbook
|
||||||
|
const wb = XLSX.utils.book_new();
|
||||||
|
|
||||||
|
// Criar worksheet
|
||||||
|
const ws = XLSX.utils.json_to_sheet(dados);
|
||||||
|
|
||||||
|
// Ajustar largura das colunas
|
||||||
|
const colWidths = [
|
||||||
|
{ wch: 30 }, // Funcionário
|
||||||
|
{ wch: 15 }, // Matrícula
|
||||||
|
{ wch: 25 }, // Tipo
|
||||||
|
{ wch: 12 }, // Data Início
|
||||||
|
{ wch: 12 }, // Data Fim
|
||||||
|
{ wch: 8 }, // Dias
|
||||||
|
{ wch: 12 }, // CID
|
||||||
|
{ wch: 12 } // Status
|
||||||
|
];
|
||||||
|
ws['!cols'] = colWidths;
|
||||||
|
|
||||||
|
// Adicionar worksheet ao workbook
|
||||||
|
XLSX.utils.book_append_sheet(wb, ws, 'Relatório');
|
||||||
|
|
||||||
|
// Gerar arquivo
|
||||||
|
const nomeArquivo = `relatorio-atestados-licencas-${format(new Date(), 'yyyy-MM-dd-HHmm')}.xlsx`;
|
||||||
|
XLSX.writeFile(wb, nomeArquivo);
|
||||||
|
|
||||||
|
toast.success('Relatório Excel gerado com sucesso!');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Erro ao gerar Excel:', error);
|
||||||
|
toast.error('Erro ao gerar relatório Excel. Tente novamente.');
|
||||||
|
} finally {
|
||||||
|
gerandoRelatorio = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Função para selecionar/deselecionar todos os campos
|
||||||
|
function toggleTodosCampos() {
|
||||||
|
if (relatorioCampos.todos) {
|
||||||
|
relatorioCampos = {
|
||||||
|
atestados: false,
|
||||||
|
licencaPaternidade: false,
|
||||||
|
licencaMaternidade: false,
|
||||||
|
declaracao: false,
|
||||||
|
todos: false
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
relatorioCampos = {
|
||||||
|
atestados: true,
|
||||||
|
licencaPaternidade: true,
|
||||||
|
licencaMaternidade: true,
|
||||||
|
declaracao: true,
|
||||||
|
todos: true
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
<svelte:head>
|
||||||
@@ -611,6 +1002,26 @@
|
|||||||
>
|
>
|
||||||
Licença Paternidade
|
Licença Paternidade
|
||||||
</button>
|
</button>
|
||||||
|
<button
|
||||||
|
class="tab {abaAtiva === 'relatorios' ? 'tab-active' : ''}"
|
||||||
|
onclick={() => (abaAtiva = 'relatorios')}
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
class="mr-2 h-5 w-5"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
stroke="currentColor"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width="2"
|
||||||
|
d="M17 17h2a2 2 0 002-2v-4a2 2 0 00-2-2H5a2 2 0 00-2 2v4a2 2 0 002 2h2m2 4h6a2 2 0 002-2v-4a2 2 0 00-2-2H9a2 2 0 00-2 2v4a2 2 0 002 2zm8-12V5a2 2 0 00-2-2H9a2 2 0 00-2 2v4h10z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
Imprimir Relatórios
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Conteúdo das Abas -->
|
<!-- Conteúdo das Abas -->
|
||||||
@@ -1659,8 +2070,196 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{:else if abaAtiva === 'relatorios'}
|
||||||
|
<!-- Aba Imprimir Relatórios -->
|
||||||
|
<div class="card bg-base-100 shadow-xl">
|
||||||
|
<div class="card-body">
|
||||||
|
<h2 class="card-title mb-6">
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
class="h-6 w-6"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
stroke="currentColor"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width="2"
|
||||||
|
d="M17 17h2a2 2 0 002-2v-4a2 2 0 00-2-2H5a2 2 0 00-2 2v4a2 2 0 002 2h2m2 4h6a2 2 0 002-2v-4a2 2 0 00-2-2H9a2 2 0 00-2 2v4a2 2 0 002 2zm8-12V5a2 2 0 00-2-2H9a2 2 0 00-2 2v4h10z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
Imprimir Relatórios
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
<div class="grid grid-cols-1 gap-6 md:grid-cols-2">
|
||||||
|
<!-- Filtro de Período -->
|
||||||
|
<div class="form-control">
|
||||||
|
<label class="label">
|
||||||
|
<span class="label-text font-medium"
|
||||||
|
>Data Início <span class="text-error">*</span></span
|
||||||
|
>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="date"
|
||||||
|
bind:value={relatorioPeriodoInicio}
|
||||||
|
class="input input-bordered"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-control">
|
||||||
|
<label class="label">
|
||||||
|
<span class="label-text font-medium">Data Fim <span class="text-error">*</span></span>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="date"
|
||||||
|
bind:value={relatorioPeriodoFim}
|
||||||
|
class="input input-bordered"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Filtro de Funcionário por Nome -->
|
||||||
|
<div class="form-control">
|
||||||
|
<label class="label">
|
||||||
|
<span class="label-text font-medium">Funcionário (Nome)</span>
|
||||||
|
</label>
|
||||||
|
<FuncionarioNomeAutocomplete
|
||||||
|
bind:value={relatorioFuncionarioNome}
|
||||||
|
placeholder="Digite o nome do funcionário"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Filtro de Funcionário por Matrícula -->
|
||||||
|
<div class="form-control">
|
||||||
|
<label class="label">
|
||||||
|
<span class="label-text font-medium">Funcionário (Matrícula)</span>
|
||||||
|
</label>
|
||||||
|
<FuncionarioMatriculaAutocomplete
|
||||||
|
bind:value={relatorioFuncionarioMatricula}
|
||||||
|
placeholder="Digite a matrícula do funcionário"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Seleção de Campos -->
|
||||||
|
<div class="mt-6">
|
||||||
|
<label class="label">
|
||||||
|
<span class="label-text font-medium"
|
||||||
|
>Campos a incluir no relatório <span class="text-error">*</span></span
|
||||||
|
>
|
||||||
|
</label>
|
||||||
|
<div class="grid grid-cols-1 gap-3 md:grid-cols-2 lg:grid-cols-3">
|
||||||
|
<label class="label cursor-pointer justify-start gap-3 rounded-lg border border-base-300 p-4 hover:bg-base-200">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
bind:checked={relatorioCampos.todos}
|
||||||
|
onchange={toggleTodosCampos}
|
||||||
|
class="checkbox checkbox-primary"
|
||||||
|
/>
|
||||||
|
<span class="label-text font-medium">Todos</span>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label class="label cursor-pointer justify-start gap-3 rounded-lg border border-base-300 p-4 hover:bg-base-200">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
bind:checked={relatorioCampos.atestados}
|
||||||
|
class="checkbox checkbox-primary"
|
||||||
|
disabled={relatorioCampos.todos}
|
||||||
|
/>
|
||||||
|
<span class="label-text font-medium">Atestados</span>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label class="label cursor-pointer justify-start gap-3 rounded-lg border border-base-300 p-4 hover:bg-base-200">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
bind:checked={relatorioCampos.licencaPaternidade}
|
||||||
|
class="checkbox checkbox-primary"
|
||||||
|
disabled={relatorioCampos.todos}
|
||||||
|
/>
|
||||||
|
<span class="label-text font-medium">Licença Paternidade</span>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label class="label cursor-pointer justify-start gap-3 rounded-lg border border-base-300 p-4 hover:bg-base-200">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
bind:checked={relatorioCampos.licencaMaternidade}
|
||||||
|
class="checkbox checkbox-primary"
|
||||||
|
disabled={relatorioCampos.todos}
|
||||||
|
/>
|
||||||
|
<span class="label-text font-medium">Licença Maternidade</span>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label class="label cursor-pointer justify-start gap-3 rounded-lg border border-base-300 p-4 hover:bg-base-200">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
bind:checked={relatorioCampos.declaracao}
|
||||||
|
class="checkbox checkbox-primary"
|
||||||
|
disabled={relatorioCampos.todos}
|
||||||
|
/>
|
||||||
|
<span class="label-text font-medium">Declaração</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Botões de Ação -->
|
||||||
|
<div class="card-actions mt-8 justify-end gap-3">
|
||||||
|
<button
|
||||||
|
class="btn btn-outline btn-error gap-2"
|
||||||
|
onclick={gerarPDF}
|
||||||
|
disabled={gerandoRelatorio}
|
||||||
|
>
|
||||||
|
{#if gerandoRelatorio}
|
||||||
|
<span class="loading loading-spinner loading-sm"></span>
|
||||||
|
{:else}
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
class="h-5 w-5"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
stroke="currentColor"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width="2"
|
||||||
|
d="M7 21h10a2 2 0 002-2V9.414a1 1 0 00-.293-.707l-5.414-5.414A1 1 0 0012.586 3H7a2 2 0 00-2 2v14a2 2 0 002 2z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
{/if}
|
{/if}
|
||||||
</main>
|
Imprimir PDF
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="btn btn-outline btn-success gap-2"
|
||||||
|
onclick={gerarExcel}
|
||||||
|
disabled={gerandoRelatorio}
|
||||||
|
>
|
||||||
|
{#if gerandoRelatorio}
|
||||||
|
<span class="loading loading-spinner loading-sm"></span>
|
||||||
|
{:else}
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
class="h-5 w-5"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
stroke="currentColor"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width="2"
|
||||||
|
d="M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
{/if}
|
||||||
|
Exportar Excel
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</main>
|
||||||
|
|
||||||
<!-- Modal de Erro -->
|
<!-- Modal de Erro -->
|
||||||
<ErrorModal
|
<ErrorModal
|
||||||
|
|||||||
19
bun.lock
19
bun.lock
@@ -54,6 +54,7 @@
|
|||||||
"lucide-svelte": "^0.552.0",
|
"lucide-svelte": "^0.552.0",
|
||||||
"papaparse": "^5.4.1",
|
"papaparse": "^5.4.1",
|
||||||
"svelte-sonner": "^1.0.5",
|
"svelte-sonner": "^1.0.5",
|
||||||
|
"xlsx": "^0.18.5",
|
||||||
"zod": "^4.1.12",
|
"zod": "^4.1.12",
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@@ -689,6 +690,8 @@
|
|||||||
|
|
||||||
"acorn-jsx": ["acorn-jsx@5.3.2", "", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="],
|
"acorn-jsx": ["acorn-jsx@5.3.2", "", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="],
|
||||||
|
|
||||||
|
"adler-32": ["adler-32@1.3.1", "", {}, "sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A=="],
|
||||||
|
|
||||||
"ajv": ["ajv@6.12.6", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g=="],
|
"ajv": ["ajv@6.12.6", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g=="],
|
||||||
|
|
||||||
"ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
|
"ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
|
||||||
@@ -753,6 +756,8 @@
|
|||||||
|
|
||||||
"canvg": ["canvg@3.0.11", "", { "dependencies": { "@babel/runtime": "^7.12.5", "@types/raf": "^3.4.0", "core-js": "^3.8.3", "raf": "^3.4.1", "regenerator-runtime": "^0.13.7", "rgbcolor": "^1.0.1", "stackblur-canvas": "^2.0.0", "svg-pathdata": "^6.0.3" } }, "sha512-5ON+q7jCTgMp9cjpu4Jo6XbvfYwSB2Ow3kzHKfIyJfaCAOHLbdKPQqGKgfED/R5B+3TFFfe8pegYA+b423SRyA=="],
|
"canvg": ["canvg@3.0.11", "", { "dependencies": { "@babel/runtime": "^7.12.5", "@types/raf": "^3.4.0", "core-js": "^3.8.3", "raf": "^3.4.1", "regenerator-runtime": "^0.13.7", "rgbcolor": "^1.0.1", "stackblur-canvas": "^2.0.0", "svg-pathdata": "^6.0.3" } }, "sha512-5ON+q7jCTgMp9cjpu4Jo6XbvfYwSB2Ow3kzHKfIyJfaCAOHLbdKPQqGKgfED/R5B+3TFFfe8pegYA+b423SRyA=="],
|
||||||
|
|
||||||
|
"cfb": ["cfb@1.2.2", "", { "dependencies": { "adler-32": "~1.3.0", "crc-32": "~1.2.0" } }, "sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA=="],
|
||||||
|
|
||||||
"chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
|
"chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
|
||||||
|
|
||||||
"chart.js": ["chart.js@4.5.1", "", { "dependencies": { "@kurkle/color": "^0.3.0" } }, "sha512-GIjfiT9dbmHRiYi6Nl2yFCq7kkwdkp1W/lp2J99rX0yo9tgJGn3lKQATztIjb5tVtevcBtIdICNWqlq5+E8/Pw=="],
|
"chart.js": ["chart.js@4.5.1", "", { "dependencies": { "@kurkle/color": "^0.3.0" } }, "sha512-GIjfiT9dbmHRiYi6Nl2yFCq7kkwdkp1W/lp2J99rX0yo9tgJGn3lKQATztIjb5tVtevcBtIdICNWqlq5+E8/Pw=="],
|
||||||
@@ -761,6 +766,8 @@
|
|||||||
|
|
||||||
"clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="],
|
"clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="],
|
||||||
|
|
||||||
|
"codepage": ["codepage@1.15.0", "", {}, "sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA=="],
|
||||||
|
|
||||||
"color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="],
|
"color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="],
|
||||||
|
|
||||||
"color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="],
|
"color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="],
|
||||||
@@ -779,6 +786,8 @@
|
|||||||
|
|
||||||
"core-js": ["core-js@3.46.0", "", {}, "sha512-vDMm9B0xnqqZ8uSBpZ8sNtRtOdmfShrvT6h2TuQGLs0Is+cR0DYbj/KWP6ALVNbWPpqA/qPLoOuppJN07humpA=="],
|
"core-js": ["core-js@3.46.0", "", {}, "sha512-vDMm9B0xnqqZ8uSBpZ8sNtRtOdmfShrvT6h2TuQGLs0Is+cR0DYbj/KWP6ALVNbWPpqA/qPLoOuppJN07humpA=="],
|
||||||
|
|
||||||
|
"crc-32": ["crc-32@1.2.2", "", { "bin": { "crc32": "bin/crc32.njs" } }, "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ=="],
|
||||||
|
|
||||||
"cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="],
|
"cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="],
|
||||||
|
|
||||||
"css-line-break": ["css-line-break@2.1.0", "", { "dependencies": { "utrie": "^1.0.2" } }, "sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w=="],
|
"css-line-break": ["css-line-break@2.1.0", "", { "dependencies": { "utrie": "^1.0.2" } }, "sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w=="],
|
||||||
@@ -909,6 +918,8 @@
|
|||||||
|
|
||||||
"for-each": ["for-each@0.3.5", "", { "dependencies": { "is-callable": "^1.2.7" } }, "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg=="],
|
"for-each": ["for-each@0.3.5", "", { "dependencies": { "is-callable": "^1.2.7" } }, "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg=="],
|
||||||
|
|
||||||
|
"frac": ["frac@1.1.2", "", {}, "sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA=="],
|
||||||
|
|
||||||
"fraction.js": ["fraction.js@4.3.7", "", {}, "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew=="],
|
"fraction.js": ["fraction.js@4.3.7", "", {}, "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew=="],
|
||||||
|
|
||||||
"fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
|
"fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
|
||||||
@@ -1269,6 +1280,8 @@
|
|||||||
|
|
||||||
"source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="],
|
"source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="],
|
||||||
|
|
||||||
|
"ssf": ["ssf@0.11.2", "", { "dependencies": { "frac": "~1.1.2" } }, "sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g=="],
|
||||||
|
|
||||||
"stackblur-canvas": ["stackblur-canvas@2.7.0", "", {}, "sha512-yf7OENo23AGJhBriGx0QivY5JP6Y1HbrrDI6WLt6C5auYZXlQrheoY8hD4ibekFKz1HOfE48Ww8kMWMnJD/zcQ=="],
|
"stackblur-canvas": ["stackblur-canvas@2.7.0", "", {}, "sha512-yf7OENo23AGJhBriGx0QivY5JP6Y1HbrrDI6WLt6C5auYZXlQrheoY8hD4ibekFKz1HOfE48Ww8kMWMnJD/zcQ=="],
|
||||||
|
|
||||||
"stop-iteration-iterator": ["stop-iteration-iterator@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "internal-slot": "^1.1.0" } }, "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ=="],
|
"stop-iteration-iterator": ["stop-iteration-iterator@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "internal-slot": "^1.1.0" } }, "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ=="],
|
||||||
@@ -1385,8 +1398,14 @@
|
|||||||
|
|
||||||
"which-typed-array": ["which-typed-array@1.1.19", "", { "dependencies": { "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", "call-bound": "^1.0.4", "for-each": "^0.3.5", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-tostringtag": "^1.0.2" } }, "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw=="],
|
"which-typed-array": ["which-typed-array@1.1.19", "", { "dependencies": { "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", "call-bound": "^1.0.4", "for-each": "^0.3.5", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-tostringtag": "^1.0.2" } }, "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw=="],
|
||||||
|
|
||||||
|
"wmf": ["wmf@1.0.2", "", {}, "sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw=="],
|
||||||
|
|
||||||
|
"word": ["word@0.3.0", "", {}, "sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA=="],
|
||||||
|
|
||||||
"word-wrap": ["word-wrap@1.2.5", "", {}, "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA=="],
|
"word-wrap": ["word-wrap@1.2.5", "", {}, "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA=="],
|
||||||
|
|
||||||
|
"xlsx": ["xlsx@0.18.5", "", { "dependencies": { "adler-32": "~1.3.0", "cfb": "~1.2.1", "codepage": "~1.15.0", "crc-32": "~1.2.1", "ssf": "~0.11.2", "wmf": "~1.0.1", "word": "~0.3.0" }, "bin": { "xlsx": "bin/xlsx.njs" } }, "sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ=="],
|
||||||
|
|
||||||
"yaml": ["yaml@1.10.2", "", {}, "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg=="],
|
"yaml": ["yaml@1.10.2", "", {}, "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg=="],
|
||||||
|
|
||||||
"yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="],
|
"yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="],
|
||||||
|
|||||||
Reference in New Issue
Block a user