feat: implement homologation deletion and detail viewing features
- Added functionality to delete homologations, restricted to users with managerial permissions. - Introduced modals for viewing details of homologations and confirming deletions, enhancing user interaction. - Updated the backend to support homologation deletion, including necessary permission checks and data integrity management. - Enhanced the UI to display alerts for unassociated employees and active dispensas during point registration, improving user feedback and error handling.
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
import { onMount } from 'svelte';
|
||||
import { useQuery, useConvexClient } from 'convex-svelte';
|
||||
import { api } from '@sgse-app/backend/convex/_generated/api';
|
||||
import { Clock, Edit, TrendingUp, TrendingDown, Save, X } from 'lucide-svelte';
|
||||
import { Clock, Edit, TrendingUp, TrendingDown, Save, X, Trash2, Eye, MoreVertical } from 'lucide-svelte';
|
||||
import type { Id } from '@sgse-app/backend/convex/_generated/dataModel';
|
||||
import { formatarHoraPonto, getTipoRegistroLabel } from '$lib/utils/ponto';
|
||||
import { toast } from 'svelte-sonner';
|
||||
@@ -14,6 +14,10 @@
|
||||
let registroSelecionado = $state<Id<'registrosPonto'> | ''>('');
|
||||
let modoEdicao = $state(false);
|
||||
let abaAtiva = $state<'editar' | 'ajustar'>('editar');
|
||||
let homologacaoDetalhada = $state<Id<'homologacoesPonto'> | null>(null);
|
||||
let homologacaoParaExcluir = $state<Id<'homologacoesPonto'> | null>(null);
|
||||
let mostrandoModalDetalhes = $state(false);
|
||||
let mostrandoModalExcluir = $state(false);
|
||||
|
||||
// Formulário de edição
|
||||
let horaNova = $state(8);
|
||||
@@ -111,6 +115,9 @@
|
||||
const motivos = $derived(motivosQuery?.data);
|
||||
const homologacoes = $derived(homologacoesQuery?.data || []);
|
||||
const registros = $derived(registrosQuery?.data || []);
|
||||
|
||||
// Verificar se é gestor (tem subordinados)
|
||||
const isGestor = $derived(subordinados.length > 0);
|
||||
|
||||
// Lista de funcionários do time
|
||||
const funcionarios = $derived.by(() => {
|
||||
@@ -142,7 +149,7 @@
|
||||
motivoDescricao = '';
|
||||
observacoes = '';
|
||||
modoEdicao = true;
|
||||
modoAjuste = false;
|
||||
abaAtiva = 'editar';
|
||||
}
|
||||
|
||||
function abrirEdicaoComAjuste(registroId: Id<'registrosPonto'>) {
|
||||
@@ -253,6 +260,61 @@
|
||||
toast.error(`Erro ao ajustar banco de horas: ${errorMessage}`);
|
||||
}
|
||||
}
|
||||
|
||||
function abrirDetalhes(homologacaoId: Id<'homologacoesPonto'>) {
|
||||
homologacaoDetalhada = homologacaoId;
|
||||
mostrandoModalDetalhes = true;
|
||||
}
|
||||
|
||||
function fecharDetalhes() {
|
||||
mostrandoModalDetalhes = false;
|
||||
homologacaoDetalhada = null;
|
||||
}
|
||||
|
||||
function abrirModalExcluir(homologacaoId: Id<'homologacoesPonto'>) {
|
||||
homologacaoParaExcluir = homologacaoId;
|
||||
mostrandoModalExcluir = true;
|
||||
}
|
||||
|
||||
function fecharModalExcluir() {
|
||||
mostrandoModalExcluir = false;
|
||||
homologacaoParaExcluir = null;
|
||||
}
|
||||
|
||||
async function excluirHomologacao() {
|
||||
if (!homologacaoParaExcluir) return;
|
||||
|
||||
try {
|
||||
await client.mutation(api.pontos.excluirHomologacao, {
|
||||
homologacaoId: homologacaoParaExcluir,
|
||||
});
|
||||
|
||||
toast.success('Homologação excluída com sucesso');
|
||||
fecharModalExcluir();
|
||||
} catch (error) {
|
||||
const errorMessage = error instanceof Error ? error.message : String(error);
|
||||
toast.error(`Erro ao excluir homologação: ${errorMessage}`);
|
||||
}
|
||||
}
|
||||
|
||||
function editarHomologacao(homologacaoId: Id<'homologacoesPonto'>) {
|
||||
const homologacao = homologacoes.find((h) => h._id === homologacaoId);
|
||||
if (!homologacao) return;
|
||||
|
||||
// Se for edição de registro, abrir edição do registro
|
||||
if (homologacao.registroId) {
|
||||
funcionarioSelecionado = homologacao.funcionarioId;
|
||||
abrirEdicaoComAjuste(homologacao.registroId);
|
||||
} else {
|
||||
// Se for ajuste de banco de horas, não há como editar diretamente
|
||||
toast.info('Ajustes de banco de horas não podem ser editados. Crie um novo ajuste para corrigir.');
|
||||
}
|
||||
}
|
||||
|
||||
const homologacaoSelecionada = $derived.by(() => {
|
||||
if (!homologacaoDetalhada) return null;
|
||||
return homologacoes.find((h) => h._id === homologacaoDetalhada) || null;
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="container mx-auto px-4 py-6">
|
||||
@@ -667,6 +729,9 @@
|
||||
<th>Detalhes</th>
|
||||
<th>Motivo</th>
|
||||
<th>Observações</th>
|
||||
{#if isGestor}
|
||||
<th>Ações</th>
|
||||
{/if}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -723,6 +788,35 @@
|
||||
{homologacao.observacoes || '-'}
|
||||
</div>
|
||||
</td>
|
||||
{#if isGestor}
|
||||
<td>
|
||||
<div class="flex gap-1">
|
||||
<button
|
||||
class="btn btn-sm btn-outline btn-info btn-square hover:btn-info hover:scale-110 transition-transform"
|
||||
onclick={() => abrirDetalhes(homologacao._id)}
|
||||
title="Ver detalhes"
|
||||
>
|
||||
<Eye class="h-4 w-4" />
|
||||
</button>
|
||||
{#if homologacao.registroId}
|
||||
<button
|
||||
class="btn btn-sm btn-square bg-primary/10 text-primary border-primary/20 hover:bg-primary/20 hover:border-primary/40 hover:shadow-md transition-all"
|
||||
onclick={() => editarHomologacao(homologacao._id)}
|
||||
title="Editar"
|
||||
>
|
||||
<Edit class="h-4 w-4" />
|
||||
</button>
|
||||
{/if}
|
||||
<button
|
||||
class="btn btn-sm btn-square bg-error/10 text-error border-error/20 hover:bg-error/20 hover:border-error/40 hover:shadow-md transition-all"
|
||||
onclick={() => abrirModalExcluir(homologacao._id)}
|
||||
title="Excluir"
|
||||
>
|
||||
<Trash2 class="h-4 w-4" />
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
{/if}
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
@@ -732,5 +826,167 @@
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<!-- Modal de Detalhes da Homologação -->
|
||||
{#if mostrandoModalDetalhes && homologacaoSelecionada}
|
||||
<div class="modal modal-open">
|
||||
<div class="modal-box max-w-3xl">
|
||||
<h3 class="font-bold text-lg mb-4">Detalhes da Homologação</h3>
|
||||
|
||||
<div class="space-y-4">
|
||||
<!-- Informações Gerais -->
|
||||
<div class="card bg-base-200">
|
||||
<div class="card-body p-4">
|
||||
<h4 class="font-semibold mb-3">Informações Gerais</h4>
|
||||
<div class="grid grid-cols-2 gap-4 text-sm">
|
||||
<div>
|
||||
<span class="font-medium">Data:</span>{' '}
|
||||
{new Date(homologacaoSelecionada.criadoEm).toLocaleDateString('pt-BR', {
|
||||
day: '2-digit',
|
||||
month: '2-digit',
|
||||
year: 'numeric',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit'
|
||||
})}
|
||||
</div>
|
||||
<div>
|
||||
<span class="font-medium">Funcionário:</span>{' '}
|
||||
{homologacaoSelecionada.funcionario?.nome || '-'}
|
||||
{#if homologacaoSelecionada.funcionario?.matricula}
|
||||
<br />
|
||||
<span class="text-xs text-base-content/70">
|
||||
Mat: {homologacaoSelecionada.funcionario.matricula}
|
||||
</span>
|
||||
{/if}
|
||||
</div>
|
||||
<div>
|
||||
<span class="font-medium">Gestor:</span>{' '}
|
||||
{homologacaoSelecionada.gestor?.nome || '-'}
|
||||
</div>
|
||||
<div>
|
||||
<span class="font-medium">Tipo:</span>{' '}
|
||||
{#if homologacaoSelecionada.registroId}
|
||||
<span class="badge badge-info">Edição de Registro</span>
|
||||
{:else if homologacaoSelecionada.tipoAjuste}
|
||||
<span class="badge badge-warning">
|
||||
Ajuste: {homologacaoSelecionada.tipoAjuste}
|
||||
</span>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Detalhes do Registro ou Ajuste -->
|
||||
{#if homologacaoSelecionada.registroId}
|
||||
<div class="card bg-base-200">
|
||||
<div class="card-body p-4">
|
||||
<h4 class="font-semibold mb-3">Edição de Registro</h4>
|
||||
<div class="text-sm space-y-2">
|
||||
<div>
|
||||
<span class="font-medium">Horário Anterior:</span>{' '}
|
||||
<span class="line-through opacity-70">
|
||||
{formatarHoraPonto(homologacaoSelecionada.horaAnterior || 0, homologacaoSelecionada.minutoAnterior || 0)}
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<span class="font-medium">Horário Novo:</span>{' '}
|
||||
<span>
|
||||
{formatarHoraPonto(homologacaoSelecionada.horaNova || 0, homologacaoSelecionada.minutoNova || 0)}
|
||||
</span>
|
||||
</div>
|
||||
{#if homologacaoSelecionada.registro}
|
||||
<div>
|
||||
<span class="font-medium">Data do Registro:</span>{' '}
|
||||
{new Date(homologacaoSelecionada.registro.data).toLocaleDateString('pt-BR')}
|
||||
</div>
|
||||
<div>
|
||||
<span class="font-medium">Tipo de Registro:</span>{' '}
|
||||
{getTipoRegistroLabel(homologacaoSelecionada.registro.tipo)}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{:else if homologacaoSelecionada.tipoAjuste}
|
||||
<div class="card bg-base-200">
|
||||
<div class="card-body p-4">
|
||||
<h4 class="font-semibold mb-3">Ajuste de Banco de Horas</h4>
|
||||
<div class="text-sm space-y-2">
|
||||
<div>
|
||||
<span class="font-medium">Tipo de Ajuste:</span>{' '}
|
||||
{homologacaoSelecionada.tipoAjuste}
|
||||
</div>
|
||||
<div>
|
||||
<span class="font-medium">Período:</span>{' '}
|
||||
{homologacaoSelecionada.periodoDias || 0}d {homologacaoSelecionada.periodoHoras || 0}h{' '}
|
||||
{homologacaoSelecionada.periodoMinutos || 0}min
|
||||
</div>
|
||||
{#if homologacaoSelecionada.ajusteMinutos}
|
||||
<div>
|
||||
<span class="font-medium">Ajuste Total:</span>{' '}
|
||||
{homologacaoSelecionada.ajusteMinutos > 0 ? '+' : ''}
|
||||
{Math.floor(Math.abs(homologacaoSelecionada.ajusteMinutos) / 60)}h{' '}
|
||||
{Math.abs(homologacaoSelecionada.ajusteMinutos) % 60}min
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<!-- Motivo e Observações -->
|
||||
<div class="card bg-base-200">
|
||||
<div class="card-body p-4">
|
||||
<h4 class="font-semibold mb-3">Motivo e Observações</h4>
|
||||
<div class="text-sm space-y-2">
|
||||
<div>
|
||||
<span class="font-medium">Tipo de Motivo:</span>{' '}
|
||||
{homologacaoSelecionada.motivoTipo || '-'}
|
||||
</div>
|
||||
<div>
|
||||
<span class="font-medium">Descrição:</span>{' '}
|
||||
{homologacaoSelecionada.motivoDescricao || '-'}
|
||||
</div>
|
||||
{#if homologacaoSelecionada.observacoes}
|
||||
<div>
|
||||
<span class="font-medium">Observações:</span>
|
||||
<div class="mt-1 p-2 bg-base-100 rounded">
|
||||
{homologacaoSelecionada.observacoes}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal-action">
|
||||
<button class="btn" onclick={fecharDetalhes}>Fechar</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-backdrop" onclick={fecharDetalhes}></div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<!-- Modal de Confirmação de Exclusão -->
|
||||
{#if mostrandoModalExcluir && homologacaoParaExcluir}
|
||||
<div class="modal modal-open">
|
||||
<div class="modal-box">
|
||||
<h3 class="font-bold text-lg mb-4">Confirmar Exclusão</h3>
|
||||
<p class="mb-4">
|
||||
Tem certeza que deseja excluir esta homologação? Esta ação não pode ser desfeita.
|
||||
</p>
|
||||
<div class="modal-action">
|
||||
<button class="btn btn-ghost" onclick={fecharModalExcluir}>Cancelar</button>
|
||||
<button class="btn btn-error" onclick={excluirHomologacao}>
|
||||
<Trash2 class="h-4 w-4 mr-2" />
|
||||
Excluir
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-backdrop" onclick={fecharModalExcluir}></div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user