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

@@ -23,6 +23,7 @@ import { CustomerSearchInput } from "@/src/components/orders/CustomerSearchInput
import { useCallback, useState, useEffect } from "react"; import { useCallback, useState, useEffect } from "react";
import { User, useAuthValidation } from "../../../hooks/useAuthValidation"; import { User, useAuthValidation } from "../../../hooks/useAuthValidation";
import { SellerSearchInput } from "@/src/components/orders/SellerSearchInput"; import { SellerSearchInput } from "@/src/components/orders/SellerSearchInput";
import { ProductSearchInput } from "@/src/components/orders/ProductSearchInput";
/** /**
* Página de Consulta de Pedidos * Página de Consulta de Pedidos
@@ -161,6 +162,36 @@ export default function FindOrdersPage() {
/> />
</div> </div>
<div className="filter-field">
<ProductSearchInput
id="productSearch"
label="Produto"
placeholder="Buscar por código, nome ou descrição"
value={searchParams.productId || ""}
onChange={(value) => handleInputChange("productId", value)}
onSelect={(product) => {
if (product) {
handleInputChange("productId", product.id);
setSearchParams(prev => ({
...prev,
selectedProduct: product
}));
} else {
handleInputChange("productId", "");
setSearchParams(prev => ({
...prev,
selectedProduct: null
}));
}
}}
selectedProduct={searchParams.selectedProduct || null}
aria-label="Buscar Produto"
/>
</div>
<div className="flex gap-4"> <div className="flex gap-4">
<div className="filter-small-field"> <div className="filter-small-field">
<FilterInput <FilterInput
@@ -413,8 +444,6 @@ export default function FindOrdersPage() {
visibleOrdersCount={visibleOrdersCount} visibleOrdersCount={visibleOrdersCount}
stores={stores} stores={stores}
transfers={[]} transfers={[]}
status={status}
leadtime={leadtime}
/> />
</CardContent> </CardContent>
</Card> </Card>

View File

@@ -20,41 +20,44 @@ import { Badge } from "../../components/ui/badge";
import { ordersApi } from "../../lib/api"; import { ordersApi } from "../../lib/api";
/** /**
* Represents a timeline event in an order's lifecycle * Representa um evento na linha do tempo do ciclo de vida de um pedido
* @typedef {Object} Leadtime
* @property {string} etapas - Descrição da etapa
* @property {string} descricao - Descrição detalhada do evento
* @property {string} date - Data e hora do evento
* @property {string} codigoFuncionario - ID do funcionário responsável
* @property {string} nomeFuncionario - Nome do funcionário responsável
* @property {string} numeroPedido - Número do pedido associado
* @property {LucideIcon} [icon] - Ícone para o evento
* @property {string} [status] - Rótulo de status do evento
* @property {string} [color] - Tema de cor do evento
*/ */
interface Leadtime { interface Leadtime {
/** Description of the stage/step */
etapas: string; etapas: string;
/** Detailed description of the event */
descricao: string; descricao: string;
/** Date and time of the event */
date: string; date: string;
/** Employee ID who handled this step */
codigoFuncionario: string; codigoFuncionario: string;
/** Employee name who handled this step */
nomeFuncionario: string; nomeFuncionario: string;
/** Order number associated with this event */
numeroPedido: string; numeroPedido: string;
/** Icon to display for this event */
icon?: LucideIcon; icon?: LucideIcon;
/** Status label for this event */
status?: string; status?: string;
/** Color theme for this event */
color?: string; color?: string;
} }
/** /**
* Props for the Timeline component * Propriedades do componente Timeline
* @typedef {Object} TimelineProps
* @property {string} [orderId] - ID do pedido para buscar dados da linha do tempo
* @property {string} [className] - Classe CSS opcional
*/ */
interface TimelineProps { interface TimelineProps {
/** Order ID to fetch timeline data for */
orderId?: string; orderId?: string;
/** Optional CSS class name */
className?: string; className?: string;
} }
/** /**
* Configuration for different order status types * Configuração para diferentes tipos de status do pedido
* @type {Record<string, {icon: LucideIcon, color: string, status: string}>}
*/ */
const statusConfig: Record< const statusConfig: Record<
string, string,
@@ -75,7 +78,8 @@ const statusConfig: Record<
}; };
/** /**
* Keywords used to match description text to status types * Palavras-chave usadas para mapear descrições de etapas para tipos de status
* @type {Object}
*/ */
const statusKeywords = { const statusKeywords = {
pedido: ['venda realizada', 'pedido', 'receb'], pedido: ['venda realizada', 'pedido', 'receb'],
@@ -87,9 +91,9 @@ const statusKeywords = {
}; };
/** /**
* Determines the status configuration based on the event description * Determina a configuração de status com base na descrição do evento
* @param descricao - The description text to analyze * @param {string} descricao - Texto da descrição a ser analisado
* @returns The matching status configuration object * @returns {{icon: LucideIcon, color: string, status: string}} Objeto de configuração de status correspondente
*/ */
function getStatusConfig(descricao: string): { icon: LucideIcon; color: string; status: string } { function getStatusConfig(descricao: string): { icon: LucideIcon; color: string; status: string } {
const desc = descricao?.toLowerCase().trim() || ""; const desc = descricao?.toLowerCase().trim() || "";
@@ -102,9 +106,9 @@ function getStatusConfig(descricao: string): { icon: LucideIcon; color: string;
} }
/** /**
* Formats a date string to localized date and time format * Formata uma string de data para o formato local brasileiro (DD/MM/AAAA HH:MM)
* @param dateString - The date string to format * @param {string} dateString - String de data a ser formatada
* @returns Formatted date string in Brazilian format (DD/MM/YYYY HH:MM) * @returns {string} Data formatada
*/ */
function formatDate(dateString: string): string { function formatDate(dateString: string): string {
try { try {
@@ -123,9 +127,9 @@ function formatDate(dateString: string): string {
} }
/** /**
* Formats a date string to show only the time portion * Formata uma string de data para exibir apenas o horário (HH:MM)
* @param dateString - The date string to format * @param {string} dateString - String de data a ser formatada
* @returns Formatted time string in Brazilian format (HH:MM) * @returns {string} Horário formatado
*/ */
function formatTimeOnly(dateString: string): string { function formatTimeOnly(dateString: string): string {
try { try {
@@ -141,10 +145,10 @@ function formatTimeOnly(dateString: string): string {
} }
/** /**
* Maps API data to the Leadtime interface with proper status configuration * Mapeia os dados da API para o formato Leadtime com configuração de status
* @param apiData - Raw API data * @param {any[]} apiData - Dados brutos da API
* @param defaultOrderId - Fallback order ID if not present in API data * @param {string} [defaultOrderId] - ID do pedido padrão caso não esteja presente nos dados
* @returns Array of processed Leadtime objects * @returns {Leadtime[]} Array de objetos Leadtime processados
*/ */
function mapApiDataToLeadtimes(apiData: any[], defaultOrderId: string = ""): Leadtime[] { function mapApiDataToLeadtimes(apiData: any[], defaultOrderId: string = ""): Leadtime[] {
return apiData.map((item) => { return apiData.map((item) => {
@@ -164,7 +168,9 @@ function mapApiDataToLeadtimes(apiData: any[], defaultOrderId: string = ""): Lea
} }
/** /**
* Timeline component that displays the history of events for an order * Componente Timeline que exibe o histórico de eventos de um pedido
* @param {TimelineProps} props
* @returns {JSX.Element}
*/ */
export default function Timeline({ orderId = "", className }: TimelineProps) { export default function Timeline({ orderId = "", className }: TimelineProps) {
const [leadtimes, setLeadtimes] = useState<Leadtime[]>([]); const [leadtimes, setLeadtimes] = useState<Leadtime[]>([]);

View File

@@ -38,6 +38,7 @@ export type ColumnKey =
| "Motorista" | "Motorista"
| "Placa" | "Placa"
const ALL_COLUMNS: ColumnKey[] = [ const ALL_COLUMNS: ColumnKey[] = [
"Data", "Data previsão de entrega", "Agendamento", "Pedido", "Tipo Pedido", "Cliente", "Vendedor", "Filial", "Situação", "Dt Carregamento", "Rota", "Valor", "Peso(kg)", "Carregamento", "Cobrança", "Trasportadora", "Praça", "Nota fiscal", "Dt Faturamento", "TV 7", "Motorista", "Placa" "Data", "Data previsão de entrega", "Agendamento", "Pedido", "Tipo Pedido", "Cliente", "Vendedor", "Filial", "Situação", "Dt Carregamento", "Rota", "Valor", "Peso(kg)", "Carregamento", "Cobrança", "Trasportadora", "Praça", "Nota fiscal", "Dt Faturamento", "TV 7", "Motorista", "Placa"
]; ];
@@ -87,12 +88,10 @@ export function OrdersTable({
}: OrdersTableProps) { }: OrdersTableProps) {
const { getStoreName } = useStoreNameResolver(stores); const { getStoreName } = useStoreNameResolver(stores);
// Find the selected order based on selectedOrderId
const selectedOrder = useMemo(() => const selectedOrder = useMemo(() =>
selectedOrderId ? orders.find(order => order.orderId === selectedOrderId) : undefined selectedOrderId ? orders.find(order => order.orderId === selectedOrderId) : undefined
, [selectedOrderId, orders]); , [selectedOrderId, orders]);
// Use custom hook for order-related data fetching
const { const {
orderStatus, orderStatus,
orderLeadtime, orderLeadtime,
@@ -108,7 +107,6 @@ export function OrdersTable({
"Peso(kg)": (order: Order) => parseFloat(String(order.totalWeigth || "0")), "Peso(kg)": (order: Order) => parseFloat(String(order.totalWeigth || "0")),
"Dt Faturamento": (order: Order) => new Date(order.invoiceDate || ""), "Dt Faturamento": (order: Order) => new Date(order.invoiceDate || ""),
"Dt Carregamento": (order: Order) => new Date(order.shipmentDate || ""), "Dt Carregamento": (order: Order) => new Date(order.shipmentDate || ""),
// Add other column accessors as needed
}), []); }), []);
// Use our custom sort hook // Use our custom sort hook

View File

@@ -7,6 +7,16 @@ import {
import { OrderRowExpandable } from "./tabela-pedidos/components/OrderRowExpandable"; import { OrderRowExpandable } from "./tabela-pedidos/components/OrderRowExpandable";
import { Order } from "@/src/components/types"; import { Order } from "@/src/components/types";
/**
* Propriedades para o componente OrdersTableBody
* @typedef {Object} OrdersTableBodyProps
* @property {Order[]} orders - Lista completa de pedidos
* @property {Order[]} currentOrders - Lista de pedidos filtrados/paginados
* @property {string|null} selectedOrderId - ID do pedido selecionado
* @property {string[]} visibleColumns - Colunas visíveis na tabela
* @property {(storeId?: string) => string} getStoreName - Função para obter o nome da loja
* @property {(orderId: string) => void} handleRowClick - Função chamada ao clicar em uma linha
*/
interface OrdersTableBodyProps { interface OrdersTableBodyProps {
orders: Order[]; orders: Order[];
currentOrders: Order[]; currentOrders: Order[];
@@ -16,7 +26,11 @@ interface OrdersTableBodyProps {
handleRowClick: (orderId: string) => void; handleRowClick: (orderId: string) => void;
} }
// Componente de linha vazia memoizado /**
* Linha vazia exibida quando não há pedidos
* @param {{ colSpan: number }} props
* @returns {JSX.Element}
*/
const EmptyRow = memo(({ colSpan }: { colSpan: number }) => ( const EmptyRow = memo(({ colSpan }: { colSpan: number }) => (
<TableRow> <TableRow>
<TableCell colSpan={colSpan} className="h-24 text-center text-xs"> <TableCell colSpan={colSpan} className="h-24 text-center text-xs">
@@ -25,7 +39,11 @@ const EmptyRow = memo(({ colSpan }: { colSpan: number }) => (
</TableRow> </TableRow>
)); ));
// Componente de linha de pedido memoizado /**
* Linha de pedido individual, expansível
* @param {{ order: Order, isSelected: boolean, visibleColumns: string[], onSelect: (orderId: string) => void, getStoreName: (storeId?: string) => string }} props
* @returns {JSX.Element}
*/
const OrderRow = memo(({ const OrderRow = memo(({
order, order,
isSelected, isSelected,
@@ -39,11 +57,11 @@ const OrderRow = memo(({
onSelect: (orderId: string) => void; onSelect: (orderId: string) => void;
getStoreName: (storeId?: string) => string; getStoreName: (storeId?: string) => string;
}) => { }) => {
// Create a new order object with the store name only (without ID prefix) // Cria um novo objeto de pedido com o nome da loja
const orderWithStore = { const orderWithStore = {
...order, ...order,
store: getStoreName(order.storeId), store: getStoreName(order.storeId),
storeId: order.storeId, // Preserve original storeId storeId: order.storeId, // Mantém o storeId original
}; };
return ( return (
@@ -60,7 +78,11 @@ const OrderRow = memo(({
); );
}); });
// Componente principal memoizado /**
* Corpo da tabela de pedidos, renderiza linhas de pedidos ou linha vazia
* @param {OrdersTableBodyProps} props
* @returns {JSX.Element}
*/
function OrdersTableBodyComponent({ function OrdersTableBodyComponent({
orders, orders,
currentOrders, currentOrders,
@@ -69,7 +91,7 @@ function OrdersTableBodyComponent({
getStoreName, getStoreName,
handleRowClick, handleRowClick,
}: OrdersTableBodyProps) { }: OrdersTableBodyProps) {
// Renderização condicional baseada na presença de orders, memoizada // Renderização condicional baseada na presença de pedidos
const content = useMemo(() => { const content = useMemo(() => {
if (orders.length === 0) { if (orders.length === 0) {
return <EmptyRow colSpan={visibleColumns.length} />; return <EmptyRow colSpan={visibleColumns.length} />;
@@ -90,4 +112,8 @@ function OrdersTableBodyComponent({
return <TableBody>{content}</TableBody>; return <TableBody>{content}</TableBody>;
} }
/**
* Exporta o componente memoizado do corpo da tabela de pedidos
* @type {React.NamedExoticComponent<OrdersTableBodyProps>}
*/
export const OrdersTableBody = memo(OrdersTableBodyComponent); export const OrdersTableBody = memo(OrdersTableBodyComponent);

View File

@@ -0,0 +1,256 @@
"use client";
import * as React from "react";
import { Check, ChevronsUpDown, Package, Loader2, X } from "lucide-react";
import { cn } from "@/src/lib/utils";
import { Button } from "@/src/components/ui/button";
import {
Command,
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
CommandList,
} from "@/src/components/ui/command";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/src/components/ui/popover";
import { dataConsultApi } from "@/src/lib/api";
import { Product } from "@/src/components/types";
import { Label } from "@/src/components/ui/label";
import { Badge } from "@/src/components/ui/badge";
interface ProductSearchInputProps {
id: string;
label: string;
placeholder: string;
value: string;
onChange: (value: string) => void;
onSelect: (product: Product | null) => void;
selectedProduct: Product | null;
disabled?: boolean;
"aria-label"?: string;
}
/**
* Componente de busca de produtos com autocompletar
* Permite buscar produtos por código, nome ou descrição
*/
export function ProductSearchInput({
id,
label,
placeholder,
value,
onChange,
onSelect,
selectedProduct,
disabled = false,
"aria-label": ariaLabel,
}: ProductSearchInputProps) {
const [open, setOpen] = React.useState(false);
const [products, setProducts] = React.useState<Product[]>([]);
const [loading, setLoading] = React.useState(false);
const [searchQuery, setSearchQuery] = React.useState("");
const [searchError, setSearchError] = React.useState<string | null>(null);
const searchInputRef = React.useRef<HTMLInputElement>(null);
// Buscar produtos baseado na query
const searchProducts = React.useCallback(async (query: string) => {
if (!query || query.length < 2) {
setProducts([]);
return;
}
try {
setLoading(true);
setSearchError(null);
const response = await dataConsultApi.getProducts(query);
if (response.success && response.data && response.data.length > 0) {
// Mapear os dados da API para o formato Product
const mappedProducts = response.data.map((product: any) => ({
id: product.id?.toString() || "",
code: product.manufacturerCode || "",
name: product.description || product.name || "",
description: product.description || product.name || "",
brand: product.brand || "",
department: product.department || "",
color: product.color || "",
}));
setProducts(mappedProducts.slice(0, 50)); // Limitar a 50 resultados
} else {
setSearchError(`Nenhum produto encontrado para "${query}"`);
setProducts([]);
}
} catch (error) {
console.error("Erro ao buscar produtos:", error);
setSearchError("Erro ao buscar produtos");
setProducts([]);
} finally {
setLoading(false);
}
}, []);
// Debounce para a busca
React.useEffect(() => {
const timer = setTimeout(() => {
if (open && searchQuery.length >= 2) {
searchProducts(searchQuery);
}
}, 300);
return () => clearTimeout(timer);
}, [searchQuery, searchProducts, open]);
// Selecionar produto
const handleSelectProduct = (product: Product) => {
onSelect(product);
setOpen(false);
setSearchQuery("");
};
// Limpar seleção
const handleClearSelection = (e?: React.MouseEvent) => {
if (e) {
e.stopPropagation();
}
onSelect(null);
onChange("");
setSearchQuery("");
setProducts([]);
};
// Quando o componente monta ou o valor muda
React.useEffect(() => {
// Se temos um value mas não temos selectedProduct, executar busca inicial
if (value && !selectedProduct && !loading && !open) {
searchProducts(value);
}
}, [value, selectedProduct, searchProducts, loading, open]);
return (
<div className="flex flex-col space-y-1">
<div className="flex justify-between items-center">
<Label
htmlFor={id}
className="text-xs font-medium"
>
{label}
{selectedProduct && (
<Badge variant="outline" className="ml-2 text-xs py-0">
{selectedProduct.code ? `Cód: ${selectedProduct.code}` : `ID: ${selectedProduct.id}`}
</Badge>
)}
</Label>
</div>
{searchError && (
<p className="text-xs text-red-500 mt-0.5">{searchError}</p>
)}
<div className="relative">
<Popover open={open} onOpenChange={setOpen}>
<PopoverTrigger asChild>
<Button
variant="outline"
role="combobox"
aria-expanded={open}
aria-label={ariaLabel || "Selecione um produto"}
disabled={disabled}
className={cn(
"w-full justify-between h-8 transition-all",
selectedProduct ? "pr-8" : "text-muted-foreground",
disabled && "opacity-50 cursor-not-allowed"
)}
>
{selectedProduct ? (
<div className="flex items-center gap-2 overflow-hidden">
<Package className="h-4 w-4 shrink-0" />
<span className="truncate">
{selectedProduct.name}
{selectedProduct.code ? ` (${selectedProduct.code})` : ""}
</span>
</div>
) : (
<span className="truncate">{placeholder}</span>
)}
{selectedProduct ? null : (
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
)}
</Button>
</PopoverTrigger>
{selectedProduct && (
<div
className="absolute right-0 top-0 h-full flex items-center pr-2"
onClick={(e) => {
e.stopPropagation();
handleClearSelection();
}}
>
<X className="h-4 w-4 cursor-pointer" />
<span className="sr-only">Limpar seleção</span>
</div>
)}
<PopoverContent className="min-w-[400px] p-0">
<Command>
<CommandInput
ref={searchInputRef}
placeholder="Buscar produto por código, nome ou descrição..."
value={searchQuery}
onValueChange={setSearchQuery}
className="h-9"
/>
{loading && (
<div className="flex items-center justify-center py-6">
<Loader2 className="h-6 w-6 animate-spin text-gray-400" />
</div>
)}
{!loading && (
<>
<CommandEmpty>
{searchQuery.length >= 2
? "Nenhum produto encontrado com esse termo"
: "Digite pelo menos 2 caracteres para buscar"}
</CommandEmpty>
{products.length > 0 && (
<CommandGroup heading="Produtos">
<CommandList>
{products.map((product, index) => (
<CommandItem
key={product.id || product.code || `product-${index}`}
value={product.name}
onSelect={() => handleSelectProduct(product)}
>
<Package className="mr-2 h-4 w-4" />
<div className="flex flex-col">
<span className="font-medium">{product.name}</span>
<div className="flex gap-2 text-xs text-gray-500">
{product.code && (
<span>Cód: {product.code}</span>
)}
{product.brand && (
<span>Marca: {product.brand}</span>
)}
{product.department && (
<span>Depto: {product.department}</span>
)}
</div>
</div>
</CommandItem>
))}
</CommandList>
</CommandGroup>
)}
</>
)}
</Command>
</PopoverContent>
</Popover>
</div>
</div>
);
}

View File

@@ -13,11 +13,23 @@ export interface TimelineEvent {
description: string; description: string;
date: string | Date; date: string | Date;
status?: string; status?: string;
icon?: LucideIcon; icon?: LucideIcon;
color?: string; color?: string;
user?: string; user?: string;
} }
export interface productsSearchParams {
productId?: string;
description?: string;
pacth?: string;
color?: string;
stockId?: string;
quantity?: string;
salePrice?: string;
department?: string;
brand?: string;
}
export interface cutitens { export interface cutitens {
productId: number; productId: number;
description: string; description: string;
@@ -81,7 +93,7 @@ export interface Order {
sellerId: string; sellerId: string;
stockId: string; stockId: string;
storeName: string; storeName: string;
store?: string; store?: string;
createDate: string | Date; createDate: string | Date;
updateDate?: string | Date; updateDate?: string | Date;
status?: string; status?: string;
@@ -159,7 +171,7 @@ export interface ClientData {
totalPurchases: number; totalPurchases: number;
} }
export type OrderStatus = export type OrderStatus =
| 'Aguardando pagamento' | 'Aguardando pagamento'
| 'Pagamento aprovado' | 'Pagamento aprovado'
| 'Em separação' | 'Em separação'
@@ -179,13 +191,13 @@ export type PaymentStatus =
name: string; name: string;
document: string; document: string;
}; };
export interface LoginDto { export interface LoginDto {
username: string; username: string;
password: string; password: string;
} }
export interface LoginResponseDto { export interface LoginResponseDto {
id: number; id: number;
sellerId: number; sellerId: number;
@@ -201,9 +213,19 @@ export type PaymentStatus =
name?: string; name?: string;
document?: string; document?: string;
} }
export interface Product {
id: string;
code: string;
name: string;
description?: string;
brand?: string;
department?: string;
color?: string;
}
/** /**
* Interface para parâmetros de pesquisa de pedidos * Interface para parâmetros de pesquisa de pedidos
* Define todos os campos possíveis usados no formulário de busca * Define todos os campos possíveis usados no formulário de busca
@@ -241,6 +263,8 @@ export interface OrderSearchParams {
deliveryLocal?: string; deliveryLocal?: string;
/** ID do vendedor */ /** ID do vendedor */
sellerId?: string | number; sellerId?: string | number;
/** Nome do vendedor */
sellerName?: string;
/** Documento do cliente (CPF/CNPJ) - duplicado para compatibilidade */ /** Documento do cliente (CPF/CNPJ) - duplicado para compatibilidade */
document?: string; document?: string;
/** Número da nota fiscal */ /** Número da nota fiscal */
@@ -251,6 +275,10 @@ export interface OrderSearchParams {
invoiceId?: string; invoiceId?: string;
/** Número da nota fiscal (NFe) */ /** Número da nota fiscal (NFe) */
nfe?: string; nfe?: string;
/** ID do produto para busca */
productId?: string;
/** Produto selecionado pelo componente de busca */
selectedProduct?: Product | null;
} }
export interface CustomerSearchParams { export interface CustomerSearchParams {

View File

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

View File

@@ -2,7 +2,10 @@ import { leadtime } from "../components/types";
import { Cliente } from "../components/types"; import { Cliente } from "../components/types";
// URL base da API // URL base da API
const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL || "https://portalapi.jurunense.com"; //const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL || "https://portalapi.jurunense.com";
const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL || "http://10.1.1.212:8805";
/** /**
* Classe de erro personalizada para requisições de API. * Classe de erro personalizada para requisições de API.
@@ -29,7 +32,7 @@ export class ApiError extends Error {
*/ */
export async function fetchApi<T>(endpoint: string, options?: RequestInit): Promise<T> { export async function fetchApi<T>(endpoint: string, options?: RequestInit): Promise<T> {
const url = `${API_BASE_URL}${endpoint}`; const url = `${API_BASE_URL}${endpoint}`;
// Obtém o token de autenticação do localStorage // Obtém o token de autenticação do localStorage
let authToken = null; let authToken = null;
try { try {
@@ -43,8 +46,8 @@ export async function fetchApi<T>(endpoint: string, options?: RequestInit): Prom
"Content-Type": "application/json", "Content-Type": "application/json",
...(options?.headers as Record<string, string>), ...(options?.headers as Record<string, string>),
}; };
if (authToken) { if (authToken) {
headers["Authorization"] = `Bearer ${authToken}`; headers["Authorization"] = `Bearer ${authToken}`;
} }
@@ -98,7 +101,7 @@ export const dataConsultApi = {
getStores: () => fetchApi<any[]>("/api/v1/data-consult/stores"), getStores: () => fetchApi<any[]>("/api/v1/data-consult/stores"),
/** @returns {Promise<any[]>} Lista de vendedores */ /** @returns {Promise<any[]>} Lista de vendedores */
getSellers: () => getSellers: () =>
fetchApi<any>("/api/v1/data-consult/sellers") fetchApi<any>("/api/v1/data-consult/sellers")
.then(response => { .then(response => {
// Tentar extrair dados de vendedores em diferentes formatos // Tentar extrair dados de vendedores em diferentes formatos
@@ -126,7 +129,7 @@ export const dataConsultApi = {
* @param {string} name - Nome do vendedor para filtrar * @param {string} name - Nome do vendedor para filtrar
* @returns {Promise<any[]>} Lista de vendedores filtrados * @returns {Promise<any[]>} Lista de vendedores filtrados
*/ */
searchSellers: (name: string) => searchSellers: (name: string) =>
fetchApi<any>(`/api/v1/data-consult/sellers/search?name=${encodeURIComponent(name)}`) fetchApi<any>(`/api/v1/data-consult/sellers/search?name=${encodeURIComponent(name)}`)
.then(response => { .then(response => {
// Tratar a resposta de forma similar a getSellers // Tratar a resposta de forma similar a getSellers
@@ -179,10 +182,10 @@ export const dataConsultApi = {
/** /**
* @param {string} filter - Filtro de produto (nome ou código). * @param {string} filter - Filtro de produto (nome ou código).
* @returns {Promise<any[]>} Produtos filtrados. * @returns {Promise<{success: boolean, data: any[]}>} Produtos filtrados.
*/ */
getProducts: (filter: string) => getProducts: (filter: string) =>
fetchApi<any[]>(`/api/v1/data-consult/products/${filter}`), fetchApi<{ success: boolean; data: any[] }>(`/api/v1/data-consult/products/${filter}`),
/** @returns {Promise<any[]>} Todos os produtos */ /** @returns {Promise<any[]>} Todos os produtos */
getAllProducts: () => fetchApi<any[]>("/api/v1/data-consult/all"), getAllProducts: () => fetchApi<any[]>("/api/v1/data-consult/all"),