/* eslint-disable @typescript-eslint/no-explicit-any */ import React, { useEffect, useState, useMemo } from "react"; import CreateOrderFooter from "./CreateOrderFooter"; import PackageGrid from "./Step1/PackageGrid"; import SelectOptions, { OptionType } from "../../../TextField/SelectOptions"; import { getMasterList, getInternationalCities, } from "../../../../Api/MastersApi"; import HourGlassLoader from "../../../Loader/Loader"; import { initiateInternationalDraftOrder } from "../../../../Api/InternationalOrderApi"; import { useAppSelector } from "@/Redux/Hooks"; import RequiredStar from "./RequiredStar"; type Step1Props = { onSave: (data: any) => void; step: number; setStep: React.Dispatch>; defaultOrderDetails: any; isEditMode?: boolean; confirmSaveStep?: (step: number, data: any) => void; }; interface PackageDetail { weight: string; length: string; width: string; height: string; package_description: string; customer_input_package_value: string; } const Step1: React.FC = ({ onSave, step, setStep, defaultOrderDetails, isEditMode, }) => { const [isLoading, setIsLoading] = useState(false); const createOrderData: any = useAppSelector( (state) => state.createOrder )?.step1; const [fromCountry, setFromCountry] = useState(null); const [toCountry, setToCountry] = useState(null); const [fromCity, setFromCity] = useState(null); const [toCity, setToCity] = useState(null); const [fromCityLoading, setFromCityLoading] = useState(false); const [toCityLoading, setToCityLoading] = useState(false); const [shipmentContent, setShipmentContent] = useState<"Document" | "Parcel">( "Parcel" ); const [noOfPackages, setNoOfPackages] = useState(1); const [packageDetails, setPackageDetails] = useState([ { weight: "", length: "", width: "", height: "", package_description: "", customer_input_package_value: "", }, ]); const [countries, setCountries] = useState([]); const [fromCities, setFromCities] = useState([]); const [toCities, setToCities] = useState([]); // Validation errors state const [errors, setErrors] = useState<{ fromCountry?: string; toCountry?: string; fromCity?: string; toCity?: string; packageDetails?: { [key: number]: { [field: string]: string } }; }>({}); useEffect(() => { const fetchCountries = async () => { try { setIsLoading(true); const response = await getMasterList("country"); if (response?.status) { const countryOptions = response.data.map((c: any) => ({ value: c.id, label: c.name, code: c.code, name: c.name, id: c.id, })); setCountries(countryOptions); // Set default From Country to Bahrain if available const bahrain = countryOptions.find( (c: any) => c.code === "BH" || c.name === "Bahrain" ); if (bahrain) setFromCountry(bahrain); } } catch (err) { console.error("Error fetching countries:", err); } finally { setIsLoading(false); } }; fetchCountries(); }, []); useEffect(() => { if (!fromCountry) return; setFromCity(null); // reset ONLY when fromCountry changes setFromCities([]); const fetchFromCities = async () => { try { setFromCityLoading(true); const response = await getInternationalCities({ country_id: fromCountry.value, }); if (response?.status) { setFromCities( response.data.map((c: any) => ({ value: c.id, label: c.name, name: c.name, id: c.id, })) ); } } catch (err) { console.error("Error fetching from cities:", err); } finally { setFromCityLoading(false); } }; fetchFromCities(); }, [fromCountry]); useEffect(() => { if (!toCountry) return; setToCity(null); // reset ONLY when toCountry changes setToCities([]); const fetchToCities = async () => { try { setToCityLoading(true); const response = await getInternationalCities({ country_id: toCountry.value, }); if (response?.status) { setToCities( response.data.map((c: any) => ({ value: c.id, label: c.name, name: c.name, id: c.id, })) ); } } catch (err) { console.error("Error fetching to cities:", err); } finally { setToCityLoading(false); } }; fetchToCities(); }, [toCountry]); useEffect(() => { if (createOrderData) { if (createOrderData.fromCountry) setFromCountry(createOrderData.fromCountry); if (createOrderData.toCountry) setToCountry(createOrderData.toCountry); if (createOrderData.fromCity) setFromCity(createOrderData.fromCity); if (createOrderData.toCity) setToCity(createOrderData.toCity); if (createOrderData.shipmentContent) setShipmentContent(createOrderData.shipmentContent); if (createOrderData.noOfPackages) setNoOfPackages(createOrderData.noOfPackages); // Check both packageDetails and package_details if (createOrderData.packageDetails) setPackageDetails(createOrderData.packageDetails); else if (createOrderData.package_details) setPackageDetails(createOrderData.package_details); } }, [createOrderData]); const handlePackagesChange = (val: number) => { const count = Math.max(1, val); setNoOfPackages(count); const updated = [...packageDetails]; if (count > updated.length) { for (let i = updated.length; i < count; i++) { updated.push({ weight: "", length: "", width: "", height: "", package_description: "", customer_input_package_value: "", }); } } else { updated.splice(count); } setPackageDetails(updated); onSave({ noOfPackages: count, packageDetails: updated }); }; const handleSwap = () => { const tempCountry = fromCountry; const tempCity = fromCity; setFromCountry(toCountry); setToCountry(tempCountry); setFromCity(toCity); setToCity(tempCity); onSave({ fromCountry: toCountry, toCountry: tempCountry, fromCity: toCity, toCity: tempCity, }); }; const totalWeight = useMemo(() => { return packageDetails.reduce( (sum, pkg) => sum + (parseFloat(pkg.weight) || 0), 0 ); }, [packageDetails]); const totalValue = useMemo(() => { return packageDetails.reduce( (sum, pkg) => sum + (parseFloat(pkg.customer_input_package_value) || 0), 0 ); }, [packageDetails]); const validateStep1 = async () => { const newErrors: typeof errors = {}; // Validate countries and cities if (!fromCountry) { newErrors.fromCountry = "From Country is required"; } if (!toCountry) { newErrors.toCountry = "To Country is required"; } if (!fromCity) { newErrors.fromCity = "From City is required"; } if (!toCity) { newErrors.toCity = "To City is required"; } // Validate package details (only length, width, height are mandatory) const packageErrors: { [key: number]: { [field: string]: string } } = {}; packageDetails.forEach((pkg, index) => { const pkgErrors: { [field: string]: string } = {}; // Only validate dimensions - weight, description, and value are optional if (!pkg.length || parseFloat(pkg.length) <= 0) { pkgErrors.length = "Length is required"; } if (!pkg.width || parseFloat(pkg.width) <= 0) { pkgErrors.width = "Width is required"; } if (!pkg.height || parseFloat(pkg.height) <= 0) { pkgErrors.height = "Height is required"; } if (!pkg.customer_input_package_value || parseFloat(pkg.customer_input_package_value) <= 0) { pkgErrors.customer_input_package_value = "Package value is required"; } if (Object.keys(pkgErrors).length > 0) { packageErrors[index] = pkgErrors; } }); if (Object.keys(packageErrors).length > 0) { newErrors.packageDetails = packageErrors; } // Set errors and return false if any validation failed if ( Object.keys(newErrors).length > 0 || Object.keys(packageErrors).length > 0 ) { setErrors(newErrors); return false; } // Clear errors if validation passed setErrors({}); const allData = JSON.parse(localStorage.getItem("ALL_DATA") || "{}")?.data; const payload = { created_for_id: allData?.user?.id, fromCountry, toCountry, fromCity, toCity, shipmentContent, noOfPackages, package_details: packageDetails, total_weight: totalWeight, total_value: totalValue, }; try { setIsLoading(true); const response = await initiateInternationalDraftOrder(payload); console.log(response, "asdf"); if (response.status) { onSave({ ...payload, draft_order_id: response.data.id }); return true; } else { // Handle API error setErrors({ fromCountry: "Failed to initiate order. Please try again.", }); return false; } } catch (error) { console.error("Error initiating draft:", error); setErrors({ fromCountry: "An error occurred. Please try again." }); return false; } finally { setIsLoading(false); } }; return ( <> {isLoading && }
{/* Main Content */}
{/* From Section */}
From
{ setFromCountry(val); if (errors.fromCountry) { setErrors({ ...errors, fromCountry: undefined, }); } }} placeholder="Select Country" divClassName="mb-0" style={{ control: (base: any) => ({ ...base, borderRadius: "15px", height: "50px", paddingLeft: "40px", border: errors.fromCountry ? "1px solid #dc3545" : "1px solid #dee2e6", boxShadow: "none", "&:hover": { borderColor: errors.fromCountry ? "#dc3545" : "#dee2e6", }, }), }} />
{errors.fromCountry && (
{errors.fromCountry}
)}
{ setFromCity(val); if (errors.fromCity) { setErrors({ ...errors, fromCity: undefined }); } }} placeholder={ !fromCountry ? "Select Country First" : fromCityLoading ? "Loading cities..." : "Select City" } divClassName="mb-0" style={{ control: (base: any) => ({ ...base, borderRadius: "15px", height: "50px", paddingLeft: "40px", border: errors.fromCity ? "1px solid #dc3545" : "1px solid #dee2e6", boxShadow: "none", "&:hover": { borderColor: errors.fromCity ? "#dc3545" : "#dee2e6", }, }), }} />
{errors.fromCity && (
{errors.fromCity}
)}
{/* Vertical Separator & Swap Button */}
{/* To Section */}
To
{ setToCountry(val); if (errors.toCountry) { setErrors({ ...errors, toCountry: undefined }); } }} placeholder="Select Country" divClassName="mb-0" style={{ control: (base: any) => ({ ...base, borderRadius: "15px", height: "50px", paddingLeft: "40px", border: errors.toCountry ? "1px solid #dc3545" : "1px solid #dee2e6", boxShadow: "none", "&:hover": { borderColor: errors.toCountry ? "#dc3545" : "#dee2e6", }, }), }} />
{errors.toCountry && (
{errors.toCountry}
)}
{ setToCity(val); if (errors.toCity) { setErrors({ ...errors, toCity: undefined }); } }} isDisabled={!toCountry || toCityLoading} placeholder={ !toCountry ? "Select Country First" : toCityLoading ? "Loading cities..." : "Select City" } divClassName="mb-0" style={{ control: (base: any) => ({ ...base, borderRadius: "15px", height: "50px", paddingLeft: "40px", border: errors.toCity ? "1px solid #dc3545" : "1px solid #dee2e6", boxShadow: "none", "&:hover": { borderColor: errors.toCity ? "#dc3545" : "#dee2e6", }, }), }} />
{errors.toCity && (
{errors.toCity}
)}
What is Your Shipment Content?
Number of Packages
handlePackagesChange(parseInt(e.target.value)) } min="1" />
Total Weight
{totalWeight.toFixed(2)} Kg
No. of Packages ({String(noOfPackages).padStart(2, "0")})
{ setPackageDetails(updatedPackages); // Clear package errors when user updates if (errors.packageDetails) { setErrors({ ...errors, packageDetails: undefined }); } }} errors={errors.packageDetails || null} />
{ if (await validateStep1()) setStep(nextStep); }} validation={validateStep1} backButton={false} defaultOrderDetails={defaultOrderDetails} isEditMode={isEditMode} isInternational={true} /> ); }; export default Step1;