From 2323f6370c0c6c146c47271d8b6df2225f973efa Mon Sep 17 00:00:00 2001 From: Joelson de brito ribeiro Date: Fri, 4 Jul 2025 16:27:54 -0300 Subject: [PATCH] Ajuste na coluna de pedidos --- src/components/orders/OrderDetail.tsx | 1 + src/components/orders/OrderItemsTable.tsx | 197 ++-------------- src/components/orders/OrdersTable.tsx | 13 +- src/components/orders/OrdersTableHeader.tsx | 2 +- .../components/cell-renderers.tsx | 3 + .../tabela-pedidos/domain/column-classes.ts | 3 + .../tabela-pedidos/domain/order-columns.ts | 5 +- src/components/ui/ClearAndNavigateButton.tsx | 29 +++ src/components/ui/NewColumnsNotification.tsx | 37 +++ src/hooks/useNavigation.ts | 12 + src/hooks/useOrderItemTableColumns.ts | 52 +++++ src/hooks/useOrderSearch.ts | 13 ++ src/hooks/useOrderTableColumns.ts | 189 +-------------- src/hooks/useTableColumns.ts | 217 ++++++++++++++++++ src/lib/api.ts | 2 +- src/utils/url-helpers.ts | 29 +++ 16 files changed, 448 insertions(+), 356 deletions(-) create mode 100644 src/components/ui/ClearAndNavigateButton.tsx create mode 100644 src/components/ui/NewColumnsNotification.tsx create mode 100644 src/hooks/useOrderItemTableColumns.ts create mode 100644 src/hooks/useTableColumns.ts diff --git a/src/components/orders/OrderDetail.tsx b/src/components/orders/OrderDetail.tsx index c76dfe8..bc7711c 100644 --- a/src/components/orders/OrderDetail.tsx +++ b/src/components/orders/OrderDetail.tsx @@ -230,6 +230,7 @@ export function OrderDetail({ order, timelineEvents }: OrderDetailProps) { } else if (window.history.length > 2) { goBack(); } else { + // Usar a função que limpa parâmetros ao navegar para orders/find goToOrdersFind(); } }; diff --git a/src/components/orders/OrderItemsTable.tsx b/src/components/orders/OrderItemsTable.tsx index dc7f9f8..26a8e4c 100644 --- a/src/components/orders/OrderItemsTable.tsx +++ b/src/components/orders/OrderItemsTable.tsx @@ -19,6 +19,8 @@ import { DropdownMenuTrigger, DropdownMenuCheckboxItem, } from "@/src/components/ui/dropdown-menu"; +import { useOrderItemTableColumns } from "@/src/hooks/useOrderItemTableColumns"; +import { NewColumnsNotification } from "@/src/components/ui/NewColumnsNotification"; interface Item { quantity: number | null | undefined; @@ -58,180 +60,23 @@ export function OrderItemsTable({ itemsError, onViewDetails, }: OrderItemsTableProps) { - // Carregar as colunas visíveis do localStorage na inicialização - const initVisibleColumns = () => { - try { - const saved = localStorage.getItem('visibleOrderItemColumns'); - if (saved) { - const parsed = JSON.parse(saved); - if (Array.isArray(parsed) && parsed.length > 0) { - return parsed; - } - } - } catch (error) { - console.error('Erro ao carregar configurações de colunas:', error); - } - return ALL_COLUMNS; - }; - - // Inicializar a ordem das colunas - const initColumnOrder = () => { - try { - const saved = localStorage.getItem('orderItemColumnsOrder'); - if (saved) { - const parsed = JSON.parse(saved); - if (Array.isArray(parsed) && parsed.length > 0) { - return parsed; - } - } - } catch (error) { - console.error('Erro ao carregar ordem das colunas:', error); - } - return ALL_COLUMNS; - }; - - // Estado para controlar quais colunas estão visíveis - const [visibleColumns, setVisibleColumns] = useState(initVisibleColumns); - // Estado para controlar a ordem das colunas - const [columnOrder, setColumnOrder] = useState(initColumnOrder); - // Estado para rastrear coluna sendo arrastada - const [draggedColumn, setDraggedColumn] = useState(null); - - // Salvar as colunas visíveis no localStorage sempre que elas mudarem - useEffect(() => { - try { - localStorage.setItem('visibleOrderItemColumns', JSON.stringify(visibleColumns)); - } catch (error) { - console.error('Erro ao salvar configurações de colunas:', error); - } - }, [visibleColumns]); - - // Salvar a ordem das colunas no localStorage - useEffect(() => { - try { - localStorage.setItem('orderItemColumnsOrder', JSON.stringify(columnOrder)); - } catch (error) { - console.error('Erro ao salvar ordem das colunas:', error); - } - }, [columnOrder]); - - // Função para alternar a visibilidade de uma coluna - const toggleColumn = (column: string) => { - setVisibleColumns((current) => { - if (current.includes(column)) { - return current.filter((c) => c !== column); - } else { - return [...current, column]; - } - }); - }; - - // Função para selecionar todas as colunas - const selectAllColumns = () => { - setVisibleColumns(ALL_COLUMNS); - }; - - // Função para desmarcar todas as colunas - const unselectAllColumns = () => { - // Mantém pelo menos uma coluna para garantir que a tabela não fique vazia - setVisibleColumns(["Código"]); - }; - - // Função para verificar se uma coluna está visível - const isColumnVisible = (column: string) => visibleColumns.includes(column); - - // Funções para drag & drop - const handleDragStart = (e: React.DragEvent, column: string) => { - setDraggedColumn(column); - e.dataTransfer.effectAllowed = 'move'; - e.dataTransfer.setData('text/plain', column); - - // Adicionar um estilo visual para o elemento sendo arrastado - if (e.currentTarget) { - setTimeout(() => { - if (e.currentTarget) { - e.currentTarget.classList.add('opacity-50'); - } - }, 0); - } - }; - - const handleDragEnd = (e: React.DragEvent) => { - setDraggedColumn(null); - e.currentTarget.classList.remove('opacity-50'); - }; - - const handleDragOver = (e: React.DragEvent) => { - e.preventDefault(); - e.dataTransfer.dropEffect = 'move'; - }; - - const handleDrop = (e: React.DragEvent, targetColumn: string) => { - e.preventDefault(); - - if (!draggedColumn || draggedColumn === targetColumn) return; - - // Reordenar as colunas - const newColumnOrder = [...columnOrder]; - const draggedIndex = newColumnOrder.indexOf(draggedColumn); - const targetIndex = newColumnOrder.indexOf(targetColumn); - - if (draggedIndex !== -1 && targetIndex !== -1) { - newColumnOrder.splice(draggedIndex, 1); - newColumnOrder.splice(targetIndex, 0, draggedColumn); - setColumnOrder(newColumnOrder); - } - }; - - const getColumnClass = (title: string) => { - const textRight = ["Preço Unitário", "Preço Total", "Quantidade"]; - const cellWidth: { [key: string]: string } = { - "Código": "w-20", - "Descrição": "w-56", - "Embalagem": "w-24", - "Cor": "w-20", - "F. Estoque": "w-24", - "Tipo Entrega": "w-28", - "Quantidade": "w-24", - "Preço Unitário": "w-24", - "Preço Total": "w-24", - "Departamento": "w-48", - "Marca": "w-24", - }; - - let classes = ""; - - // Não aplicar whitespace-nowrap para Departamento - if (title !== "Departamento") { - classes += "whitespace-nowrap "; - } - - // Alinhar à direita - if (textRight.includes(title)) { - classes += "text-right "; - } - - // Aplicar larguras - if (cellWidth[title]) { - classes += cellWidth[title] + " "; - } - - return classes; - }; - - // Função para ordenar as colunas visíveis na ordem definida pelo usuário - const getOrderedVisibleColumns = () => { - // Primeiro, filtrar as colunas que são visíveis - const visibleColumnSet = new Set(visibleColumns); - - // Depois, ordenar as colunas de acordo com a ordem definida pelo usuário - return columnOrder.filter(column => visibleColumnSet.has(column)); - }; - - // Resetar a ordem das colunas - const resetColumnOrder = () => { - setColumnOrder(ALL_COLUMNS); - }; + const { + visibleColumns, + columnOrder, + getColumnClass, + getOrderedVisibleColumns, + resetColumnOrder, + toggleColumn, + selectAllColumns, + unselectAllColumns, + isColumnVisible, + handleDragStart, + handleDragEnd, + handleDragOver, + handleDrop, + newColumnsDetected, + dismissNewColumnsNotification, + } = useOrderItemTableColumns(ALL_COLUMNS); const handleEvent = (e: React.SyntheticEvent) => { const element = e.currentTarget; @@ -261,6 +106,10 @@ const handleEvent = (e: React.SyntheticEvent) => { return (
+
diff --git a/src/components/orders/OrdersTable.tsx b/src/components/orders/OrdersTable.tsx index 4831dbf..9c060a0 100644 --- a/src/components/orders/OrdersTable.tsx +++ b/src/components/orders/OrdersTable.tsx @@ -12,6 +12,7 @@ import { SelectedOrderItems } from "./SelectedOrderItems"; import { RefreshCw, ChevronLeft, ChevronRight } from "lucide-react"; import { useOrderData } from "../../../src/hooks/useOrderData"; import { useSort } from "@/src/hooks/useSort"; +import { NewColumnsNotification } from "@/src/components/ui/NewColumnsNotification"; export type ColumnKey = | "Data" @@ -28,14 +29,17 @@ export type ColumnKey = | "Valor" | "Peso(kg)" | "Carregamento" + | "Dt Carregamento" | "Cobrança" | "Trasportadora" | "Praça" | "Nota fiscal" | "Dt Faturamento" + | "Motorista" + | "Placa" const ALL_COLUMNS: ColumnKey[] = [ - "Data", "Data previsão de entrega", "Agendamento", "Pedido", "Tipo Pedido", "Cliente", "Vendedor", "Filial", "Situação", "Rota", "Valor", "Peso(kg)", "Carregamento", "Cobrança", "Trasportadora", "Praça", "Nota fiscal", "Dt Faturamento", "TV 7" + "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" ]; export interface OrdersTableProps { @@ -103,6 +107,7 @@ export function OrdersTable({ "Valor": (order: Order) => parseFloat(String(order.totalValue || "0")), "Peso(kg)": (order: Order) => parseFloat(String(order.totalWeigth || "0")), "Dt Faturamento": (order: Order) => new Date(order.invoiceDate || ""), + "Dt Carregamento": (order: Order) => new Date(order.shipmentDate || ""), // Add other column accessors as needed }), []); @@ -126,6 +131,8 @@ export function OrdersTable({ handleDragEnd, handleDragOver, handleDrop, + newColumnsDetected, + dismissNewColumnsNotification, } = useOrderTableColumns(ALL_COLUMNS); const { tableRef } = useKeyboardNavigation({ @@ -248,6 +255,10 @@ export function OrdersTable({ return (
+ {renderHeaderControls()} {renderOrdersTable()} void; }) => { // Determinar se esta coluna é ordenável - const isSortable = ["Data", "Valor", "Peso(kg)", "Pedido", "Dt Faturamento"].includes(title); + const isSortable = ["Data", "Valor", "Peso(kg)", "Pedido", "Dt Faturamento", "Dt Carregamento"].includes(title); // Verificar se esta coluna está sendo ordenada const isSorted = sortConfig && sortConfig.key === title; diff --git a/src/components/orders/tabela-pedidos/components/cell-renderers.tsx b/src/components/orders/tabela-pedidos/components/cell-renderers.tsx index 5598881..dc8bfe1 100644 --- a/src/components/orders/tabela-pedidos/components/cell-renderers.tsx +++ b/src/components/orders/tabela-pedidos/components/cell-renderers.tsx @@ -82,6 +82,7 @@ export const orderCellRenderers: Record> [OrderColumn.Value]: ({ order }) => <>{formatCurrency(order.amount || 0)}, [OrderColumn.Weight]: ({ order }) => <>{order.totalWeigth}, [OrderColumn.Shipment]: ({ order }) => <>{order.shipmentId}, + [OrderColumn.ShipmentDate]: ({ order }) => <>{formatDateSafe(order.shipmentDate)}, [OrderColumn.Billing]: ({ order }) => <>{order.billingName}, [OrderColumn.Carrier]: ({ order }) => <>{order.carrier}, [OrderColumn.Place]: ({ order }) => <>{order.deliveryLocal}, @@ -89,4 +90,6 @@ export const orderCellRenderers: Record> [OrderColumn.InvoiceDate]: ({ order }) => <>{formatDateSafe(order.invoiceDate)}, [OrderColumn.DeliveryDate]: ({ order }) => <>{order.deliveryDate ? formatDateSafe(order.deliveryDate) : "-"}, [OrderColumn.SchedulerDelivery]: ({ order }) => <>{order.schedulerDelivery || "-"}, + [OrderColumn.Driver]: ({ order }) => <>{order.driver || "-"}, + [OrderColumn.CarIdentification]: ({ order }) => <>{order.carIdentification || "-"}, }; diff --git a/src/components/orders/tabela-pedidos/domain/column-classes.ts b/src/components/orders/tabela-pedidos/domain/column-classes.ts index 65de7d1..9b9bd42 100644 --- a/src/components/orders/tabela-pedidos/domain/column-classes.ts +++ b/src/components/orders/tabela-pedidos/domain/column-classes.ts @@ -15,10 +15,13 @@ export const columnClasses: Record = { [OrderColumn.Value]: "p-2 whitespace-nowrap text-right border-r border-gray-200 text-xs", [OrderColumn.Weight]: "p-2 whitespace-nowrap text-right border-r border-gray-200 text-xs", [OrderColumn.Shipment]: "p-2 whitespace-nowrap border-r border-gray-200 text-xs", + [OrderColumn.ShipmentDate]: "p-2 whitespace-nowrap border-r border-gray-200 text-xs", [OrderColumn.Billing]: "p-2 whitespace-nowrap border-r border-gray-200 text-xs", [OrderColumn.Carrier]: "p-2 whitespace-nowrap border-r border-gray-200 text-xs", [OrderColumn.Place]: "p-2 whitespace-nowrap border-r border-gray-200 text-xs", [OrderColumn.Invoice]: "p-2 whitespace-nowrap border-r border-gray-200 text-xs", [OrderColumn.InvoiceDate]: "p-2 whitespace-nowrap border-r border-gray-200 text-xs", [OrderColumn.DeliveryDate]: "p-2 whitespace-nowrap border-r border-gray-200 text-xs", + [OrderColumn.Driver]: "p-2 whitespace-nowrap border-r border-gray-200 text-xs", + [OrderColumn.CarIdentification]: "p-2 whitespace-nowrap border-r border-gray-200 text-xs", }; diff --git a/src/components/orders/tabela-pedidos/domain/order-columns.ts b/src/components/orders/tabela-pedidos/domain/order-columns.ts index b406ddb..8141dff 100644 --- a/src/components/orders/tabela-pedidos/domain/order-columns.ts +++ b/src/components/orders/tabela-pedidos/domain/order-columns.ts @@ -15,10 +15,13 @@ export enum OrderColumn { Value = "Valor", Weight = "Peso(kg)", Shipment = "Carregamento", + ShipmentDate = "Dt Carregamento", Billing = "Cobrança", Carrier = "Trasportadora", Place = "Praça", Invoice = "Nota fiscal", - InvoiceDate = "Dt Faturamento" + InvoiceDate = "Dt Faturamento", + Driver = "Motorista", + CarIdentification = "Placa" } \ No newline at end of file diff --git a/src/components/ui/ClearAndNavigateButton.tsx b/src/components/ui/ClearAndNavigateButton.tsx new file mode 100644 index 0000000..1cbe190 --- /dev/null +++ b/src/components/ui/ClearAndNavigateButton.tsx @@ -0,0 +1,29 @@ +import { Button } from "@/src/components/ui/button"; +import { useOrderSearch } from "@/src/hooks/useOrderSearch"; + +interface ClearAndNavigateButtonProps { + children: React.ReactNode; + variant?: "default" | "destructive" | "outline" | "secondary" | "ghost" | "link"; + size?: "default" | "sm" | "lg" | "icon"; + className?: string; +} + +export function ClearAndNavigateButton({ + children, + variant = "outline", + size = "default", + className +}: ClearAndNavigateButtonProps) { + const { clearAndNavigateToOrders } = useOrderSearch(8); + + return ( + + ); +} \ No newline at end of file diff --git a/src/components/ui/NewColumnsNotification.tsx b/src/components/ui/NewColumnsNotification.tsx new file mode 100644 index 0000000..a0acc3c --- /dev/null +++ b/src/components/ui/NewColumnsNotification.tsx @@ -0,0 +1,37 @@ +import { Alert, AlertDescription } from "@/src/components/ui/alert"; +import { X, Info } from "lucide-react"; +import { Button } from "@/src/components/ui/button"; + +interface NewColumnsNotificationProps { + newColumns: string[]; + onDismiss: () => void; +} + +export function NewColumnsNotification({ newColumns, onDismiss }: NewColumnsNotificationProps) { + if (newColumns.length === 0) return null; + + return ( + +
+
+ + + Novas colunas detectadas:{" "} + {newColumns.join(", ")}. + + {" "}Elas foram automaticamente adicionadas à sua visualização. + + +
+ +
+
+ ); +} \ No newline at end of file diff --git a/src/hooks/useNavigation.ts b/src/hooks/useNavigation.ts index de7d4ff..a64cfcc 100644 --- a/src/hooks/useNavigation.ts +++ b/src/hooks/useNavigation.ts @@ -27,6 +27,16 @@ export function useNavigation() { router.push("/orders/find"); }; + // Função para navegar para orders/find limpando parâmetros da URL + const goToOrdersFindClean = () => { + router.push("/orders/find"); + }; + + // Função para navegar para outras páginas limpando parâmetros + const navigateToClean = (path: string) => { + router.push(path); + }; + // Helper to build URL with search params const buildUrl = (base: string, params: Record) => { const url = new URL(base, window.location.origin); @@ -46,6 +56,8 @@ export function useNavigation() { goBack, goToHome, goToOrdersFind, + goToOrdersFindClean, + navigateToClean, buildUrl }; } \ No newline at end of file diff --git a/src/hooks/useOrderItemTableColumns.ts b/src/hooks/useOrderItemTableColumns.ts new file mode 100644 index 0000000..3922e4e --- /dev/null +++ b/src/hooks/useOrderItemTableColumns.ts @@ -0,0 +1,52 @@ +import { useCallback } from "react"; +import { useTableColumns } from "./useTableColumns"; + +export function useOrderItemTableColumns(allColumns: string[]) { + const tableColumns = useTableColumns({ + allColumns, + storageKey: "OrderItem", + defaultVisibleColumns: allColumns, + }); + + // Memoizar as classes de colunas para evitar recálculos desnecessários + const getColumnClass = useCallback((title: string) => { + const textRight = ["Preço Unitário", "Preço Total", "Quantidade"]; + const cellWidth: { [key: string]: string } = { + "Código": "w-20", + "Descrição": "w-56", + "Embalagem": "w-24", + "Cor": "w-20", + "F. Estoque": "w-24", + "Tipo Entrega": "w-28", + "Quantidade": "w-24", + "Preço Unitário": "w-24", + "Preço Total": "w-24", + "Departamento": "w-48", + "Marca": "w-24", + }; + + let classes = ""; + + // Não aplicar whitespace-nowrap para Departamento + if (title !== "Departamento") { + classes += "whitespace-nowrap "; + } + + // Alinhar à direita + if (textRight.includes(title)) { + classes += "text-right "; + } + + // Aplicar larguras + if (cellWidth[title]) { + classes += cellWidth[title] + " "; + } + + return classes; + }, []); + + return { + ...tableColumns, + getColumnClass, + }; +} \ No newline at end of file diff --git a/src/hooks/useOrderSearch.ts b/src/hooks/useOrderSearch.ts index 6384c9f..8a5071f 100644 --- a/src/hooks/useOrderSearch.ts +++ b/src/hooks/useOrderSearch.ts @@ -6,6 +6,7 @@ import { extractApiData } from "@/src/utils/api-helpers"; import { useSearchParams, useRouter } from "next/navigation"; import { clientesApi } from "@/src/lib/api" import { setStorage, getStorage } from "@/src/utils/storage"; +import { clearUrlAndNavigate } from "@/src/utils/url-helpers"; /** * Função de utilidade para aplicar debounce em funções @@ -66,6 +67,7 @@ interface UseOrderSearchReturn { handleCustomerFilter: (value: string) => Promise; setInitialOrders: (savedOrders: Order[], savedPage: number, savedHasSearched: boolean) => void; clearStoredParams: () => void; + clearAndNavigateToOrders: () => void; handleMultiInputChange: (field: keyof OrderSearchParams, values: string[]) => void; } @@ -657,6 +659,16 @@ export function useOrderSearch(ordersPerPage: number = 8): UseOrderSearchReturn setStorage("orderSearchParams", null); }, []); + // Função para limpar parâmetros e navegar para orders/find + const clearAndNavigateToOrders = useCallback(() => { + // Limpar parâmetros do localStorage + setStorage("orderSearchParams", null); + // Limpar parâmetros do estado + setSearchParams({}); + // Limpar URL e navegar + clearUrlAndNavigate("/orders/find"); + }, [setSearchParams]); + /** * Manipula a seleção de múltiplos valores em filtros de seleção * @@ -701,6 +713,7 @@ export function useOrderSearch(ordersPerPage: number = 8): UseOrderSearchReturn handleCustomerFilter, setInitialOrders, clearStoredParams, + clearAndNavigateToOrders, handleMultiInputChange }; } diff --git a/src/hooks/useOrderTableColumns.ts b/src/hooks/useOrderTableColumns.ts index 0c11e36..4ed53f8 100644 --- a/src/hooks/useOrderTableColumns.ts +++ b/src/hooks/useOrderTableColumns.ts @@ -1,87 +1,12 @@ -import { useState, useEffect, useMemo, useCallback } from "react"; -import { ChevronDown, ChevronUp } from "lucide-react"; - - - +import { useCallback } from "react"; +import { useTableColumns } from "./useTableColumns"; export function useOrderTableColumns(allColumns: string[]) { - // Carregar as colunas visíveis do localStorage na inicialização - const initVisibleColumns = () => { - try { - const saved = localStorage.getItem('visibleOrderColumns'); - if (saved) { - const parsed = JSON.parse(saved); - if (Array.isArray(parsed) && parsed.length > 0) { - return parsed; - } - } - } catch (error) { - console.error('Erro ao carregar configurações de colunas:', error); - } - return allColumns; - }; - - // Inicializar a ordem das colunas - const initColumnOrder = () => { - try { - const saved = localStorage.getItem('orderColumnsOrder'); - if (saved) { - const parsed = JSON.parse(saved); - if (Array.isArray(parsed) && parsed.length > 0) { - return parsed; - } - } - } catch (error) { - console.error('Erro ao carregar ordem das colunas:', error); - } - return allColumns; - }; - - // Estado para controlar quais colunas estão visíveis - const [visibleColumns, setVisibleColumns] = useState(initVisibleColumns); - // Estado para controlar a ordem das colunas - const [columnOrder, setColumnOrder] = useState(initColumnOrder); - // Estado para rastrear coluna sendo arrastada - const [draggedColumn, setDraggedColumn] = useState(null); - - useEffect(() => { - try { - const savedAllColumns = localStorage.getItem('allOrderColumns'); - if (savedAllColumns) { - const parsedAllColumns = JSON.parse(savedAllColumns); - if (JSON.stringify(parsedAllColumns) !== JSON.stringify(allColumns)) { - localStorage.removeItem('visibleOrderColumns'); - localStorage.removeItem('orderColumnsOrder'); - localStorage.setItem('allOrderColumns', JSON.stringify(allColumns)); - // Recarregar a página para aplicar as novas colunas - window.location.reload(); - - } - } else { - localStorage.setItem('allOrderColumns', JSON.stringify(allColumns)); - } - } catch (error) { - console.error('Erro ao verificar as colunas no localStorage:', error); - } - }, [allColumns]); - - // Salvar as colunas visíveis no localStorage sempre que elas mudarem - useEffect(() => { - try { - localStorage.setItem('visibleOrderColumns', JSON.stringify(visibleColumns)); - } catch (error) { - console.error('Erro ao salvar configurações de colunas:', error); - } - }, [visibleColumns]); - - // Salvar a ordem das colunas no localStorage - useEffect(() => { - try { - localStorage.setItem('orderColumnsOrder', JSON.stringify(columnOrder)); - } catch (error) { - console.error('Erro ao salvar ordem das colunas:', error); - } - }, [columnOrder]); + const tableColumns = useTableColumns({ + allColumns, + storageKey: "Order", + defaultVisibleColumns: allColumns, + }); // Memoizar as classes de colunas para evitar recálculos desnecessários const getColumnClass = useCallback((title: string) => { @@ -98,12 +23,15 @@ export function useOrderTableColumns(allColumns: string[]) { "Valor": "w-20", "Peso(kg)": "w-20", "Carregamento": "w-28", + "Dt Carregamento": "w-28", "Cobrança": "w-28", "Praça": "w-20", "Nota fiscal": "w-20", "Dt Faturamento": "w-28", "Rota": "w-24", "Trasportadora": "w-28", + "Motorista": "w-24", + "Placa": "w-32", }; let classes = "whitespace-nowrap "; @@ -121,103 +49,8 @@ export function useOrderTableColumns(allColumns: string[]) { return classes; }, []); - // Funções para drag & drop - const handleDragStart = useCallback((e: React.DragEvent, column: string) => { - setDraggedColumn(column); - e.dataTransfer.effectAllowed = 'move'; - e.dataTransfer.setData('text/plain', column); - - const target = e.currentTarget as HTMLDivElement; // salve a referência antes - - // aplique a classe imediatamente (sem setTimeout) - target.classList.add('opacity-50'); - }, []); - - - const handleDragEnd = useCallback((e: React.DragEvent) => { - setDraggedColumn(null); - e.currentTarget.classList.remove('opacity-50'); - }, []); - - const handleDragOver = useCallback((e: React.DragEvent) => { - e.preventDefault(); - e.dataTransfer.dropEffect = 'move'; - }, []); - - const handleDrop = useCallback((e: React.DragEvent, targetColumn: string) => { - e.preventDefault(); - - if (!draggedColumn || draggedColumn === targetColumn) return; - - // Reordenar as colunas - const newColumnOrder = [...columnOrder]; - const draggedIndex = newColumnOrder.indexOf(draggedColumn); - const targetIndex = newColumnOrder.indexOf(targetColumn); - - if (draggedIndex !== -1 && targetIndex !== -1) { - newColumnOrder.splice(draggedIndex, 1); - newColumnOrder.splice(targetIndex, 0, draggedColumn); - setColumnOrder(newColumnOrder); - } - }, [draggedColumn, columnOrder]); - - // Função para alternar a visibilidade de uma coluna - const toggleColumn = useCallback((column: string) => { - setVisibleColumns((current) => { - if (current.includes(column)) { - return current.filter((c) => c !== column); - } else { - return [...current, column]; - } - }); - }, []); - - // Função para selecionar todas as colunas - const selectAllColumns = useCallback(() => { - setVisibleColumns(allColumns); - }, [allColumns]); - - // Função para desmarcar todas as colunas - const unselectAllColumns = useCallback(() => { - // Mantém pelo menos uma coluna para garantir que a tabela não fique vazia - setVisibleColumns(["Pedido"]); - }, []); - - // Função para verificar se uma coluna está visível - const isColumnVisible = useCallback((column: string) => { - return visibleColumns.includes(column); - }, [visibleColumns]); - - // Função para ordenar as colunas visíveis na ordem definida pelo usuário - // Memoizando o resultado para evitar recálculos desnecessários - const getOrderedVisibleColumns = useCallback(() => { - // Primeiro, filtrar as colunas que são visíveis - const visibleColumnSet = new Set(visibleColumns); - - // Depois, ordenar as colunas de acordo com a ordem definida pelo usuário - return columnOrder.filter(column => visibleColumnSet.has(column)); - }, [visibleColumns, columnOrder]); - - // Resetar a ordem das colunas - const resetColumnOrder = useCallback(() => { - setColumnOrder(allColumns); - }, [allColumns]); - - - return { - visibleColumns, - columnOrder, + ...tableColumns, getColumnClass, - getOrderedVisibleColumns, - resetColumnOrder, - toggleColumn, - selectAllColumns, - unselectAllColumns, - isColumnVisible, - handleDragStart, - handleDragEnd, - handleDragOver, - handleDrop }; } \ No newline at end of file diff --git a/src/hooks/useTableColumns.ts b/src/hooks/useTableColumns.ts new file mode 100644 index 0000000..924f2e2 --- /dev/null +++ b/src/hooks/useTableColumns.ts @@ -0,0 +1,217 @@ +import { useState, useEffect, useCallback } from "react"; + +interface UseTableColumnsOptions { + allColumns: string[]; + storageKey: string; + defaultVisibleColumns?: string[]; +} + +export function useTableColumns({ + allColumns, + storageKey, + defaultVisibleColumns +}: UseTableColumnsOptions) { + // Carregar as colunas visíveis do localStorage na inicialização + const initVisibleColumns = () => { + try { + const saved = localStorage.getItem(`visible${storageKey}Columns`); + if (saved) { + const parsed = JSON.parse(saved); + if (Array.isArray(parsed) && parsed.length > 0) { + return parsed; + } + } + } catch (error) { + console.error('Erro ao carregar configurações de colunas:', error); + } + return defaultVisibleColumns || allColumns; + }; + + // Inicializar a ordem das colunas + const initColumnOrder = () => { + try { + const saved = localStorage.getItem(`${storageKey}ColumnsOrder`); + if (saved) { + const parsed = JSON.parse(saved); + if (Array.isArray(parsed) && parsed.length > 0) { + return parsed; + } + } + } catch (error) { + console.error('Erro ao carregar ordem das colunas:', error); + } + return allColumns; + }; + + // Estado para controlar quais colunas estão visíveis + const [visibleColumns, setVisibleColumns] = useState(initVisibleColumns); + // Estado para controlar a ordem das colunas + const [columnOrder, setColumnOrder] = useState(initColumnOrder); + // Estado para rastrear coluna sendo arrastada + const [draggedColumn, setDraggedColumn] = useState(null); + // Estado para notificar sobre novas colunas + const [newColumnsDetected, setNewColumnsDetected] = useState([]); + + useEffect(() => { + try { + const savedAllColumns = localStorage.getItem(`all${storageKey}Columns`); + if (savedAllColumns) { + const parsedAllColumns = JSON.parse(savedAllColumns); + if (JSON.stringify(parsedAllColumns) !== JSON.stringify(allColumns)) { + // Detectar novas colunas + const newColumns = allColumns.filter(col => !parsedAllColumns.includes(col)); + + if (newColumns.length > 0) { + // Adicionar novas colunas às colunas visíveis + setVisibleColumns(prev => { + const updated = [...prev, ...newColumns]; + // Remover duplicatas + return [...new Set(updated)]; + }); + + // Atualizar a ordem das colunas incluindo as novas + setColumnOrder(prev => { + const updated = [...prev, ...newColumns]; + // Remover duplicatas + return [...new Set(updated)]; + }); + + // Notificar sobre as novas colunas + setNewColumnsDetected(newColumns); + + // Limpar a notificação após 5 segundos + setTimeout(() => { + setNewColumnsDetected([]); + }, 5000); + } + + // Salvar as novas colunas no localStorage + localStorage.setItem(`all${storageKey}Columns`, JSON.stringify(allColumns)); + } + } else { + localStorage.setItem(`all${storageKey}Columns`, JSON.stringify(allColumns)); + } + } catch (error) { + console.error('Erro ao verificar as colunas no localStorage:', error); + } + }, [allColumns, storageKey]); + + // Salvar as colunas visíveis no localStorage sempre que elas mudarem + useEffect(() => { + try { + localStorage.setItem(`visible${storageKey}Columns`, JSON.stringify(visibleColumns)); + } catch (error) { + console.error('Erro ao salvar configurações de colunas:', error); + } + }, [visibleColumns, storageKey]); + + // Salvar a ordem das colunas no localStorage + useEffect(() => { + try { + localStorage.setItem(`${storageKey}ColumnsOrder`, JSON.stringify(columnOrder)); + } catch (error) { + console.error('Erro ao salvar ordem das colunas:', error); + } + }, [columnOrder, storageKey]); + + // Funções para drag & drop + const handleDragStart = useCallback((e: React.DragEvent, column: string) => { + setDraggedColumn(column); + e.dataTransfer.effectAllowed = 'move'; + e.dataTransfer.setData('text/plain', column); + + const target = e.currentTarget as HTMLDivElement; + target.classList.add('opacity-50'); + }, []); + + const handleDragEnd = useCallback((e: React.DragEvent) => { + setDraggedColumn(null); + e.currentTarget.classList.remove('opacity-50'); + }, []); + + const handleDragOver = useCallback((e: React.DragEvent) => { + e.preventDefault(); + e.dataTransfer.dropEffect = 'move'; + }, []); + + const handleDrop = useCallback((e: React.DragEvent, targetColumn: string) => { + e.preventDefault(); + + if (!draggedColumn || draggedColumn === targetColumn) return; + + // Reordenar as colunas + const newColumnOrder = [...columnOrder]; + const draggedIndex = newColumnOrder.indexOf(draggedColumn); + const targetIndex = newColumnOrder.indexOf(targetColumn); + + if (draggedIndex !== -1 && targetIndex !== -1) { + newColumnOrder.splice(draggedIndex, 1); + newColumnOrder.splice(targetIndex, 0, draggedColumn); + setColumnOrder(newColumnOrder); + } + }, [draggedColumn, columnOrder]); + + // Função para alternar a visibilidade de uma coluna + const toggleColumn = useCallback((column: string) => { + setVisibleColumns((current) => { + if (current.includes(column)) { + return current.filter((c) => c !== column); + } else { + return [...current, column]; + } + }); + }, []); + + // Função para selecionar todas as colunas + const selectAllColumns = useCallback(() => { + setVisibleColumns(allColumns); + }, [allColumns]); + + // Função para desmarcar todas as colunas + const unselectAllColumns = useCallback(() => { + // Mantém pelo menos uma coluna para garantir que a tabela não fique vazia + const firstColumn = allColumns[0] || "Código"; + setVisibleColumns([firstColumn]); + }, [allColumns]); + + // Função para verificar se uma coluna está visível + const isColumnVisible = useCallback((column: string) => { + return visibleColumns.includes(column); + }, [visibleColumns]); + + // Função para ordenar as colunas visíveis na ordem definida pelo usuário + const getOrderedVisibleColumns = useCallback(() => { + // Primeiro, filtrar as colunas que são visíveis + const visibleColumnSet = new Set(visibleColumns); + + // Depois, ordenar as colunas de acordo com a ordem definida pelo usuário + return columnOrder.filter(column => visibleColumnSet.has(column)); + }, [visibleColumns, columnOrder]); + + // Resetar a ordem das colunas + const resetColumnOrder = useCallback(() => { + setColumnOrder(allColumns); + }, [allColumns]); + + // Função para limpar a notificação de novas colunas + const dismissNewColumnsNotification = useCallback(() => { + setNewColumnsDetected([]); + }, []); + + return { + visibleColumns, + columnOrder, + getOrderedVisibleColumns, + resetColumnOrder, + toggleColumn, + selectAllColumns, + unselectAllColumns, + isColumnVisible, + handleDragStart, + handleDragEnd, + handleDragOver, + handleDrop, + newColumnsDetected, + dismissNewColumnsNotification, + }; +} \ No newline at end of file diff --git a/src/lib/api.ts b/src/lib/api.ts index 814627c..9c288b6 100644 --- a/src/lib/api.ts +++ b/src/lib/api.ts @@ -2,7 +2,7 @@ import { leadtime } from "../components/types"; import { Cliente } from "../components/types"; // URL base da API -const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL || "http://10.40.0.2:8888"; +const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL || "https://portalapi.jurunense.com"; /** * Classe de erro personalizada para requisições de API. diff --git a/src/utils/url-helpers.ts b/src/utils/url-helpers.ts index dde19ce..ea3bcd1 100644 --- a/src/utils/url-helpers.ts +++ b/src/utils/url-helpers.ts @@ -71,4 +71,33 @@ export function buildUrl( } return queryString ? `${baseUrl}?${queryString}` : baseUrl; +} + +/** + * Limpa os parâmetros da URL atual e navega para uma nova URL + * + * @param path Caminho para navegar (sem parâmetros) + */ +export function clearUrlAndNavigate(path: string): void { + if (typeof window !== 'undefined') { + window.history.replaceState({ path }, "", path); + window.location.href = path; + } +} + +/** + * Limpa parâmetros específicos da URL atual + * + * @param paramsToRemove Array de parâmetros para remover + */ +export function clearUrlParams(paramsToRemove: string[]): void { + if (typeof window !== 'undefined') { + const url = new URL(window.location.href); + + paramsToRemove.forEach(param => { + url.searchParams.delete(param); + }); + + window.history.replaceState({ path: url.pathname + url.search }, "", url.pathname + url.search); + } } \ No newline at end of file