import React, { useState, useEffect } from "react"; import LoadingSpinner from "../LoadingSpinner"; import { env } from "../../src/config/env"; import { authService } from "../../src/services/auth.service"; import { formatCurrency } from "../../utils/formatters"; import ConfirmDialog from "../ConfirmDialog"; import OrderItemsModal from "../OrderItemsModal"; import PrintOrderDialog from "../PrintOrderDialog"; import StimulsoftViewer from "../StimulsoftViewer"; import ReceivePixDialog from "../ReceivePixDialog"; import NoData from "../NoData"; import { Input } from "../ui/input"; import { Label } from "../ui/label"; import { Button } from "../ui/button"; import { CustomAutocomplete } from "../ui/autocomplete"; import { DateInput } from "../ui/date-input"; import { DataGridPremium, GridColDef } from "@mui/x-data-grid-premium"; import "../../lib/mui-license"; import { Box, IconButton } from "@mui/material"; import { Edit, Visibility, Print } from "@mui/icons-material"; interface Order { createDate: string; orderId: number; sellerId: number; store: string; status: string; customerId: number; customerName: string; orderValue: number; itens: number; pixCreate: string; } interface OrderItem { productId: number; description: string; package: string; color?: string; local: string; quantity: number; price: number; subTotal: number; } interface Store { id: string; shortName: string; name: string; } const OrdersView: React.FC = () => { const [orders, setOrders] = useState([]); const [stores, setStores] = useState([]); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); // Filtros const [selectedStore, setSelectedStore] = useState(""); const [orderId, setOrderId] = useState(""); const [startDate, setStartDate] = useState(""); const [endDate, setEndDate] = useState(""); const [document, setDocument] = useState(""); const [customerName, setCustomerName] = useState(""); // Modais const [showOrderItems, setShowOrderItems] = useState(false); const [orderItems, setOrderItems] = useState([]); const [selectedOrder, setSelectedOrder] = useState(null); const [showConfirmDialog, setShowConfirmDialog] = useState(false); const [confirmDialogType, setConfirmDialogType] = useState< "info" | "warning" | "error" | "success" | "delete" | "confirm" >("info"); const [confirmDialogTitle, setConfirmDialogTitle] = useState(""); const [confirmDialogMessage, setConfirmDialogMessage] = useState(""); const [showCartLoadedDialog, setShowCartLoadedDialog] = useState(false); const [showPrintDialog, setShowPrintDialog] = useState(false); const [orderToPrint, setOrderToPrint] = useState(null); const [showPrintViewer, setShowPrintViewer] = useState(false); const [printUrl, setPrintUrl] = useState(""); const [printOrderId, setPrintOrderId] = useState( undefined ); const [printModel, setPrintModel] = useState(undefined); // Estados PIX const [showReceivePix, setShowReceivePix] = useState(false); const [orderForPix, setOrderForPix] = useState(null); const [pixValue, setPixValue] = useState(0); const [showQrCode, setShowQrCode] = useState(false); const [qrCodePix, setQrCodePix] = useState(""); const [processingPix, setProcessingPix] = useState(false); const [showPixReceipt, setShowPixReceipt] = useState(false); const [pixReceiptUrl, setPixReceiptUrl] = useState(""); useEffect(() => { fetchStores(); }, []); const fetchStores = async () => { try { const token = authService.getToken(); const apiUrl = env.API_URL.replace(/\/$/, ""); const response = await fetch(`${apiUrl}/lists/store`, { method: "GET", headers: { "Content-Type": "application/json", ...(token && { Authorization: `Basic ${token}` }), }, }); if (response.ok) { const data = await response.json(); setStores(data); } } catch (err) { console.error("Erro ao buscar filiais:", err); } }; const handleSearch = async () => { setLoading(true); setError(null); try { const token = authService.getToken(); const apiUrl = env.API_URL.replace(/\/$/, ""); // Seguindo o padrão do Angular: sellerId sempre 0 let sellerId = 0; if (authService.isManager()) { sellerId = 0; } const params = new URLSearchParams({ "x-store": selectedStore || "", document: document || "", name: customerName.toUpperCase() || "", initialDate: startDate || "", finalDate: endDate || "", sellerId: sellerId.toString(), idOrder: orderId || "", }); const response = await fetch( `${apiUrl}/order/list?${params.toString()}`, { method: "GET", headers: { Accept: "application/json, text/plain, */*", "Content-Type": "application/json", ...(token && { Authorization: `Basic ${token}` }), }, } ); if (!response.ok) { const errorData = await response.json().catch(() => ({})); throw new Error( errorData.message || `Erro ao buscar pedidos: ${response.statusText}` ); } const data = await response.json(); setOrders(data || []); } catch (err) { console.error("Erro ao buscar pedidos:", err); setError( err instanceof Error ? err.message : "Erro ao buscar pedidos. Tente novamente." ); } finally { setLoading(false); } }; const handleClear = () => { setSelectedStore(""); setOrderId(""); setStartDate(""); setEndDate(""); setDocument(""); setCustomerName(""); setOrders([]); setError(null); }; const handleViewItems = async (order: Order) => { try { const token = authService.getToken(); const apiUrl = env.API_URL.replace(/\/$/, ""); const response = await fetch(`${apiUrl}/order/itens/${order.orderId}`, { method: "GET", headers: { "Content-Type": "application/json", ...(token && { Authorization: `Basic ${token}` }), }, }); if (response.ok) { const items = await response.json(); setOrderItems(items); setSelectedOrder(order); setShowOrderItems(true); } } catch (err) { console.error("Erro ao buscar itens do pedido:", err); setConfirmDialogType("error"); setConfirmDialogTitle("Erro"); setConfirmDialogMessage("Não foi possível carregar os itens do pedido."); setShowConfirmDialog(true); } }; const handleCloseOrderItemsModal = () => { setShowOrderItems(false); setOrderItems([]); setSelectedOrder(null); }; const handleEditOrder = async (order: Order) => { // Validação 1: Status FATURADO ou MONTADO (exatamente como no Angular) if (order.status === "FATURADO" || order.status === "MONTADO") { setConfirmDialogType("warning"); setConfirmDialogTitle("Pedido não pode ser alterado!"); setConfirmDialogMessage( `Pedido encontra-se ${order.status}, sua alteração não é permitida.` ); setShowConfirmDialog(true); return; } // Validação 2: PIX já gerado (exatamente como no Angular) if (order.pixCreate === "S") { setConfirmDialogType("warning"); setConfirmDialogTitle("Pedido não pode ser alterado!"); setConfirmDialogMessage( "PIX já gerado para este pedido, alteração não é permitida." ); setShowConfirmDialog(true); return; } // Validação 3: Vendedor diferente e não é manager (exatamente como no Angular) const sellerId = authService.getSeller(); const isManager = authService.isManager(); if (order.sellerId !== Number(sellerId) && !isManager) { setConfirmDialogType("warning"); setConfirmDialogTitle("Pedido não pode ser alterado!"); setConfirmDialogMessage( "Pedido de outro vendedor, somente o vendedor responsável da venda poderá alterar o pedido." ); setShowConfirmDialog(true); return; } // Buscar dados do carrinho e navegar para edição try { const token = authService.getToken(); const apiUrl = env.API_URL.replace(/\/$/, ""); console.log( "📦 [ORDER] Buscando dados do carrinho para pedido:", order.orderId ); const response = await fetch(`${apiUrl}/order/cart/${order.orderId}`, { method: "GET", headers: { "Content-Type": "application/json", ...(token && { Authorization: `Basic ${token}` }), }, }); if (response.ok) { const result = await response.json(); console.log("📦 [ORDER] Dados recebidos do backend:", { cartId: result.cartId, hasCustomer: !!result.customer, hasPaymentPlan: !!result.paymentPlan, hasBilling: !!result.billing, hasPartner: !!result.partner, hasAddress: !!result.address, hasInvoiceStore: !!result.invoiceStore, }); // Salvar dados no localStorage exatamente como no Angular console.log( "📦 [ORDER] Salvando cartId no localStorage:", result.cartId ); localStorage.setItem("cart", result.cartId); localStorage.setItem("customer", JSON.stringify(result.customer)); localStorage.setItem("paymentPlan", JSON.stringify(result.paymentPlan)); localStorage.setItem("billing", JSON.stringify(result.billing)); if (result.partner) { console.log("📦 [ORDER] Salvando partner"); localStorage.setItem("partner", JSON.stringify(result.partner)); } if (result.address) { console.log("📦 [ORDER] Salvando address"); localStorage.setItem("address", JSON.stringify(result.address)); } console.log("📦 [ORDER] Salvando invoiceStore"); localStorage.setItem( "invoiceStore", JSON.stringify(result.invoiceStore) ); // Criar OrderDelivery exatamente como no Angular // Nota: No Angular é 'scheduleDelivery', não 'squeduleDelivery' const orderDelivery = { notification: result.notification1 ?? "", notification1: result.notification2 ?? "", notification2: "", notificationDelivery1: result.notificationDelivery1 ?? "", notificationDelivery2: result.notificationDelivery2 ?? "", notificationDelivery3: result.notificationDelivery3 ?? "", dateDelivery: result.deliveryDate, scheduleDelivery: result.scheduleDelivery ?? result.squeduleDelivery ?? "", priorityDelivery: result.priorityDelivery, }; console.log("📦 [ORDER] Salvando dataDelivery"); localStorage.setItem("dataDelivery", JSON.stringify(orderDelivery)); // IMPORTANTE: Carregar os itens do carrinho ANTES de navegar // No Angular, o componente home-sales dispara LoadShoppingAction no ngOnInit // No React, precisamos garantir que os itens sejam carregados antes da navegação console.log( "📦 [ORDER] Carregando itens do carrinho antes de navegar..." ); try { const { shoppingService } = await import( "../../src/services/shopping.service" ); const items = await shoppingService.getShoppingItems(result.cartId); console.log("📦 [ORDER] Itens do carrinho carregados:", items.length); console.log("📦 [ORDER] Itens:", items); // Salvar os itens no sessionStorage para garantir que sejam carregados após navegação sessionStorage.setItem("pendingCartItems", JSON.stringify(items)); sessionStorage.setItem("pendingCartId", result.cartId); console.log( "📦 [ORDER] Itens salvos no sessionStorage para carregamento após navegação" ); } catch (loadError) { console.error( "📦 [ORDER] Erro ao carregar itens antes de navegar:", loadError ); // Continuar mesmo se houver erro, o useCart tentará carregar depois } // Disparar evento customizado para notificar mudança no cartId console.log("📦 [ORDER] Disparando evento cartUpdated"); const storageEvent = new Event("cartUpdated") as any; storageEvent.key = "cart"; storageEvent.newValue = result.cartId; window.dispatchEvent(storageEvent); // Mostrar mensagem de sucesso informando que os dados do carrinho foram carregados console.log("📦 [ORDER] Dados do carrinho carregados com sucesso"); setShowCartLoadedDialog(true); } else { const errorData = await response.json().catch(() => ({})); throw new Error( errorData.message || "Erro ao carregar dados do pedido" ); } } catch (err) { console.error("📦 [ORDER] Erro ao editar pedido:", err); setConfirmDialogType("error"); setConfirmDialogTitle("Erro"); setConfirmDialogMessage( err instanceof Error ? err.message : "Não foi possível carregar os dados do pedido." ); setShowConfirmDialog(true); } }; const formatDate = (dateString: string | null | undefined): string => { if (!dateString || dateString === "null" || dateString === "undefined") { return ""; } try { // Tentar criar a data let date: Date; // Se já for uma string de data válida, usar diretamente if (typeof dateString === "string") { // Tentar parsear diferentes formatos date = new Date(dateString); // Se falhar, tentar formatos alternativos if (isNaN(date.getTime())) { // Tentar formato brasileiro DD/MM/YYYY const parts = dateString.split("/"); if (parts.length === 3) { date = new Date( parseInt(parts[2]), parseInt(parts[1]) - 1, parseInt(parts[0]) ); } else { // Tentar formato ISO date = new Date( dateString.replace(/(\d{2})\/(\d{2})\/(\d{4})/, "$3-$2-$1") ); } } } else { date = new Date(dateString); } // Verificar se a data é válida if (isNaN(date.getTime())) { console.warn("Data inválida:", dateString); return ""; } // Formatar no padrão DD/MM/YYYY const day = String(date.getDate()).padStart(2, "0"); const month = String(date.getMonth() + 1).padStart(2, "0"); const year = date.getFullYear(); return `${day}/${month}/${year}`; } catch (error) { console.error("Erro ao formatar data:", dateString, error); return ""; } }; const getStatusColor = (status: string): string => { switch (status) { case "FATURADO": return "bg-green-100 text-green-800"; case "MONTADO": return "bg-blue-100 text-blue-800"; case "LIBERADO": return "bg-yellow-100 text-yellow-800"; case "BLOQUEADO": return "bg-red-100 text-red-800"; default: return "bg-slate-100 text-slate-800"; } }; const handlePrintOrder = (order: Order) => { setOrderToPrint(order); setShowPrintDialog(true); }; const handleConfirmPrint = (model: "A" | "B") => { if (!orderToPrint) return; // Construir URL do viewer seguindo o padrão do Angular const viewerUrl = env.PRINT_VIEWER_URL.replace( "{action}", "InitViewerOrder" ); const url = `${viewerUrl}?orderId=${orderToPrint.orderId}&model=${model}`; // Configurar e mostrar o viewer setPrintUrl(url); setPrintOrderId(orderToPrint.orderId); setPrintModel(model); setShowPrintViewer(true); setShowPrintDialog(false); }; const handlePixOrder = async (order: Order) => { // Validações if (order.status === "FATURADO") { setConfirmDialogType("warning"); setConfirmDialogTitle("Pedido já esta FATURADO!"); setConfirmDialogMessage( "Para geração do PIX o pedido deve estar na posição LIBERADO ou BLOQUEADO." ); setShowConfirmDialog(true); return; } if (order.customerId === 1) { setConfirmDialogType("warning"); setConfirmDialogTitle("Não é permido gerar PIX para CONSUMIDOR FINAL!"); setConfirmDialogMessage( "Para geração do PIX é necessário identificar o cliente no pedido de venda!" ); setShowConfirmDialog(true); return; } setOrderForPix(order); setPixValue(parseFloat(order.orderValue.toFixed(2))); setShowQrCode(false); setQrCodePix(""); // Buscar PIX existente try { const token = authService.getToken(); const apiUrl = env.API_URL_PIX.replace(/\/$/, ""); const response = await fetch( `${apiUrl}/payment/pix/santander/find/${order.orderId}`, { method: "GET", headers: { "Content-Type": "application/json", ...(token && { Authorization: `Basic ${token}` }), }, } ); if (response.ok) { const pix = await response.json(); if (!pix || pix === null) { // Não existe PIX, mostrar dialog para criar setShowReceivePix(true); } else { if (pix.status === "CONCLUIDA") { // PIX já foi concluído, abrir comprovante openPixReceipt(order.orderId); } else { // PIX existe mas não foi concluído, mostrar QR Code setQrCodePix(pix.qrCode); setPixValue(pix.valor?.original || order.orderValue); setShowQrCode(true); setShowReceivePix(true); } } } else { // Erro ao buscar, mostrar dialog para criar setShowReceivePix(true); } } catch (err) { console.error("Erro ao consultar PIX:", err); setConfirmDialogType("error"); setConfirmDialogTitle("Pedido de venda"); setConfirmDialogMessage( "Erro ao consultar dados do PIX, comunique ao TI." ); setShowConfirmDialog(true); } }; const handleConfirmPix = async (value: number) => { if (!orderForPix) return; // Validação: valor não pode ser superior ao valor do pedido if (value > orderForPix.orderValue) { setConfirmDialogType("warning"); setConfirmDialogTitle("Geração pix"); setConfirmDialogMessage( "O Valor do PIX não pode ser superior ao valor do pedido de venda!" ); setShowConfirmDialog(true); setShowReceivePix(false); return; } setProcessingPix(true); try { const token = authService.getToken(); const apiUrl = env.API_URL_PIX.replace(/\/$/, ""); const pixOrder = { orderId: orderForPix.orderId, customerId: orderForPix.customerId, amount: value, }; const response = await fetch(`${apiUrl}/payment/pix/santander/create`, { method: "POST", headers: { "Content-Type": "application/json", ...(token && { Authorization: `Basic ${token}` }), }, body: JSON.stringify(pixOrder), }); if (response.ok) { const result = await response.json(); setQrCodePix(result.qrCode); setPixValue(value); setShowQrCode(true); setProcessingPix(false); } else { const errorData = await response.json().catch(() => ({})); throw new Error(errorData.message || "Erro ao criar cobrança PIX"); } } catch (err) { console.error("Erro ao gerar PIX:", err); setProcessingPix(false); setShowReceivePix(false); setConfirmDialogType("error"); setConfirmDialogTitle("Pedido de venda"); setConfirmDialogMessage( err instanceof Error ? err.message : "Erro ao criar cobrança PIX" ); setShowConfirmDialog(true); } }; const openPixReceipt = (orderId: number) => { const viewerUrl = env.PRINT_VIEWER_URL.replace( "{action}", "InitViewerComprovantePix" ); const url = `${viewerUrl}?order=${orderId}`; setPixReceiptUrl(url); setShowPixReceipt(true); }; // Definir colunas do DataGrid const columns: GridColDef[] = [ { field: "actions", headerName: "Ações", width: 200, sortable: false, filterable: false, disableColumnMenu: true, renderCell: (params) => { const order = params.row as Order; return ( handleEditOrder(order)} sx={{ color: "#64748b", "&:hover": { backgroundColor: "#f1f5f9", color: "#475569" }, }} title="Editar pedido" > handleViewItems(order)} sx={{ color: "#64748b", "&:hover": { backgroundColor: "#f1f5f9", color: "#475569" }, }} title="Ver itens do pedido" > handlePrintOrder(order)} sx={{ color: "#64748b", "&:hover": { backgroundColor: "#f1f5f9", color: "#475569" }, }} title="Imprimir pedido" > handlePixOrder(order)} sx={{ color: "#14b8a6", "&:hover": { backgroundColor: "#f0fdfa", color: "#0d9488" }, }} title="Gerar PIX" > ); }, }, { field: "createDate", headerName: "Data", width: 120, valueFormatter: (value) => { if (!value) return ""; return formatDate(String(value)); }, }, { field: "orderId", headerName: "N.Pedido", width: 130, headerAlign: "left", }, { field: "status", headerName: "Situação", width: 130, renderCell: (params) => { const status = params.value as string; return ( {status} ); }, }, { field: "customerId", headerName: "Cód.Cliente", width: 120, headerAlign: "left", }, { field: "customerName", headerName: "Cliente", width: 300, flex: 1, }, { field: "orderValue", headerName: "Valor", width: 130, headerAlign: "right", align: "right", valueFormatter: (value) => formatCurrency(value as number), }, ]; return ( //
{/* Header */}

