231 lines
6.3 KiB
TypeScript
231 lines
6.3 KiB
TypeScript
import React, { useEffect, useCallback } from 'react';
|
|
import { Models } from 'appwrite';
|
|
import SearchBar from './SearchBar';
|
|
import DataTable, { Column } from './DataTable';
|
|
import Pagination from './Pagination';
|
|
import TableActions from './TableActions';
|
|
|
|
interface ProdutoListProps {
|
|
produtos: Models.Document[];
|
|
loading: boolean;
|
|
isCreating?: boolean;
|
|
error: string | null;
|
|
totalProdutos: number;
|
|
currentPage: number;
|
|
isChangingPage: boolean;
|
|
pageSize: number;
|
|
onEdit: (produto: Models.Document) => void;
|
|
onDelete: (id: string) => Promise<boolean>;
|
|
onPrevPage: () => void;
|
|
onNextPage: () => void;
|
|
onSearch: (termo: string) => void;
|
|
searchTerm?: string;
|
|
}
|
|
|
|
const ProdutoList: React.FC<ProdutoListProps> = ({
|
|
produtos,
|
|
loading,
|
|
isCreating = false,
|
|
error,
|
|
totalProdutos,
|
|
currentPage,
|
|
isChangingPage,
|
|
pageSize,
|
|
onEdit,
|
|
onDelete,
|
|
onPrevPage,
|
|
onNextPage,
|
|
onSearch,
|
|
searchTerm = ''
|
|
}) => {
|
|
const totalPages = Math.ceil(totalProdutos / pageSize);
|
|
const startItem = (currentPage - 1) * pageSize + 1;
|
|
const endItem = Math.min(currentPage * pageSize, totalProdutos);
|
|
|
|
const [search, setSearch] = React.useState(searchTerm);
|
|
|
|
// Sincronizar o estado local com o searchTerm externo
|
|
useEffect(() => {
|
|
setSearch(searchTerm);
|
|
}, [searchTerm]);
|
|
|
|
// Debounce para busca em tempo real
|
|
const debouncedSearch = useCallback(
|
|
debounce((term: string) => {
|
|
onSearch(term);
|
|
}, 500),
|
|
[onSearch]
|
|
);
|
|
|
|
// Função para lidar com mudanças no campo de busca
|
|
const handleSearchChange = (value: string) => {
|
|
setSearch(value);
|
|
debouncedSearch(value);
|
|
};
|
|
|
|
const handleSearch = () => {
|
|
onSearch(search);
|
|
};
|
|
|
|
// Função debounce
|
|
function debounce<T extends (...args: any[]) => any>(
|
|
func: T,
|
|
wait: number
|
|
): (...args: Parameters<T>) => void {
|
|
let timeout: NodeJS.Timeout;
|
|
return (...args: Parameters<T>) => {
|
|
clearTimeout(timeout);
|
|
timeout = setTimeout(() => func(...args), wait);
|
|
};
|
|
}
|
|
|
|
const handleDelete = async (id: string) => {
|
|
// Popup de confirmação é tratado pela função onDelete
|
|
await onDelete(id);
|
|
};
|
|
|
|
const formatarPreco = (preco: number) => {
|
|
return new Intl.NumberFormat('pt-BR', {
|
|
style: 'currency',
|
|
currency: 'BRL'
|
|
}).format(preco || 0);
|
|
};
|
|
|
|
const columns: Column<Models.Document>[] = [
|
|
{
|
|
key: 'nome',
|
|
header: 'Nome do Produto',
|
|
render: produto => {
|
|
console.log('🔍 Debug Produto:', {
|
|
id: produto.$id,
|
|
nome: produto.nome,
|
|
catalogoProdutos: produto['catalogo-produtos'],
|
|
allKeys: Object.keys(produto)
|
|
});
|
|
return produto.nome || produto['catalogo-produtos']?.descricao || 'N/A';
|
|
}
|
|
},
|
|
{
|
|
key: 'descricao',
|
|
header: 'Descrição (Catálogo)',
|
|
render: produto => produto['catalogo-produtos']?.descricao || 'N/A',
|
|
className: 'max-w-xs'
|
|
},
|
|
{
|
|
key: 'codigo',
|
|
header: 'Código',
|
|
render: produto => produto['catalogo-produtos']?.['codigo-interno'] || produto['catalogo-produtos']?.['codigo-ean'] || produto.codigo || 'N/A'
|
|
},
|
|
{
|
|
key: 'estoque',
|
|
header: 'Estoque',
|
|
render: produto => {
|
|
// Usar quantidade real do produto ou padrão se não informado
|
|
const estoque = produto.quantidade || produto.estoque || 0;
|
|
return (
|
|
<span className={`font-medium ${
|
|
estoque > 20 ? 'text-green-600' :
|
|
estoque > 5 ? 'text-yellow-600' :
|
|
'text-red-600'
|
|
}`}>
|
|
{estoque > 0 ? `${estoque} unidades` : 'Sem estoque'}
|
|
</span>
|
|
);
|
|
}
|
|
},
|
|
{
|
|
key: 'preco-fabrica',
|
|
header: 'Preço Fábrica',
|
|
render: produto => formatarPreco(produto['preco-original'] || produto['preco-fabrica'])
|
|
},
|
|
{
|
|
key: 'preco-atual',
|
|
header: 'Preço Atual',
|
|
render: produto => formatarPreco(produto['preco-atual'] || produto['preco-venda'])
|
|
},
|
|
{
|
|
key: 'laboratorio',
|
|
header: 'Laboratório',
|
|
render: produto => {
|
|
const laboratorios = produto['catalogo-produtos']?.laboratorios;
|
|
if (laboratorios && laboratorios.length > 0) {
|
|
return laboratorios[0].nome || 'N/A';
|
|
}
|
|
return produto.laboratorio || 'N/A';
|
|
}
|
|
},
|
|
{
|
|
key: 'categoria',
|
|
header: 'Categoria',
|
|
render: produto => produto['catalogo-produtos']?.categorias?.nome || produto.categoria || 'N/A'
|
|
}
|
|
];
|
|
|
|
return (
|
|
<>
|
|
{/* Barra de busca */}
|
|
<div className="mb-4">
|
|
<SearchBar
|
|
value={search}
|
|
onChange={handleSearchChange}
|
|
onSearch={handleSearch}
|
|
placeholder="Buscar por nome, descrição, código ou laboratório..."
|
|
/>
|
|
</div>
|
|
|
|
{error && (
|
|
<div className="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded mb-4">
|
|
{error}
|
|
</div>
|
|
)}
|
|
|
|
{isCreating && (
|
|
<div className="bg-indigo-50 border border-indigo-200 rounded-md p-3 mb-4">
|
|
<div className="flex items-center">
|
|
<div className="animate-spin rounded-full h-4 w-4 border-b-2 border-indigo-600 mr-2"></div>
|
|
<span className="text-indigo-800 text-sm">Criando novo produto...</span>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{loading ? (
|
|
<div className="flex justify-center items-center py-8">
|
|
<div className="text-lg">Carregando produtos...</div>
|
|
</div>
|
|
) : (
|
|
<>
|
|
<DataTable
|
|
columns={columns}
|
|
data={produtos}
|
|
actions={(produto) => (
|
|
<TableActions
|
|
onEdit={() => onEdit(produto)}
|
|
onDelete={() => handleDelete(produto.$id)}
|
|
/>
|
|
)}
|
|
/>
|
|
|
|
<div className="mt-4 flex justify-between items-center">
|
|
<div className="text-sm text-gray-600">
|
|
{totalProdutos > 0 ? (
|
|
<>Mostrando {startItem} - {endItem} de {totalProdutos} produtos</>
|
|
) : (
|
|
'Total de produtos: 0'
|
|
)}
|
|
</div>
|
|
|
|
<Pagination
|
|
page={currentPage}
|
|
total={totalPages}
|
|
onPrev={onPrevPage}
|
|
onNext={onNextPage}
|
|
isChangingPage={isChangingPage}
|
|
/>
|
|
</div>
|
|
</>
|
|
)}
|
|
</>
|
|
);
|
|
};
|
|
|
|
export default ProdutoList;
|