update deploy ci
This commit is contained in:
23
.github/workflows/deploy.yml
vendored
23
.github/workflows/deploy.yml
vendored
@@ -1,8 +1,10 @@
|
||||
name: Build Docker images
|
||||
name: Build and Deploy Docker images
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: ["master"]
|
||||
branches: ['master']
|
||||
pull_request:
|
||||
branches: ['master']
|
||||
|
||||
jobs:
|
||||
build-and-push-dockerfile-image:
|
||||
@@ -12,25 +14,22 @@ jobs:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Setup Bun
|
||||
uses: oven-sh/setup-bun@v2
|
||||
|
||||
|
||||
- name: Log in to Docker Hub
|
||||
# Only login if we are actually going to push
|
||||
if: github.event_name == 'push'
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }} # Make sure to add the secrets in your repository in -> Settings -> Secrets (Actions) -> New repository secret
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }} # Make sure to add the secrets in your repository in -> Settings -> Secrets (Actions) -> New repository secret
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Build and push Docker image
|
||||
uses: docker/build-push-action@v4
|
||||
with:
|
||||
context: .
|
||||
file: ./apps/web/Dockerfile
|
||||
push: true
|
||||
# Make sure to replace with your own namespace and repository
|
||||
tags: |
|
||||
killercf/sgc:latest
|
||||
# Only push on 'push' event (merge to master), not on 'pull_request'
|
||||
push: ${{ github.event_name == 'push' }}
|
||||
tags: killercf/sgc:latest
|
||||
platforms: linux/amd64
|
||||
build-args: |
|
||||
PUBLIC_CONVEX_URL=${{ secrets.PUBLIC_CONVEX_URL }}
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
Package,
|
||||
AlertTriangle,
|
||||
ArrowLeftRight,
|
||||
Download,
|
||||
CheckCircle,
|
||||
ArrowDown,
|
||||
ArrowUp,
|
||||
@@ -21,6 +20,7 @@
|
||||
FileText,
|
||||
FileSpreadsheet
|
||||
} from 'lucide-svelte';
|
||||
import { SvelteDate } from 'svelte/reactivity';
|
||||
|
||||
const statsQuery = useQuery(api.almoxarifado.obterEstatisticas, {});
|
||||
const materiaisQuery = useQuery(api.almoxarifado.listarMateriais, {});
|
||||
@@ -31,7 +31,7 @@
|
||||
let tipoRelatorioGerando = $state<string | null>(null);
|
||||
|
||||
// Agrupar materiais por categoria
|
||||
const materiaisPorCategoria = $derived(() => {
|
||||
const materiaisPorCategoria = $derived.by(() => {
|
||||
if (!materiaisQuery.data) return {};
|
||||
const agrupado: Record<string, number> = {};
|
||||
materiaisQuery.data.forEach((m) => {
|
||||
@@ -41,10 +41,10 @@
|
||||
});
|
||||
|
||||
// Movimentações do mês
|
||||
const movimentacoesMes = $derived(() => {
|
||||
const movimentacoesMes = $derived.by(() => {
|
||||
if (!movimentacoesQuery.data) return { entrada: 0, saida: 0, ajuste: 0 };
|
||||
const agora = Date.now();
|
||||
const inicioMes = new Date(agora);
|
||||
const inicioMes = new SvelteDate(agora);
|
||||
inicioMes.setDate(1);
|
||||
inicioMes.setHours(0, 0, 0, 0);
|
||||
|
||||
@@ -124,7 +124,10 @@
|
||||
if (ctx) {
|
||||
ctx.drawImage(logoImg, 0, 0);
|
||||
const blob = await new Promise<Blob>((resolve, reject) => {
|
||||
canvas.toBlob((b) => (b ? resolve(b) : reject(new Error('Falha ao converter'))), 'image/png');
|
||||
canvas.toBlob(
|
||||
(b) => (b ? resolve(b) : reject(new Error('Falha ao converter'))),
|
||||
'image/png'
|
||||
);
|
||||
});
|
||||
return await blob.arrayBuffer();
|
||||
}
|
||||
@@ -257,7 +260,10 @@
|
||||
yPos += 15;
|
||||
|
||||
const dados = materiaisPorCategoria;
|
||||
const dadosArray = Object.entries(dados).map(([categoria, quantidade]) => [categoria, String(quantidade)]);
|
||||
const dadosArray = Object.entries(dados).map(([categoria, quantidade]) => [
|
||||
categoria,
|
||||
String(quantidade)
|
||||
]);
|
||||
|
||||
if (dadosArray.length === 0) {
|
||||
doc.text('Nenhum dado disponível', 14, yPos);
|
||||
@@ -532,7 +538,12 @@
|
||||
{ header: 'Estoque Mínimo', key: 'estoqueMinimo', width: 18 }
|
||||
];
|
||||
|
||||
await adicionarTituloExcel(worksheet, 'RELATÓRIO DE MATERIAIS COM ESTOQUE BAIXO', 4, workbook);
|
||||
await adicionarTituloExcel(
|
||||
worksheet,
|
||||
'RELATÓRIO DE MATERIAIS COM ESTOQUE BAIXO',
|
||||
4,
|
||||
workbook
|
||||
);
|
||||
|
||||
const headerRow = worksheet.getRow(2);
|
||||
headerRow.values = ['Código', 'Material', 'Estoque Atual', 'Estoque Mínimo'];
|
||||
@@ -751,9 +762,7 @@
|
||||
<div class="breadcrumbs mb-6 text-sm">
|
||||
<ul>
|
||||
<li>
|
||||
<a href={resolve('/almoxarifado')} class="text-primary hover:underline"
|
||||
>Almoxarifado</a
|
||||
>
|
||||
<a href={resolve('/almoxarifado')} class="text-primary hover:underline">Almoxarifado</a>
|
||||
</li>
|
||||
<li>Relatórios</li>
|
||||
</ul>
|
||||
@@ -762,14 +771,18 @@
|
||||
<!-- Cabeçalho -->
|
||||
<div class="mb-8">
|
||||
<div class="flex items-center gap-4">
|
||||
<div class="rounded-2xl bg-gradient-to-br from-primary/20 via-primary/10 to-primary/5 p-4 shadow-lg border border-primary/20">
|
||||
<BarChart3 class="h-10 w-10 text-primary" strokeWidth={2.5} />
|
||||
<div
|
||||
class="from-primary/20 via-primary/10 to-primary/5 border-primary/20 rounded-2xl border bg-linear-to-br p-4 shadow-lg"
|
||||
>
|
||||
<BarChart3 class="text-primary h-10 w-10" strokeWidth={2.5} />
|
||||
</div>
|
||||
<div class="flex-1">
|
||||
<h1 class="text-4xl font-bold tracking-tight bg-gradient-to-r from-primary to-primary/70 bg-clip-text text-transparent">
|
||||
<h1
|
||||
class="from-primary to-primary/70 bg-linear-to-r bg-clip-text text-4xl font-bold tracking-tight text-transparent"
|
||||
>
|
||||
Relatórios
|
||||
</h1>
|
||||
<p class="text-base-content/70 text-lg mt-1">Estatísticas e relatórios do almoxarifado</p>
|
||||
<p class="text-base-content/70 mt-1 text-lg">Estatísticas e relatórios do almoxarifado</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -777,379 +790,414 @@
|
||||
<!-- Estatísticas Gerais -->
|
||||
{#if statsQuery.data}
|
||||
<div class="mb-10">
|
||||
<div class="mb-6 flex items-center gap-3 border-b-2 border-primary/20 pb-4">
|
||||
<div class="rounded-lg bg-primary/10 p-2.5">
|
||||
<BarChart3 class="h-5 w-5 text-primary" strokeWidth={2.5} />
|
||||
<div class="border-primary/20 mb-6 flex items-center gap-3 border-b-2 pb-4">
|
||||
<div class="bg-primary/10 rounded-lg p-2.5">
|
||||
<BarChart3 class="text-primary h-5 w-5" strokeWidth={2.5} />
|
||||
</div>
|
||||
<h2 class="text-xl font-bold text-base-content">Estatísticas Gerais</h2>
|
||||
<h2 class="text-base-content text-xl font-bold">Estatísticas Gerais</h2>
|
||||
</div>
|
||||
<div class="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}</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
|
||||
class="card from-primary/10 via-primary/5 to-base-100 border-primary/20 border bg-linear-to-br shadow-xl transition-all duration-300 hover:scale-105 hover:shadow-2xl"
|
||||
>
|
||||
<div class="card-body">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex-1">
|
||||
<div class="text-base-content/60 mb-1 text-sm font-medium">Total de Materiais</div>
|
||||
<div class="text-primary mb-1 text-3xl font-bold">
|
||||
{statsQuery.data.totalMateriais}
|
||||
</div>
|
||||
<div class="text-base-content/50 text-xs">Cadastrados no sistema</div>
|
||||
</div>
|
||||
<div class="bg-primary/20 rounded-xl p-3">
|
||||
<Package class="text-primary h-8 w-8" strokeWidth={2.5} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<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="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">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
|
||||
class="card from-success/10 via-success/5 to-base-100 border-success/20 border bg-linear-to-br shadow-xl transition-all duration-300 hover:scale-105 hover:shadow-2xl"
|
||||
>
|
||||
<div class="card-body">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex-1">
|
||||
<div class="text-base-content/60 mb-1 text-sm font-medium">Materiais Ativos</div>
|
||||
<div class="text-success mb-1 text-3xl font-bold">
|
||||
{statsQuery.data.totalMateriaisAtivos}
|
||||
</div>
|
||||
<div class="text-base-content/50 text-xs">Em estoque</div>
|
||||
</div>
|
||||
<div class="bg-success/20 rounded-xl p-3">
|
||||
<CheckCircle class="text-success h-8 w-8" strokeWidth={2.5} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<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="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">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
|
||||
class="card from-warning/10 via-warning/5 to-base-100 border-warning/20 border bg-linear-to-br shadow-xl transition-all duration-300 hover:scale-105 hover:shadow-2xl"
|
||||
>
|
||||
<div class="card-body">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex-1">
|
||||
<div class="text-base-content/60 mb-1 text-sm font-medium">Alertas Ativos</div>
|
||||
<div class="text-warning mb-1 text-3xl font-bold">
|
||||
{statsQuery.data.totalAlertasAtivos}
|
||||
</div>
|
||||
<div class="text-base-content/50 text-xs">Estoque baixo</div>
|
||||
</div>
|
||||
<div class="bg-warning/20 rounded-xl p-3">
|
||||
<AlertTriangle class="text-warning h-8 w-8" strokeWidth={2.5} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<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="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">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
|
||||
class="card from-info/10 via-info/5 to-base-100 border-info/20 border bg-linear-to-br shadow-xl transition-all duration-300 hover:scale-105 hover:shadow-2xl"
|
||||
>
|
||||
<div class="card-body">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex-1">
|
||||
<div class="text-base-content/60 mb-1 text-sm font-medium">Movimentações</div>
|
||||
<div class="text-info mb-1 text-3xl font-bold">
|
||||
{statsQuery.data.movimentacoesMes}
|
||||
</div>
|
||||
<div class="text-base-content/50 text-xs">Este mês</div>
|
||||
</div>
|
||||
<div class="bg-info/20 rounded-xl p-3">
|
||||
<ArrowLeftRight class="text-info h-8 w-8" strokeWidth={2.5} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<!-- Relatórios Disponíveis -->
|
||||
<div class="mb-8">
|
||||
<div class="mb-6 flex items-center gap-3 border-b-2 border-info/20 pb-4">
|
||||
<div class="rounded-lg bg-info/10 p-2.5">
|
||||
<FileText class="h-5 w-5 text-info" strokeWidth={2.5} />
|
||||
<div class="border-info/20 mb-6 flex items-center gap-3 border-b-2 pb-4">
|
||||
<div class="bg-info/10 rounded-lg p-2.5">
|
||||
<FileText class="text-info h-5 w-5" strokeWidth={2.5} />
|
||||
</div>
|
||||
<h2 class="text-xl font-bold text-base-content">Relatórios Disponíveis</h2>
|
||||
<h2 class="text-base-content text-xl font-bold">Relatórios Disponíveis</h2>
|
||||
</div>
|
||||
<div class="grid grid-cols-1 gap-6 md:grid-cols-2">
|
||||
<!-- Relatório de Materiais por Categoria -->
|
||||
<div class="card bg-base-100 border border-base-300 shadow-2xl">
|
||||
<div class="card-body p-6">
|
||||
<div class="flex items-center justify-between mb-6 border-b-2 border-base-300 pb-4">
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="rounded-lg bg-primary/10 p-2">
|
||||
<Package class="h-5 w-5 text-primary" strokeWidth={2.5} />
|
||||
<!-- Relatório de Materiais por Categoria -->
|
||||
<div class="card bg-base-100 border-base-300 border shadow-2xl">
|
||||
<div class="card-body p-6">
|
||||
<div class="border-base-300 mb-6 flex items-center justify-between border-b-2 pb-4">
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="bg-primary/10 rounded-lg p-2">
|
||||
<Package class="text-primary h-5 w-5" strokeWidth={2.5} />
|
||||
</div>
|
||||
<h2 class="text-base-content text-lg font-bold">Materiais por Categoria</h2>
|
||||
</div>
|
||||
<div class="flex gap-2">
|
||||
<button
|
||||
class="btn btn-sm btn-primary shadow-md transition-all hover:shadow-lg"
|
||||
onclick={gerarPDFMateriaisCategoria}
|
||||
disabled={gerandoRelatorio}
|
||||
title="Gerar PDF"
|
||||
>
|
||||
{#if gerandoRelatorio && tipoRelatorioGerando === 'materiais-categoria-pdf'}
|
||||
<span class="loading loading-spinner loading-xs"></span>
|
||||
{:else}
|
||||
<FileText class="h-4 w-4" />
|
||||
{/if}
|
||||
PDF
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-sm btn-success shadow-md transition-all hover:shadow-lg"
|
||||
onclick={gerarExcelMateriaisCategoria}
|
||||
disabled={gerandoRelatorio}
|
||||
title="Gerar Excel"
|
||||
>
|
||||
{#if gerandoRelatorio && tipoRelatorioGerando === 'materiais-categoria-excel'}
|
||||
<span class="loading loading-spinner loading-xs"></span>
|
||||
{:else}
|
||||
<FileSpreadsheet class="h-4 w-4" />
|
||||
{/if}
|
||||
Excel
|
||||
</button>
|
||||
</div>
|
||||
<h2 class="text-lg font-bold text-base-content">Materiais por Categoria</h2>
|
||||
</div>
|
||||
<div class="flex gap-2">
|
||||
<button
|
||||
class="btn btn-sm btn-primary shadow-md hover:shadow-lg transition-all"
|
||||
onclick={gerarPDFMateriaisCategoria}
|
||||
disabled={gerandoRelatorio}
|
||||
title="Gerar PDF"
|
||||
>
|
||||
{#if gerandoRelatorio && tipoRelatorioGerando === 'materiais-categoria-pdf'}
|
||||
<span class="loading loading-spinner loading-xs"></span>
|
||||
{:else}
|
||||
<FileText class="h-4 w-4" />
|
||||
{/if}
|
||||
PDF
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-sm btn-success shadow-md hover:shadow-lg transition-all"
|
||||
onclick={gerarExcelMateriaisCategoria}
|
||||
disabled={gerandoRelatorio}
|
||||
title="Gerar Excel"
|
||||
>
|
||||
{#if gerandoRelatorio && tipoRelatorioGerando === 'materiais-categoria-excel'}
|
||||
<span class="loading loading-spinner loading-xs"></span>
|
||||
{:else}
|
||||
<FileSpreadsheet class="h-4 w-4" />
|
||||
{/if}
|
||||
Excel
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{#if materiaisQuery.data && Object.keys(materiaisPorCategoria).length > 0}
|
||||
<div class="space-y-3">
|
||||
{#each Object.entries(materiaisPorCategoria) as [categoria, quantidade]}
|
||||
<div class="flex items-center justify-between p-2 rounded-lg hover:bg-base-200/50 transition-colors">
|
||||
<span class="font-semibold text-base-content">{categoria}</span>
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="w-40">
|
||||
<div class="h-3 bg-base-300 rounded-full overflow-hidden shadow-inner">
|
||||
<div
|
||||
class="h-full bg-gradient-to-r from-primary to-primary/70 transition-all rounded-full"
|
||||
style="width: {(quantidade / (materiaisQuery.data?.length || 1)) * 100}%"
|
||||
></div>
|
||||
{#if materiaisQuery.data && Object.keys(materiaisPorCategoria).length > 0}
|
||||
<div class="space-y-3">
|
||||
{#each Object.entries(materiaisPorCategoria) as [categoria, quantidade] (categoria)}
|
||||
<div
|
||||
class="hover:bg-base-200/50 flex items-center justify-between rounded-lg p-2 transition-colors"
|
||||
>
|
||||
<span class="text-base-content font-semibold">{categoria}</span>
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="w-40">
|
||||
<div class="bg-base-300 h-3 overflow-hidden rounded-full shadow-inner">
|
||||
<div
|
||||
class="from-primary to-primary/70 h-full rounded-full bg-linear-to-r transition-all"
|
||||
style="width: {(quantidade / (materiaisQuery.data?.length || 1)) * 100}%"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
<span class="text-primary w-12 text-right text-base font-bold"
|
||||
>{quantidade}</span
|
||||
>
|
||||
</div>
|
||||
<span class="text-base font-bold text-primary w-12 text-right">{quantidade}</span>
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
{:else}
|
||||
<div class="alert alert-info border-info/30 bg-info/10">
|
||||
<Package class="h-5 w-5 text-info" />
|
||||
<span class="font-medium">Nenhum dado disponível</span>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Movimentações do Mês -->
|
||||
<div class="card bg-base-100 border border-base-300 shadow-2xl">
|
||||
<div class="card-body p-6">
|
||||
<div class="flex items-center justify-between mb-6 border-b-2 border-base-300 pb-4">
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="rounded-lg bg-info/10 p-2">
|
||||
<ArrowLeftRight class="h-5 w-5 text-info" strokeWidth={2.5} />
|
||||
{/each}
|
||||
</div>
|
||||
<h2 class="text-lg font-bold text-base-content">Movimentações do Mês</h2>
|
||||
</div>
|
||||
<div class="flex gap-2">
|
||||
<button
|
||||
class="btn btn-sm btn-primary shadow-md hover:shadow-lg transition-all"
|
||||
onclick={gerarPDFMovimentacoesMes}
|
||||
disabled={gerandoRelatorio}
|
||||
title="Gerar PDF"
|
||||
>
|
||||
{#if gerandoRelatorio && tipoRelatorioGerando === 'movimentacoes-mes-pdf'}
|
||||
<span class="loading loading-spinner loading-xs"></span>
|
||||
{:else}
|
||||
<FileText class="h-4 w-4" />
|
||||
{/if}
|
||||
PDF
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-sm btn-success shadow-md hover:shadow-lg transition-all"
|
||||
onclick={gerarExcelMovimentacoesMes}
|
||||
disabled={gerandoRelatorio}
|
||||
title="Gerar Excel"
|
||||
>
|
||||
{#if gerandoRelatorio && tipoRelatorioGerando === 'movimentacoes-mes-excel'}
|
||||
<span class="loading loading-spinner loading-xs"></span>
|
||||
{:else}
|
||||
<FileSpreadsheet class="h-4 w-4" />
|
||||
{/if}
|
||||
Excel
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="space-y-4">
|
||||
<div class="flex items-center justify-between p-3 rounded-lg bg-success/10 border border-success/20 hover:bg-success/15 transition-colors">
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="rounded-lg bg-success/20 p-2">
|
||||
<ArrowDown class="h-5 w-5 text-success" strokeWidth={2.5} />
|
||||
</div>
|
||||
<span class="font-semibold text-base-content">Entradas</span>
|
||||
</div>
|
||||
<span class="text-2xl font-bold text-success">{movimentacoesMes.entrada}</span>
|
||||
</div>
|
||||
<div class="flex items-center justify-between p-3 rounded-lg bg-error/10 border border-error/20 hover:bg-error/15 transition-colors">
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="rounded-lg bg-error/20 p-2">
|
||||
<ArrowUp class="h-5 w-5 text-error" strokeWidth={2.5} />
|
||||
</div>
|
||||
<span class="font-semibold text-base-content">Saídas</span>
|
||||
</div>
|
||||
<span class="text-2xl font-bold text-error">{movimentacoesMes.saida}</span>
|
||||
</div>
|
||||
<div class="flex items-center justify-between p-3 rounded-lg bg-warning/10 border border-warning/20 hover:bg-warning/15 transition-colors">
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="rounded-lg bg-warning/20 p-2">
|
||||
<Settings class="h-5 w-5 text-warning" strokeWidth={2.5} />
|
||||
</div>
|
||||
<span class="font-semibold text-base-content">Ajustes</span>
|
||||
</div>
|
||||
<span class="text-2xl font-bold text-warning">{movimentacoesMes.ajuste}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Materiais com Estoque Baixo -->
|
||||
<div class="card bg-base-100 border border-base-300 shadow-2xl">
|
||||
<div class="card-body p-6">
|
||||
<div class="flex items-center justify-between mb-6 border-b-2 border-base-300 pb-4">
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="rounded-lg bg-warning/10 p-2">
|
||||
<AlertTriangle class="h-5 w-5 text-warning" strokeWidth={2.5} />
|
||||
</div>
|
||||
<h2 class="text-lg font-bold text-base-content">Materiais com Estoque Baixo</h2>
|
||||
</div>
|
||||
<div class="flex gap-2">
|
||||
<button
|
||||
class="btn btn-sm btn-primary shadow-md hover:shadow-lg transition-all"
|
||||
onclick={gerarPDFEstoqueBaixo}
|
||||
disabled={gerandoRelatorio}
|
||||
title="Gerar PDF"
|
||||
>
|
||||
{#if gerandoRelatorio && tipoRelatorioGerando === 'estoque-baixo-pdf'}
|
||||
<span class="loading loading-spinner loading-xs"></span>
|
||||
{:else}
|
||||
<FileText class="h-4 w-4" />
|
||||
{/if}
|
||||
PDF
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-sm btn-success shadow-md hover:shadow-lg transition-all"
|
||||
onclick={gerarExcelEstoqueBaixo}
|
||||
disabled={gerandoRelatorio}
|
||||
title="Gerar Excel"
|
||||
>
|
||||
{#if gerandoRelatorio && tipoRelatorioGerando === 'estoque-baixo-excel'}
|
||||
<span class="loading loading-spinner loading-xs"></span>
|
||||
{:else}
|
||||
<FileSpreadsheet class="h-4 w-4" />
|
||||
{/if}
|
||||
Excel
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{#if materiaisQuery.data}
|
||||
{@const estoqueBaixo = materiaisQuery.data.filter(m => m.estoqueAtual <= m.estoqueMinimo)}
|
||||
{#if estoqueBaixo.length > 0}
|
||||
<div class="overflow-x-auto rounded-lg border border-base-300">
|
||||
<table class="table table-zebra table-sm">
|
||||
<thead>
|
||||
<tr class="bg-base-200">
|
||||
<th class="font-bold text-base-content">Material</th>
|
||||
<th class="font-bold text-base-content">Atual</th>
|
||||
<th class="font-bold text-base-content">Mínimo</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{#each estoqueBaixo.slice(0, 10) as material}
|
||||
<tr class="hover:bg-base-200/50 transition-colors">
|
||||
<td>
|
||||
<div class="font-medium">{material.nome}</div>
|
||||
<div class="text-xs text-base-content/60 font-mono">{material.codigo}</div>
|
||||
</td>
|
||||
<td>
|
||||
<span class="font-bold text-error">{material.estoqueAtual}</span>
|
||||
</td>
|
||||
<td>
|
||||
<span class="font-medium">{material.estoqueMinimo}</span>
|
||||
</td>
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{#if estoqueBaixo.length > 10}
|
||||
<p class="text-sm text-base-content/70 mt-4 text-center font-medium">
|
||||
E mais <span class="text-primary font-bold">{estoqueBaixo.length - 10}</span> materiais...
|
||||
</p>
|
||||
{/if}
|
||||
{:else}
|
||||
<div class="alert alert-success border-success/30 bg-success/10">
|
||||
<CheckCircle class="h-6 w-6 text-success" />
|
||||
<span class="font-medium">Todos os materiais estão com estoque adequado!</span>
|
||||
<div class="alert alert-info border-info/30 bg-info/10">
|
||||
<Package class="text-info h-5 w-5" />
|
||||
<span class="font-medium">Nenhum dado disponível</span>
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Alertas Recentes -->
|
||||
<div class="card bg-base-100 border border-base-300 shadow-2xl">
|
||||
<div class="card-body p-6">
|
||||
<div class="flex items-center justify-between mb-6 border-b-2 border-base-300 pb-4">
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="rounded-lg bg-warning/10 p-2">
|
||||
<AlertTriangle class="h-5 w-5 text-warning" strokeWidth={2.5} />
|
||||
<!-- Movimentações do Mês -->
|
||||
<div class="card bg-base-100 border-base-300 border shadow-2xl">
|
||||
<div class="card-body p-6">
|
||||
<div class="border-base-300 mb-6 flex items-center justify-between border-b-2 pb-4">
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="bg-info/10 rounded-lg p-2">
|
||||
<ArrowLeftRight class="text-info h-5 w-5" strokeWidth={2.5} />
|
||||
</div>
|
||||
<h2 class="text-base-content text-lg font-bold">Movimentações do Mês</h2>
|
||||
</div>
|
||||
<div class="flex gap-2">
|
||||
<button
|
||||
class="btn btn-sm btn-primary shadow-md transition-all hover:shadow-lg"
|
||||
onclick={gerarPDFMovimentacoesMes}
|
||||
disabled={gerandoRelatorio}
|
||||
title="Gerar PDF"
|
||||
>
|
||||
{#if gerandoRelatorio && tipoRelatorioGerando === 'movimentacoes-mes-pdf'}
|
||||
<span class="loading loading-spinner loading-xs"></span>
|
||||
{:else}
|
||||
<FileText class="h-4 w-4" />
|
||||
{/if}
|
||||
PDF
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-sm btn-success shadow-md transition-all hover:shadow-lg"
|
||||
onclick={gerarExcelMovimentacoesMes}
|
||||
disabled={gerandoRelatorio}
|
||||
title="Gerar Excel"
|
||||
>
|
||||
{#if gerandoRelatorio && tipoRelatorioGerando === 'movimentacoes-mes-excel'}
|
||||
<span class="loading loading-spinner loading-xs"></span>
|
||||
{:else}
|
||||
<FileSpreadsheet class="h-4 w-4" />
|
||||
{/if}
|
||||
Excel
|
||||
</button>
|
||||
</div>
|
||||
<h2 class="text-lg font-bold text-base-content">Alertas Recentes</h2>
|
||||
</div>
|
||||
<div class="flex gap-2">
|
||||
<button
|
||||
class="btn btn-sm btn-primary shadow-md hover:shadow-lg transition-all"
|
||||
onclick={gerarPDFAlertas}
|
||||
disabled={gerandoRelatorio}
|
||||
title="Gerar PDF"
|
||||
<div class="space-y-4">
|
||||
<div
|
||||
class="bg-success/10 border-success/20 hover:bg-success/15 flex items-center justify-between rounded-lg border p-3 transition-colors"
|
||||
>
|
||||
{#if gerandoRelatorio && tipoRelatorioGerando === 'alertas-pdf'}
|
||||
<span class="loading loading-spinner loading-xs"></span>
|
||||
{:else}
|
||||
<FileText class="h-4 w-4" />
|
||||
{/if}
|
||||
PDF
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-sm btn-success shadow-md hover:shadow-lg transition-all"
|
||||
onclick={gerarExcelAlertas}
|
||||
disabled={gerandoRelatorio}
|
||||
title="Gerar Excel"
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="bg-success/20 rounded-lg p-2">
|
||||
<ArrowDown class="text-success h-5 w-5" strokeWidth={2.5} />
|
||||
</div>
|
||||
<span class="text-base-content font-semibold">Entradas</span>
|
||||
</div>
|
||||
<span class="text-success text-2xl font-bold">{movimentacoesMes.entrada}</span>
|
||||
</div>
|
||||
<div
|
||||
class="bg-error/10 border-error/20 hover:bg-error/15 flex items-center justify-between rounded-lg border p-3 transition-colors"
|
||||
>
|
||||
{#if gerandoRelatorio && tipoRelatorioGerando === 'alertas-excel'}
|
||||
<span class="loading loading-spinner loading-xs"></span>
|
||||
{:else}
|
||||
<FileSpreadsheet class="h-4 w-4" />
|
||||
{/if}
|
||||
Excel
|
||||
</button>
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="bg-error/20 rounded-lg p-2">
|
||||
<ArrowUp class="text-error h-5 w-5" strokeWidth={2.5} />
|
||||
</div>
|
||||
<span class="text-base-content font-semibold">Saídas</span>
|
||||
</div>
|
||||
<span class="text-error text-2xl font-bold">{movimentacoesMes.saida}</span>
|
||||
</div>
|
||||
<div
|
||||
class="bg-warning/10 border-warning/20 hover:bg-warning/15 flex items-center justify-between rounded-lg border p-3 transition-colors"
|
||||
>
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="bg-warning/20 rounded-lg p-2">
|
||||
<Settings class="text-warning h-5 w-5" strokeWidth={2.5} />
|
||||
</div>
|
||||
<span class="text-base-content font-semibold">Ajustes</span>
|
||||
</div>
|
||||
<span class="text-warning text-2xl font-bold">{movimentacoesMes.ajuste}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{#if alertasQuery.data && alertasQuery.data.length > 0}
|
||||
<div class="space-y-3">
|
||||
{#each alertasQuery.data.slice(0, 5) as alerta}
|
||||
<div class="flex items-center justify-between p-3 bg-warning/10 rounded-lg border border-warning/20 hover:bg-warning/15 transition-colors">
|
||||
<div class="flex-1">
|
||||
<div class="font-semibold text-sm text-base-content">
|
||||
{#if materiaisQuery.data}
|
||||
{@const material = materiaisQuery.data.find(m => m._id === alerta.materialId)}
|
||||
{material?.nome || 'Carregando...'}
|
||||
{/if}
|
||||
</div>
|
||||
<div class="text-xs text-base-content/60 mt-1 font-mono">
|
||||
Estoque: <span class="font-bold text-error">{alerta.quantidadeAtual}</span> / Mínimo: <span class="font-bold">{alerta.quantidadeMinima}</span>
|
||||
</div>
|
||||
</div>
|
||||
<span class="badge badge-warning badge-lg ml-3">
|
||||
{#if alerta.tipo === 'estoque_zerado'}
|
||||
Zerado
|
||||
{:else}
|
||||
Mínimo
|
||||
{/if}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- Materiais com Estoque Baixo -->
|
||||
<div class="card bg-base-100 border-base-300 border shadow-2xl">
|
||||
<div class="card-body p-6">
|
||||
<div class="border-base-300 mb-6 flex items-center justify-between border-b-2 pb-4">
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="bg-warning/10 rounded-lg p-2">
|
||||
<AlertTriangle class="text-warning h-5 w-5" strokeWidth={2.5} />
|
||||
</div>
|
||||
{/each}
|
||||
<h2 class="text-base-content text-lg font-bold">Materiais com Estoque Baixo</h2>
|
||||
</div>
|
||||
<div class="flex gap-2">
|
||||
<button
|
||||
class="btn btn-sm btn-primary shadow-md transition-all hover:shadow-lg"
|
||||
onclick={gerarPDFEstoqueBaixo}
|
||||
disabled={gerandoRelatorio}
|
||||
title="Gerar PDF"
|
||||
>
|
||||
{#if gerandoRelatorio && tipoRelatorioGerando === 'estoque-baixo-pdf'}
|
||||
<span class="loading loading-spinner loading-xs"></span>
|
||||
{:else}
|
||||
<FileText class="h-4 w-4" />
|
||||
{/if}
|
||||
PDF
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-sm btn-success shadow-md transition-all hover:shadow-lg"
|
||||
onclick={gerarExcelEstoqueBaixo}
|
||||
disabled={gerandoRelatorio}
|
||||
title="Gerar Excel"
|
||||
>
|
||||
{#if gerandoRelatorio && tipoRelatorioGerando === 'estoque-baixo-excel'}
|
||||
<span class="loading loading-spinner loading-xs"></span>
|
||||
{:else}
|
||||
<FileSpreadsheet class="h-4 w-4" />
|
||||
{/if}
|
||||
Excel
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="alert alert-success border-success/30 bg-success/10">
|
||||
<CheckCircle class="h-6 w-6 text-success" />
|
||||
<span class="font-medium">Nenhum alerta ativo</span>
|
||||
{#if materiaisQuery.data}
|
||||
{@const estoqueBaixo = materiaisQuery.data.filter(
|
||||
(m) => m.estoqueAtual <= m.estoqueMinimo
|
||||
)}
|
||||
{#if estoqueBaixo.length > 0}
|
||||
<div class="border-base-300 overflow-x-auto rounded-lg border">
|
||||
<table class="table-zebra table-sm table">
|
||||
<thead>
|
||||
<tr class="bg-base-200">
|
||||
<th class="text-base-content font-bold">Material</th>
|
||||
<th class="text-base-content font-bold">Atual</th>
|
||||
<th class="text-base-content font-bold">Mínimo</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{#each estoqueBaixo.slice(0, 10) as material (material._id)}
|
||||
<tr class="hover:bg-base-200/50 transition-colors">
|
||||
<td>
|
||||
<div class="font-medium">{material.nome}</div>
|
||||
<div class="text-base-content/60 font-mono text-xs">
|
||||
{material.codigo}
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<span class="text-error font-bold">{material.estoqueAtual}</span>
|
||||
</td>
|
||||
<td>
|
||||
<span class="font-medium">{material.estoqueMinimo}</span>
|
||||
</td>
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{#if estoqueBaixo.length > 10}
|
||||
<p class="text-base-content/70 mt-4 text-center text-sm font-medium">
|
||||
E mais <span class="text-primary font-bold">{estoqueBaixo.length - 10}</span> materiais...
|
||||
</p>
|
||||
{/if}
|
||||
{:else}
|
||||
<div class="alert alert-success border-success/30 bg-success/10">
|
||||
<CheckCircle class="text-success h-6 w-6" />
|
||||
<span class="font-medium">Todos os materiais estão com estoque adequado!</span>
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Alertas Recentes -->
|
||||
<div class="card bg-base-100 border-base-300 border shadow-2xl">
|
||||
<div class="card-body p-6">
|
||||
<div class="border-base-300 mb-6 flex items-center justify-between border-b-2 pb-4">
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="bg-warning/10 rounded-lg p-2">
|
||||
<AlertTriangle class="text-warning h-5 w-5" strokeWidth={2.5} />
|
||||
</div>
|
||||
<h2 class="text-base-content text-lg font-bold">Alertas Recentes</h2>
|
||||
</div>
|
||||
<div class="flex gap-2">
|
||||
<button
|
||||
class="btn btn-sm btn-primary shadow-md transition-all hover:shadow-lg"
|
||||
onclick={gerarPDFAlertas}
|
||||
disabled={gerandoRelatorio}
|
||||
title="Gerar PDF"
|
||||
>
|
||||
{#if gerandoRelatorio && tipoRelatorioGerando === 'alertas-pdf'}
|
||||
<span class="loading loading-spinner loading-xs"></span>
|
||||
{:else}
|
||||
<FileText class="h-4 w-4" />
|
||||
{/if}
|
||||
PDF
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-sm btn-success shadow-md transition-all hover:shadow-lg"
|
||||
onclick={gerarExcelAlertas}
|
||||
disabled={gerandoRelatorio}
|
||||
title="Gerar Excel"
|
||||
>
|
||||
{#if gerandoRelatorio && tipoRelatorioGerando === 'alertas-excel'}
|
||||
<span class="loading loading-spinner loading-xs"></span>
|
||||
{:else}
|
||||
<FileSpreadsheet class="h-4 w-4" />
|
||||
{/if}
|
||||
Excel
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
{#if alertasQuery.data && alertasQuery.data.length > 0}
|
||||
<div class="space-y-3">
|
||||
{#each alertasQuery.data.slice(0, 5) as alerta (alerta._id)}
|
||||
<div
|
||||
class="bg-warning/10 border-warning/20 hover:bg-warning/15 flex items-center justify-between rounded-lg border p-3 transition-colors"
|
||||
>
|
||||
<div class="flex-1">
|
||||
<div class="text-base-content text-sm font-semibold">
|
||||
{#if materiaisQuery.data}
|
||||
{@const material = materiaisQuery.data.find(
|
||||
(m) => m._id === alerta.materialId
|
||||
)}
|
||||
{material?.nome || 'Carregando...'}
|
||||
{/if}
|
||||
</div>
|
||||
<div class="text-base-content/60 mt-1 font-mono text-xs">
|
||||
Estoque: <span class="text-error font-bold">{alerta.quantidadeAtual}</span> /
|
||||
Mínimo: <span class="font-bold">{alerta.quantidadeMinima}</span>
|
||||
</div>
|
||||
</div>
|
||||
<span class="badge badge-warning badge-lg ml-3">
|
||||
{#if alerta.tipo === 'estoque_zerado'}
|
||||
Zerado
|
||||
{:else}
|
||||
Mínimo
|
||||
{/if}
|
||||
</span>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
{:else}
|
||||
<div class="alert alert-success border-success/30 bg-success/10">
|
||||
<CheckCircle class="text-success h-6 w-6" />
|
||||
<span class="font-medium">Nenhum alerta ativo</span>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user