266 lines
15 KiB
TypeScript
266 lines
15 KiB
TypeScript
import React, { useState } from "react";
|
||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||
import { faCircleInfo, faCubesStacked, faXmark } from "@fortawesome/free-solid-svg-icons";
|
||
import { ShipmentRequest } from "@/interfaces/interfaces";
|
||
import ShipmentItemsAccordion from "@/Components/ShipmentItemsAccordion";
|
||
import axios from "axios";
|
||
|
||
interface WarehouseExpediceDialogProps {
|
||
selectedShipment: ShipmentRequest;
|
||
isDialogOpen: boolean;
|
||
closeDialog: () => void;
|
||
handleDialogProcess: () => void;
|
||
dialogToggleClose: () => void;
|
||
selectedType: "parcels" | "processed" | null;
|
||
buttonStates: { [itemId: number]: any };
|
||
setButtonStates: React.Dispatch<React.SetStateAction<{ [itemId: number]: any }>>;
|
||
}
|
||
|
||
export default function WarehouseExpediceDialog({
|
||
selectedShipment,
|
||
isDialogOpen,
|
||
closeDialog,
|
||
handleDialogProcess,
|
||
dialogToggleClose,
|
||
selectedType,
|
||
buttonStates,
|
||
setButtonStates,
|
||
}: WarehouseExpediceDialogProps) {
|
||
// Track which tab is active: "stock" or "details"
|
||
const [activeTab, setActiveTab] = useState<"stock" | "details">("stock");
|
||
const [imageArray, setImageArray] = useState<Record<number, { url: string }>>({});
|
||
|
||
const fetchItemImages = () => {
|
||
axios
|
||
.get("/api/expediceListWMS/getImage", {
|
||
params: { selectedShipmentID: selectedShipment.id },
|
||
})
|
||
.then((response) => {
|
||
setImageArray(response.data.imageArray || {});
|
||
})
|
||
.catch((error) => {
|
||
console.error("Error fetching image:", error);
|
||
});
|
||
};
|
||
|
||
// Whenever user clicks the Details tab, fetch images
|
||
const handleDetailsClick = () => {
|
||
setActiveTab("details");
|
||
fetchItemImages();
|
||
};
|
||
|
||
return (
|
||
<div className={`modal ${isDialogOpen ? "modal-open" : ""}`}>
|
||
{/* Modal backdrop + box */}
|
||
<div className="modal-box max-w-4xl max-h-[90vh] overflow-hidden p-0">
|
||
{/* Header with Tabs and Close button */}
|
||
<div className="flex items-center justify-between border-b border-base-300 px-4 py-3">
|
||
<div className="tabs">
|
||
<a
|
||
className={`tab ${activeTab === "stock" ? "tab-active" : ""}`}
|
||
onClick={() => setActiveTab("stock")}
|
||
>
|
||
<div className="flex items-center gap-2">
|
||
<FontAwesomeIcon icon={faCubesStacked} />
|
||
<span>Items</span>
|
||
</div>
|
||
</a>
|
||
<a
|
||
className={`tab ${activeTab === "details" ? "tab-active" : ""}`}
|
||
onClick={handleDetailsClick}
|
||
>
|
||
<div className="flex items-center gap-2">
|
||
<FontAwesomeIcon icon={faCircleInfo} />
|
||
<span>Details</span>
|
||
</div>
|
||
</a>
|
||
</div>
|
||
<button
|
||
onClick={closeDialog}
|
||
className="btn btn-ghost btn-circle text-base-content hover:text-error"
|
||
>
|
||
<FontAwesomeIcon icon={faXmark} size="lg" />
|
||
</button>
|
||
</div>
|
||
|
||
{/* Body */}
|
||
<div className="overflow-y-auto p-4 bg-base-100">
|
||
{activeTab === "details" && (
|
||
<div className="space-y-6">
|
||
{/* Reference */}
|
||
<div>
|
||
<h2 className="text-2xl font-semibold text-base-content">
|
||
Ref. # {selectedShipment.shipment_reference}
|
||
</h2>
|
||
</div>
|
||
|
||
{/* Items Section */}
|
||
<div className="bg-base-200 p-4 rounded-lg shadow">
|
||
<h3 className="text-lg font-semibold text-base-content mb-2">Items</h3>
|
||
{selectedShipment.items.map((item) => (
|
||
<div key={item.id} className="collapse collapse-arrow border border-base-300 rounded-lg mb-4">
|
||
<input type="checkbox" className="peer" />
|
||
<div className="collapse-title flex items-center justify-between bg-base-100 text-base-content px-4 py-2">
|
||
<div className="flex items-center gap-4">
|
||
<a
|
||
href={imageArray[item.id]?.url || "#"}
|
||
target="_blank"
|
||
rel="noopener noreferrer"
|
||
className="w-24 h-36 bg-base-300 rounded overflow-hidden flex-shrink-0 flex items-center justify-center"
|
||
>
|
||
{imageArray[item.id]?.url ? (
|
||
<img src={imageArray[item.id].url} alt="Item" className="w-full h-full object-center" />
|
||
) : (
|
||
<div className="w-auto h-full bg-base-200 flex items-center justify-center text-sm text-base-content">
|
||
No Image
|
||
</div>
|
||
)}
|
||
</a>
|
||
<div>
|
||
<p className="font-bold">{item.quantity}× {item.name}</p>
|
||
</div>
|
||
</div>
|
||
<div className="text-sm text-base-content">
|
||
{(item.price / item.quantity).toFixed(2)} {selectedShipment.currency}
|
||
</div>
|
||
</div>
|
||
<div className="collapse-content bg-base-100 text-base-content p-4">
|
||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||
<div>
|
||
<p className="font-semibold">Model Number:</p>
|
||
<p>{item.model_number}</p>
|
||
</div>
|
||
<div>
|
||
<p className="font-semibold">Origin:</p>
|
||
<p>{item.originCountry}</p>
|
||
</div>
|
||
<div>
|
||
<p className="font-semibold">Weight:</p>
|
||
<p>{item.weight}</p>
|
||
</div>
|
||
</div>
|
||
{item.stockData && Object.keys(item.stockData).length > 0 && (
|
||
<div className="mt-4">
|
||
<p className="font-semibold mb-2">Stock Information:</p>
|
||
{Object.entries(item.stockData).flatMap(([stockName, stockArray]) =>
|
||
stockArray.map((stock, idx) => (
|
||
<div
|
||
key={`${stockName}-${idx}`}
|
||
className="border-b border-base-300 pb-2 mb-2 last:border-0"
|
||
>
|
||
<p className="font-bold">{stockName}</p>
|
||
<p>Location: {stock.location}</p>
|
||
<p>Count: {stock.count} ks</p>
|
||
</div>
|
||
))
|
||
)}
|
||
</div>
|
||
)}
|
||
</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
|
||
{/* Delivery Address */}
|
||
<div className="bg-base-200 p-4 rounded-lg shadow">
|
||
<h3 className="text-lg font-semibold text-base-content mb-2">Delivery Address</h3>
|
||
<div className="space-y-1 text-base-content">
|
||
<p>
|
||
<span className="font-semibold">Name:</span> {selectedShipment.delivery_address_name}
|
||
</p>
|
||
{selectedShipment.delivery_address_company_name && (
|
||
<p>
|
||
<span className="font-semibold">Company:</span> {selectedShipment.delivery_address_company_name}
|
||
</p>
|
||
)}
|
||
<p>
|
||
<span className="font-semibold">Street:</span> {selectedShipment.delivery_address_street_name}{" "}
|
||
{selectedShipment.delivery_address_street_number}
|
||
</p>
|
||
<p>
|
||
<span className="font-semibold">City:</span> {selectedShipment.delivery_address_city}
|
||
</p>
|
||
<p>
|
||
<span className="font-semibold">ZIP:</span> {selectedShipment.delivery_address_zip}
|
||
</p>
|
||
{selectedShipment.delivery_address_state_iso && (
|
||
<p>
|
||
<span className="font-semibold">State:</span> {selectedShipment.delivery_address_state_iso}
|
||
</p>
|
||
)}
|
||
<p>
|
||
<span className="font-semibold">Country:</span> {selectedShipment.delivery_address_country_iso}
|
||
</p>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Carrier & Shipment Info */}
|
||
<div className="bg-base-200 p-4 rounded-lg shadow">
|
||
<h3 className="text-lg font-semibold text-base-content mb-2">Carrier & Shipment Info</h3>
|
||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 text-base-content">
|
||
<div>
|
||
<p className="font-semibold">Carrier:</p>
|
||
<p>{selectedShipment.carrier.carrier_name}</p>
|
||
</div>
|
||
<div>
|
||
<p className="font-semibold">Tracking Number:</p>
|
||
<p>{selectedShipment.shipment?.tracking_number || ""}</p>
|
||
</div>
|
||
<div>
|
||
<p className="font-semibold">Shipment Price:</p>
|
||
<p>
|
||
{selectedShipment.currency} {selectedShipment.shipment_price}
|
||
</p>
|
||
</div>
|
||
<div>
|
||
<p className="font-semibold">Shipment Value:</p>
|
||
<p>{selectedShipment.shipment_value}</p>
|
||
</div>
|
||
<div>
|
||
<p className="font-semibold">Weight:</p>
|
||
<p>{selectedShipment.weight}</p>
|
||
</div>
|
||
<div>
|
||
<p className="font-semibold">Dimensions:</p>
|
||
<p>
|
||
{selectedShipment.length} × {selectedShipment.width} × {selectedShipment.height}
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
)}
|
||
|
||
{activeTab === "stock" && (
|
||
<div className="text-base-content">
|
||
<ShipmentItemsAccordion
|
||
selectedShipment={selectedShipment}
|
||
buttonStates={buttonStates}
|
||
setButtonStates={setButtonStates}
|
||
parentModalHandle={dialogToggleClose}
|
||
/>
|
||
</div>
|
||
)}
|
||
</div>
|
||
|
||
{/* Footer with action button */}
|
||
<div className="modal-action border-t border-base-300 p-4 flex justify-end gap-2">
|
||
{selectedType === "parcels" && (
|
||
<button className="btn btn-primary" onClick={handleDialogProcess}>
|
||
Mark as Processed
|
||
</button>
|
||
)}
|
||
{selectedType === "processed" && (
|
||
<button className="btn btn-error" onClick={handleDialogProcess}>
|
||
Mark as Unprocessed
|
||
</button>
|
||
)}
|
||
<button className="btn btn-ghost" onClick={closeDialog}>
|
||
Close
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|