ajuste na grid
This commit is contained in:
6
.eslintrc.json
Normal file
6
.eslintrc.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"extends": [
|
||||
"next/core-web-vitals",
|
||||
"next/typescript"
|
||||
]
|
||||
}
|
||||
@@ -122,6 +122,8 @@
|
||||
"autoprefixer": "^10.4.21",
|
||||
"babel-jest": "^29.7.0",
|
||||
"cypress": "^14.3.3",
|
||||
"eslint": "^9.32.0",
|
||||
"eslint-config-next": "15.4.4",
|
||||
"identity-obj-proxy": "^3.0.0",
|
||||
"jest": "^29.7.0",
|
||||
"jest-environment-jsdom": "^29.7.0",
|
||||
|
||||
1858
pnpm-lock.yaml
generated
1858
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -24,6 +24,7 @@ import { useCallback, useState, useEffect } from "react";
|
||||
import { User, useAuthValidation } from "../../../hooks/useAuthValidation";
|
||||
import { SellerSearchInput } from "@/src/components/orders/SellerSearchInput";
|
||||
import { ProductSearchInput } from "@/src/components/orders/ProductSearchInput";
|
||||
import { Checkbox } from "@/src/components/ui/checkbox";
|
||||
|
||||
/**
|
||||
* Página de Consulta de Pedidos
|
||||
@@ -53,6 +54,7 @@ export default function FindOrdersPage() {
|
||||
const [usePreloadClients, setUsePreloadClients] = useState(false);
|
||||
const [status, setStatus] = useState("");
|
||||
const [leadtime, setLeadtime] = useState("");
|
||||
const [onlyPendingTransfers, setOnlyPendingTransfers] = useState(false);
|
||||
|
||||
const {
|
||||
orders,
|
||||
@@ -62,6 +64,7 @@ export default function FindOrdersPage() {
|
||||
selectedOrderId,
|
||||
orderItems,
|
||||
cutitens,
|
||||
transfers,
|
||||
loadingItems,
|
||||
itemsError,
|
||||
currentPage,
|
||||
@@ -91,6 +94,7 @@ export default function FindOrdersPage() {
|
||||
*/
|
||||
const handleClearFilters = useCallback(() => {
|
||||
setSearchParams({});
|
||||
setOnlyPendingTransfers(false);
|
||||
}, [setSearchParams]);
|
||||
|
||||
/**
|
||||
@@ -369,6 +373,26 @@ export default function FindOrdersPage() {
|
||||
aria-label="Período"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="filter-field">
|
||||
<div className="flex items-center space-x-2">
|
||||
<Checkbox
|
||||
id="onlyPendingTransfers"
|
||||
checked={onlyPendingTransfers}
|
||||
onCheckedChange={(checked) => {
|
||||
setOnlyPendingTransfers(checked as boolean);
|
||||
handleInputChange("onlyPendingTransfer", checked ? "S" : "");
|
||||
}}
|
||||
aria-label="Somente transferências pendentes de entrada"
|
||||
/>
|
||||
<label
|
||||
htmlFor="onlyPendingTransfers"
|
||||
className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
||||
>
|
||||
Somente transferências pendentes de entrada
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-4">
|
||||
@@ -443,7 +467,7 @@ export default function FindOrdersPage() {
|
||||
loadMoreOrders={loadMoreOrders}
|
||||
visibleOrdersCount={visibleOrdersCount}
|
||||
stores={stores}
|
||||
transfers={[]}
|
||||
transfers={transfers}
|
||||
/>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
@@ -172,8 +172,8 @@ export function OrderDetail({ order, timelineEvents }: OrderDetailProps) {
|
||||
setTransfers(
|
||||
items.map(item => ({
|
||||
...item,
|
||||
oldShipmentId: Number(item.oldShipmentId ?? 0),
|
||||
newShipmentId: Number(item.newShipmentId ?? 0),
|
||||
oldShipmentId: Number(item.oldShipmentId ?? item.oldShipment ?? 0),
|
||||
newShipmentId: Number(item.newShipmentId ?? item.newShipment ?? 0),
|
||||
}))
|
||||
);
|
||||
} catch {}
|
||||
|
||||
@@ -13,6 +13,7 @@ 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";
|
||||
import { OrdersTableFooter } from "./OrdersTableFooter";
|
||||
|
||||
export type ColumnKey =
|
||||
| "Data"
|
||||
@@ -233,6 +234,11 @@ export function OrdersTable({
|
||||
</div>
|
||||
</div>
|
||||
{renderPagination()}
|
||||
<OrdersTableFooter
|
||||
orders={orders}
|
||||
currentOrders={sortedItems}
|
||||
totalOrders={orders.length}
|
||||
/>
|
||||
</div>
|
||||
), [
|
||||
orders,
|
||||
|
||||
96
src/components/orders/OrdersTableFooter.tsx
Normal file
96
src/components/orders/OrdersTableFooter.tsx
Normal file
@@ -0,0 +1,96 @@
|
||||
import { Order } from "@/src/components/types";
|
||||
import { useMemo } from "react";
|
||||
import { Calculator, Package, DollarSign } from "lucide-react";
|
||||
|
||||
interface OrdersTableFooterProps {
|
||||
orders: Order[];
|
||||
currentOrders: Order[];
|
||||
totalOrders: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Componente de rodapé compacto para exibir totais da tabela de pedidos
|
||||
*/
|
||||
export function OrdersTableFooter({ orders, currentOrders, totalOrders }: OrdersTableFooterProps) {
|
||||
const totals = useMemo(() => {
|
||||
// Calcular totais apenas dos pedidos da página atual
|
||||
const totalValue = currentOrders.reduce((sum, order) => {
|
||||
// Debug: verificar os valores disponíveis
|
||||
console.log('Order ID:', order.orderId, 'totalValue:', order.totalValue, 'amount:', order.amount, 'totalValue type:', typeof order.totalValue);
|
||||
|
||||
let value = 0;
|
||||
|
||||
if (typeof order.totalValue === 'number' && order.totalValue > 0) {
|
||||
value = order.totalValue;
|
||||
} else if (typeof order.amount === 'number' && order.amount > 0) {
|
||||
value = order.amount;
|
||||
} else if (typeof order.totalValue === 'string' && order.totalValue) {
|
||||
value = parseFloat(order.totalValue) || 0;
|
||||
} else if (typeof order.amount === 'string' && order.amount) {
|
||||
value = parseFloat(order.amount) || 0;
|
||||
}
|
||||
|
||||
return sum + value;
|
||||
}, 0);
|
||||
|
||||
const totalWeight = currentOrders.reduce((sum, order) => {
|
||||
let weight = 0;
|
||||
if (order.totalWeigth) {
|
||||
if (typeof order.totalWeigth === 'number') {
|
||||
weight = order.totalWeigth;
|
||||
} else if (typeof order.totalWeigth === 'string') {
|
||||
weight = parseFloat(order.totalWeigth) || 0;
|
||||
}
|
||||
}
|
||||
return sum + weight;
|
||||
}, 0);
|
||||
|
||||
console.log('Totals calculated:', { totalValue, totalWeight, orderCount: currentOrders.length });
|
||||
|
||||
return {
|
||||
totalValue,
|
||||
totalWeight,
|
||||
orderCount: currentOrders.length,
|
||||
totalOrders
|
||||
};
|
||||
}, [currentOrders, totalOrders]);
|
||||
|
||||
// Formatar valor em reais
|
||||
const formatCurrency = (value: number) => {
|
||||
return new Intl.NumberFormat('pt-BR', {
|
||||
style: 'currency',
|
||||
currency: 'BRL'
|
||||
}).format(value);
|
||||
};
|
||||
|
||||
// Formatar peso em kg
|
||||
const formatWeight = (weight: number) => {
|
||||
return new Intl.NumberFormat('pt-BR', {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2
|
||||
}).format(weight);
|
||||
};
|
||||
|
||||
if (currentOrders.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="bg-gray-50 border-t border-gray-200 px-4 py-3">
|
||||
<div className="flex items-center justify-between text-sm">
|
||||
<div className="flex items-center gap-2 text-gray-600">
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="flex items-center gap-1 text-gray-700">
|
||||
<span className="font-medium">Valor Total : {formatCurrency(totals.totalValue)}</span>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-1 text-white-700">
|
||||
<span className="font-medium">Peso Total : {formatWeight(totals.totalWeight)} kg</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
97
src/components/orders/OrdersTableSummary.tsx
Normal file
97
src/components/orders/OrdersTableSummary.tsx
Normal file
@@ -0,0 +1,97 @@
|
||||
import { Order } from "@/src/components/types";
|
||||
import { useMemo } from "react";
|
||||
import { Card, CardContent } from "@/src/components/ui/card";
|
||||
import { Badge } from "@/src/components/ui/badge";
|
||||
import { Calculator, Package, DollarSign } from "lucide-react";
|
||||
|
||||
interface OrdersTableSummaryProps {
|
||||
orders: Order[];
|
||||
currentOrders: Order[];
|
||||
totalOrders: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Componente que exibe o resumo dos totais de valor e peso dos pedidos
|
||||
*/
|
||||
export function OrdersTableSummary({ orders, currentOrders, totalOrders }: OrdersTableSummaryProps) {
|
||||
const totals = useMemo(() => {
|
||||
// Calcular totais apenas dos pedidos da página atual
|
||||
const totalValue = currentOrders.reduce((sum, order) => {
|
||||
const value = typeof order.totalValue === 'number' ? order.totalValue : 0;
|
||||
return sum + value;
|
||||
}, 0);
|
||||
|
||||
const totalWeight = currentOrders.reduce((sum, order) => {
|
||||
let weight = 0;
|
||||
if (order.totalWeigth) {
|
||||
if (typeof order.totalWeigth === 'number') {
|
||||
weight = order.totalWeigth;
|
||||
} else if (typeof order.totalWeigth === 'string') {
|
||||
weight = parseFloat(order.totalWeigth) || 0;
|
||||
}
|
||||
}
|
||||
return sum + weight;
|
||||
}, 0);
|
||||
|
||||
return {
|
||||
totalValue,
|
||||
totalWeight,
|
||||
orderCount: currentOrders.length,
|
||||
totalOrders
|
||||
};
|
||||
}, [currentOrders, totalOrders]);
|
||||
|
||||
// Formatar valor em reais
|
||||
const formatCurrency = (value: number) => {
|
||||
return new Intl.NumberFormat('pt-BR', {
|
||||
style: 'currency',
|
||||
currency: 'BRL'
|
||||
}).format(value);
|
||||
};
|
||||
|
||||
// Formatar peso em kg
|
||||
const formatWeight = (weight: number) => {
|
||||
return new Intl.NumberFormat('pt-BR', {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2
|
||||
}).format(weight);
|
||||
};
|
||||
|
||||
if (currentOrders.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Card className="mt-4 border-t-2 border-blue-200 bg-blue-50">
|
||||
<CardContent className="p-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
<Calculator className="h-5 w-5 text-blue-600" />
|
||||
<span className="text-sm font-medium text-gray-700">
|
||||
Resumo da página atual: {totals.orderCount} pedido{totals.orderCount !== 1 ? 's' : ''}
|
||||
(Total: {totals.totalOrders} pedido{totals.totalOrders !== 1 ? 's' : ''})
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-6">
|
||||
<div className="flex items-center gap-2">
|
||||
<DollarSign className="h-4 w-4 text-green-600" />
|
||||
<span className="text-sm font-medium text-gray-700">Total Valor:</span>
|
||||
<Badge variant="outline" className="bg-green-50 text-green-700 border-green-200">
|
||||
{formatCurrency(totals.totalValue)}
|
||||
</Badge>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-2">
|
||||
<Package className="h-4 w-4 text-orange-600" />
|
||||
<span className="text-sm font-medium text-gray-700">Total Peso:</span>
|
||||
<Badge variant="outline" className="bg-orange-50 text-orange-700 border-orange-200">
|
||||
{formatWeight(totals.totalWeight)} kg
|
||||
</Badge>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
import { transfer } from "../../types";
|
||||
import { formatDateToDisplay } from "../../../utils/format-helpers";
|
||||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/src/components/ui/table";
|
||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/src/components/ui/tooltip";
|
||||
|
||||
interface TransferenciaTabProps {
|
||||
transfers: transfer[];
|
||||
@@ -12,7 +13,31 @@ interface TransferenciaTabProps {
|
||||
*/
|
||||
export function TransferenciaTab({ transfers = [] }: TransferenciaTabProps) {
|
||||
const cellClass = "whitespace-nowrap";
|
||||
|
||||
|
||||
// Componente para renderizar texto com tooltip quando necessário
|
||||
const TruncatedText = ({ text, maxLength = 30 }: { text: string; maxLength?: number }) => {
|
||||
const isLong = text.length > maxLength;
|
||||
|
||||
if (!isLong) {
|
||||
return <span>{text}</span>;
|
||||
}
|
||||
|
||||
return (
|
||||
<TooltipProvider>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<span className="cursor-help">
|
||||
{text.substring(0, maxLength)}...
|
||||
</span>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent className="max-w-xs">
|
||||
<p className="whitespace-pre-wrap">{text}</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="border border-gray-300">
|
||||
<div className="overflow-x-auto">
|
||||
@@ -35,13 +60,13 @@ export function TransferenciaTab({ transfers = [] }: TransferenciaTabProps) {
|
||||
{transfers && transfers.length > 0 ? (
|
||||
transfers.map((item, index) => {
|
||||
// Get ship IDs from either standard or alternative field names
|
||||
const oldShipId = item.oldShipmentId ?? item.oldshipmentid ?? 0;
|
||||
const newShipId = item.newShipmentId ?? item.newshipmentid ?? 0;
|
||||
|
||||
const oldShipId = item.oldShipmentId ?? item.oldShipment ?? item.oldshipmentid ?? 0;
|
||||
const newShipId = item.newShipmentId ?? item.newShipment ?? item.newshipmentid ?? 0;
|
||||
|
||||
// Format for display
|
||||
const oldShipmentIdText = oldShipId !== 0 ? String(oldShipId) : "-";
|
||||
const newShipmentIdText = newShipId !== 0 ? String(newShipId) : "-";
|
||||
|
||||
|
||||
return (
|
||||
<TableRow
|
||||
key={`transfer-${index}`}
|
||||
@@ -65,8 +90,8 @@ export function TransferenciaTab({ transfers = [] }: TransferenciaTabProps) {
|
||||
<TableCell className={`border-r border-gray-200 p-2 text-xs`}>
|
||||
{item.cause || "-"}
|
||||
</TableCell>
|
||||
<TableCell className={`border-r border-gray-200 p-2 text-xs max-w-[120px] truncate`}>
|
||||
{item.transferText || "-"}
|
||||
<TableCell className={`border-r border-gray-200 p-2 text-xs max-w-[120px]`}>
|
||||
<TruncatedText text={item.transferText || "-"} maxLength={25} />
|
||||
</TableCell>
|
||||
<TableCell className={`${cellClass} border-r border-gray-200 p-2 text-xs`}>
|
||||
{item.userName}
|
||||
@@ -90,4 +115,4 @@ export function TransferenciaTab({ transfers = [] }: TransferenciaTabProps) {
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -53,6 +53,8 @@ export interface transfer {
|
||||
program: string;
|
||||
oldshipmentid?: number;
|
||||
newshipmentid?: number;
|
||||
oldShipment?: number;
|
||||
newShipment?: number;
|
||||
}
|
||||
|
||||
export interface leadtime{
|
||||
@@ -279,6 +281,8 @@ export interface OrderSearchParams {
|
||||
productId?: string;
|
||||
/** Produto selecionado pelo componente de busca */
|
||||
selectedProduct?: Product | null;
|
||||
/** Filtro para mostrar apenas transferências pendentes de entrada */
|
||||
onlyPendingTransfer?: string;
|
||||
}
|
||||
|
||||
export interface CustomerSearchParams {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useState, useEffect, useCallback, useRef } from "react";
|
||||
import { ordersApi, dataConsultApi } from "@/src/lib/api";
|
||||
import { OrderSearchParams, OrderItem, Customer, Order, Cliente, cutitens } from "@/src/components/types";
|
||||
import { OrderSearchParams, OrderItem, Customer, Order, Cliente, cutitens, transfer } from "@/src/components/types";
|
||||
import { validateDateRange } from "@/src/utils/date-helpers";
|
||||
import { extractApiData } from "@/src/utils/api-helpers";
|
||||
import { useSearchParams, useRouter } from "next/navigation";
|
||||
@@ -45,6 +45,7 @@ interface UseOrderSearchReturn {
|
||||
selectedOrderId: string | null;
|
||||
orderItems: OrderItem[];
|
||||
cutitens: cutitens[];
|
||||
transfers: transfer[];
|
||||
loadingItems: boolean;
|
||||
itemsError: string | null;
|
||||
currentPage: number;
|
||||
@@ -140,6 +141,7 @@ export function useOrderSearch(ordersPerPage: number = 8): UseOrderSearchReturn
|
||||
const [selectedOrderId, setSelectedOrderId] = useState<string | null>(null);
|
||||
const [orderItems, setOrderItems] = useState<OrderItem[]>([]);
|
||||
const [cutItems, setCutItems] = useState<cutitens[]>([]);
|
||||
const [transfers, setTransfers] = useState<transfer[]>([]);
|
||||
const [loadingItems, setLoadingItems] = useState(false);
|
||||
const [itemsError, setItemsError] = useState<string | null>(null);
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
@@ -337,6 +339,7 @@ export function useOrderSearch(ordersPerPage: number = 8): UseOrderSearchReturn
|
||||
setSelectedOrderId(null);
|
||||
setOrderItems([]);
|
||||
setCutItems([]);
|
||||
setTransfers([]);
|
||||
|
||||
// Reset pagination state
|
||||
setCurrentPage(1);
|
||||
@@ -414,6 +417,7 @@ export function useOrderSearch(ordersPerPage: number = 8): UseOrderSearchReturn
|
||||
setSelectedOrderId(null);
|
||||
setOrderItems([]);
|
||||
setCutItems([]);
|
||||
setTransfers([]);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -438,6 +442,25 @@ export function useOrderSearch(ordersPerPage: number = 8): UseOrderSearchReturn
|
||||
}
|
||||
setCutItems(cutItems);
|
||||
|
||||
// Load transfers
|
||||
const transfersResponse = await ordersApi.getTransfer(parseInt(orderId));
|
||||
let transfers: transfer[] = [];
|
||||
if (transfersResponse && typeof transfersResponse === "object") {
|
||||
if ("data" in transfersResponse && Array.isArray(transfersResponse.data)) {
|
||||
transfers = transfersResponse.data;
|
||||
} else if (Array.isArray(transfersResponse)) {
|
||||
transfers = transfersResponse;
|
||||
}
|
||||
}
|
||||
// Apply the same mapping as in OrderDetail
|
||||
setTransfers(
|
||||
transfers.map(item => ({
|
||||
...item,
|
||||
oldShipmentId: Number(item.oldShipmentId ?? item.oldShipment ?? 0),
|
||||
newShipmentId: Number(item.newShipmentId ?? item.newShipment ?? 0),
|
||||
}))
|
||||
);
|
||||
|
||||
} catch (err) {
|
||||
setItemsError("Erro ao carregar itens do pedido.");
|
||||
console.error(err);
|
||||
@@ -464,6 +487,7 @@ export function useOrderSearch(ordersPerPage: number = 8): UseOrderSearchReturn
|
||||
setSelectedOrderId(null);
|
||||
setOrderItems([]);
|
||||
setCutItems([]);
|
||||
setTransfers([]);
|
||||
}
|
||||
}, [currentPage, totalPages]);
|
||||
|
||||
@@ -475,6 +499,7 @@ export function useOrderSearch(ordersPerPage: number = 8): UseOrderSearchReturn
|
||||
setSelectedOrderId(null);
|
||||
setOrderItems([]);
|
||||
setCutItems([]);
|
||||
setTransfers([]);
|
||||
}
|
||||
}, [currentPage]);
|
||||
|
||||
@@ -492,6 +517,7 @@ export function useOrderSearch(ordersPerPage: number = 8): UseOrderSearchReturn
|
||||
setSelectedOrderId(null);
|
||||
setOrderItems([]);
|
||||
setCutItems([]);
|
||||
setTransfers([]);
|
||||
}
|
||||
}, [totalPages]);
|
||||
|
||||
@@ -704,6 +730,7 @@ export function useOrderSearch(ordersPerPage: number = 8): UseOrderSearchReturn
|
||||
selectedOrderId,
|
||||
orderItems,
|
||||
cutitens: cutItems,
|
||||
transfers,
|
||||
loadingItems,
|
||||
itemsError,
|
||||
currentPage,
|
||||
|
||||
@@ -3,7 +3,7 @@ import { Cliente } from "../components/types";
|
||||
|
||||
// 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 || "http://10.1.1.212:8805";
|
||||
const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL || "http://10.1.1.212:8807";
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user