refactor: enhance 'Almoxarifado' UI with improved layout, updated styling, and efficient data handling for better user experience and performance

This commit is contained in:
2025-12-21 02:32:59 -03:00
parent fc633c5708
commit 500b7b362c
9 changed files with 483 additions and 325 deletions

View File

@@ -2,6 +2,7 @@
import { api } from '@sgse-app/backend/convex/_generated/api'; import { api } from '@sgse-app/backend/convex/_generated/api';
import { useConvexClient, useQuery } from 'convex-svelte'; import { useConvexClient, useQuery } from 'convex-svelte';
import { goto } from '$app/navigation'; import { goto } from '$app/navigation';
import { resolve } from '$app/paths';
import { import {
Package, Package,
AlertTriangle, AlertTriangle,
@@ -14,69 +15,114 @@
const statsQuery = useQuery(api.almoxarifado.obterEstatisticas, {}); const statsQuery = useQuery(api.almoxarifado.obterEstatisticas, {});
const alertasQuery = useQuery(api.almoxarifado.listarAlertas, { status: 'ativo' }); const alertasQuery = useQuery(api.almoxarifado.listarAlertas, { status: 'ativo' });
const materiaisQuery = useQuery(api.almoxarifado.listarMateriais, {}); const materiaisQuery = useQuery(api.almoxarifado.listarMateriais, {});
// Criar mapa de materiais para lookup eficiente
const materiaisMap = $derived.by(() => {
if (!materiaisQuery.data) return new Map();
const map = new Map();
for (const material of materiaisQuery.data) {
map.set(material._id, material);
}
return map;
});
</script> </script>
<main class="container mx-auto px-4 py-4"> <main class="container mx-auto px-4 py-6">
<!-- Breadcrumb -->
<div class="breadcrumbs mb-6 text-sm">
<ul>
<li><a href={resolve('/')} class="text-primary hover:underline">Dashboard</a></li>
<li>Almoxarifado</li>
</ul>
</div>
<!-- Cabeçalho --> <!-- Cabeçalho -->
<div class="mb-8"> <div class="mb-8">
<h1 class="text-primary mb-2 text-4xl font-bold">Almoxarifado</h1> <div class="mb-4 flex items-center gap-4">
<div class="rounded-2xl bg-gradient-to-br from-primary/20 to-primary/30 p-4 shadow-lg">
<Package class="h-10 w-10 text-primary" strokeWidth={2.5} />
</div>
<div>
<h1 class="text-primary mb-2 text-4xl font-bold tracking-tight">Almoxarifado</h1>
<p class="text-base-content/70 text-lg"> <p class="text-base-content/70 text-lg">
Controle de estoque e gestão de materiais Controle de estoque e gestão de materiais
</p> </p>
</div> </div>
</div>
</div>
<!-- Estatísticas --> <!-- Estatísticas -->
{#if statsQuery.data} {#if statsQuery === undefined}
<div class="mb-8 grid grid-cols-1 gap-4 md:grid-cols-4"> <div class="mb-8 flex items-center justify-center py-12">
<div class="stats from-primary/10 to-primary/20 bg-linear-to-br shadow-lg"> <span class="loading loading-spinner loading-lg text-primary"></span>
<div class="stat">
<div class="stat-figure text-primary">
<Package class="h-8 w-8" strokeWidth={2} />
</div> </div>
<div class="stat-title">Total de Materiais</div> {:else if statsQuery.data}
<div class="stat-value text-primary"> <div class="mb-8 grid grid-cols-1 gap-6 md:grid-cols-2 lg:grid-cols-4">
<div class="card bg-gradient-to-br from-primary/10 via-primary/5 to-base-100 border border-primary/20 shadow-xl hover:shadow-2xl transition-all duration-300 hover:scale-105">
<div class="card-body">
<div class="flex items-center justify-between">
<div class="flex-1">
<div class="text-sm font-medium text-base-content/60 mb-1">Total de Materiais</div>
<div class="text-3xl font-bold text-primary mb-1">
{statsQuery.data.totalMateriais} {statsQuery.data.totalMateriais}
</div> </div>
<div class="stat-desc">Materiais cadastrados</div> <div class="text-xs text-base-content/50">Materiais cadastrados</div>
</div>
<div class="rounded-xl bg-primary/20 p-3">
<Package class="h-8 w-8 text-primary" strokeWidth={2.5} />
</div>
</div>
</div> </div>
</div> </div>
<div class="stats from-success/10 to-success/20 bg-linear-to-br shadow-lg"> <div class="card bg-gradient-to-br from-success/10 via-success/5 to-base-100 border border-success/20 shadow-xl hover:shadow-2xl transition-all duration-300 hover:scale-105">
<div class="stat"> <div class="card-body">
<div class="stat-figure text-success"> <div class="flex items-center justify-between">
<CheckCircle2 class="h-8 w-8" strokeWidth={2} /> <div class="flex-1">
</div> <div class="text-sm font-medium text-base-content/60 mb-1">Materiais Ativos</div>
<div class="stat-title">Materiais Ativos</div> <div class="text-3xl font-bold text-success mb-1">
<div class="stat-value text-success">
{statsQuery.data.totalMateriaisAtivos} {statsQuery.data.totalMateriaisAtivos}
</div> </div>
<div class="stat-desc">Em estoque</div> <div class="text-xs text-base-content/50">Em estoque</div>
</div>
<div class="rounded-xl bg-success/20 p-3">
<CheckCircle2 class="h-8 w-8 text-success" strokeWidth={2.5} />
</div>
</div>
</div> </div>
</div> </div>
<div class="stats from-warning/10 to-warning/20 bg-linear-to-br shadow-lg"> <div class="card bg-gradient-to-br from-warning/10 via-warning/5 to-base-100 border border-warning/20 shadow-xl hover:shadow-2xl transition-all duration-300 hover:scale-105">
<div class="stat"> <div class="card-body">
<div class="stat-figure text-warning"> <div class="flex items-center justify-between">
<AlertTriangle class="h-8 w-8" strokeWidth={2} /> <div class="flex-1">
</div> <div class="text-sm font-medium text-base-content/60 mb-1">Alertas Ativos</div>
<div class="stat-title">Alertas Ativos</div> <div class="text-3xl font-bold text-warning mb-1">
<div class="stat-value text-warning">
{statsQuery.data.totalAlertasAtivos} {statsQuery.data.totalAlertasAtivos}
</div> </div>
<div class="stat-desc">Estoque baixo</div> <div class="text-xs text-base-content/50">Estoque baixo</div>
</div>
<div class="rounded-xl bg-warning/20 p-3">
<AlertTriangle class="h-8 w-8 text-warning" strokeWidth={2.5} />
</div>
</div>
</div> </div>
</div> </div>
<div class="stats from-info/10 to-info/20 bg-linear-to-br shadow-lg"> <div class="card bg-gradient-to-br from-info/10 via-info/5 to-base-100 border border-info/20 shadow-xl hover:shadow-2xl transition-all duration-300 hover:scale-105">
<div class="stat"> <div class="card-body">
<div class="stat-figure text-info"> <div class="flex items-center justify-between">
<ArrowLeftRight class="h-8 w-8" strokeWidth={2} /> <div class="flex-1">
</div> <div class="text-sm font-medium text-base-content/60 mb-1">Movimentações</div>
<div class="stat-title">Movimentações</div> <div class="text-3xl font-bold text-info mb-1">
<div class="stat-value text-info">
{statsQuery.data.movimentacoesMes} {statsQuery.data.movimentacoesMes}
</div> </div>
<div class="stat-desc">Este mês</div> <div class="text-xs text-base-content/50">Este mês</div>
</div>
<div class="rounded-xl bg-info/20 p-3">
<ArrowLeftRight class="h-8 w-8 text-info" strokeWidth={2.5} />
</div>
</div>
</div> </div>
</div> </div>
</div> </div>
@@ -84,45 +130,58 @@
<!-- Alertas Recentes --> <!-- Alertas Recentes -->
<div class="mb-8"> <div class="mb-8">
<div class="card bg-base-100 shadow-xl"> <div class="card bg-base-100 border border-base-300 shadow-xl">
<div class="card-body"> <div class="card-body">
<h2 class="card-title text-2xl"> <h2 class="card-title text-2xl mb-1">
<div class="rounded-lg bg-warning/20 p-2">
<AlertTriangle class="h-6 w-6 text-warning" /> <AlertTriangle class="h-6 w-6 text-warning" />
Alertas de Estoque </div>
<span>Alertas de Estoque</span>
</h2> </h2>
{#if alertasQuery.data && alertasQuery.data.length > 0} {#if alertasQuery === undefined}
<div class="flex items-center justify-center py-8">
<span class="loading loading-spinner loading-lg text-primary"></span>
</div>
{:else if alertasQuery.data && alertasQuery.data.length > 0}
<div class="overflow-x-auto"> <div class="overflow-x-auto">
<table class="table"> <table class="table table-zebra">
<thead> <thead>
<tr> <tr class="bg-base-200">
<th>Material</th> <th class="font-semibold">Material</th>
<th>Tipo</th> <th class="font-semibold">Tipo</th>
<th>Quantidade Atual</th> <th class="font-semibold">Quantidade Atual</th>
<th>Quantidade Mínima</th> <th class="font-semibold">Quantidade Mínima</th>
<th>Ações</th> <th class="font-semibold">Ações</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{#each alertasQuery.data.slice(0, 5) as alerta} {#each alertasQuery.data.slice(0, 5) as alerta}
{@const material = materiaisQuery.data?.find(m => m._id === alerta.materialId)} {@const material = materiaisMap.get(alerta.materialId)}
<tr> <tr class="hover:bg-base-200/50 transition-colors">
<td> <td>
{material?.nome || 'Carregando...'} <div class="font-medium">{material?.nome || 'Carregando...'}</div>
{#if material?.codigo}
<div class="text-xs text-base-content/50 font-mono">{material.codigo}</div>
{/if}
</td> </td>
<td> <td>
{#if alerta.tipo === 'estoque_zerado'} {#if alerta.tipo === 'estoque_zerado'}
<span class="badge badge-error">Zerado</span> <span class="badge badge-error badge-lg">Zerado</span>
{:else if alerta.tipo === 'estoque_minimo'} {:else if alerta.tipo === 'estoque_minimo'}
<span class="badge badge-warning">Mínimo</span> <span class="badge badge-warning badge-lg">Mínimo</span>
{:else} {:else}
<span class="badge badge-info">Reposição</span> <span class="badge badge-info badge-lg">Reposição</span>
{/if} {/if}
</td> </td>
<td>{alerta.quantidadeAtual}</td> <td>
<td>{alerta.quantidadeMinima}</td> <span class="font-bold text-error">{alerta.quantidadeAtual}</span>
</td>
<td>
<span class="font-medium">{alerta.quantidadeMinima}</span>
</td>
<td> <td>
<button <button
class="btn btn-sm btn-primary" class="btn btn-sm btn-primary btn-outline"
onclick={() => goto('/almoxarifado/alertas')} onclick={() => goto('/almoxarifado/alertas')}
> >
Ver Detalhes Ver Detalhes
@@ -133,7 +192,7 @@
</tbody> </tbody>
</table> </table>
</div> </div>
<div class="card-actions justify-end"> <div class="card-actions justify-end mt-4">
<button <button
class="btn btn-primary" class="btn btn-primary"
onclick={() => goto('/almoxarifado/alertas')} onclick={() => goto('/almoxarifado/alertas')}
@@ -142,9 +201,9 @@
</button> </button>
</div> </div>
{:else} {:else}
<div class="alert alert-success"> <div class="alert alert-success shadow-lg">
<CheckCircle2 class="h-6 w-6" /> <CheckCircle2 class="h-6 w-6" />
<span>Nenhum alerta ativo no momento!</span> <span class="font-medium">Nenhum alerta ativo no momento!</span>
</div> </div>
{/if} {/if}
</div> </div>
@@ -152,43 +211,49 @@
</div> </div>
<!-- Ações Rápidas --> <!-- Ações Rápidas -->
<div class="grid grid-cols-1 gap-4 md:grid-cols-3"> <div class="grid grid-cols-1 gap-6 md:grid-cols-3">
<button <button
class="card bg-base-100 shadow-xl hover:shadow-2xl transition-shadow" class="card bg-base-100 border border-base-300 shadow-xl hover:shadow-2xl hover:border-primary/50 transition-all duration-300 hover:scale-[1.02] group"
onclick={() => goto('/almoxarifado/materiais/cadastro')} onclick={() => goto('/almoxarifado/materiais/cadastro')}
> >
<div class="card-body"> <div class="card-body">
<h3 class="card-title"> <div class="flex items-center gap-4 mb-2">
<Package class="h-6 w-6 text-primary" /> <div class="rounded-xl bg-primary/20 p-3 group-hover:bg-primary/30 transition-colors">
Cadastrar Material <Package class="h-7 w-7 text-primary" strokeWidth={2.5} />
</h3> </div>
<p class="text-base-content/70">Adicionar novo material ao estoque</p> <h3 class="card-title text-lg mb-0">Cadastrar Material</h3>
</div>
<p class="text-base-content/70 text-sm">Adicionar novo material ao estoque</p>
</div> </div>
</button> </button>
<button <button
class="card bg-base-100 shadow-xl hover:shadow-2xl transition-shadow" class="card bg-base-100 border border-base-300 shadow-xl hover:shadow-2xl hover:border-info/50 transition-all duration-300 hover:scale-[1.02] group"
onclick={() => goto('/almoxarifado/movimentacoes')} onclick={() => goto('/almoxarifado/movimentacoes')}
> >
<div class="card-body"> <div class="card-body">
<h3 class="card-title"> <div class="flex items-center gap-4 mb-2">
<ArrowLeftRight class="h-6 w-6 text-info" /> <div class="rounded-xl bg-info/20 p-3 group-hover:bg-info/30 transition-colors">
Registrar Movimentação <ArrowLeftRight class="h-7 w-7 text-info" strokeWidth={2.5} />
</h3> </div>
<p class="text-base-content/70">Registrar entrada ou saída de material</p> <h3 class="card-title text-lg mb-0">Registrar Movimentação</h3>
</div>
<p class="text-base-content/70 text-sm">Registrar entrada ou saída de material</p>
</div> </div>
</button> </button>
<button <button
class="card bg-base-100 shadow-xl hover:shadow-2xl transition-shadow" class="card bg-base-100 border border-base-300 shadow-xl hover:shadow-2xl hover:border-success/50 transition-all duration-300 hover:scale-[1.02] group"
onclick={() => goto('/almoxarifado/relatorios')} onclick={() => goto('/almoxarifado/relatorios')}
> >
<div class="card-body"> <div class="card-body">
<h3 class="card-title"> <div class="flex items-center gap-4 mb-2">
<BarChart3 class="h-6 w-6 text-success" /> <div class="rounded-xl bg-success/20 p-3 group-hover:bg-success/30 transition-colors">
Relatórios <BarChart3 class="h-7 w-7 text-success" strokeWidth={2.5} />
</h3> </div>
<p class="text-base-content/70">Visualizar relatórios e estatísticas</p> <h3 class="card-title text-lg mb-0">Relatórios</h3>
</div>
<p class="text-base-content/70 text-sm">Visualizar relatórios e estatísticas</p>
</div> </div>
</button> </button>
</div> </div>

View File

@@ -77,15 +77,10 @@
} }
</script> </script>
<main class="container mx-auto px-4 py-4"> <main class="container mx-auto px-4 py-6">
<!-- Breadcrumb --> <!-- Breadcrumb -->
<div class="breadcrumbs mb-4 text-sm"> <div class="breadcrumbs mb-6 text-sm">
<ul> <ul>
<li>
<a href={resolve('/recursos-humanos')} class="text-primary hover:underline"
>Recursos Humanos</a
>
</li>
<li> <li>
<a href={resolve('/almoxarifado')} class="text-primary hover:underline" <a href={resolve('/almoxarifado')} class="text-primary hover:underline"
>Almoxarifado</a >Almoxarifado</a
@@ -96,14 +91,14 @@
</div> </div>
<!-- Cabeçalho --> <!-- Cabeçalho -->
<div class="mb-6"> <div class="mb-8">
<div class="flex items-center gap-4"> <div class="flex items-center gap-4">
<div class="rounded-xl bg-warning/20 p-3"> <div class="rounded-2xl bg-gradient-to-br from-warning/20 to-warning/30 p-4 shadow-lg">
<AlertTriangle class="h-8 w-8 text-warning" strokeWidth={2} /> <AlertTriangle class="h-10 w-10 text-warning" strokeWidth={2.5} />
</div> </div>
<div> <div>
<h1 class="text-3xl font-bold">Alertas de Estoque</h1> <h1 class="text-3xl font-bold tracking-tight">Alertas de Estoque</h1>
<p class="text-base-content/70">Visualize e gerencie alertas de estoque baixo</p> <p class="text-base-content/70 text-lg">Visualize e gerencie alertas de estoque baixo</p>
</div> </div>
</div> </div>
</div> </div>
@@ -116,8 +111,9 @@
{/if} {/if}
<!-- Filtros --> <!-- Filtros -->
<div class="card bg-base-100 mb-6 shadow-xl"> <div class="card bg-base-100 border border-base-300 mb-6 shadow-xl">
<div class="card-body"> <div class="card-body">
<h3 class="text-lg font-semibold mb-4">Filtros</h3>
<div class="grid grid-cols-1 gap-4 md:grid-cols-2"> <div class="grid grid-cols-1 gap-4 md:grid-cols-2">
<div class="form-control"> <div class="form-control">
<label class="label"> <label class="label">
@@ -147,39 +143,39 @@
</div> </div>
<!-- Lista de Alertas --> <!-- Lista de Alertas -->
<div class="card bg-base-100 shadow-xl"> <div class="card bg-base-100 border border-base-300 shadow-xl">
<div class="card-body"> <div class="card-body">
{#if alertasQuery.data && alertasQuery.data.length > 0} {#if alertasQuery.data && alertasQuery.data.length > 0}
<div class="overflow-x-auto"> <div class="overflow-x-auto">
<table class="table"> <table class="table table-zebra">
<thead> <thead>
<tr> <tr class="bg-base-200">
<th>Material</th> <th class="font-semibold">Material</th>
<th>Tipo</th> <th class="font-semibold">Tipo</th>
<th>Quantidade Atual</th> <th class="font-semibold">Quantidade Atual</th>
<th>Quantidade Mínima</th> <th class="font-semibold">Quantidade Mínima</th>
<th>Diferença</th> <th class="font-semibold">Diferença</th>
<th>Status</th> <th class="font-semibold">Status</th>
<th>Data</th> <th class="font-semibold">Data</th>
<th>Ações</th> <th class="font-semibold">Ações</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{#each alertasQuery.data as alerta} {#each alertasQuery.data as alerta}
{@const material = materiaisQuery.data?.find(m => m._id === alerta.materialId)} {@const material = materiaisQuery.data?.find(m => m._id === alerta.materialId)}
{@const diferenca = alerta.quantidadeMinima - alerta.quantidadeAtual} {@const diferenca = alerta.quantidadeMinima - alerta.quantidadeAtual}
<tr> <tr class="hover:bg-base-200/50 transition-colors">
<td> <td>
<div class="font-medium">{material?.nome || 'Carregando...'}</div> <div class="font-medium">{material?.nome || 'Carregando...'}</div>
<div class="text-sm text-base-content/60">{material?.codigo || ''}</div> <div class="text-sm text-base-content/60 font-mono">{material?.codigo || ''}</div>
</td> </td>
<td> <td>
<span class="badge {getTipoBadge(alerta.tipo)}"> <span class="badge {getTipoBadge(alerta.tipo)} badge-lg">
{getTipoLabel(alerta.tipo)} {getTipoLabel(alerta.tipo)}
</span> </span>
</td> </td>
<td> <td>
<div class="font-bold text-error">{alerta.quantidadeAtual}</div> <div class="font-bold text-error text-lg">{alerta.quantidadeAtual}</div>
</td> </td>
<td> <td>
<div class="font-medium">{alerta.quantidadeMinima}</div> <div class="font-medium">{alerta.quantidadeMinima}</div>
@@ -189,14 +185,16 @@
</td> </td>
<td> <td>
{#if alerta.status === 'ativo'} {#if alerta.status === 'ativo'}
<span class="badge badge-warning">Ativo</span> <span class="badge badge-warning badge-lg">Ativo</span>
{:else if alerta.status === 'resolvido'} {:else if alerta.status === 'resolvido'}
<span class="badge badge-success">Resolvido</span> <span class="badge badge-success badge-lg">Resolvido</span>
{:else} {:else}
<span class="badge badge-ghost">Ignorado</span> <span class="badge badge-ghost badge-lg">Ignorado</span>
{/if} {/if}
</td> </td>
<td>{new Date(alerta.criadoEm).toLocaleDateString('pt-BR')}</td> <td>
<span class="text-sm">{new Date(alerta.criadoEm).toLocaleDateString('pt-BR')}</span>
</td>
<td> <td>
{#if alerta.status === 'ativo'} {#if alerta.status === 'ativo'}
<div class="flex gap-2"> <div class="flex gap-2">
@@ -208,7 +206,7 @@
Resolver Resolver
</button> </button>
<button <button
class="btn btn-sm btn-ghost" class="btn btn-sm btn-ghost hover:btn-error"
onclick={() => ignorarAlerta(alerta._id)} onclick={() => ignorarAlerta(alerta._id)}
> >
<XCircle class="h-4 w-4" /> <XCircle class="h-4 w-4" />
@@ -224,9 +222,9 @@
</div> </div>
{:else} {:else}
<div class="text-center py-12"> <div class="text-center py-12">
<CheckCircle class="mx-auto mb-4 h-16 w-16 text-success" /> <CheckCircle class="mx-auto mb-4 h-20 w-20 text-success" />
<h3 class="text-xl font-bold mb-2">Nenhum alerta encontrado</h3> <h3 class="text-2xl font-bold mb-2">Nenhum alerta encontrado</h3>
<p class="text-base-content/70"> <p class="text-base-content/70 text-lg">
{#if filtroStatus === 'ativo'} {#if filtroStatus === 'ativo'}
Não há alertas ativos no momento. Todos os materiais estão com estoque adequado! Não há alertas ativos no momento. Todos os materiais estão com estoque adequado!
{:else} {:else}

View File

@@ -52,9 +52,9 @@
} }
</script> </script>
<main class="container mx-auto px-4 py-4"> <main class="container mx-auto px-4 py-6">
<!-- Breadcrumb --> <!-- Breadcrumb -->
<div class="breadcrumbs mb-4 text-sm"> <div class="breadcrumbs mb-6 text-sm">
<ul> <ul>
<li> <li>
<a href={resolve('/almoxarifado')} class="text-primary hover:underline">Almoxarifado</a> <a href={resolve('/almoxarifado')} class="text-primary hover:underline">Almoxarifado</a>
@@ -64,18 +64,18 @@
</div> </div>
<!-- Cabeçalho --> <!-- Cabeçalho -->
<div class="mb-6"> <div class="mb-8">
<div class="flex flex-col items-start justify-between gap-4 sm:flex-row sm:items-center"> <div class="flex flex-col items-start justify-between gap-4 sm:flex-row sm:items-center">
<div class="flex items-center gap-4"> <div class="flex items-center gap-4">
<div class="rounded-xl bg-amber-500/20 p-3"> <div class="rounded-2xl bg-gradient-to-br from-amber-500/20 to-amber-600/30 p-4 shadow-lg">
<Package class="h-8 w-8 text-amber-600" strokeWidth={2} /> <Package class="h-10 w-10 text-amber-600" strokeWidth={2.5} />
</div> </div>
<div> <div>
<h1 class="text-3xl font-bold">Materiais</h1> <h1 class="text-3xl font-bold tracking-tight">Materiais</h1>
<p class="text-base-content/70">Gerencie o cadastro de materiais do almoxarifado</p> <p class="text-base-content/70 text-lg">Gerencie o cadastro de materiais do almoxarifado</p>
</div> </div>
</div> </div>
<button class="btn btn-primary" onclick={navCadastro}> <button class="btn btn-primary shadow-lg hover:shadow-xl transition-all" onclick={navCadastro}>
<Plus class="h-5 w-5" /> <Plus class="h-5 w-5" />
Cadastrar Material Cadastrar Material
</button> </button>
@@ -83,8 +83,9 @@
</div> </div>
<!-- Filtros --> <!-- Filtros -->
<div class="card bg-base-100 mb-6 shadow-xl"> <div class="card bg-base-100 border border-base-300 mb-6 shadow-xl">
<div class="card-body"> <div class="card-body">
<h3 class="text-lg font-semibold mb-4">Filtros de Busca</h3>
<div class="grid grid-cols-1 gap-4 md:grid-cols-4"> <div class="grid grid-cols-1 gap-4 md:grid-cols-4">
<div class="form-control"> <div class="form-control">
<label class="label"> <label class="label">
@@ -135,68 +136,74 @@
</div> </div>
<!-- Tabela --> <!-- Tabela -->
<div class="card bg-base-100 shadow-xl"> <div class="card bg-base-100 border border-base-300 shadow-xl">
<div class="card-body"> <div class="card-body">
<div class="overflow-x-auto"> <div class="overflow-x-auto">
<table class="table"> <table class="table table-zebra">
<thead> <thead>
<tr> <tr class="bg-base-200">
<th>Código</th> <th class="font-semibold">Código</th>
<th>Nome</th> <th class="font-semibold">Nome</th>
<th>Categoria</th> <th class="font-semibold">Categoria</th>
<th>Estoque Atual</th> <th class="font-semibold">Estoque Atual</th>
<th>Estoque Mínimo</th> <th class="font-semibold">Estoque Mínimo</th>
<th>Unidade</th> <th class="font-semibold">Unidade</th>
<th>Status</th> <th class="font-semibold">Status</th>
<th>Ações</th> <th class="font-semibold">Ações</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{#if filtered.length === 0} {#if filtered.length === 0}
<tr> <tr>
<td colspan="8" class="text-center"> <td colspan="8" class="text-center">
<div class="py-8"> <div class="py-12">
<Package class="mx-auto mb-4 h-12 w-12 text-base-content/30" /> <Package class="mx-auto mb-4 h-16 w-16 text-base-content/30" />
<p class="text-base-content/70">Nenhum material encontrado</p> <p class="text-base-content/70 text-lg font-medium">Nenhum material encontrado</p>
<p class="text-base-content/50 text-sm mt-2">Tente ajustar os filtros de busca</p>
</div> </div>
</td> </td>
</tr> </tr>
{:else} {:else}
{#each filtered as material} {#each filtered as material}
<tr> <tr class="hover:bg-base-200/50 transition-colors">
<td> <td>
<div class="font-mono font-bold">{material.codigo}</div> <div class="font-mono font-bold text-primary">{material.codigo}</div>
</td> </td>
<td> <td>
<div class="font-medium">{material.nome}</div> <div class="font-medium">{material.nome}</div>
{#if material.descricao} {#if material.descricao}
<div class="text-sm text-base-content/60">{material.descricao}</div> <div class="text-sm text-base-content/60 line-clamp-1">{material.descricao}</div>
{/if} {/if}
</td> </td>
<td> <td>
<span class="badge badge-outline">{material.categoria}</span> <span class="badge badge-outline badge-lg">{material.categoria}</span>
</td> </td>
<td> <td>
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<span class="font-bold">{material.estoqueAtual}</span> <span class="font-bold {material.estoqueAtual <= material.estoqueMinimo ? 'text-error' : 'text-success'}">{material.estoqueAtual}</span>
{#if material.estoqueAtual <= material.estoqueMinimo} {#if material.estoqueAtual <= material.estoqueMinimo}
<AlertTriangle class="h-4 w-4 text-warning" /> <AlertTriangle class="h-4 w-4 text-warning" />
{/if} {/if}
</div> </div>
</td> </td>
<td>{material.estoqueMinimo}</td> <td>
<td>{material.unidadeMedida}</td> <span class="font-medium">{material.estoqueMinimo}</span>
</td>
<td>
<span class="badge badge-ghost">{material.unidadeMedida}</span>
</td>
<td> <td>
{#if material.ativo} {#if material.ativo}
<span class="badge badge-success">Ativo</span> <span class="badge badge-success badge-lg">Ativo</span>
{:else} {:else}
<span class="badge badge-error">Inativo</span> <span class="badge badge-error badge-lg">Inativo</span>
{/if} {/if}
</td> </td>
<td> <td>
<div class="flex gap-2"> <div class="flex gap-2">
<button <button
class="btn btn-sm btn-ghost" class="btn btn-sm btn-ghost hover:btn-primary"
title="Visualizar"
onclick={() => onclick={() =>
goto( goto(
resolve( resolve(
@@ -208,7 +215,8 @@
<Eye class="h-4 w-4" /> <Eye class="h-4 w-4" />
</button> </button>
<button <button
class="btn btn-sm btn-ghost" class="btn btn-sm btn-ghost hover:btn-info"
title="Editar"
onclick={() => onclick={() =>
goto( goto(
resolve( resolve(
@@ -230,8 +238,10 @@
</div> </div>
{#if filtered.length > 0} {#if filtered.length > 0}
<div class="mt-4 text-sm text-base-content/70"> <div class="mt-6 flex items-center justify-between border-t border-base-300 pt-4">
Mostrando {filtered.length} de {materiais.length} materiais <div class="text-sm text-base-content/70">
Mostrando <span class="font-semibold text-base-content">{filtered.length}</span> de <span class="font-semibold text-base-content">{materiais.length}</span> materiais
</div>
</div> </div>
{/if} {/if}
</div> </div>

View File

@@ -61,9 +61,9 @@
<span class="loading loading-spinner loading-lg"></span> <span class="loading loading-spinner loading-lg"></span>
</div> </div>
{:else if material} {:else if material}
<main class="container mx-auto px-4 py-4"> <main class="container mx-auto px-4 py-6">
<!-- Breadcrumb --> <!-- Breadcrumb -->
<div class="breadcrumbs mb-4 text-sm"> <div class="breadcrumbs mb-6 text-sm">
<ul> <ul>
<li> <li>
<a href={resolve('/almoxarifado')} class="text-primary hover:underline">Almoxarifado</a> <a href={resolve('/almoxarifado')} class="text-primary hover:underline">Almoxarifado</a>
@@ -78,7 +78,7 @@
</div> </div>
<!-- Cabeçalho --> <!-- Cabeçalho -->
<div class="mb-6"> <div class="mb-8">
<div class="flex flex-col items-start justify-between gap-4 sm:flex-row sm:items-center"> <div class="flex flex-col items-start justify-between gap-4 sm:flex-row sm:items-center">
<div class="flex items-center gap-4"> <div class="flex items-center gap-4">
<button <button
@@ -87,15 +87,15 @@
> >
<ArrowLeft class="h-5 w-5" /> <ArrowLeft class="h-5 w-5" />
</button> </button>
<div class="rounded-xl bg-amber-500/20 p-3"> <div class="rounded-2xl bg-gradient-to-br from-amber-500/20 to-amber-600/30 p-4 shadow-lg">
<Package class="h-8 w-8 text-amber-600" strokeWidth={2} /> <Package class="h-10 w-10 text-amber-600" strokeWidth={2.5} />
</div> </div>
<div> <div>
<h1 class="text-3xl font-bold">{material.nome}</h1> <h1 class="text-3xl font-bold tracking-tight">{material.nome}</h1>
<p class="text-base-content/70">Detalhes do material</p> <p class="text-base-content/70 text-lg">Detalhes do material</p>
</div> </div>
</div> </div>
<button class="btn btn-primary" onclick={navEditar}> <button class="btn btn-primary shadow-lg hover:shadow-xl transition-all" onclick={navEditar}>
<Edit class="h-5 w-5" /> <Edit class="h-5 w-5" />
Editar Material Editar Material
</button> </button>
@@ -105,10 +105,12 @@
<!-- Informações Principais --> <!-- Informações Principais -->
<div class="grid grid-cols-1 gap-6 md:grid-cols-2"> <div class="grid grid-cols-1 gap-6 md:grid-cols-2">
<!-- Card: Informações Básicas --> <!-- Card: Informações Básicas -->
<div class="card bg-base-100 shadow-xl"> <div class="card bg-base-100 border border-base-300 shadow-xl">
<div class="card-body"> <div class="card-body">
<h2 class="card-title mb-4"> <h2 class="card-title mb-6 text-xl">
<Package class="h-5 w-5" /> <div class="rounded-lg bg-primary/20 p-2">
<Package class="h-6 w-6 text-primary" />
</div>
Informações Básicas Informações Básicas
</h2> </h2>
<div class="space-y-4"> <div class="space-y-4">
@@ -157,10 +159,12 @@
</div> </div>
<!-- Card: Estoque --> <!-- Card: Estoque -->
<div class="card bg-base-100 shadow-xl"> <div class="card bg-base-100 border border-base-300 shadow-xl">
<div class="card-body"> <div class="card-body">
<h2 class="card-title mb-4"> <h2 class="card-title mb-6 text-xl">
<Boxes class="h-5 w-5" /> <div class="rounded-lg bg-info/20 p-2">
<Boxes class="h-6 w-6 text-info" />
</div>
Estoque Estoque
</h2> </h2>
<div class="space-y-4"> <div class="space-y-4">
@@ -196,10 +200,12 @@
</div> </div>
<!-- Informações Adicionais --> <!-- Informações Adicionais -->
<div class="card bg-base-100 mt-6 shadow-xl"> <div class="card bg-base-100 border border-base-300 mt-6 shadow-xl">
<div class="card-body"> <div class="card-body">
<h2 class="card-title mb-4"> <h2 class="card-title mb-6 text-xl">
<Tag class="h-5 w-5" /> <div class="rounded-lg bg-success/20 p-2">
<Tag class="h-6 w-6 text-success" />
</div>
Informações Adicionais Informações Adicionais
</h2> </h2>
<div class="grid grid-cols-1 gap-4 md:grid-cols-2"> <div class="grid grid-cols-1 gap-4 md:grid-cols-2">

View File

@@ -138,9 +138,9 @@
<span class="loading loading-spinner loading-lg"></span> <span class="loading loading-spinner loading-lg"></span>
</div> </div>
{:else} {:else}
<main class="container mx-auto px-4 py-4"> <main class="container mx-auto px-4 py-6">
<!-- Breadcrumb --> <!-- Breadcrumb -->
<div class="breadcrumbs mb-4 text-sm"> <div class="breadcrumbs mb-6 text-sm">
<ul> <ul>
<li> <li>
<a href={resolve('/almoxarifado')} class="text-primary hover:underline">Almoxarifado</a> <a href={resolve('/almoxarifado')} class="text-primary hover:underline">Almoxarifado</a>
@@ -160,7 +160,7 @@
</div> </div>
<!-- Cabeçalho --> <!-- Cabeçalho -->
<div class="mb-6"> <div class="mb-8">
<div class="flex items-center gap-4"> <div class="flex items-center gap-4">
<button <button
class="btn btn-ghost btn-sm" class="btn btn-ghost btn-sm"
@@ -168,12 +168,12 @@
> >
<ArrowLeft class="h-5 w-5" /> <ArrowLeft class="h-5 w-5" />
</button> </button>
<div class="rounded-xl bg-amber-500/20 p-3"> <div class="rounded-2xl bg-gradient-to-br from-amber-500/20 to-amber-600/30 p-4 shadow-lg">
<Package class="h-8 w-8 text-amber-600" strokeWidth={2} /> <Package class="h-10 w-10 text-amber-600" strokeWidth={2.5} />
</div> </div>
<div> <div>
<h1 class="text-3xl font-bold">Editar Material</h1> <h1 class="text-3xl font-bold tracking-tight">Editar Material</h1>
<p class="text-base-content/70">Atualize as informações do material</p> <p class="text-base-content/70 text-lg">Atualize as informações do material</p>
</div> </div>
</div> </div>
</div> </div>
@@ -186,7 +186,7 @@
{/if} {/if}
<!-- Formulário --> <!-- Formulário -->
<div class="card bg-base-100 shadow-xl"> <div class="card bg-base-100 border border-base-300 shadow-xl">
<div class="card-body"> <div class="card-body">
<form onsubmit={(e) => { e.preventDefault(); handleSubmit(); }}> <form onsubmit={(e) => { e.preventDefault(); handleSubmit(); }}>
<div class="grid grid-cols-1 gap-6 md:grid-cols-2"> <div class="grid grid-cols-1 gap-6 md:grid-cols-2">

View File

@@ -89,9 +89,9 @@
} }
</script> </script>
<main class="container mx-auto px-4 py-4"> <main class="container mx-auto px-4 py-6">
<!-- Breadcrumb --> <!-- Breadcrumb -->
<div class="breadcrumbs mb-4 text-sm"> <div class="breadcrumbs mb-6 text-sm">
<ul> <ul>
<li> <li>
<a href={resolve('/almoxarifado')} class="text-primary hover:underline">Almoxarifado</a> <a href={resolve('/almoxarifado')} class="text-primary hover:underline">Almoxarifado</a>
@@ -106,7 +106,7 @@
</div> </div>
<!-- Cabeçalho --> <!-- Cabeçalho -->
<div class="mb-6"> <div class="mb-8">
<div class="flex items-center gap-4"> <div class="flex items-center gap-4">
<button <button
class="btn btn-ghost btn-sm" class="btn btn-ghost btn-sm"
@@ -114,12 +114,12 @@
> >
<ArrowLeft class="h-5 w-5" /> <ArrowLeft class="h-5 w-5" />
</button> </button>
<div class="rounded-xl bg-amber-500/20 p-3"> <div class="rounded-2xl bg-gradient-to-br from-amber-500/20 to-amber-600/30 p-4 shadow-lg">
<Package class="h-8 w-8 text-amber-600" strokeWidth={2} /> <Package class="h-10 w-10 text-amber-600" strokeWidth={2.5} />
</div> </div>
<div> <div>
<h1 class="text-3xl font-bold">Cadastrar Material</h1> <h1 class="text-3xl font-bold tracking-tight">Cadastrar Material</h1>
<p class="text-base-content/70">Adicione um novo material ao almoxarifado</p> <p class="text-base-content/70 text-lg">Adicione um novo material ao almoxarifado</p>
</div> </div>
</div> </div>
</div> </div>
@@ -132,7 +132,7 @@
{/if} {/if}
<!-- Formulário --> <!-- Formulário -->
<div class="card bg-base-100 shadow-xl"> <div class="card bg-base-100 border border-base-300 shadow-xl">
<div class="card-body"> <div class="card-body">
<form onsubmit={(e) => { e.preventDefault(); handleSubmit(); }}> <form onsubmit={(e) => { e.preventDefault(); handleSubmit(); }}>
<div class="grid grid-cols-1 gap-6 md:grid-cols-2"> <div class="grid grid-cols-1 gap-6 md:grid-cols-2">

View File

@@ -39,6 +39,26 @@
const setoresQuery = useQuery(api.setores.list, {}); const setoresQuery = useQuery(api.setores.list, {});
const movimentacoesQuery = useQuery(api.almoxarifado.listarMovimentacoes, {}); const movimentacoesQuery = useQuery(api.almoxarifado.listarMovimentacoes, {});
// Criar mapa de funcionários para lookup eficiente
const funcionariosMap = $derived.by(() => {
if (!funcionariosQuery.data) return new Map();
const map = new Map();
for (const funcionario of funcionariosQuery.data) {
map.set(funcionario._id, funcionario);
}
return map;
});
// Criar mapa de materiais para lookup eficiente
const materiaisMap = $derived.by(() => {
if (!materiaisQuery.data) return new Map();
const map = new Map();
for (const material of materiaisQuery.data) {
map.set(material._id, material);
}
return map;
});
let notice = $state<{ kind: 'success' | 'error'; text: string } | null>(null); let notice = $state<{ kind: 'success' | 'error'; text: string } | null>(null);
function mostrarMensagem(kind: 'success' | 'error', text: string) { function mostrarMensagem(kind: 'success' | 'error', text: string) {
@@ -151,15 +171,10 @@
}); });
</script> </script>
<main class="container mx-auto px-4 py-4"> <main class="container mx-auto px-4 py-6">
<!-- Breadcrumb --> <!-- Breadcrumb -->
<div class="breadcrumbs mb-4 text-sm"> <div class="breadcrumbs mb-6 text-sm">
<ul> <ul>
<li>
<a href={resolve('/recursos-humanos')} class="text-primary hover:underline"
>Recursos Humanos</a
>
</li>
<li> <li>
<a href={resolve('/almoxarifado')} class="text-primary hover:underline" <a href={resolve('/almoxarifado')} class="text-primary hover:underline"
>Almoxarifado</a >Almoxarifado</a
@@ -170,14 +185,14 @@
</div> </div>
<!-- Cabeçalho --> <!-- Cabeçalho -->
<div class="mb-6"> <div class="mb-8">
<div class="flex items-center gap-4"> <div class="flex items-center gap-4">
<div class="rounded-xl bg-info/20 p-3"> <div class="rounded-2xl bg-gradient-to-br from-info/20 to-info/30 p-4 shadow-lg">
<ArrowLeftRight class="h-8 w-8 text-info" strokeWidth={2} /> <ArrowLeftRight class="h-10 w-10 text-info" strokeWidth={2.5} />
</div> </div>
<div> <div>
<h1 class="text-3xl font-bold">Movimentações de Estoque</h1> <h1 class="text-3xl font-bold tracking-tight">Movimentações de Estoque</h1>
<p class="text-base-content/70">Registre entradas, saídas e ajustes de estoque</p> <p class="text-base-content/70 text-lg">Registre entradas, saídas e ajustes de estoque</p>
</div> </div>
</div> </div>
</div> </div>
@@ -190,30 +205,30 @@
{/if} {/if}
<!-- Abas --> <!-- Abas -->
<div class="tabs tabs-boxed mb-6"> <div class="tabs tabs-boxed mb-6 bg-base-200 shadow-md">
<button <button
class="tab {abaAtiva === 'entrada' ? 'tab-active' : ''}" class="tab {abaAtiva === 'entrada' ? 'tab-active' : ''} transition-all"
onclick={() => (abaAtiva = 'entrada')} onclick={() => (abaAtiva = 'entrada')}
> >
<ArrowDown class="h-5 w-5 mr-2" /> <ArrowDown class="h-5 w-5 mr-2" />
Entrada Entrada
</button> </button>
<button <button
class="tab {abaAtiva === 'saida' ? 'tab-active' : ''}" class="tab {abaAtiva === 'saida' ? 'tab-active' : ''} transition-all"
onclick={() => (abaAtiva = 'saida')} onclick={() => (abaAtiva = 'saida')}
> >
<ArrowUp class="h-5 w-5 mr-2" /> <ArrowUp class="h-5 w-5 mr-2" />
Saída Saída
</button> </button>
<button <button
class="tab {abaAtiva === 'ajuste' ? 'tab-active' : ''}" class="tab {abaAtiva === 'ajuste' ? 'tab-active' : ''} transition-all"
onclick={() => (abaAtiva = 'ajuste')} onclick={() => (abaAtiva = 'ajuste')}
> >
<Settings class="h-5 w-5 mr-2" /> <Settings class="h-5 w-5 mr-2" />
Ajuste Ajuste
</button> </button>
<button <button
class="tab {abaAtiva === 'historico' ? 'tab-active' : ''}" class="tab {abaAtiva === 'historico' ? 'tab-active' : ''} transition-all"
onclick={() => (abaAtiva = 'historico')} onclick={() => (abaAtiva = 'historico')}
> >
<History class="h-5 w-5 mr-2" /> <History class="h-5 w-5 mr-2" />
@@ -223,9 +238,14 @@
<!-- Conteúdo das Abas --> <!-- Conteúdo das Abas -->
{#if abaAtiva === 'entrada'} {#if abaAtiva === 'entrada'}
<div class="card bg-base-100 shadow-xl"> <div class="card bg-base-100 border border-base-300 shadow-xl">
<div class="card-body"> <div class="card-body">
<h2 class="card-title mb-4">Registrar Entrada de Material</h2> <h2 class="card-title mb-6 text-xl">
<div class="rounded-lg bg-success/20 p-2">
<ArrowDown class="h-6 w-6 text-success" />
</div>
Registrar Entrada de Material
</h2>
<form onsubmit={(e) => { e.preventDefault(); registrarEntrada(); }}> <form onsubmit={(e) => { e.preventDefault(); registrarEntrada(); }}>
<div class="grid grid-cols-1 gap-4 md:grid-cols-2"> <div class="grid grid-cols-1 gap-4 md:grid-cols-2">
<div class="form-control md:col-span-2"> <div class="form-control md:col-span-2">
@@ -310,9 +330,14 @@
</div> </div>
</div> </div>
{:else if abaAtiva === 'saida'} {:else if abaAtiva === 'saida'}
<div class="card bg-base-100 shadow-xl"> <div class="card bg-base-100 border border-base-300 shadow-xl">
<div class="card-body"> <div class="card-body">
<h2 class="card-title mb-4">Registrar Saída de Material</h2> <h2 class="card-title mb-6 text-xl">
<div class="rounded-lg bg-error/20 p-2">
<ArrowUp class="h-6 w-6 text-error" />
</div>
Registrar Saída de Material
</h2>
<form onsubmit={(e) => { e.preventDefault(); registrarSaida(); }}> <form onsubmit={(e) => { e.preventDefault(); registrarSaida(); }}>
<div class="grid grid-cols-1 gap-4 md:grid-cols-2"> <div class="grid grid-cols-1 gap-4 md:grid-cols-2">
<div class="form-control md:col-span-2"> <div class="form-control md:col-span-2">
@@ -413,12 +438,17 @@
</div> </div>
</div> </div>
{:else if abaAtiva === 'ajuste'} {:else if abaAtiva === 'ajuste'}
<div class="card bg-base-100 shadow-xl"> <div class="card bg-base-100 border border-base-300 shadow-xl">
<div class="card-body"> <div class="card-body">
<h2 class="card-title mb-4">Ajustar Estoque</h2> <h2 class="card-title mb-6 text-xl">
<div class="alert alert-warning mb-4"> <div class="rounded-lg bg-warning/20 p-2">
<Settings class="h-6 w-6 text-warning" />
</div>
Ajustar Estoque
</h2>
<div class="alert alert-warning mb-6 shadow-lg">
<Settings class="h-6 w-6" /> <Settings class="h-6 w-6" />
<span>Ajustes de estoque devem ser justificados e são registrados no histórico.</span> <span class="font-medium">Ajustes de estoque devem ser justificados e são registrados no histórico.</span>
</div> </div>
<form onsubmit={(e) => { e.preventDefault(); ajustarEstoque(); }}> <form onsubmit={(e) => { e.preventDefault(); ajustarEstoque(); }}>
<div class="grid grid-cols-1 gap-4 md:grid-cols-2"> <div class="grid grid-cols-1 gap-4 md:grid-cols-2">
@@ -491,50 +521,82 @@
</div> </div>
</div> </div>
{:else if abaAtiva === 'historico'} {:else if abaAtiva === 'historico'}
<div class="card bg-base-100 shadow-xl"> <div class="card bg-base-100 border border-base-300 shadow-xl">
<div class="card-body"> <div class="card-body">
<h2 class="card-title mb-4">Histórico de Movimentações</h2> <h2 class="card-title mb-6 text-xl">
<div class="rounded-lg bg-info/20 p-2">
<History class="h-6 w-6 text-info" />
</div>
Histórico de Movimentações
</h2>
<div class="overflow-x-auto"> <div class="overflow-x-auto">
<table class="table"> <table class="table table-zebra">
<thead> <thead>
<tr> <tr class="bg-base-200">
<th>Data</th> <th class="font-semibold">Data</th>
<th>Material</th> <th class="font-semibold">Material</th>
<th>Tipo</th> <th class="font-semibold">Tipo</th>
<th>Quantidade</th> <th class="font-semibold">Quantidade</th>
<th>Anterior</th> <th class="font-semibold">Anterior</th>
<th>Nova</th> <th class="font-semibold">Nova</th>
<th>Motivo</th> <th class="font-semibold">Funcionário</th>
<th class="font-semibold">Motivo</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{#if movimentacoesQuery.data && movimentacoesQuery.data.length > 0} {#if movimentacoesQuery.data && movimentacoesQuery.data.length > 0}
{#each movimentacoesQuery.data.slice(0, 50) as mov} {#each movimentacoesQuery.data.slice(0, 50) as mov}
{@const material = materiaisQuery.data?.find(m => m._id === mov.materialId)} {@const material = materiaisMap.get(mov.materialId)}
<tr> {@const funcionario = mov.funcionarioId ? funcionariosMap.get(mov.funcionarioId) : null}
<td>{new Date(mov.data).toLocaleString('pt-BR')}</td> <tr class="hover:bg-base-200/50 transition-colors">
<td>{material?.nome || 'Carregando...'}</td>
<td> <td>
{#if mov.tipo === 'entrada'} <span class="text-sm">{new Date(mov.data).toLocaleString('pt-BR')}</span>
<span class="badge badge-success">Entrada</span> </td>
{:else if mov.tipo === 'saida'} <td>
<span class="badge badge-error">Saída</span> <div class="font-medium">{material?.nome || 'Carregando...'}</div>
{:else} {#if material?.codigo}
<span class="badge badge-warning">Ajuste</span> <div class="text-xs text-base-content/50 font-mono">{material.codigo}</div>
{/if} {/if}
</td> </td>
<td>{mov.quantidade}</td> <td>
<td>{mov.quantidadeAnterior}</td> {#if mov.tipo === 'entrada'}
<td>{mov.quantidadeNova}</td> <span class="badge badge-success badge-lg">Entrada</span>
<td>{mov.motivo}</td> {:else if mov.tipo === 'saida'}
<span class="badge badge-error badge-lg">Saída</span>
{:else}
<span class="badge badge-warning badge-lg">Ajuste</span>
{/if}
</td>
<td>
<span class="font-bold">{mov.quantidade}</span>
</td>
<td>
<span class="text-base-content/70">{mov.quantidadeAnterior}</span>
</td>
<td>
<span class="font-semibold">{mov.quantidadeNova}</span>
</td>
<td>
{#if funcionario}
<div class="font-medium">{funcionario.nome}</div>
{#if funcionario.matricula}
<div class="text-xs text-base-content/50">Mat: {funcionario.matricula}</div>
{/if}
{:else}
<span class="text-base-content/50 text-sm italic"></span>
{/if}
</td>
<td>
<span class="text-sm">{mov.motivo}</span>
</td>
</tr> </tr>
{/each} {/each}
{:else} {:else}
<tr> <tr>
<td colspan="7" class="text-center"> <td colspan="8" class="text-center">
<div class="py-8"> <div class="py-12">
<History class="mx-auto mb-4 h-12 w-12 text-base-content/30" /> <History class="mx-auto mb-4 h-16 w-16 text-base-content/30" />
<p class="text-base-content/70">Nenhuma movimentação registrada</p> <p class="text-base-content/70 text-lg font-medium">Nenhuma movimentação registrada</p>
</div> </div>
</td> </td>
</tr> </tr>

View File

@@ -51,15 +51,10 @@
} }
</script> </script>
<main class="container mx-auto px-4 py-4"> <main class="container mx-auto px-4 py-6">
<!-- Breadcrumb --> <!-- Breadcrumb -->
<div class="breadcrumbs mb-4 text-sm"> <div class="breadcrumbs mb-6 text-sm">
<ul> <ul>
<li>
<a href={resolve('/recursos-humanos')} class="text-primary hover:underline"
>Recursos Humanos</a
>
</li>
<li> <li>
<a href={resolve('/almoxarifado')} class="text-primary hover:underline" <a href={resolve('/almoxarifado')} class="text-primary hover:underline"
>Almoxarifado</a >Almoxarifado</a
@@ -70,62 +65,78 @@
</div> </div>
<!-- Cabeçalho --> <!-- Cabeçalho -->
<div class="mb-6"> <div class="mb-8">
<div class="flex items-center gap-4"> <div class="flex items-center gap-4">
<div class="rounded-xl bg-success/20 p-3"> <div class="rounded-2xl bg-gradient-to-br from-success/20 to-success/30 p-4 shadow-lg">
<BarChart3 class="h-8 w-8 text-success" strokeWidth={2} /> <BarChart3 class="h-10 w-10 text-success" strokeWidth={2.5} />
</div> </div>
<div> <div>
<h1 class="text-3xl font-bold">Relatórios</h1> <h1 class="text-3xl font-bold tracking-tight">Relatórios</h1>
<p class="text-base-content/70">Estatísticas e relatórios do almoxarifado</p> <p class="text-base-content/70 text-lg">Estatísticas e relatórios do almoxarifado</p>
</div> </div>
</div> </div>
</div> </div>
<!-- Estatísticas Gerais --> <!-- Estatísticas Gerais -->
{#if statsQuery.data} {#if statsQuery.data}
<div class="grid grid-cols-1 gap-4 md:grid-cols-4 mb-8"> <div class="grid grid-cols-1 gap-6 md:grid-cols-2 lg:grid-cols-4 mb-8">
<div class="stats bg-base-100 shadow-lg"> <div class="card bg-gradient-to-br from-primary/10 via-primary/5 to-base-100 border border-primary/20 shadow-xl hover:shadow-2xl transition-all duration-300 hover:scale-105">
<div class="stat"> <div class="card-body">
<div class="stat-figure text-primary"> <div class="flex items-center justify-between">
<Package class="h-8 w-8" /> <div class="flex-1">
<div class="text-sm font-medium text-base-content/60 mb-1">Total de Materiais</div>
<div class="text-3xl font-bold text-primary mb-1">{statsQuery.data.totalMateriais}</div>
<div class="text-xs text-base-content/50">Cadastrados no sistema</div>
</div>
<div class="rounded-xl bg-primary/20 p-3">
<Package class="h-8 w-8 text-primary" strokeWidth={2.5} />
</div>
</div> </div>
<div class="stat-title">Total de Materiais</div>
<div class="stat-value text-primary">{statsQuery.data.totalMateriais}</div>
<div class="stat-desc">Cadastrados no sistema</div>
</div> </div>
</div> </div>
<div class="stats bg-base-100 shadow-lg"> <div class="card bg-gradient-to-br from-success/10 via-success/5 to-base-100 border border-success/20 shadow-xl hover:shadow-2xl transition-all duration-300 hover:scale-105">
<div class="stat"> <div class="card-body">
<div class="stat-figure text-success"> <div class="flex items-center justify-between">
<CheckCircle class="h-8 w-8" /> <div class="flex-1">
<div class="text-sm font-medium text-base-content/60 mb-1">Materiais Ativos</div>
<div class="text-3xl font-bold text-success mb-1">{statsQuery.data.totalMateriaisAtivos}</div>
<div class="text-xs text-base-content/50">Em estoque</div>
</div>
<div class="rounded-xl bg-success/20 p-3">
<CheckCircle class="h-8 w-8 text-success" strokeWidth={2.5} />
</div>
</div> </div>
<div class="stat-title">Materiais Ativos</div>
<div class="stat-value text-success">{statsQuery.data.totalMateriaisAtivos}</div>
<div class="stat-desc">Em estoque</div>
</div> </div>
</div> </div>
<div class="stats bg-base-100 shadow-lg"> <div class="card bg-gradient-to-br from-warning/10 via-warning/5 to-base-100 border border-warning/20 shadow-xl hover:shadow-2xl transition-all duration-300 hover:scale-105">
<div class="stat"> <div class="card-body">
<div class="stat-figure text-warning"> <div class="flex items-center justify-between">
<AlertTriangle class="h-8 w-8" /> <div class="flex-1">
<div class="text-sm font-medium text-base-content/60 mb-1">Alertas Ativos</div>
<div class="text-3xl font-bold text-warning mb-1">{statsQuery.data.totalAlertasAtivos}</div>
<div class="text-xs text-base-content/50">Estoque baixo</div>
</div>
<div class="rounded-xl bg-warning/20 p-3">
<AlertTriangle class="h-8 w-8 text-warning" strokeWidth={2.5} />
</div>
</div> </div>
<div class="stat-title">Alertas Ativos</div>
<div class="stat-value text-warning">{statsQuery.data.totalAlertasAtivos}</div>
<div class="stat-desc">Estoque baixo</div>
</div> </div>
</div> </div>
<div class="stats bg-base-100 shadow-lg"> <div class="card bg-gradient-to-br from-info/10 via-info/5 to-base-100 border border-info/20 shadow-xl hover:shadow-2xl transition-all duration-300 hover:scale-105">
<div class="stat"> <div class="card-body">
<div class="stat-figure text-info"> <div class="flex items-center justify-between">
<ArrowLeftRight class="h-8 w-8" /> <div class="flex-1">
<div class="text-sm font-medium text-base-content/60 mb-1">Movimentações</div>
<div class="text-3xl font-bold text-info mb-1">{statsQuery.data.movimentacoesMes}</div>
<div class="text-xs text-base-content/50">Este mês</div>
</div>
<div class="rounded-xl bg-info/20 p-3">
<ArrowLeftRight class="h-8 w-8 text-info" strokeWidth={2.5} />
</div>
</div> </div>
<div class="stat-title">Movimentações</div>
<div class="stat-value text-info">{statsQuery.data.movimentacoesMes}</div>
<div class="stat-desc">Este mês</div>
</div> </div>
</div> </div>
</div> </div>
@@ -134,7 +145,7 @@
<!-- Relatórios Disponíveis --> <!-- Relatórios Disponíveis -->
<div class="grid grid-cols-1 gap-6 md:grid-cols-2"> <div class="grid grid-cols-1 gap-6 md:grid-cols-2">
<!-- Relatório de Materiais por Categoria --> <!-- Relatório de Materiais por Categoria -->
<div class="card bg-base-100 shadow-xl"> <div class="card bg-base-100 border border-base-300 shadow-xl">
<div class="card-body"> <div class="card-body">
<div class="flex items-center justify-between mb-4"> <div class="flex items-center justify-between mb-4">
<h2 class="card-title">Materiais por Categoria</h2> <h2 class="card-title">Materiais por Categoria</h2>
@@ -171,7 +182,7 @@
</div> </div>
<!-- Movimentações do Mês --> <!-- Movimentações do Mês -->
<div class="card bg-base-100 shadow-xl"> <div class="card bg-base-100 border border-base-300 shadow-xl">
<div class="card-body"> <div class="card-body">
<div class="flex items-center justify-between mb-4"> <div class="flex items-center justify-between mb-4">
<h2 class="card-title">Movimentações do Mês</h2> <h2 class="card-title">Movimentações do Mês</h2>
@@ -209,7 +220,7 @@
</div> </div>
<!-- Materiais com Estoque Baixo --> <!-- Materiais com Estoque Baixo -->
<div class="card bg-base-100 shadow-xl"> <div class="card bg-base-100 border border-base-300 shadow-xl">
<div class="card-body"> <div class="card-body">
<div class="flex items-center justify-between mb-4"> <div class="flex items-center justify-between mb-4">
<h2 class="card-title">Materiais com Estoque Baixo</h2> <h2 class="card-title">Materiais com Estoque Baixo</h2>
@@ -264,7 +275,7 @@
</div> </div>
<!-- Alertas Recentes --> <!-- Alertas Recentes -->
<div class="card bg-base-100 shadow-xl"> <div class="card bg-base-100 border border-base-300 shadow-xl">
<div class="card-body"> <div class="card-body">
<div class="flex items-center justify-between mb-4"> <div class="flex items-center justify-between mb-4">
<h2 class="card-title">Alertas Recentes</h2> <h2 class="card-title">Alertas Recentes</h2>

View File

@@ -184,15 +184,10 @@
} }
</script> </script>
<main class="container mx-auto px-4 py-4"> <main class="container mx-auto px-4 py-6">
<!-- Breadcrumb --> <!-- Breadcrumb -->
<div class="breadcrumbs mb-4 text-sm"> <div class="breadcrumbs mb-6 text-sm">
<ul> <ul>
<li>
<a href={resolve('/recursos-humanos')} class="text-primary hover:underline"
>Recursos Humanos</a
>
</li>
<li> <li>
<a href={resolve('/almoxarifado')} class="text-primary hover:underline" <a href={resolve('/almoxarifado')} class="text-primary hover:underline"
>Almoxarifado</a >Almoxarifado</a
@@ -203,18 +198,18 @@
</div> </div>
<!-- Cabeçalho --> <!-- Cabeçalho -->
<div class="mb-6"> <div class="mb-8">
<div class="flex flex-col items-start justify-between gap-4 sm:flex-row sm:items-center"> <div class="flex flex-col items-start justify-between gap-4 sm:flex-row sm:items-center">
<div class="flex items-center gap-4"> <div class="flex items-center gap-4">
<div class="rounded-xl bg-purple-500/20 p-3"> <div class="rounded-2xl bg-gradient-to-br from-purple-500/20 to-purple-600/30 p-4 shadow-lg">
<ClipboardList class="h-8 w-8 text-purple-600" strokeWidth={2} /> <ClipboardList class="h-10 w-10 text-purple-600" strokeWidth={2.5} />
</div> </div>
<div> <div>
<h1 class="text-3xl font-bold">Requisições de Material</h1> <h1 class="text-3xl font-bold tracking-tight">Requisições de Material</h1>
<p class="text-base-content/70">Gerencie requisições de material dos funcionários</p> <p class="text-base-content/70 text-lg">Gerencie requisições de material dos funcionários</p>
</div> </div>
</div> </div>
<button class="btn btn-primary" onclick={abrirModalNova}> <button class="btn btn-primary shadow-lg hover:shadow-xl transition-all" onclick={abrirModalNova}>
<Plus class="h-5 w-5" /> <Plus class="h-5 w-5" />
Nova Requisição Nova Requisição
</button> </button>
@@ -229,11 +224,12 @@
{/if} {/if}
<!-- Filtros --> <!-- Filtros -->
<div class="card bg-base-100 mb-6 shadow-xl"> <div class="card bg-base-100 border border-base-300 mb-6 shadow-xl">
<div class="card-body"> <div class="card-body">
<h3 class="text-lg font-semibold mb-4">Filtros</h3>
<div class="form-control"> <div class="form-control">
<label class="label"> <label class="label">
<span class="label-text">Filtrar por Status</span> <span class="label-text font-medium">Filtrar por Status</span>
</label> </label>
<select class="select select-bordered" bind:value={filtroStatus}> <select class="select select-bordered" bind:value={filtroStatus}>
<option value="">Todos</option> <option value="">Todos</option>
@@ -248,27 +244,28 @@
</div> </div>
<!-- Lista de Requisições --> <!-- Lista de Requisições -->
<div class="card bg-base-100 shadow-xl"> <div class="card bg-base-100 border border-base-300 shadow-xl">
<div class="card-body"> <div class="card-body">
<div class="overflow-x-auto"> <div class="overflow-x-auto">
<table class="table"> <table class="table table-zebra">
<thead> <thead>
<tr> <tr class="bg-base-200">
<th>Número</th> <th class="font-semibold">Número</th>
<th>Solicitante</th> <th class="font-semibold">Solicitante</th>
<th>Setor</th> <th class="font-semibold">Setor</th>
<th>Status</th> <th class="font-semibold">Status</th>
<th>Data</th> <th class="font-semibold">Data</th>
<th>Ações</th> <th class="font-semibold">Ações</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{#if requisicoes.length === 0} {#if requisicoes.length === 0}
<tr> <tr>
<td colspan="6" class="text-center"> <td colspan="6" class="text-center">
<div class="py-8"> <div class="py-12">
<ClipboardList class="mx-auto mb-4 h-12 w-12 text-base-content/30" /> <ClipboardList class="mx-auto mb-4 h-16 w-16 text-base-content/30" />
<p class="text-base-content/70">Nenhuma requisição encontrada</p> <p class="text-base-content/70 text-lg font-medium">Nenhuma requisição encontrada</p>
<p class="text-base-content/50 text-sm mt-2">Tente ajustar os filtros ou criar uma nova requisição</p>
</div> </div>
</td> </td>
</tr> </tr>
@@ -276,18 +273,24 @@
{#each requisicoes as requisicao} {#each requisicoes as requisicao}
{@const solicitante = funcionariosQuery.data?.find(f => f._id === requisicao.solicitanteId)} {@const solicitante = funcionariosQuery.data?.find(f => f._id === requisicao.solicitanteId)}
{@const setor = setoresQuery.data?.find(s => s._id === requisicao.setorId)} {@const setor = setoresQuery.data?.find(s => s._id === requisicao.setorId)}
<tr> <tr class="hover:bg-base-200/50 transition-colors">
<td> <td>
<div class="font-mono font-bold">{requisicao.numero}</div> <div class="font-mono font-bold text-primary">{requisicao.numero}</div>
</td> </td>
<td>{solicitante?.nome || 'Carregando...'}</td>
<td>{setor?.nome || 'Carregando...'}</td>
<td> <td>
<span class="badge {getStatusBadge(requisicao.status)}"> <div class="font-medium">{solicitante?.nome || 'Carregando...'}</div>
</td>
<td>
<span class="badge badge-ghost">{setor?.nome || 'Carregando...'}</span>
</td>
<td>
<span class="badge {getStatusBadge(requisicao.status)} badge-lg">
{getStatusLabel(requisicao.status)} {getStatusLabel(requisicao.status)}
</span> </span>
</td> </td>
<td>{new Date(requisicao.criadoEm).toLocaleDateString('pt-BR')}</td> <td>
<span class="text-sm">{new Date(requisicao.criadoEm).toLocaleDateString('pt-BR')}</span>
</td>
<td> <td>
<div class="flex gap-2"> <div class="flex gap-2">
{#if requisicao.status === 'pendente'} {#if requisicao.status === 'pendente'}
@@ -321,8 +324,11 @@
<!-- Modal Nova Requisição --> <!-- Modal Nova Requisição -->
{#if showModalNova} {#if showModalNova}
<div class="modal modal-open"> <div class="modal modal-open">
<div class="modal-box max-w-4xl"> <div class="modal-box max-w-4xl border border-base-300 shadow-2xl">
<h3 class="text-lg font-bold">Nova Requisição de Material</h3> <h3 class="text-2xl font-bold mb-4 flex items-center gap-2">
<ClipboardList class="h-6 w-6 text-primary" />
Nova Requisição de Material
</h3>
<div class="grid grid-cols-1 gap-4 md:grid-cols-2 mt-4"> <div class="grid grid-cols-1 gap-4 md:grid-cols-2 mt-4">
<div class="form-control"> <div class="form-control">