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

@@ -1,10 +1,37 @@
<script lang="ts">
import { useQuery, useConvexClient } from "convex-svelte";
import { useConvexClient } from "convex-svelte";
import { api } from "@sgse-app/backend/convex/_generated/api";
import type { Id } from "@sgse-app/backend/convex/_generated/dataModel";
import { onMount } from "svelte";
const client = useConvexClient();
const simbolosQuery = useQuery(api.simbolos.getAll, {});
let isLoading = true;
let list: Array<any> = [];
let filtroNome = "";
let filtroTipo: "" | "cargo_comissionado" | "funcao_gratificada" = "";
let filtroDescricao = "";
let filtered: Array<any> = [];
let notice: { kind: "success" | "error"; text: string } | null = null;
$: needsScroll = filtered.length > 8;
let openMenuId: string | null = null;
function toggleMenu(id: string) {
openMenuId = openMenuId === id ? null : id;
}
$: filtered = (list ?? []).filter((s) => {
const nome = (filtroNome || "").toLowerCase();
const desc = (filtroDescricao || "").toLowerCase();
const okNome = !nome || (s.nome || "").toLowerCase().includes(nome);
const okDesc = !desc || (s.descricao || "").toLowerCase().includes(desc);
const okTipo = !filtroTipo || s.tipo === filtroTipo;
return okNome && okDesc && okTipo;
});
onMount(async () => {
try {
list = await client.query(api.simbolos.getAll, {} as any);
} finally {
isLoading = false;
}
});
let deletingId: Id<"simbolos"> | null = null;
let simboloToDelete: { id: Id<"simbolos">; nome: string } | null = null;
@@ -21,14 +48,15 @@
async function confirmDelete() {
if (!simboloToDelete) return;
try {
deletingId = simboloToDelete.id;
await client.mutation(api.simbolos.remove, { id: simboloToDelete.id });
// reload list
list = await client.query(api.simbolos.getAll, {} as any);
notice = { kind: "success", text: "Símbolo excluído com sucesso." };
closeDeleteModal();
} catch (error) {
console.error("Erro ao excluir símbolo:", error);
alert("Erro ao excluir símbolo. Tente novamente.");
notice = { kind: "error", text: "Erro ao excluir símbolo." };
} finally {
deletingId = null;
}
@@ -46,6 +74,11 @@
</script>
<div class="space-y-6 pb-32">
{#if notice}
<div class="alert" class:alert-success={notice.kind === "success"} class:alert-error={notice.kind === "error"}>
<span>{notice.text}</span>
</div>
{/if}
<div class="flex justify-between items-center">
<h2 class="text-3xl font-bold text-brand-dark">Símbolos</h2>
<a href="/recursos-humanos/simbolos/cadastro" class="btn btn-primary">
@@ -65,12 +98,31 @@
</a>
</div>
{#if simbolosQuery.isLoading}
<div class="grid md:grid-cols-3 gap-3 items-end">
<div class="form-control">
<label class="label" for="symbol_nome"><span class="label-text">Nome</span></label>
<input id="symbol_nome" class="input input-bordered" bind:value={filtroNome} />
</div>
<div class="form-control">
<label class="label" for="symbol_tipo"><span class="label-text">Tipo</span></label>
<select id="symbol_tipo" class="select select-bordered" bind:value={filtroTipo}>
<option value="">Todos</option>
<option value="cargo_comissionado">Cargo comissionado</option>
<option value="funcao_gratificada">Função gratificada</option>
</select>
</div>
<div class="form-control">
<label class="label" for="symbol_desc"><span class="label-text">Descrição</span></label>
<input id="symbol_desc" class="input input-bordered" bind:value={filtroDescricao} />
</div>
</div>
{#if isLoading}
<div class="flex justify-center items-center py-12">
<span class="loading loading-spinner loading-lg"></span>
</div>
{:else if simbolosQuery.data && simbolosQuery.data.length > 0}
<div class="overflow-x-auto bg-base-100 rounded-lg shadow-sm mb-8">
{:else}
<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>
@@ -84,8 +136,9 @@
</tr>
</thead>
<tbody>
{#each simbolosQuery.data as simbolo}
<tr class="hover">
{#if filtered.length > 0}
{#each filtered as simbolo}
<tr class="hover">
<td class="font-medium">{simbolo.nome}</td>
<td>
<span
@@ -101,8 +154,8 @@
<td class="font-semibold">{formatMoney(simbolo.valor)}</td>
<td class="max-w-xs truncate">{simbolo.descricao}</td>
<td class="text-right">
<div class="dropdown dropdown-end">
<div tabindex="0" role="button" class="btn btn-ghost btn-sm">
<div class="dropdown dropdown-end" class:dropdown-open={openMenuId === simbolo._id}>
<button type="button" class="btn btn-ghost btn-sm" onclick={() => toggleMenu(simbolo._id)}>
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-5 w-5"
@@ -113,13 +166,10 @@
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>
</div>
<ul
tabindex="0"
class="dropdown-content menu bg-base-100 rounded-box z-10 w-52 p-2 shadow-lg border border-base-300"
>
</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/simbolos/{simbolo._id}/editar">
<a href={"/recursos-humanos/simbolos/" + simbolo._id + "/editar"}>
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-4 w-4"
@@ -134,10 +184,7 @@
</a>
</li>
<li>
<button
on:click={() => openDeleteModal(simbolo._id, simbolo.nome)}
class="text-error"
>
<button type="button" onclick={() => openDeleteModal(simbolo._id, simbolo.nome)} class="text-error">
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-4 w-4"
@@ -156,28 +203,16 @@
</ul>
</div>
</td>
</tr>
{/each}
{:else}
<tr>
<td colspan="7" class="text-center opacity-70">Nenhum símbolo encontrado com os filtros atuais.</td>
</tr>
{/each}
{/if}
</tbody>
</table>
</div>
{:else}
<div class="alert">
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
class="stroke-info 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>Nenhum símbolo encontrado. Crie um novo para começar.</span>
</div>
{/if}
</div>
@@ -210,12 +245,12 @@
{/if}
<div class="modal-action">
<form method="dialog" class="flex gap-2">
<button class="btn btn-ghost" on:click={closeDeleteModal} type="button">
<button class="btn btn-ghost" onclick={closeDeleteModal} type="button">
Cancelar
</button>
<button
class="btn btn-error"
on:click={confirmDelete}
onclick={confirmDelete}
disabled={deletingId !== null}
type="button"
>