298 lines
8.2 KiB
TypeScript
298 lines
8.2 KiB
TypeScript
"use client";
|
|
|
|
import { useEffect, useState } from "react";
|
|
import { ArrowLeft } from "lucide-react";
|
|
import { useNavigation } from "../../hooks";
|
|
import { Button } from "../../components/ui/button";
|
|
import { OrderInfoCard } from "./OrderInfoCard";
|
|
import Timeline from "./OrderTimeline";
|
|
import { QRCodeRastreamento } from "../../components/qr-code-rastreamento";
|
|
import { ordersApi } from "../../lib/api";
|
|
import { createSearchParams, buildUrl } from "../../utils/url-helpers";
|
|
import {
|
|
Order,
|
|
OrderItem,
|
|
OrderDelivery,
|
|
cutitens,
|
|
transfer,
|
|
status,
|
|
TimelineEvent,
|
|
leadtime,
|
|
} from "../types";
|
|
|
|
/**
|
|
* Interface que define a estrutura de um evento na timeline
|
|
*/
|
|
export interface TimelineDisplayEvent {
|
|
id: string;
|
|
codigoFuncionario: string;
|
|
description: string;
|
|
date: string;
|
|
status: string;
|
|
icon: any;
|
|
color: string;
|
|
}
|
|
|
|
interface OrderDetailProps {
|
|
order: Order;
|
|
timelineEvents: leadtime[];
|
|
}
|
|
|
|
export function mapTimelineEvents(events: leadtime[]): TimelineEvent[] {
|
|
return events.map(event => ({
|
|
id: event.descricaoEtapa,
|
|
title: event.descricaoEtapa,
|
|
description: event.descricao || "Status updated",
|
|
date: event.data || new Date().toISOString(),
|
|
status: event.descricaoEtapa,
|
|
user: event.codigoFuncionario,
|
|
}));
|
|
}
|
|
|
|
export function OrderDetail({ order, timelineEvents }: OrderDetailProps) {
|
|
const { searchParams, navigateTo, goBack, goToOrdersFind } = useNavigation();
|
|
const from = searchParams.get("from");
|
|
const returnPath = searchParams.get("returnPath") || "/orders/find";
|
|
|
|
const [orderItems, setOrderItems] = useState<OrderItem[]>([]);
|
|
const [loadingItems, setLoadingItems] = useState(false);
|
|
const [itemsError, setItemsError] = useState<string | null>(null);
|
|
const [cutItems, setCutItems] = useState<cutitens[]>([]);
|
|
const [transfers, setTransfers] = useState<transfer[]>([]);
|
|
const [statusData, setStatusData] = useState<status[]>([]);
|
|
const [orderDelivery, setOrderDelivery] = useState<OrderDelivery>({
|
|
placeId: 0,
|
|
placeName: "",
|
|
street: "",
|
|
addressNumber: "",
|
|
bairro: "",
|
|
city: "",
|
|
state: "",
|
|
addressComplement: "",
|
|
cep: "",
|
|
commentOrder1: "",
|
|
commentOrder2: "",
|
|
commentDelivery1: order.commentDelivery1 || "",
|
|
commentDelivery2: "",
|
|
commentDelivery3: "",
|
|
commentDelivery4: "",
|
|
shippimentId: 0,
|
|
shippimentDate: new Date(),
|
|
shippimentComment: "",
|
|
place: order.deliveryLocal || "",
|
|
driver: order.driver || "",
|
|
car: order.carDescription || "",
|
|
closeDate: new Date(),
|
|
separatorName: "",
|
|
confName: "",
|
|
releaseDate: new Date(),
|
|
});
|
|
|
|
useEffect(() => {
|
|
async function fetchOrderItems() {
|
|
if (!order.orderId) return;
|
|
|
|
setLoadingItems(true);
|
|
setItemsError(null);
|
|
|
|
try {
|
|
const response = await ordersApi.getOrderItems(order.orderId);
|
|
const items = Array.isArray(response?.data)
|
|
? response.data
|
|
: Array.isArray(response)
|
|
? response
|
|
: [];
|
|
|
|
setOrderItems(items);
|
|
} catch {
|
|
setItemsError("Could not load order items.");
|
|
} finally {
|
|
setLoadingItems(false);
|
|
}
|
|
}
|
|
|
|
fetchOrderItems();
|
|
}, [order.orderId]);
|
|
|
|
useEffect(() => {
|
|
async function fetchDeliveryInfo() {
|
|
if (!order.orderId) return;
|
|
|
|
try {
|
|
const response = await ordersApi.getOrderDelivery(order.orderId);
|
|
const data = response?.data || response;
|
|
|
|
setOrderDelivery(prev => ({
|
|
...prev,
|
|
...data,
|
|
place: data.place || order.deliveryLocal || "",
|
|
driver: data.driver || order.driver || "",
|
|
car: data.car || order.carDescription || "",
|
|
}));
|
|
} catch {}
|
|
}
|
|
|
|
fetchDeliveryInfo();
|
|
}, [order.orderId]);
|
|
|
|
useEffect(() => {
|
|
async function fetchCutItems() {
|
|
if (!order.orderId) return;
|
|
|
|
try {
|
|
const response = await ordersApi.getCutItems(order.orderId);
|
|
const items = Array.isArray(response?.data)
|
|
? response.data
|
|
: Array.isArray(response)
|
|
? response
|
|
: [];
|
|
|
|
setCutItems(items);
|
|
} catch {}
|
|
}
|
|
|
|
fetchCutItems();
|
|
}, [order.orderId]);
|
|
|
|
useEffect(() => {
|
|
async function fetchTransfers() {
|
|
if (!order.orderId) return;
|
|
|
|
try {
|
|
const id = parseInt(order.orderId);
|
|
if (isNaN(id)) return;
|
|
|
|
const response = await ordersApi.getTransfer(id);
|
|
const items = Array.isArray(response?.data)
|
|
? response.data
|
|
: Array.isArray(response)
|
|
? response
|
|
: [];
|
|
|
|
setTransfers(
|
|
items.map(item => ({
|
|
...item,
|
|
oldShipmentId: Number(item.oldShipmentId ?? item.oldShipment ?? 0),
|
|
newShipmentId: Number(item.newShipmentId ?? item.newShipment ?? 0),
|
|
}))
|
|
);
|
|
} catch {}
|
|
}
|
|
|
|
fetchTransfers();
|
|
}, [order.orderId]);
|
|
|
|
useEffect(() => {
|
|
async function fetchStatusData() {
|
|
if (!order.orderId) return;
|
|
|
|
try {
|
|
const id = parseInt(order.orderId);
|
|
if (isNaN(id)) return;
|
|
|
|
const response = await ordersApi.getStatusOrder(id);
|
|
const items = Array.isArray(response?.data)
|
|
? response.data
|
|
: Array.isArray(response)
|
|
? response
|
|
: [];
|
|
|
|
setStatusData(items);
|
|
} catch {}
|
|
}
|
|
|
|
fetchStatusData();
|
|
}, [order.orderId]);
|
|
|
|
const returnParams = createSearchParams(
|
|
searchParams,
|
|
["from", "returnPath"],
|
|
{ origem: "detail", noRefresh: "true", preserveState: "true" }
|
|
);
|
|
|
|
const handleBack = () => {
|
|
if (from === "lista") {
|
|
const baseUrl = returnPath;
|
|
const params: Record<string, string> = {};
|
|
|
|
searchParams.forEach((value, key) => {
|
|
params[key] = value;
|
|
});
|
|
|
|
const url = buildUrl(baseUrl, {
|
|
...params,
|
|
origem: "detail",
|
|
noRefresh: "true",
|
|
preserveState: "true",
|
|
});
|
|
|
|
navigateTo(url);
|
|
} else if (window.history.length > 2) {
|
|
goBack();
|
|
} else {
|
|
// Usar a função que limpa parâmetros ao navegar para orders/find
|
|
goToOrdersFind();
|
|
}
|
|
};
|
|
|
|
const orderWithDefaults = {
|
|
...order,
|
|
paymentMethod: order.paymentMethod || "Not provided",
|
|
paymentPlan: order.paymentPlan || "Not provided",
|
|
paymentName: order.paymentName || "Not provided",
|
|
driver: order.driver || "Not provided",
|
|
carDescription: order.carDescription || "Not provided",
|
|
deliveryLocal: order.deliveryLocal || "Not provided",
|
|
deliveryType: order.deliveryType || "Not provided",
|
|
};
|
|
|
|
const store = {
|
|
storeId: order.storeId,
|
|
store: order.storeName,
|
|
};
|
|
|
|
return (
|
|
<div className="pl-0 w-full">
|
|
<div className="bg-white border-b p-1 sm:p-2 flex justify-between items-center">
|
|
<div className="flex items-center gap-1 sm:gap-3">
|
|
<Button variant="ghost" size="sm" onClick={handleBack} className="p-1 sm:p-2">
|
|
<ArrowLeft className="h-4 w-4 sm:h-5 sm:w-5 text-muted-foreground" />
|
|
<span className="ml-1 sm:ml-2 text-xs sm:text-sm hidden xs:inline">Voltar</span>
|
|
</Button>
|
|
<div>
|
|
<h1 className="text-sm sm:text-lg font-semibold text-gray-800">
|
|
Pedido {order.orderId}
|
|
</h1>
|
|
<p className="text-xs sm:text-sm text-muted-foreground hidden sm:block">
|
|
Detalhes completos do pedido
|
|
</p>
|
|
</div>
|
|
</div>
|
|
<div className="flex justify-center p-2">
|
|
<QRCodeRastreamento orderId={order.orderId} /> </div>
|
|
</div>
|
|
|
|
<div className="p-2 sm:p-6 space-y-4 sm:space-y-6 mx-auto">
|
|
<div className="grid grid-cols-1 xl:grid-cols-4 gap-4 sm:gap-6">
|
|
<div className="xl:col-span-3">
|
|
<OrderInfoCard
|
|
order={orderWithDefaults}
|
|
delivery={orderDelivery}
|
|
loja={store}
|
|
orderItems={orderItems}
|
|
cutitens={cutItems}
|
|
transfers={transfers}
|
|
status={statusData}
|
|
/>
|
|
</div>
|
|
<div className="hidden sm:block xl:col-span-1">
|
|
<div className="bg-white border rounded-lg p-4 shadow-sm">
|
|
<Timeline orderId={order.orderId} />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|