import React, { useState, useEffect } from 'react'; import { Modal } from '../Modal'; import { stockService, type StockMovement, type StockItem } from '../../services/stockService'; import { format } from 'date-fns'; import { ArrowUp, ArrowDown, RefreshCw, Trash2, Edit2, Save, X, FileText, Activity } from 'lucide-react'; import { useAuth } from '../../context/useAuth'; interface StockHistoryModalProps { isOpen: boolean; onClose: () => void; item: StockItem; onUpdate?: () => void; } interface AuditLog { _id: string; action: 'CREATE' | 'UPDATE' | 'DELETE'; userName: string; details: string; timestamp: string; movementNumber?: number; } export const StockHistoryModal: React.FC = ({ isOpen, onClose, item, onUpdate }) => { const { isAdmin } = useAuth(); const [activeTab, setActiveTab] = useState<'movements' | 'logs'>('movements'); const [movements, setMovements] = useState([]); const [logs, setLogs] = useState([]); const [currentItem, setCurrentItem] = useState(item); const [loading, setLoading] = useState(true); const [editingId, setEditingId] = useState(null); const [searchTerm, setSearchTerm] = useState(''); const [editValues, setEditValues] = useState<{ date: string; quantity: string; notes: string }>({ date: '', quantity: '', notes: '' }); const fetchData = async () => { if (item._id) { setLoading(true); try { // Always fetch item to keep balance fresh const itemData = await stockService.getById(item._id); setCurrentItem(itemData); if (activeTab === 'movements') { const data = await stockService.getMovements(item._id); setMovements(data); } else { const data = await stockService.getAuditLogs(item._id); setLogs(data); } } catch (error) { console.error('Error fetching data:', error); } finally { setLoading(false); } } }; useEffect(() => { if (isOpen) { fetchData(); } }, [isOpen, item, activeTab]); const formatMovementId = (num?: number) => { if (!num) return '-'; return `${item.rrNumber}/${String(num).padStart(2, '0')}`; }; const filteredLogs = logs.filter(log => { const term = searchTerm.toLowerCase(); const formattedId = formatMovementId(log.movementNumber); return ( log.details.toLowerCase().includes(term) || log.userName.toLowerCase().includes(term) || formattedId.toLowerCase().includes(term) ); }); const handleEditClick = (move: StockMovement) => { setEditingId(move._id!); const dateStr = new Date(move.date).toISOString().slice(0, 16); setEditValues({ date: dateStr, quantity: String(move.quantity), notes: move.notes || '' }); }; const handleCancelEdit = () => { setEditingId(null); setEditValues({ date: '', quantity: '', notes: '' }); }; const handleSave = async (id: string) => { try { await stockService.updateMovement(id, { date: new Date(editValues.date).toISOString(), quantity: Number(editValues.quantity), notes: editValues.notes }); setEditingId(null); fetchData(); if (onUpdate) onUpdate(); } catch (error) { console.error('Error updating movement:', error); alert('Erro ao atualizar movimentação.'); } }; const handleDelete = async (id: string, qty: number) => { if (confirm(`Tem certeza que deseja excluir esta movimentação de ${qty}? O saldo do lote será revertido.`)) { try { await stockService.deleteMovement(id); fetchData(); if (onUpdate) onUpdate(); } catch (error) { console.error('Error deleting movement:', error); alert('Erro ao excluir movimentação.'); } } }; const getMovementIcon = (type: string) => { switch (type) { case 'ENTRY': return ; case 'CONSUMPTION': return ; case 'ADJUSTMENT': return ; default: return null; } }; const getMovementLabel = (type: string) => { switch (type) { case 'ENTRY': return 'Entrada'; case 'CONSUMPTION': return 'Consumo'; case 'ADJUSTMENT': return 'Ajuste'; default: return type; } }; return (

Produto: {typeof currentItem.dataSheetId === 'object' ? currentItem.dataSheetId.name : '...'}

Lote: {currentItem.batchNumber}

Saldo Atual

{currentItem.quantity} {currentItem.unit}
{/* Tabs */}
{activeTab === 'logs' && ( setSearchTerm(e.target.value)} className="text-xs bg-surface border border-border/40 rounded-lg px-3 py-1.5 focus:outline-none focus:border-primary w-64 text-text-main placeholder-text-muted" /> )}
{loading ? (
Carregando...
) : activeTab === 'movements' ? ( // MOVEMENTS TABLE movements.length === 0 ? (
Nenhuma movimentação registrada.
) : (
{isAdmin() && } {movements.map((move: any) => { const isEditing = editingId === move._id; return (
ID Data Tipo Qtd Responsável DetalhesAções
{formatMovementId(move.movementNumber)} {isEditing ? ( setEditValues({ ...editValues, date: e.target.value })} className="w-full bg-background border border-border rounded px-2 py-1 text-xs" /> ) : ( {format(new Date(move.date), 'dd/MM/yyyy HH:mm')} )}
{getMovementIcon(move.type)} {getMovementLabel(move.type)}
{isEditing ? ( setEditValues({ ...editValues, quantity: e.target.value })} className="w-full bg-background border border-border rounded px-2 py-1 text-xs" placeholder="(ex: -10)" /> ) : ( 0 ? 'text-green-500' : 'text-red-500'}> {move.quantity > 0 ? '+' : ''}{move.quantity} )}
{move.responsible}
{isEditing ? (