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