Ajustes final etapa1 #71

Merged
killer-cf merged 12 commits from ajustes_final_etapa1 into master 2025-12-29 17:28:52 +00:00
13 changed files with 803 additions and 190 deletions
Showing only changes of commit 5369a2ecc9 - Show all commits

View File

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

View File

@@ -6,6 +6,7 @@
import FuncionarioMatriculaAutocomplete from '$lib/components/FuncionarioMatriculaAutocomplete.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 ConfirmModal from '$lib/components/ConfirmModal.svelte';
import CalendarioAfastamentos from '$lib/components/CalendarioAfastamentos.svelte'; import CalendarioAfastamentos from '$lib/components/CalendarioAfastamentos.svelte';
import AreaChart from '$lib/components/ti/charts/AreaChart.svelte'; import AreaChart from '$lib/components/ti/charts/AreaChart.svelte';
import UserAvatar from '$lib/components/chat/UserAvatar.svelte'; import UserAvatar from '$lib/components/chat/UserAvatar.svelte';
@@ -132,6 +133,14 @@
titulo: '' 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) // Licenças maternidade para prorrogação (derivar dos dados já carregados)
let licencasMaternidade = $derived.by(() => { let licencasMaternidade = $derived.by(() => {
const dados = dadosQuery?.data; const dados = dadosQuery?.data;
@@ -651,26 +660,42 @@
} }
}); });
// Excluir registro // Abrir modal de exclusão
async function excluirRegistro(tipo: 'atestado' | 'licenca', id: string) { function abrirModalExclusao(tipo: 'atestado' | 'licenca', id: string, nome: string) {
if (!confirm(`Tem certeza que deseja excluir este ${tipo}?`)) return; exclusaoModal = {
aberto: true,
tipo,
id,
nome
};
}
// Confirmar exclusão
async function confirmarExclusao() {
if (!exclusaoModal.tipo || !exclusaoModal.id) return;
try { try {
if (tipo === 'atestado') { if (exclusaoModal.tipo === 'atestado') {
await client.mutation(api.atestadosLicencas.excluirAtestado, { await client.mutation(api.atestadosLicencas.excluirAtestado, {
id: id as Id<'atestados'> id: exclusaoModal.id as Id<'atestados'>
}); });
} else { } else {
await client.mutation(api.atestadosLicencas.excluirLicenca, { await client.mutation(api.atestadosLicencas.excluirLicenca, {
id: id as Id<'licencas'> id: exclusaoModal.id as Id<'licencas'>
}); });
} }
toast.success('Registro excluído com sucesso!'); toast.success('Registro excluído com sucesso!');
exclusaoModal.aberto = false;
} catch (error: unknown) { } catch (error: unknown) {
toast.error(getErrorMessage(error, 'Erro ao excluir registro')); toast.error(getErrorMessage(error, 'Erro ao excluir registro'));
} }
} }
// Cancelar exclusão
function cancelarExclusao() {
exclusaoModal.aberto = false;
}
// Filtrar registros // Filtrar registros
let registrosFiltrados = $derived.by(() => { let registrosFiltrados = $derived.by(() => {
const dados = dadosQuery?.data; const dados = dadosQuery?.data;
@@ -1677,7 +1702,12 @@
{/if} {/if}
<button <button
class="btn btn-xs btn-error" 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 Excluir
</button> </button>
@@ -1739,7 +1769,12 @@
{/if} {/if}
<button <button
class="btn btn-xs btn-error" 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 Excluir
</button> </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 --> <!-- Modal de Documento -->
{#if documentoModal.aberto} {#if documentoModal.aberto}
<!-- svelte-ignore a11y_click_events_have_key_events --> <!-- svelte-ignore a11y_click_events_have_key_events -->

View File

@@ -182,7 +182,7 @@ export const listarTodos = query({
fotoPerfilUrl, fotoPerfilUrl,
criadoPorNome: criadoPor?.nome || 'Sistema', criadoPorNome: criadoPor?.nome || 'Sistema',
dias: calcularDias(a.dataInicio, a.dataFim), 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) { } catch (error) {
console.error('Erro ao buscar detalhes do atestado:', error); console.error('Erro ao buscar detalhes do atestado:', error);
@@ -192,7 +192,7 @@ export const listarTodos = query({
fotoPerfilUrl: null, fotoPerfilUrl: null,
criadoPorNome: 'Sistema', criadoPorNome: 'Sistema',
dias: calcularDias(a.dataInicio, a.dataFim), 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', criadoPorNome: criadoPor?.nome || 'Sistema',
licencaOriginal, licencaOriginal,
dias: calcularDias(l.dataInicio, l.dataFim), 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) { } catch (error) {
console.error('Erro ao buscar detalhes da licença:', error); console.error('Erro ao buscar detalhes da licença:', error);
@@ -237,7 +237,7 @@ export const listarTodos = query({
criadoPorNome: 'Sistema', criadoPorNome: 'Sistema',
licencaOriginal: null, licencaOriginal: null,
dias: calcularDias(l.dataInicio, l.dataFim), 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 dataInicio = atestado.dataInicio; // Data início do atestado
const dataFim = atestado.dataFim; // Data fim do atestado const dataFim = atestado.dataFim; // Data fim do atestado
const atestadoId = args.id.toString(); // ID do atestado para remover ajustes 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 // Excluir o registro do banco de dados
await ctx.db.delete(args.id); await ctx.db.delete(args.id);
@@ -1319,6 +1345,33 @@ export const excluirLicenca = mutation({
const funcionarioId = licenca.funcionarioId; const funcionarioId = licenca.funcionarioId;
const dataInicio = licenca.dataInicio; // Data início da licença const dataInicio = licenca.dataInicio; // Data início da licença
const dataFim = licenca.dataFim; // Data fim 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 // Excluir o registro do banco de dados
await ctx.db.delete(args.id); await ctx.db.delete(args.id);
@@ -1332,6 +1385,19 @@ export const excluirLicenca = mutation({
args.id 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 // 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 // Isso garante que os dias da licença sejam removidos corretamente dos registros de ponto
await recalcularBancoHorasPeriodo(ctx, funcionarioId, dataInicio, dataFim); await recalcularBancoHorasPeriodo(ctx, funcionarioId, dataInicio, dataFim);