Consulta de pedidos

{/* Filtros */}
{/* Filial de venda */}
({ value: store.id, label: store.shortName, }))} value={selectedStore} onValueChange={setSelectedStore} placeholder="Selecione a filial de venda..." />
{/* Número do pedido */}
setOrderId(e.target.value)} placeholder="Informe o número do pedido" />
{/* Data inicial */}
setStartDate(e.target.value)} />
{/* Data final */}
setEndDate(e.target.value)} />
{/* CPF/CNPJ */}
setDocument(e.target.value)} placeholder="Informe o CPF ou CNPJ do cliente" />
{/* Nome do cliente */}
setCustomerName(e.target.value)} placeholder="Informe o nome ou razão social do cliente" />
{/* Botões de Ação */}
{/* Tabela de Pedidos */} {error && (

{error}

)} {loading && (
)} {!loading && orders.length > 0 && (

Pedidos encontrados: {orders.length}

row.orderId} disableRowSelectionOnClick hideFooter sx={{ border: "none", "& .MuiDataGrid-columnHeaders": { backgroundColor: "#f8fafc", borderBottom: "1px solid #e2e8f0", "& .MuiDataGrid-columnHeader": { fontSize: "9px", fontWeight: 900, color: "#94a3b8", textTransform: "uppercase", letterSpacing: "0.2em", padding: "12px 16px", "& .MuiDataGrid-columnHeaderTitle": { fontWeight: 900, }, }, }, "& .MuiDataGrid-row": { "&:hover": { backgroundColor: "#f8fafc", }, }, "& .MuiDataGrid-cell": { fontSize: "12px", color: "#475569", padding: "16px", borderBottom: "1px solid #f1f5f9", "&:focus": { outline: "none", }, "&:focus-within": { outline: "none", }, }, "& .MuiDataGrid-cell[data-field='orderId']": { fontWeight: 700, color: "#0f172a", }, "& .MuiDataGrid-cell[data-field='orderValue']": { fontWeight: 700, color: "#0f172a", }, "& .MuiDataGrid-cell[data-field='createDate']": { color: "#64748b", }, "& .MuiDataGrid-cell[data-field='customerId']": { color: "#64748b", }, "& .MuiDataGrid-cell[data-field='customerName']": { color: "#64748b", }, "& .MuiDataGrid-virtualScroller": { overflowY: "auto", }, "& .MuiDataGrid-virtualScrollerContent": { height: "auto !important", }, }} />
)} {!loading && orders.length === 0 && !error && (
)} {/* Modal de Itens do Pedido */} {/* Dialog de Confirmação/Informação */} setShowConfirmDialog(false)} onConfirm={() => setShowConfirmDialog(false)} type={confirmDialogType} title={confirmDialogTitle} message={confirmDialogMessage} confirmText="OK" showWarning={ confirmDialogType === "warning" || confirmDialogType === "error" } /> {/* Dialog de Carrinho Carregado */} { setShowCartLoadedDialog(false); // Navegar para página de produtos após fechar o dialog setTimeout(() => { window.location.href = "/#/sales/home"; }, 100); }} onConfirm={() => { setShowCartLoadedDialog(false); // Navegar para página de produtos após confirmar setTimeout(() => { window.location.href = "/#/sales/home"; }, 100); }} type="success" title="Carrinho carregado" message="Os dados do carrinho foram carregados com sucesso!\n\nVocê será redirecionado para a página de produtos." confirmText="OK" showWarning={false} /> {/* Dialog de Seleção de Modelo de Impressão */} {orderToPrint && ( { setShowPrintDialog(false); setOrderToPrint(null); }} onConfirm={handleConfirmPrint} orderId={orderToPrint.orderId} /> )} {/* Dialog de Recebimento PIX */} {orderForPix && ( { setShowReceivePix(false); setOrderForPix(null); setShowQrCode(false); setQrCodePix(""); setPixValue(0); }} onConfirm={handleConfirmPix} orderId={orderForPix.orderId} customerName={orderForPix.customerName} orderValue={orderForPix.orderValue} showQrCode={showQrCode} qrCodeValue={qrCodePix} pixValue={pixValue} processing={processingPix} /> )} {/* Modal do Viewer de Comprovante PIX */} {showPixReceipt && pixReceiptUrl && (
{/* Header */}

Recibo de PIX

Comprovante de pagamento

{/* Viewer Content */}
{ setShowPixReceipt(false); setPixReceiptUrl(""); }} />
)} {/* Modal do Viewer de Impressão */} {showPrintViewer && printUrl && (
{/* Header */}

Pedido de venda

Visualização e Impressão

{/* Viewer Content */}
{ setShowPrintViewer(false); setPrintUrl(""); setPrintOrderId(undefined); setPrintModel(undefined); }} />
)}
); }; export default OrdersView;