feat: add employee management features including filtering, deletion, and improved layout

This commit is contained in:
2025-10-25 01:24:40 -03:00
parent be3522ae74
commit f0d3625045
10 changed files with 735 additions and 132 deletions

View File

@@ -0,0 +1,172 @@
<script lang="ts">
import { useConvexClient } from "convex-svelte";
import { api } from "@sgse-app/backend/convex/_generated/api";
import { goto } from "$app/navigation";
import type { SimboloTipo } from "@sgse-app/backend/convex/schema";
const client = useConvexClient();
let list: Array<any> = [];
let filtered: Array<any> = [];
let selectedId: string | null = null;
let deletingId: string | null = null;
let toDelete: { id: string; nome: string } | null = null;
let openMenuId: string | null = null;
let filtroNome = "";
let filtroCPF = "";
let filtroMatricula = "";
let filtroTipo: SimboloTipo | "" = "";
function applyFilters() {
const nome = filtroNome.toLowerCase();
const cpf = filtroCPF.replace(/\D/g, "");
const mat = filtroMatricula.toLowerCase();
filtered = list.filter((f) => {
const okNome = !nome || (f.nome || "").toLowerCase().includes(nome);
const okCPF = !cpf || (f.cpf || "").includes(cpf);
const okMat = !mat || (f.matricula || "").toLowerCase().includes(mat);
const okTipo = !filtroTipo || f.simboloTipo === filtroTipo;
return okNome && okCPF && okMat && okTipo;
});
}
async function load() {
list = await client.query(api.funcionarios.getAll, {} as any);
applyFilters();
}
function editSelected() {
if (selectedId) goto(`/recursos-humanos/funcionarios/${selectedId}/editar`);
}
function openDeleteModal(id: string, nome: string) {
toDelete = { id, nome };
(document.getElementById("delete_modal_func") as HTMLDialogElement)?.showModal();
}
function closeDeleteModal() {
toDelete = null;
(document.getElementById("delete_modal_func") as HTMLDialogElement)?.close();
}
async function confirmDelete() {
if (!toDelete) return;
try {
deletingId = toDelete.id;
await client.mutation(api.funcionarios.remove, { id: toDelete.id } as any);
closeDeleteModal();
await load();
} finally {
deletingId = null;
}
}
function navCadastro() { goto("/recursos-humanos/funcionarios/cadastro"); }
load();
function toggleMenu(id: string) {
openMenuId = openMenuId === id ? null : id;
}
$: needsScroll = filtered.length > 8;
</script>
<div class="space-y-6 pb-32">
<div class="flex items-center justify-between">
<h2 class="text-3xl font-bold text-brand-dark">Funcionários</h2>
<div class="space-x-2 flex items-center">
<button class="btn btn-primary" onclick={navCadastro}>Novo Funcionário</button>
</div>
</div>
<div class="grid md:grid-cols-4 gap-3 items-end">
<div class="form-control">
<label class="label" for="func_nome"><span class="label-text">Nome</span></label>
<input id="func_nome" class="input input-bordered" bind:value={filtroNome} oninput={applyFilters} />
</div>
<div class="form-control">
<label class="label" for="func_cpf"><span class="label-text">CPF</span></label>
<input id="func_cpf" class="input input-bordered" bind:value={filtroCPF} oninput={applyFilters} />
</div>
<div class="form-control">
<label class="label" for="func_matricula"><span class="label-text">Matrícula</span></label>
<input id="func_matricula" class="input input-bordered" bind:value={filtroMatricula} oninput={applyFilters} />
</div>
<div class="form-control">
<label class="label" for="func_tipo"><span class="label-text">Símbolo Tipo</span></label>
<select id="func_tipo" class="select select-bordered" bind:value={filtroTipo} oninput={applyFilters}>
<option value="">Todos</option>
<option value="cargo_comissionado">Cargo comissionado</option>
<option value="funcao_gratificada">Função gratificada</option>
</select>
</div>
</div>
<div class="overflow-x-auto bg-base-100 rounded-lg shadow-sm mb-2" class:overflow-y-auto={needsScroll} style={needsScroll ? "max-height: calc(100vh - 180px);" : "overflow-y: visible;"}>
<table class="table table-zebra">
<thead>
<tr>
<th>Nome</th>
<th>CPF</th>
<th>Matrícula</th>
<th>Tipo</th>
<th>Cidade</th>
<th>UF</th>
<th class="text-right">Ações</th>
</tr>
</thead>
<tbody>
{#each filtered as f}
<tr class="hover">
<td class="font-medium">{f.nome}</td>
<td>{f.cpf}</td>
<td>{f.matricula}</td>
<td>{f.simboloTipo}</td>
<td>{f.cidade}</td>
<td>{f.uf}</td>
<td class="text-right">
<div class="dropdown dropdown-end" class:dropdown-open={openMenuId === f._id}>
<button type="button" aria-label="Abrir menu" class="btn btn-ghost btn-sm" onclick={() => toggleMenu(f._id)}>
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor"><path d="M10 6a2 2 0 110-4 2 2 0 010 4zM10 12a2 2 0 110-4 2 2 0 010 4zM10 18a2 2 0 110-4 2 2 0 010 4z"/></svg>
</button>
<ul class="dropdown-content menu bg-base-100 rounded-box z-10 w-52 p-2 shadow-lg border border-base-300">
<li><a href={`/recursos-humanos/funcionarios/${f._id}/editar`}>Editar</a></li>
<li><button class="text-error" onclick={() => openDeleteModal(f._id, f.nome)}>Excluir</button></li>
</ul>
</div>
</td>
</tr>
{/each}
</tbody>
</table>
</div>
<dialog id="delete_modal_func" class="modal">
<div class="modal-box">
<h3 class="font-bold text-lg mb-4">Confirmar Exclusão</h3>
<div class="alert alert-warning mb-4">
<svg xmlns="http://www.w3.org/2000/svg" class="stroke-current shrink-0 h-6 w-6" fill="none" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"/></svg>
<span>Esta ação não pode ser desfeita!</span>
</div>
{#if toDelete}
<p class="py-2">Tem certeza que deseja excluir o funcionário <strong class="text-error">{toDelete.nome}</strong>?</p>
{/if}
<div class="modal-action">
<form method="dialog" class="flex gap-2">
<button class="btn btn-ghost" onclick={closeDeleteModal} type="button">Cancelar</button>
<button class="btn btn-error" onclick={confirmDelete} disabled={deletingId !== null} type="button">
{#if deletingId}
<span class="loading loading-spinner loading-sm"></span>
Excluindo...
{:else}
Confirmar Exclusão
{/if}
</button>
</form>
</div>
</div>
<form method="dialog" class="modal-backdrop">
<button>close</button>
</form>
</dialog>
</div>