feat: implementa busca de produtos com API externa e corrige URL base

- Adiciona integração com API de produtos em http://10.1.1.212:8805/api/v1/data-consult/products/{id}
- Corrige mapeamento de resposta da API para formato Product interface
- Atualiza ProductSearchInput para usar description como nome do produto
- Corrige API_BASE_URL adicionando protocolo http:// para evitar URLs relativas
- Resolve erro 404 causado por URLs malformadas em requisições de API
This commit is contained in:
JuruSysadmin
2025-07-25 15:17:36 -03:00
parent a967e2774d
commit 85d202298f
8 changed files with 474 additions and 115 deletions

View File

@@ -11,7 +11,7 @@ import { clearUrlAndNavigate } from "@/src/utils/url-helpers";
/**
* Função de utilidade para aplicar debounce em funções
* Evita execuções repetidas em curtos intervalos de tempo
*
*
* @template T - Tipo da função a ser debounced
* @param {T} callback - Função a aplicar debounce
* @param {number} delay - Tempo de espera em ms
@@ -84,31 +84,32 @@ const paramMapping: Record<string, keyof OrderSearchParams> = {
createDateEnd: "createDateEnd",
customerId: "customerId",
document: "document",
invoiceNumber: "invoiceNumber"
invoiceNumber: "invoiceNumber",
productId: "productId"
};
/**
* Hook personalizado para gerenciar a consulta e exibição de pedidos
* Gerencia busca, paginação, armazenamento de parâmetros e detalhes de pedidos
*
*
* @param {number} ordersPerPage - Número de pedidos a serem exibidos por página
* @returns {UseOrderSearchReturn} Objeto contendo estado e funções para lidar com pedidos
*/
export function useOrderSearch(ordersPerPage: number = 8): UseOrderSearchReturn {
const router = useRouter();
const urlSearchParams = useSearchParams();
// Inicializar searchParams com valores da URL ou do localStorage ou um objeto vazio
const [searchParams, setSearchParams] = useState<OrderSearchParams>(() => {
const params: OrderSearchParams = {};
// Tentar recuperar do localStorage primeiro
const savedParams = getStorage("orderSearchParams");
if (savedParams) {
return savedParams;
}
// Se não houver no localStorage, extrair valores da URL para o estado
Object.entries(paramMapping).forEach(([urlParam, stateParam]) => {
const value = urlSearchParams.get(urlParam);
@@ -117,7 +118,7 @@ export function useOrderSearch(ordersPerPage: number = 8): UseOrderSearchReturn
(params as any)[stateParam] = value;
}
});
// Tratamento especial para parâmetros que podem ser arrays
const typeParam = urlSearchParams.get('type');
if (typeParam) {
@@ -128,10 +129,10 @@ export function useOrderSearch(ordersPerPage: number = 8): UseOrderSearchReturn
params.type = typeParam;
}
}
return params;
});
const [orders, setOrders] = useState<Order[]>([]);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
@@ -147,7 +148,7 @@ export function useOrderSearch(ordersPerPage: number = 8): UseOrderSearchReturn
// Detectar parâmetro origem=detalhe para realizar pesquisa automática ao retornar
const origin = urlSearchParams.get("origem");
// Limpar todos os campos relacionados ao cliente
const clearCustomerFields = useCallback(() => {
setSearchParams(prev => ({
@@ -158,12 +159,21 @@ export function useOrderSearch(ordersPerPage: number = 8): UseOrderSearchReturn
selectedCustomer: null,
}));
}, []);
// Efeito para realizar pesquisa automática quando a página é carregada
// Limpar todos os campos relacionados ao produto
const clearProductFields = useCallback(() => {
setSearchParams(prev => ({
...prev,
productId: "",
selectedProduct: null,
}));
}, []);
// Efeito para realizar pesquisa automática quando a página é carregada
// e há parâmetros de pesquisa na URL ou se estamos voltando da página de detalhes
useEffect(() => {
const hasSearchParams = Object.keys(searchParams).length > 0;
// Se temos parâmetros de busca e vamos retornando da página de detalhes,
// ou se temos parâmetros e ainda não realizamos a pesquisa
if ((hasSearchParams && origin === "detalhe") || (hasSearchParams && !hasSearched)) {
@@ -177,7 +187,7 @@ export function useOrderSearch(ordersPerPage: number = 8): UseOrderSearchReturn
*/
const updateUrlWithSearchParams = useCallback(() => {
const urlParams = new URLSearchParams();
// Adicionar apenas parâmetros com valores
Object.entries(searchParams).forEach(([key, value]) => {
if (value) {
@@ -189,15 +199,15 @@ export function useOrderSearch(ordersPerPage: number = 8): UseOrderSearchReturn
}
}
});
// Preservar parâmetro origem se existir
if (origin) {
urlParams.append("origem", origin);
}
// Salvar no localStorage
setStorage("orderSearchParams", searchParams);
// Atualizar URL sem causar recarregamento da página
const url = window.location.pathname + (urlParams.toString() ? `?${urlParams.toString()}` : "");
window.history.replaceState({ path: url }, "", url);
@@ -206,7 +216,7 @@ export function useOrderSearch(ordersPerPage: number = 8): UseOrderSearchReturn
/**
* Executa a busca de pedidos com base nos parâmetros definidos
* Valida datas, formata parâmetros e faz a chamada à API
*
*
* @returns {Promise<void>}
*/
const handleSearch = async () => {
@@ -236,27 +246,30 @@ export function useOrderSearch(ordersPerPage: number = 8): UseOrderSearchReturn
// Transform the ALL value to empty string for the API call
const paramsToSend: OrderSearchParams = { ...searchParams };
// Remover o campo selectedCustomer antes de enviar para a API
// Remover campos que não devem ser enviados para a API
if (paramsToSend.selectedCustomer) {
delete paramsToSend.selectedCustomer;
}
if (paramsToSend.selectedProduct) {
delete paramsToSend.selectedProduct;
}
// Se tiver customerId definido, remover name e document para evitar conflito
if (paramsToSend.customerId) {
delete paramsToSend.name;
delete paramsToSend.document;
}
// Se temos o nome do vendedor mas não temos o ID, tentar buscar o ID primeiro
if (paramsToSend.sellerName && !paramsToSend.sellerId) {
try {
const sellers = await dataConsultApi.getSellers();
if (Array.isArray(sellers)) {
const matchingSeller = sellers.find((seller: any) =>
const matchingSeller = sellers.find((seller: any) =>
seller.name.toLowerCase().includes(paramsToSend.sellerName?.toLowerCase() || '')
);
if (matchingSeller) {
// Extrair código numérico (887 de "887 - SAL-SIMONI")
if (matchingSeller.name && matchingSeller.name.includes("-")) {
@@ -276,18 +289,18 @@ export function useOrderSearch(ordersPerPage: number = 8): UseOrderSearchReturn
console.error("Erro ao buscar vendedores:", err);
}
}
// Mapear invoiceNumber para invoiceId que é o que a API espera
if (paramsToSend.invoiceNumber) {
paramsToSend.invoiceId = paramsToSend.invoiceNumber;
delete paramsToSend.invoiceNumber;
}
// Remover campos que a API não espera
if ('nfe' in paramsToSend) {
delete paramsToSend.nfe;
}
if (paramsToSend.codfilial === "ALL") {
paramsToSend.codfilial = "";
}
@@ -305,7 +318,7 @@ export function useOrderSearch(ordersPerPage: number = 8): UseOrderSearchReturn
// Construir manualmente o queryParams para lidar com arrays
const queryParams = new URLSearchParams();
Object.entries(paramsToSend).forEach(([key, value]) => {
if ((key === 'type' || key === 'status' || key === 'deliveryType') && Array.isArray(value)) {
// Unir os valores do array em uma única string separada por vírgula
@@ -314,21 +327,21 @@ export function useOrderSearch(ordersPerPage: number = 8): UseOrderSearchReturn
queryParams.append(key, String(value));
}
});
const queryString = queryParams.toString();
const response = await ordersApi.findOrders(paramsToSend, queryString);
setOrders(extractApiData<Order>(response));
setError(null);
// Reset selected order and items when new search is performed
setSelectedOrderId(null);
setOrderItems([]);
setCutItems([]);
// Reset pagination state
setCurrentPage(1);
setVisibleOrdersCount(ordersPerPage);
// Atualizar a URL com os parâmetros de pesquisa
updateUrlWithSearchParams();
} catch (err) {
@@ -342,13 +355,13 @@ export function useOrderSearch(ordersPerPage: number = 8): UseOrderSearchReturn
/**
* Manipula alterações em campos de entrada do formulário
* Atualiza o estado searchParams com o novo valor
*
*
* @param {keyof OrderSearchParams} field - Nome do campo a atualizar
* @param {any} value - Novo valor para o campo
*/
const handleInputChange = (field: keyof OrderSearchParams, value: any) => {
// Garantir que valores apropriados sejam tratados como arrays
if ((field === 'type' || field === 'status' || field === 'deliveryType') &&
if ((field === 'type' || field === 'status' || field === 'deliveryType') &&
typeof value === 'string' && value !== '') {
// Converter string para array com um único item
setSearchParams((prev) => ({
@@ -370,7 +383,7 @@ export function useOrderSearch(ordersPerPage: number = 8): UseOrderSearchReturn
/**
* Seleciona um cliente após busca, preenchendo campos relacionados
*
*
* @param {Cliente | null} customer - Cliente selecionado ou null para limpar
*/
const handleCustomerSelect = (customer: Cliente | null) => {
@@ -378,7 +391,7 @@ export function useOrderSearch(ordersPerPage: number = 8): UseOrderSearchReturn
clearCustomerFields();
return;
}
setSearchParams((prev) => ({
...prev,
selectedCustomer: customer,
@@ -391,7 +404,7 @@ export function useOrderSearch(ordersPerPage: number = 8): UseOrderSearchReturn
/**
* Manipula clique em linha da tabela de pedidos
* Carrega detalhes do pedido selecionado
*
*
* @param {string} orderId - ID do pedido selecionado
* @returns {Promise<void>}
*/
@@ -435,14 +448,14 @@ export function useOrderSearch(ordersPerPage: number = 8): UseOrderSearchReturn
// Pagination calculations - corrigir para garantir 8 itens por página
const totalPages = Math.ceil(orders.length / ordersPerPage);
// Calcular o índice inicial baseado na página atual
const indexOfFirstOrder = (currentPage - 1) * ordersPerPage;
// Calcular o índice final
const indexOfLastOrder = Math.min(indexOfFirstOrder + ordersPerPage, orders.length);
// Selecionar apenas os itens da página atual
const currentOrders = orders.slice(indexOfFirstOrder, indexOfLastOrder);
// Navigate to next page
const goToNextPage = useCallback(() => {
if (currentPage < totalPages) {
@@ -490,7 +503,7 @@ export function useOrderSearch(ordersPerPage: number = 8): UseOrderSearchReturn
/**
* Identifica o tipo de entrada para busca de cliente
* Diferencia entre ID, documento ou nome baseado no formato
*
*
* @param {string} value - Valor de entrada a ser analisado
* @returns {'id' | 'document' | 'name'} Tipo identificado da entrada
*/
@@ -503,7 +516,7 @@ export function useOrderSearch(ordersPerPage: number = 8): UseOrderSearchReturn
/**
* Busca cliente por ID
*
*
* @param {string} value - ID do cliente a ser buscado
* @returns {Promise<void>}
*/
@@ -514,10 +527,10 @@ export function useOrderSearch(ordersPerPage: number = 8): UseOrderSearchReturn
clearCustomerFields();
return;
}
let customerData: any = null;
const inputType = identifyInputType(value);
// Atualizar o campo apropriado com base no tipo de entrada
setSearchParams(prev => ({
...prev,
@@ -525,7 +538,7 @@ export function useOrderSearch(ordersPerPage: number = 8): UseOrderSearchReturn
document: inputType === 'document' ? value : "",
name: inputType === 'name' ? value : "",
}));
// Buscar cliente pelo tipo correto
if (inputType === 'id') {
const response = await dataConsultApi.getCustomer(value);
@@ -538,7 +551,7 @@ export function useOrderSearch(ordersPerPage: number = 8): UseOrderSearchReturn
customerData = response[0];
}
}
if (customerData) {
// Create customer object from response
const customer: Customer = {
@@ -546,7 +559,7 @@ export function useOrderSearch(ordersPerPage: number = 8): UseOrderSearchReturn
name: customerData.name,
document: customerData.document || ""
};
// Update selected customer in form state
setSearchParams(prev => ({
...prev,
@@ -564,21 +577,21 @@ export function useOrderSearch(ordersPerPage: number = 8): UseOrderSearchReturn
/**
* Busca cliente por nome
*
*
* @param {string} value - Nome parcial ou completo para busca
* @returns {Promise<void>}
*/
const searchCustomerByName = async (value: string) => {
if (!value || value.length < 2) return;
try {
const matches = await clientesApi.search(value);
if (matches.length > 0) {
const c = matches[0];
const clientId = c.id || "";
const clientName = c.name || "";
setSearchParams(prev => ({
...prev,
customerId: clientId,
@@ -596,14 +609,14 @@ export function useOrderSearch(ordersPerPage: number = 8): UseOrderSearchReturn
setError("Erro ao buscar cliente. Por favor, tente novamente.");
}
};
// Criar versão com debounce da função de busca
const debouncedSearchCustomer = useDebounce(searchCustomerByName, 500);
/**
* Manipula entrada de filtro de cliente
* Identifica o tipo de filtro e direciona para a busca apropriada
*
*
* @param {string} value - Termo de busca (ID, documento ou nome)
* @returns {Promise<void>}
*/
@@ -613,7 +626,7 @@ export function useOrderSearch(ordersPerPage: number = 8): UseOrderSearchReturn
clearCustomerFields();
return;
}
// Atualiza o campo de nome no estado
setSearchParams(prev => ({
...prev,
@@ -624,11 +637,11 @@ export function useOrderSearch(ordersPerPage: number = 8): UseOrderSearchReturn
// Se ainda está digitando poucas letras, apenas atualiza o estado
if (value.length < 2) return;
// Aciona a busca com debounce para evitar chamadas excessivas à API
debouncedSearchCustomer(value);
};
/**
* Restaura o estado da pesquisa com dados previamente salvos
* Útil para preservar o estado ao voltar da página de detalhes
@@ -638,12 +651,12 @@ export function useOrderSearch(ordersPerPage: number = 8): UseOrderSearchReturn
setCurrentPage(savedPage);
setHasSearched(savedHasSearched);
setVisibleOrdersCount(ordersPerPage * savedPage);
// Calcular total de páginas
const calculatedTotalPages = Math.ceil(savedOrders.length / ordersPerPage);
// Como não temos acesso direto à função setTotalPages, calcular localmente
const totalPages = calculatedTotalPages;
// Atualizar pedidos da página atual
const indexOfLastOrder = savedPage * ordersPerPage;
const indexOfFirstOrder = indexOfLastOrder - ordersPerPage;
@@ -671,7 +684,7 @@ export function useOrderSearch(ordersPerPage: number = 8): UseOrderSearchReturn
/**
* Manipula a seleção de múltiplos valores em filtros de seleção
*
*
* @param {string} field - Nome do campo no objeto searchParams
* @param {string[]} values - Array de valores selecionados
*/
@@ -716,4 +729,4 @@ export function useOrderSearch(ordersPerPage: number = 8): UseOrderSearchReturn
clearAndNavigateToOrders,
handleMultiInputChange
};
}
}