feat: update PDF generation to use symbols for day types and implement confirmation modal for record deletion in absence and license management, enhancing user experience and data integrity

This commit is contained in:
2025-12-23 19:39:10 -03:00
parent a731015c89
commit 5369a2ecc9
3 changed files with 135 additions and 21 deletions

View File

@@ -216,14 +216,14 @@ function gerarTabelaRegistrosPDF(
}
};
// Função auxiliar para obter ícone do tipo de dia
const obterIconeTipoDia = (dia: DiaFichaPonto): string => {
if (dia.atestado) return '🏥';
if (dia.ausencia) return '🚫';
if (dia.licenca) return '📋';
if (dia.tipoDia === 'abonado') return '';
if (dia.tipoDia === 'nao_computado') return '';
if (dia.inconsistencias.length > 0) return '';
// Função auxiliar para obter símbolo do tipo de dia
const obterSimboloTipoDia = (dia: DiaFichaPonto): string => {
if (dia.atestado) return 'AT';
if (dia.ausencia) return 'AUS';
if (dia.licenca) return 'LIC';
if (dia.tipoDia === 'abonado') return 'ABO';
if (dia.tipoDia === 'nao_computado') return 'NC';
if (dia.inconsistencias.length > 0) return 'INC';
return '';
};
@@ -258,8 +258,10 @@ function gerarTabelaRegistrosPDF(
// Coluna Data (apenas na primeira linha)
if (i === 0) {
const simbolo = obterSimboloTipoDia(dia);
const dataComSimbolo = simbolo ? `${dataFormatada} [${simbolo}]` : dataFormatada;
linha.push({
content: `${dataFormatada} ${obterIconeTipoDia(dia)}`,
content: dataComSimbolo,
styles: {
fillColor: obterCorFundoTipoDia(dia.tipoDia),
fontStyle: 'bold'

View File

@@ -6,6 +6,7 @@
import FuncionarioMatriculaAutocomplete from '$lib/components/FuncionarioMatriculaAutocomplete.svelte';
import FileUpload from '$lib/components/FileUpload.svelte';
import ErrorModal from '$lib/components/ErrorModal.svelte';
import ConfirmModal from '$lib/components/ConfirmModal.svelte';
import CalendarioAfastamentos from '$lib/components/CalendarioAfastamentos.svelte';
import AreaChart from '$lib/components/ti/charts/AreaChart.svelte';
import UserAvatar from '$lib/components/chat/UserAvatar.svelte';
@@ -132,6 +133,14 @@
titulo: ''
});
// Modal de exclusão
let exclusaoModal = $state({
aberto: false,
tipo: null as 'atestado' | 'licenca' | null,
id: null as string | null,
nome: ''
});
// Licenças maternidade para prorrogação (derivar dos dados já carregados)
let licencasMaternidade = $derived.by(() => {
const dados = dadosQuery?.data;
@@ -651,26 +660,42 @@
}
});
// Excluir registro
async function excluirRegistro(tipo: 'atestado' | 'licenca', id: string) {
if (!confirm(`Tem certeza que deseja excluir este ${tipo}?`)) return;
// Abrir modal de exclusão
function abrirModalExclusao(tipo: 'atestado' | 'licenca', id: string, nome: string) {
exclusaoModal = {
aberto: true,
tipo,
id,
nome
};
}
// Confirmar exclusão
async function confirmarExclusao() {
if (!exclusaoModal.tipo || !exclusaoModal.id) return;
try {
if (tipo === 'atestado') {
if (exclusaoModal.tipo === 'atestado') {
await client.mutation(api.atestadosLicencas.excluirAtestado, {
id: id as Id<'atestados'>
id: exclusaoModal.id as Id<'atestados'>
});
} else {
await client.mutation(api.atestadosLicencas.excluirLicenca, {
id: id as Id<'licencas'>
id: exclusaoModal.id as Id<'licencas'>
});
}
toast.success('Registro excluído com sucesso!');
exclusaoModal.aberto = false;
} catch (error: unknown) {
toast.error(getErrorMessage(error, 'Erro ao excluir registro'));
}
}
// Cancelar exclusão
function cancelarExclusao() {
exclusaoModal.aberto = false;
}
// Filtrar registros
let registrosFiltrados = $derived.by(() => {
const dados = dadosQuery?.data;
@@ -1677,7 +1702,12 @@
{/if}
<button
class="btn btn-xs btn-error"
onclick={() => excluirRegistro('atestado', atestado._id)}
onclick={() =>
abrirModalExclusao(
'atestado',
atestado._id,
`${atestado.tipo === 'atestado_medico' ? 'Atestado Médico' : 'Declaração'} - ${atestado.funcionario?.nome || 'Funcionário'}`
)}
>
Excluir
</button>
@@ -1739,7 +1769,12 @@
{/if}
<button
class="btn btn-xs btn-error"
onclick={() => excluirRegistro('licenca', licenca._id)}
onclick={() =>
abrirModalExclusao(
'licenca',
licenca._id,
`${licenca.tipo === 'maternidade' ? 'Licença Maternidade' : 'Licença Paternidade'} - ${licenca.funcionario?.nome || 'Funcionário'}`
)}
>
Excluir
</button>
@@ -2438,6 +2473,17 @@
}}
/>
<!-- Modal de Confirmação de Exclusão -->
<ConfirmModal
bind:open={exclusaoModal.aberto}
title="Confirmar Exclusão"
message="Tem certeza que deseja excluir este registro? Esta ação não pode ser desfeita e todos os dados relacionados serão removidos permanentemente."
confirmText="Excluir"
cancelText="Cancelar"
onConfirm={confirmarExclusao}
onCancel={cancelarExclusao}
/>
<!-- Modal de Documento -->
{#if documentoModal.aberto}
<!-- svelte-ignore a11y_click_events_have_key_events -->

View File

@@ -182,7 +182,7 @@ export const listarTodos = query({
fotoPerfilUrl,
criadoPorNome: criadoPor?.nome || 'Sistema',
dias: calcularDias(a.dataInicio, a.dataFim),
status: new Date(a.dataFim) >= new Date() ? 'ativo' : 'finalizado'
status: new Date() > new Date(a.dataFim) ? 'finalizado' : 'ativo'
};
} catch (error) {
console.error('Erro ao buscar detalhes do atestado:', error);
@@ -192,7 +192,7 @@ export const listarTodos = query({
fotoPerfilUrl: null,
criadoPorNome: 'Sistema',
dias: calcularDias(a.dataInicio, a.dataFim),
status: new Date(a.dataFim) >= new Date() ? 'ativo' : 'finalizado'
status: new Date() > new Date(a.dataFim) ? 'finalizado' : 'ativo'
};
}
})
@@ -226,7 +226,7 @@ export const listarTodos = query({
criadoPorNome: criadoPor?.nome || 'Sistema',
licencaOriginal,
dias: calcularDias(l.dataInicio, l.dataFim),
status: new Date(l.dataFim) >= new Date() ? 'ativo' : 'finalizado'
status: new Date() > new Date(l.dataFim) ? 'finalizado' : 'ativo'
};
} catch (error) {
console.error('Erro ao buscar detalhes da licença:', error);
@@ -237,7 +237,7 @@ export const listarTodos = query({
criadoPorNome: 'Sistema',
licencaOriginal: null,
dias: calcularDias(l.dataInicio, l.dataFim),
status: new Date(l.dataFim) >= new Date() ? 'ativo' : 'finalizado'
status: new Date() > new Date(l.dataFim) ? 'finalizado' : 'ativo'
};
}
})
@@ -1255,6 +1255,32 @@ export const excluirAtestado = mutation({
const dataInicio = atestado.dataInicio; // Data início do atestado
const dataFim = atestado.dataFim; // Data fim do atestado
const atestadoId = args.id.toString(); // ID do atestado para remover ajustes
const documentoId = atestado.documentoId; // ID do documento para remover do storage
// Remover logs de atividades relacionados ao atestado
try {
const logs = await ctx.db
.query('logsAtividades')
.withIndex('by_recurso_id', (q) =>
q.eq('recurso', 'atestados').eq('recursoId', atestadoId)
)
.collect();
for (const log of logs) {
await ctx.db.delete(log._id);
}
} catch (error) {
console.error('[excluirAtestado] Erro ao remover logs de atividades:', error);
}
// Remover documento do storage se existir
if (documentoId) {
try {
await ctx.storage.delete(documentoId);
} catch (error) {
console.error('[excluirAtestado] Erro ao remover documento do storage:', error);
// Não falhar a exclusão se o documento não existir mais
}
}
// Excluir o registro do banco de dados
await ctx.db.delete(args.id);
@@ -1319,6 +1345,33 @@ export const excluirLicenca = mutation({
const funcionarioId = licenca.funcionarioId;
const dataInicio = licenca.dataInicio; // Data início da licença
const dataFim = licenca.dataFim; // Data fim da licença
const licencaId = args.id.toString(); // ID da licença para remover logs
const documentoId = licenca.documentoId; // ID do documento para remover do storage
// Remover logs de atividades relacionados à licença
try {
const logs = await ctx.db
.query('logsAtividades')
.withIndex('by_recurso_id', (q) =>
q.eq('recurso', 'licencas').eq('recursoId', licencaId)
)
.collect();
for (const log of logs) {
await ctx.db.delete(log._id);
}
} catch (error) {
console.error('[excluirLicenca] Erro ao remover logs de atividades:', error);
}
// Remover documento do storage se existir
if (documentoId) {
try {
await ctx.storage.delete(documentoId);
} catch (error) {
console.error('[excluirLicenca] Erro ao remover documento do storage:', error);
// Não falhar a exclusão se o documento não existir mais
}
}
// Excluir o registro do banco de dados
await ctx.db.delete(args.id);
@@ -1332,6 +1385,19 @@ export const excluirLicenca = mutation({
args.id
);
// Remover ajustes automáticos relacionados à licença excluída
try {
await ctx.runMutation(internal.pontos.removerAjustesAutomaticosInternal, {
funcionarioId,
motivoTipo: 'licenca',
motivoId: licencaId,
dataInicio,
dataFim
});
} catch (error) {
console.error('[excluirLicenca] Erro ao remover ajustes automáticos:', error);
}
// Recalcular banco de horas APENAS para o período específico da licença excluída
// Isso garante que os dias da licença sejam removidos corretamente dos registros de ponto
await recalcularBancoHorasPeriodo(ctx, funcionarioId, dataInicio, dataFim);