refactor: improve UI and functionality in employee registration and audit pages

- Enhanced the employee registration form by adding a dependents management section, allowing users to input details such as relationship, name, CPF, and birth date.
- Updated the layout and styling of the audit page, including improved statistics display and user feedback elements.
- Refined the handling of user actions in the audit logs, providing clearer labels and better organization of information.
- Improved the overall user experience by ensuring consistent design patterns and responsive elements across the registration and audit interfaces.
This commit is contained in:
2025-11-04 06:31:28 -03:00
parent d5c01aabab
commit f7cc758d33
3 changed files with 1848 additions and 1708 deletions

View File

@@ -15,7 +15,8 @@
month: '2-digit',
year: 'numeric',
hour: '2-digit',
minute: '2-digit'
minute: '2-digit',
second: '2-digit'
});
}
@@ -30,55 +31,128 @@
};
return colors[acao] || "badge-neutral";
}
function getAcaoLabel(acao: string) {
const labels: Record<string, string> = {
criar: "Criar",
editar: "Editar",
excluir: "Excluir",
bloquear: "Bloquear",
desbloquear: "Desbloquear",
resetar_senha: "Resetar Senha"
};
return labels[acao] || acao;
}
// Estatísticas
const totalAtividades = $derived(atividades?.data?.length || 0);
const totalLogins = $derived(logins?.data?.length || 0);
const loginsSucesso = $derived(logins?.data?.filter(l => l.sucesso).length || 0);
const loginsFalha = $derived(logins?.data?.filter(l => !l.sucesso).length || 0);
</script>
<div class="container mx-auto px-4 py-6 max-w-7xl">
<main class="container mx-auto px-4 py-6 max-w-7xl">
<!-- Breadcrumb -->
<div class="text-sm breadcrumbs mb-4">
<ul>
<li><a href="/ti" class="text-primary hover:underline">TI</a></li>
<li>Auditoria e Logs</li>
</ul>
</div>
<!-- Header -->
<div class="flex items-center justify-between mb-6">
<div class="flex items-center gap-4">
<div class="p-3 bg-accent/10 rounded-xl">
<svg xmlns="http://www.w3.org/2000/svg" class="h-8 w-8 text-accent" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<div class="mb-6">
<div class="flex items-center gap-4 mb-2">
<div class="p-3 bg-blue-500/20 rounded-xl">
<svg xmlns="http://www.w3.org/2000/svg" class="h-8 w-8 text-blue-600" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 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>
</div>
<div>
<h1 class="text-3xl font-bold text-base-content">Auditoria e Logs</h1>
<p class="text-base-content/60 mt-1">Histórico completo de atividades e acessos</p>
<h1 class="text-3xl font-bold text-primary">Auditoria e Logs</h1>
<p class="text-base-content/70">Monitoramento completo de atividades e acessos do sistema</p>
</div>
</div>
</div>
<!-- Cards de Estatísticas -->
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4 mb-6">
<div class="stat bg-base-100 shadow-lg rounded-lg">
<div class="stat-figure text-primary">
<svg xmlns="http://www.w3.org/2000/svg" class="h-8 w-8" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2" />
</svg>
</div>
<div class="stat-title text-xs">Atividades</div>
<div class="stat-value text-2xl">{totalAtividades}</div>
<div class="stat-desc">Registros exibidos</div>
</div>
<div class="stat bg-base-100 shadow-lg rounded-lg">
<div class="stat-figure text-success">
<svg xmlns="http://www.w3.org/2000/svg" class="h-8 w-8" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 16l-4-4m0 0l4-4m-4 4h14m-5 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h7a3 3 0 013 3v1" />
</svg>
</div>
<div class="stat-title text-xs">Logins Totais</div>
<div class="stat-value text-2xl">{totalLogins}</div>
<div class="stat-desc">Tentativas de acesso</div>
</div>
<div class="stat bg-base-100 shadow-lg rounded-lg">
<div class="stat-figure text-success">
<svg xmlns="http://www.w3.org/2000/svg" class="h-8 w-8" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
</div>
<div class="stat-title text-xs">Logins Bem-sucedidos</div>
<div class="stat-value text-2xl text-success">{loginsSucesso}</div>
<div class="stat-desc">{totalLogins > 0 ? Math.round((loginsSucesso / totalLogins) * 100) : 0}% de sucesso</div>
</div>
<div class="stat bg-base-100 shadow-lg rounded-lg">
<div class="stat-figure text-error">
<svg xmlns="http://www.w3.org/2000/svg" class="h-8 w-8" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
</div>
<div class="stat-title text-xs">Logins Falhados</div>
<div class="stat-value text-2xl text-error">{loginsFalha}</div>
<div class="stat-desc">{totalLogins > 0 ? Math.round((loginsFalha / totalLogins) * 100) : 0}% de falhas</div>
</div>
</div>
<!-- Tabs -->
<div class="tabs tabs-boxed mb-6 bg-base-100 shadow-lg p-2">
<div class="tabs tabs-boxed mb-6 bg-base-100 shadow-lg p-1">
<button
class="tab {abaAtiva === 'atividades' ? 'tab-active' : ''}"
class="tab flex items-center gap-2 {abaAtiva === 'atividades' ? 'tab-active' : ''}"
onclick={() => abaAtiva = "atividades"}
>
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-2" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2" />
<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="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4" />
</svg>
Atividades no Sistema
<span class="font-medium">Atividades no Sistema</span>
</button>
<button
class="tab {abaAtiva === 'logins' ? 'tab-active' : ''}"
class="tab flex items-center gap-2 {abaAtiva === 'logins' ? 'tab-active' : ''}"
onclick={() => abaAtiva = "logins"}
>
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-2" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<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="M11 16l-4-4m0 0l4-4m-4 4h14m-5 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h7a3 3 0 013 3v1" />
</svg>
Histórico de Logins
<span class="font-medium">Histórico de Logins</span>
</button>
</div>
<!-- Controles -->
<div class="card bg-base-100 shadow-xl mb-6">
<div class="card-body">
<div class="card-body py-4">
<div class="flex flex-wrap items-center justify-between gap-4">
<div class="form-control">
<label class="label">
<span class="label-text">Quantidade de registros</span>
<label class="label py-1">
<span class="label-text font-medium">Quantidade de registros</span>
</label>
<select bind:value={limite} class="select select-bordered">
<select bind:value={limite} class="select select-bordered select-sm w-full max-w-xs">
<option value={20}>20 registros</option>
<option value={50}>50 registros</option>
<option value={100}>100 registros</option>
@@ -87,14 +161,14 @@
</div>
<div class="flex gap-2">
<button class="btn btn-outline btn-primary">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<button class="btn btn-outline btn-primary btn-sm gap-2">
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4" />
</svg>
Exportar CSV
</button>
<button class="btn btn-outline btn-secondary">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<button class="btn btn-outline btn-secondary btn-sm gap-2">
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 4a1 1 0 011-1h16a1 1 0 011 1v2.586a1 1 0 01-.293.707l-6.414 6.414a1 1 0 00-.293.707V17l-4 4v-6.586a1 1 0 00-.293-.707L3.293 7.293A1 1 0 013 6.586V4z" />
</svg>
Filtros Avançados
@@ -108,45 +182,79 @@
{#if abaAtiva === "atividades"}
<div class="card bg-base-100 shadow-xl">
<div class="card-body">
<h2 class="card-title mb-4">Atividades Recentes</h2>
<div class="flex items-center justify-between mb-4">
<h2 class="card-title text-xl">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-2" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2" />
</svg>
Atividades Recentes
</h2>
{#if atividades?.data}
<div class="badge badge-outline badge-lg">{atividades.data.length} registro{atividades.data.length !== 1 ? 's' : ''}</div>
{/if}
</div>
{#if !atividades?.data}
<div class="flex justify-center py-10">
<span class="loading loading-spinner loading-lg text-primary"></span>
<div class="flex flex-col items-center justify-center py-16">
<span class="loading loading-spinner loading-lg text-primary mb-4"></span>
<p class="text-base-content/60">Carregando atividades...</p>
</div>
{:else if atividades.data.length === 0}
<div class="text-center py-10 text-base-content/60">
Nenhuma atividade registrada
<div class="flex flex-col items-center justify-center py-16 text-base-content/60">
<svg xmlns="http://www.w3.org/2000/svg" class="h-16 w-16 mb-4 opacity-50" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 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>
<p class="text-lg font-medium">Nenhuma atividade registrada</p>
<p class="text-sm mt-1">As atividades do sistema aparecerão aqui</p>
</div>
{:else}
<div class="overflow-x-auto">
<table class="table table-sm">
<thead>
<div class="overflow-x-auto rounded-lg">
<table class="table table-zebra">
<thead class="bg-base-200">
<tr>
<th>Data/Hora</th>
<th>Usuário</th>
<th>Ação</th>
<th>Recurso</th>
<th>Detalhes</th>
<th class="font-semibold">Data/Hora</th>
<th class="font-semibold">Usuário</th>
<th class="font-semibold">Ação</th>
<th class="font-semibold">Recurso</th>
<th class="font-semibold">Detalhes</th>
</tr>
</thead>
<tbody>
{#each atividades.data as atividade}
<tr class="hover">
<td class="font-mono text-xs">{formatarData(atividade.timestamp)}</td>
<tr class="hover transition-colors">
<td>
<div class="font-medium">{atividade.usuarioNome || "Sistema"}</div>
<div class="text-xs opacity-60">{atividade.usuarioMatricula || "-"}</div>
<div class="flex items-center gap-2">
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 text-base-content/40" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
<span class="font-mono text-xs">{formatarData(atividade.timestamp)}</span>
</div>
</td>
<td>
<span class="badge {getAcaoColor(atividade.acao)} badge-sm">
{atividade.acao}
<div class="flex flex-col">
<div class="font-semibold text-sm">{atividade.usuarioNome || "Sistema"}</div>
{#if atividade.usuarioMatricula}
<div class="text-xs text-base-content/50 font-mono">{atividade.usuarioMatricula}</div>
{/if}
</div>
</td>
<td>
<span class="badge {getAcaoColor(atividade.acao)} badge-sm gap-1">
{getAcaoLabel(atividade.acao)}
</span>
</td>
<td class="font-medium">{atividade.recurso}</td>
<td>
<div class="text-xs max-w-md truncate" title={atividade.detalhes}>
{atividade.detalhes || "-"}
<span class="font-medium text-sm">{atividade.recurso}</span>
</td>
<td>
<div class="max-w-md">
{#if atividade.detalhes}
<div class="text-xs text-base-content/70 truncate" title={atividade.detalhes}>
{atividade.detalhes}
</div>
{:else}
<span class="text-base-content/40 text-xs">-</span>
{/if}
</div>
</td>
</tr>
@@ -160,49 +268,98 @@
{:else}
<div class="card bg-base-100 shadow-xl">
<div class="card-body">
<h2 class="card-title mb-4">Histórico de Logins</h2>
<div class="flex items-center justify-between mb-4">
<h2 class="card-title text-xl">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-2" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 16l-4-4m0 0l4-4m-4 4h14m-5 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h7a3 3 0 013 3v1" />
</svg>
Histórico de Logins
</h2>
{#if logins?.data}
<div class="badge badge-outline badge-lg">{logins.data.length} registro{logins.data.length !== 1 ? 's' : ''}</div>
{/if}
</div>
{#if !logins?.data}
<div class="flex justify-center py-10">
<span class="loading loading-spinner loading-lg text-primary"></span>
<div class="flex flex-col items-center justify-center py-16">
<span class="loading loading-spinner loading-lg text-primary mb-4"></span>
<p class="text-base-content/60">Carregando logins...</p>
</div>
{:else if logins.data.length === 0}
<div class="text-center py-10 text-base-content/60">
Nenhum login registrado
<div class="flex flex-col items-center justify-center py-16 text-base-content/60">
<svg xmlns="http://www.w3.org/2000/svg" class="h-16 w-16 mb-4 opacity-50" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 16l-4-4m0 0l4-4m-4 4h14m-5 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h7a3 3 0 013 3v1" />
</svg>
<p class="text-lg font-medium">Nenhum login registrado</p>
<p class="text-sm mt-1">Os acessos ao sistema aparecerão aqui</p>
</div>
{:else}
<div class="overflow-x-auto">
<table class="table table-sm">
<thead>
<div class="overflow-x-auto rounded-lg">
<table class="table table-zebra">
<thead class="bg-base-200">
<tr>
<th>Data/Hora</th>
<th>Usuário/Email</th>
<th>Status</th>
<th>IP</th>
<th>Dispositivo</th>
<th>Navegador</th>
<th>Sistema</th>
<th class="font-semibold">Data/Hora</th>
<th class="font-semibold">Usuário/Email</th>
<th class="font-semibold">Status</th>
<th class="font-semibold">IP</th>
<th class="font-semibold">Dispositivo</th>
<th class="font-semibold">Navegador</th>
<th class="font-semibold">Sistema</th>
</tr>
</thead>
<tbody>
{#each logins.data as login}
<tr class="hover">
<td class="font-mono text-xs">{formatarData(login.timestamp)}</td>
<td class="text-sm">{login.matriculaOuEmail}</td>
<tr class="hover transition-colors">
<td>
{#if login.sucesso}
<span class="badge badge-success badge-sm">Sucesso</span>
{:else}
<span class="badge badge-error badge-sm">Falhou</span>
{#if login.motivoFalha}
<div class="text-xs text-error mt-1">{login.motivoFalha}</div>
{/if}
{/if}
<div class="flex items-center gap-2">
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 text-base-content/40" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
<span class="font-mono text-xs">{formatarData(login.timestamp)}</span>
</div>
</td>
<td>
<div class="flex items-center gap-2">
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 text-base-content/40" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" />
</svg>
<span class="text-sm font-medium">{login.matriculaOuEmail}</span>
</div>
</td>
<td>
<div class="flex flex-col gap-1">
{#if login.sucesso}
<span class="badge badge-success badge-sm gap-1">
<svg xmlns="http://www.w3.org/2000/svg" class="h-3 w-3" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7" />
</svg>
Sucesso
</span>
{:else}
<span class="badge badge-error badge-sm gap-1">
<svg xmlns="http://www.w3.org/2000/svg" class="h-3 w-3" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
</svg>
Falhou
</span>
{#if login.motivoFalha}
<div class="text-xs text-error mt-1 font-medium">{login.motivoFalha}</div>
{/if}
{/if}
</div>
</td>
<td>
<span class="font-mono text-xs bg-base-200 px-2 py-1 rounded">{login.ipAddress || "-"}</span>
</td>
<td>
<div class="text-xs text-base-content/70">{login.device || "-"}</div>
</td>
<td>
<div class="text-xs text-base-content/70">{login.browser || "-"}</div>
</td>
<td>
<div class="text-xs text-base-content/70">{login.sistema || "-"}</div>
</td>
<td class="font-mono text-xs">{login.ipAddress || "-"}</td>
<td class="text-xs">{login.device || "-"}</td>
<td class="text-xs">{login.browser || "-"}</td>
<td class="text-xs">{login.sistema || "-"}</td>
</tr>
{/each}
</tbody>
@@ -214,11 +371,14 @@
{/if}
<!-- Informação -->
<div class="alert alert-info mt-6">
<div class="alert alert-info shadow-lg mt-6">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" class="stroke-current shrink-0 w-6 h-6">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
<span>Os logs são armazenados permanentemente e não podem ser alterados ou excluídos.</span>
<div>
<h3 class="font-bold">Informação Importante</h3>
<div class="text-sm">Os logs são armazenados permanentemente e não podem ser alterados ou excluídos.</div>
</div>
</div>
</div>
</main>