🚀 Initial commit: Versão atual do TrackSteel APP
This commit is contained in:
@@ -0,0 +1,240 @@
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table';
|
||||
import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger } from '@/components/ui/alert-dialog';
|
||||
import { Plus, TrendingUp, Trash2, Package } from 'lucide-react';
|
||||
import { useMovimentacoesEstoque, useExcluirMovimentacao } from '@/hooks/useEstoqueMovimentacoes';
|
||||
import { MovimentacaoModal } from './MovimentacaoModal';
|
||||
import { MovimentacaoTableHeader } from './MovimentacaoTableHeader';
|
||||
import { Skeleton } from '@/components/ui/skeleton';
|
||||
|
||||
export const MovimentacaoEstoqueSimplificada = () => {
|
||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||
const [sortConfig, setSortConfig] = useState<{
|
||||
key: string;
|
||||
direction: 'asc' | 'desc';
|
||||
}>({ key: 'created_at', direction: 'desc' });
|
||||
|
||||
const { data: movimentacoes = [], isLoading } = useMovimentacoesEstoque();
|
||||
const excluirMovimentacao = useExcluirMovimentacao();
|
||||
|
||||
const handleSort = (key: string) => {
|
||||
let direction: 'asc' | 'desc' = 'asc';
|
||||
if (sortConfig.key === key && sortConfig.direction === 'asc') {
|
||||
direction = 'desc';
|
||||
}
|
||||
setSortConfig({ key, direction });
|
||||
};
|
||||
|
||||
const sortedMovimentacoes = React.useMemo(() => {
|
||||
if (!movimentacoes) return [];
|
||||
|
||||
return [...movimentacoes].sort((a, b) => {
|
||||
let aValue: any;
|
||||
let bValue: any;
|
||||
|
||||
switch (sortConfig.key) {
|
||||
case 'material':
|
||||
aValue = a.estoque_materiais?.codigo || '';
|
||||
bValue = b.estoque_materiais?.codigo || '';
|
||||
break;
|
||||
case 'data_movimentacao':
|
||||
aValue = new Date(a.data_movimentacao);
|
||||
bValue = new Date(b.data_movimentacao);
|
||||
break;
|
||||
case 'created_at':
|
||||
aValue = new Date(a.created_at);
|
||||
bValue = new Date(b.created_at);
|
||||
break;
|
||||
default:
|
||||
aValue = a[sortConfig.key as keyof typeof a];
|
||||
bValue = b[sortConfig.key as keyof typeof b];
|
||||
}
|
||||
|
||||
if (aValue < bValue) return sortConfig.direction === 'asc' ? -1 : 1;
|
||||
if (aValue > bValue) return sortConfig.direction === 'asc' ? 1 : -1;
|
||||
return 0;
|
||||
});
|
||||
}, [movimentacoes, sortConfig]);
|
||||
|
||||
const getStatusColor = (tipo: string) => {
|
||||
switch (tipo) {
|
||||
case 'entrada':
|
||||
return 'bg-green-500';
|
||||
case 'saida':
|
||||
return 'bg-red-500';
|
||||
case 'empenho':
|
||||
return 'bg-yellow-500';
|
||||
case 'desempenho':
|
||||
return 'bg-blue-500';
|
||||
case 'ajuste':
|
||||
return 'bg-purple-500';
|
||||
case 'transferencia':
|
||||
return 'bg-orange-500';
|
||||
default:
|
||||
return 'bg-gray-500';
|
||||
}
|
||||
};
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
{[1, 2, 3].map((i) => (
|
||||
<Skeleton key={i} className="w-full h-12" />
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Card className="bg-card border-border">
|
||||
<CardHeader>
|
||||
<div className="flex items-center justify-between">
|
||||
<CardTitle className="flex items-center gap-2">
|
||||
<TrendingUp className="h-5 w-5" />
|
||||
Histórico de Movimentações
|
||||
</CardTitle>
|
||||
<Button onClick={() => setIsModalOpen(true)} className="bg-blue-600 hover:bg-blue-700">
|
||||
<Plus className="h-4 w-4 mr-2" />
|
||||
Nova Movimentação
|
||||
</Button>
|
||||
</div>
|
||||
</CardHeader>
|
||||
|
||||
<CardContent>
|
||||
{sortedMovimentacoes.length > 0 ? (
|
||||
<div className="overflow-x-auto">
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<MovimentacaoTableHeader
|
||||
label="Material"
|
||||
sortKey="material"
|
||||
currentSort={sortConfig}
|
||||
onSort={handleSort}
|
||||
/>
|
||||
<MovimentacaoTableHeader
|
||||
label="Tipo"
|
||||
sortKey="tipo_movimentacao"
|
||||
currentSort={sortConfig}
|
||||
onSort={handleSort}
|
||||
/>
|
||||
<MovimentacaoTableHeader
|
||||
label="Quantidade"
|
||||
sortKey="quantidade"
|
||||
currentSort={sortConfig}
|
||||
onSort={handleSort}
|
||||
/>
|
||||
<TableHead>OF Vinculada</TableHead>
|
||||
<MovimentacaoTableHeader
|
||||
label="Data Movim."
|
||||
sortKey="data_movimentacao"
|
||||
currentSort={sortConfig}
|
||||
onSort={handleSort}
|
||||
/>
|
||||
<TableHead>Lote</TableHead>
|
||||
<TableHead>Observações</TableHead>
|
||||
<TableHead>Ações</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{sortedMovimentacoes.map((movimentacao) => (
|
||||
<TableRow key={movimentacao.id}>
|
||||
<TableCell>
|
||||
<div>
|
||||
<div className="font-medium">
|
||||
{movimentacao.estoque_materiais?.codigo}
|
||||
</div>
|
||||
<div className="text-sm text-muted-foreground">
|
||||
{movimentacao.estoque_materiais?.descricao}
|
||||
</div>
|
||||
</div>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Badge
|
||||
variant="outline"
|
||||
className={`${getStatusColor(movimentacao.tipo_movimentacao)} text-white border-none`}
|
||||
>
|
||||
{movimentacao.tipo_movimentacao}
|
||||
</Badge>
|
||||
</TableCell>
|
||||
<TableCell className="font-medium">
|
||||
{movimentacao.quantidade.toFixed(2)}
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
{movimentacao.of_vinculada || '-'}
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
{new Date(movimentacao.data_movimentacao).toLocaleDateString('pt-BR')}
|
||||
</TableCell>
|
||||
<TableCell>{movimentacao.lote || '-'}</TableCell>
|
||||
<TableCell>
|
||||
<div className="max-w-xs truncate" title={movimentacao.observacoes || ''}>
|
||||
{movimentacao.observacoes || '-'}
|
||||
</div>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<AlertDialog>
|
||||
<AlertDialogTrigger asChild>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="h-6 w-6 p-0 text-red-500 hover:text-red-700"
|
||||
disabled={excluirMovimentacao.isPending}
|
||||
>
|
||||
<Trash2 className="h-3 w-3" />
|
||||
</Button>
|
||||
</AlertDialogTrigger>
|
||||
<AlertDialogContent>
|
||||
<AlertDialogHeader>
|
||||
<AlertDialogTitle>Excluir Movimentação</AlertDialogTitle>
|
||||
<AlertDialogDescription>
|
||||
Tem certeza que deseja excluir esta movimentação de{' '}
|
||||
<strong>{movimentacao.tipo_movimentacao}</strong> para{' '}
|
||||
<strong>{movimentacao.estoque_materiais?.codigo}</strong>?
|
||||
{movimentacao.tipo_movimentacao === 'empenho' && (
|
||||
<div className="mt-2 p-2 bg-yellow-50 border border-yellow-200 rounded">
|
||||
<strong>Atenção:</strong> Esta ação também cancelará o empenho vinculado e reverterá as quantidades no estoque.
|
||||
</div>
|
||||
)}
|
||||
<br />
|
||||
Esta ação não pode ser desfeita.
|
||||
</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel>Cancelar</AlertDialogCancel>
|
||||
<AlertDialogAction
|
||||
onClick={() => excluirMovimentacao.mutate(movimentacao.id)}
|
||||
className="bg-red-500 hover:bg-red-600"
|
||||
>
|
||||
Excluir
|
||||
</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
) : (
|
||||
<div className="text-center py-8 text-muted-foreground">
|
||||
<Package className="h-16 w-16 mx-auto mb-4 opacity-50" />
|
||||
<p>Nenhuma movimentação registrada</p>
|
||||
</div>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<MovimentacaoModal
|
||||
isOpen={isModalOpen}
|
||||
onClose={() => setIsModalOpen(false)}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user