Files
advx/src/components/clientes/ClientesList.tsx
T
2025-04-10 23:07:21 -03:00

147 lines
5.4 KiB
TypeScript

import React, { useState, useEffect } from 'react';
import { useQuery } from 'react-query';
import { useNavigate } from 'react-router-dom';
import { PencilIcon } from '@heroicons/react/24/outline';
import api from '../../services/api';
import { formatCPF, formatTelefone } from '../../utils/formatters';
interface ClientesListProps {
searchTerm: string;
onEdit: (cliente: any) => void;
}
export const ClientesList: React.FC<ClientesListProps> = ({ searchTerm, onEdit }) => {
const navigate = useNavigate();
const [debouncedTerm, setDebouncedTerm] = useState(searchTerm);
// Implementar debounce para a busca
useEffect(() => {
const timer = setTimeout(() => {
setDebouncedTerm(searchTerm);
}, 300); // 300ms de delay
return () => clearTimeout(timer);
}, [searchTerm]);
const { data: clientes = [], isLoading } = useQuery(
['clientes', debouncedTerm],
async () => {
const response = await api.get('/clientes', {
params: {
search: debouncedTerm.toUpperCase()
}
});
// Filtrar localmente para melhor resposta
let filteredClientes = response.data;
if (debouncedTerm) {
const searchTerms = debouncedTerm.toUpperCase().split(' ');
filteredClientes = response.data.filter((cliente: any) =>
searchTerms.every(term =>
cliente.nome.includes(term) ||
cliente.cpfCnpj.includes(term) ||
cliente.email?.includes(term.toLowerCase())
)
);
}
// Ordenar por nome
return filteredClientes.sort((a: any, b: any) =>
a.nome.localeCompare(b.nome)
);
},
{
staleTime: 1000 * 60, // 1 minuto
cacheTime: 1000 * 60 * 5, // 5 minutos
keepPreviousData: true // Manter dados anteriores enquanto carrega novos
}
);
if (isLoading) {
return (
<div className="bg-white dark:bg-gray-800 shadow rounded-lg p-6">
<div className="animate-pulse flex space-x-4">
<div className="flex-1 space-y-4">
<div className="h-4 bg-gray-200 dark:bg-gray-700 rounded w-3/4"></div>
<div className="h-4 bg-gray-200 dark:bg-gray-700 rounded"></div>
<div className="h-4 bg-gray-200 dark:bg-gray-700 rounded w-5/6"></div>
</div>
</div>
</div>
);
}
return (
<div className="bg-white dark:bg-gray-800 shadow rounded-lg overflow-hidden">
{clientes.length === 0 ? (
<div className="p-6 text-center text-gray-500 dark:text-gray-400">
Nenhum cliente encontrado
</div>
) : (
<table className="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
<thead className="bg-gray-50 dark:bg-gray-900">
<tr>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
Nome
</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
CPF/CNPJ
</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
Telefone
</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
Email
</th>
<th className="relative px-6 py-3">
<span className="sr-only">Ações</span>
</th>
</tr>
</thead>
<tbody className="divide-y divide-gray-200 dark:divide-gray-700">
{clientes.map((cliente: any) => (
<tr
key={cliente.id}
className="hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors cursor-pointer bg-white dark:bg-gray-800"
onClick={() => navigate(`/clientes/${cliente.id}`)}
>
<td className="px-6 py-4 whitespace-nowrap">
<div className="text-sm font-medium text-gray-900 dark:text-white">
{cliente.nome}
</div>
</td>
<td className="px-6 py-4 whitespace-nowrap">
<div className="text-sm text-gray-500 dark:text-gray-400">
{formatCPF(cliente.cpfCnpj)}
</div>
</td>
<td className="px-6 py-4 whitespace-nowrap">
<div className="text-sm text-gray-500 dark:text-gray-400">
{formatTelefone(cliente.telefone)}
</div>
</td>
<td className="px-6 py-4 whitespace-nowrap">
<div className="text-sm text-gray-500 dark:text-gray-400">
{cliente.email}
</div>
</td>
<td className="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
<button
onClick={(e) => {
e.stopPropagation();
onEdit(cliente);
}}
className="text-blue-600 dark:text-blue-400 hover:text-blue-900 dark:hover:text-blue-300"
>
<PencilIcon className="h-5 w-5" />
</button>
</td>
</tr>
))}
</tbody>
</table>
)}
</div>
);
};