'use client'; import React, { useState, useEffect, Fragment } from 'react'; import { PlusIcon, MagnifyingGlassIcon, FunnelIcon, Square3Stack3DIcon as PackageIcon, CurrencyDollarIcon, ExclamationTriangleIcon, TrashIcon, PencilSquareIcon, XMarkIcon, CheckIcon, TagIcon, } from '@heroicons/react/24/outline'; import { erpApi, Product } from '@/lib/api-erp'; import { formatCurrency } from '@/lib/format'; import { useToast } from '@/components/layout/ToastContext'; import { PageHeader, StatsCard, DataTable, Input, Card, BulkActionBar, ConfirmDialog, } from "@/components/ui"; import { Dialog, DialogPanel, DialogTitle, Transition, TransitionChild } from '@headlessui/react'; export default function ProductsPage() { const toast = useToast(); const [products, setProducts] = useState([]); const [loading, setLoading] = useState(true); const [searchTerm, setSearchTerm] = useState(''); const [isModalOpen, setIsModalOpen] = useState(false); const [confirmOpen, setConfirmOpen] = useState(false); const [bulkConfirmOpen, setBulkConfirmOpen] = useState(false); const [productToDelete, setProductToDelete] = useState(null); const [editingProduct, setEditingProduct] = useState(null); const [selectedIds, setSelectedIds] = useState<(string | number)[]>([]); const [formData, setFormData] = useState>({ name: '', sku: '', description: '', price: 0, cost_price: 0, type: 'product', stock_quantity: 0, is_active: true }); useEffect(() => { fetchProducts(); }, []); const fetchProducts = async (silent = false) => { try { if (!silent) setLoading(true); const data = await erpApi.getProducts(); setProducts(data || []); } catch (error) { toast.error('Erro ao carregar', 'Não foi possível carregar os produtos'); } finally { setLoading(false); setSelectedIds([]); } }; const handleSave = async (e: React.FormEvent) => { e.preventDefault(); try { if (editingProduct?.id) { await erpApi.updateProduct(editingProduct.id, formData); toast.success('Produto atualizado com sucesso!'); } else { await erpApi.createProduct(formData); toast.success('Produto cadastrado com sucesso!'); } setIsModalOpen(false); setEditingProduct(null); resetForm(); await fetchProducts(true); } catch (error) { toast.error(editingProduct ? 'Erro ao atualizar produto' : 'Erro ao salvar produto'); } }; const handleDelete = (id: string) => { setProductToDelete(id); setConfirmOpen(true); }; const handleConfirmDelete = async () => { if (!productToDelete) return; const originalProducts = [...products]; const idToDelete = String(productToDelete); // Dynamic: remove instantly setProducts(prev => prev.filter(p => String(p.id) !== idToDelete)); try { await erpApi.deleteProduct(idToDelete); toast.success('Exclusão completa', 'O item foi removido com sucesso.'); setTimeout(() => fetchProducts(true), 500); } catch (error) { setProducts(originalProducts); toast.error('Erro ao excluir', 'Ocorreu um erro ao excluir o produto.'); } finally { setConfirmOpen(false); setProductToDelete(null); } }; const handleBulkDelete = async () => { if (selectedIds.length === 0) return; setBulkConfirmOpen(true); }; const handleConfirmBulkDelete = async () => { if (selectedIds.length === 0) return; const originalProducts = [...products]; const idsToDelete = selectedIds.map(String); // Dynamic: remove instantly setProducts(prev => prev.filter(p => !idsToDelete.includes(String(p.id)))); const deletedCount = selectedIds.length; try { await Promise.all(idsToDelete.map(id => erpApi.deleteProduct(id))); toast.success('Exclusão completa', `${deletedCount} produtos excluídos com sucesso.`); setTimeout(() => fetchProducts(true), 500); } catch (error) { setProducts(originalProducts); toast.error('Erro ao excluir', 'Ocorreu um erro ao excluir alguns produtos.'); } finally { setBulkConfirmOpen(false); setSelectedIds([]); } }; const handleEdit = (product: Product) => { setEditingProduct(product); setFormData({ name: product.name, sku: product.sku, description: product.description, price: Number(product.price), cost_price: Number(product.cost_price), type: product.type, stock_quantity: Number(product.stock_quantity), is_active: product.is_active }); setIsModalOpen(true); }; const resetForm = () => { setFormData({ name: '', sku: '', description: '', price: 0, cost_price: 0, type: 'product', stock_quantity: 0, is_active: true }); }; const filteredProducts = products.filter(p => p.name.toLowerCase().includes(searchTerm.toLowerCase()) || (p.sku || '').toLowerCase().includes(searchTerm.toLowerCase()) ); const totalStockValue = products.reduce((sum, p) => sum + (Number(p.price) * Number(p.stock_quantity)), 0); const lowStockItems = products.filter(p => p.type === 'product' && p.stock_quantity < 5).length; const servicesCount = products.filter(p => p.type === 'service').length; const columns = [ { header: 'Produto / SKU', accessor: (row: Product) => (
{row.type === 'product' ? : }
{row.name} {row.sku || 'SEM SKU'}
) }, { header: 'Tipo', accessor: (row: Product) => ( {row.type === 'product' ? 'Produto' : 'Serviço'} ) }, { header: 'Estoque', accessor: (row: Product) => ( row.type === 'product' ? (
{row.stock_quantity} {row.stock_quantity < 5 && ( )}
) : ( N/A ) ) }, { header: 'Preço de Venda', className: 'text-right', accessor: (row: Product) => ( {formatCurrency(row.price)} ) }, { header: '', className: 'text-right', accessor: (row: Product) => (
) } ]; return (
, onClick: () => { setEditingProduct(null); resetForm(); setIsModalOpen(true); } }} />
} /> } /> p.type === 'product').length} icon={} /> } />
setSearchTerm(e.target.value)} />
setIsModalOpen(false)}>
{editingProduct ? 'Editar Item' : 'Novo Produto/Serviço'}
setFormData({ ...formData, name: e.target.value })} className="bg-zinc-50 dark:bg-zinc-800 border-zinc-200 dark:border-zinc-700" />
setFormData({ ...formData, sku: e.target.value })} className="bg-zinc-50 dark:bg-zinc-800 border-zinc-200 dark:border-zinc-700" /> setFormData({ ...formData, stock_quantity: Number(e.target.value) })} className="bg-zinc-50 dark:bg-zinc-800 border-zinc-200 dark:border-zinc-700" /> setFormData({ ...formData, price: Number(e.target.value) })} className="bg-zinc-50 dark:bg-zinc-800 border-zinc-200 dark:border-zinc-700" /> setFormData({ ...formData, cost_price: Number(e.target.value) })} className="bg-zinc-50 dark:bg-zinc-800 border-zinc-200 dark:border-zinc-700" />