Files
tracksteel_app/src/components/estoque/MovimentacaoEstoqueSimplificada.tsx
T

241 lines
9.6 KiB
TypeScript

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)}
/>
</>
);
};