import { useState, useEffect, useCallback, useRef, useMemo } from 'react'; import { Form, Input, AutoComplete, Select, Tooltip, Modal } from 'antd'; import { BsUpcScan } from 'react-icons/bs'; import { ArrowRightOutlined, DeleteFilled } from '@ant-design/icons'; import { useDispatch, useSelector } from 'react-redux'; import FormHeader from '../PageComponents/FormHeader.jsx'; import FloatLabel from '../../Components/Forms/FloatLabel/index.jsx'; import { Messages } from '../../Components/Notifications/Messages.jsx'; import Buttons from '../../Components/Forms/Buttons.jsx'; import './CreateTransfer.scss'; import { getPurProdvaraiantdata } from '../../Features/PurchaseOrder/PurchaseOrder.js'; import { getProductDataPageNo } from '../../Features/ProductPage/ProductPage.js'; import { DropDowns } from '../../Components/Forms/DropDown.jsx'; import { getSession } from '../../Services/Others.js'; import { getCompBranchData } from '../../Features/BrachLogin/BranchLogin.js'; import { changeBreadCrumb } from '../../Features/AppPage/CenterPage.js'; import { useLocation, useNavigate } from 'react-router-dom'; import { postRequestStock } from '../../Features/RequestStock/RequestStock.js'; import BarCodeScan from '../../Services/BarCodeScan.jsx'; import { isMobile } from 'react-device-detect'; const subDirectory = import.meta.env.BASE_URL; function debounce(func, wait) { let timeout; return function executedFunction(...args) { const later = () => { clearTimeout(timeout); func(...args); }; clearTimeout(timeout); timeout = setTimeout(later, wait); }; } const CreateTransfer = () => { const dispatch = useDispatch(); const formProductRef = useRef(); const productInputRef = useRef(null); const location = useLocation(); const state = location?.state; const status = state.transform; const navigate = useNavigate(); const CompId = getSession('CompId'); const BranchId = getSession('BranchId'); const AppId = getSession('AppId'); const UserId = getSession('UserId'); const [messageType, setMessageType] = useState(null); const [messageData, setMessageData] = useState(null); const [productSearchText, setProductSearchText] = useState(''); const [searchText, setSearchText] = useState(''); const [tableData, setTableData] = useState([]); const [scanner, setScanner] = useState(false); const [productdata, setProductData] = useState([]); const [variantDetails, setVariantDetails] = useState([]); const [showVariantModal, setShowVariantModal] = useState(false); const [selectedProductForVariant, setSelectedProductForVariant] = useState(null); const [selectedVariants, setSelectedVariants] = useState([]); const [isProcessing, setIsProcessing] = useState(false); const [isManualSelection, setIsManualSelection] = useState(false); const [branchData, setBranchData] = useState(); const [selectedBranchId, setSelectedBranchId] = useState(); const [selectedComponent, setselectedComponent] = useState('Create'); const items = [ { name: 'Home', link: `${subDirectory}app-page/home`, }, { name: 'Transfer List', link: `${subDirectory}setting/stock-transferlist`, }, { name: status === 'Create' ? 'Create' : 'Request ', link: null, }, ]; useEffect(() => { dispatch(changeBreadCrumb({ items: items })); fetchbranchData(); fetchProductData(); }, []); const onComplete = useCallback(() => { setMessageType(null); setMessageData(null); }, []); const productOptions = useMemo( () => productdata?.map((p) => ({ value: p.ProdId, label: p.ProdName, })) || [], [productdata] ); const filteredTableData = useMemo(() => { if (!searchText) return tableData; return tableData.filter( (item) => item.ProdName?.toLowerCase().includes(searchText.toLowerCase()) || item.ProdVariantName?.toLowerCase().includes(searchText.toLowerCase()) ); }, [tableData, searchText]); console.log(filteredTableData, 'AaADaAadaadAD'); const fetchbranchData = async () => { const data = { AppId: AppId, CompId: CompId, }; const res = await dispatch(getCompBranchData(data)).unwrap(); if (res?.data?.statusCode === 1) { const filteredBranches = res?.data?.data?.filter( (branch) => branch.BrId !== BranchId ); setBranchData(filteredBranches); } }; const fetchProductData = async () => { try { const ProdData = { AppId: AppId, CompId: CompId, BranchId: BranchId, }; const res = await dispatch(getProductDataPageNo(ProdData)).unwrap(); if (res?.data?.statusCode === 1) { setProductData(res?.data?.data || []); } else { setMessageType('error'); setMessageData('Failed to load products'); } } catch (error) { console.error('Error fetching products:', error); setMessageType('error'); setMessageData('Error loading products'); } }; const branchDropDownChange = async (e) => { setSelectedBranchId(e); }; const addProductToTable = useCallback((product, variant = null) => { const newProduct = { ...product, ...(variant && { ProdVariantId: variant.ProdVariantId, ProdVariantName: variant.ProdVariantName, InwardDtlId: variant.StockDetails?.[0]?.InwardDtlId, MRP: variant.StockDetails?.[0]?.MRP, SellPrice: variant.StockDetails?.[0]?.SellPrice, OverAllQty: variant.OverAllQty, }), }; let success = false; setTableData((prev) => { const safePrev = Array.isArray(prev) ? prev : []; const exists = safePrev.some( (p) => p.ProdId === newProduct.ProdId && (variant ? p.ProdVariantId === variant.ProdVariantId : !p.ProdVariantId) ); if (exists) { return safePrev; } success = true; // return [...safePrev, newProduct]; return [newProduct, ...safePrev]; }); if (!success) { setMessageType('warning'); setMessageData( variant ? `Already added in the table` : 'This product is already Added' ); } if (scanner) { setTimeout(() => { productInputRef.current?.focus(); setProductSearchText(''); }, 100); } formProductRef?.current?.resetFields(); if (!scanner) { setProductSearchText(''); } return success; }, [scanner]); const focusProductInput = () => { // give React a tick to close modal/input blur before refocusing setTimeout(() => productInputRef.current?.focus(), 0); }; const handleProductSearch = (value) => { setProductSearchText(value); if (scanner && value.length >= 3 && !isManualSelection) { debouncedScannerSearch(value); } if (isManualSelection) { setIsManualSelection(false); } }; const debouncedScannerSearch = useCallback( debounce((value) => { handleScannerResult(value); }, 300), [productdata] ); const handleScannerResult = async (qrCode) => { if (isProcessing) return; // Prevent multiple simultaneous processing setIsProcessing(true); try { const selectedProduct = productdata.find( (p) => p.QRCode === qrCode || p.ProdId === qrCode ); if (!selectedProduct) { setMessageType('error'); setMessageData('Product not found for scanned code'); setProductSearchText(''); return; } await processSelectedProduct(selectedProduct, true); // Pass true to indicate scanner mode } finally { setIsProcessing(false); } }; const handleProductSelect = async (prodId, option) => { if (isProcessing) return; setIsManualSelection(true); setIsProcessing(true); try { const selectedProduct = productdata.find((p) => p.ProdId === prodId); if (!selectedProduct) { setMessageType('error'); setMessageData('Product not found'); return; } await processSelectedProduct(selectedProduct, false); } finally { setIsProcessing(false); } }; const processSelectedProduct = async (selectedProduct, isScanner = false) => { if (!selectedProduct) { setMessageType('error'); setMessageData('Product not found'); return; } formProductRef.current?.setFieldsValue({ ProdId: selectedProduct.ProdId }); setProductSearchText(selectedProduct.ProdName); setSelectedProductForVariant(selectedProduct); try { const data = { CompId, AppId, BranchId, prodName: selectedProduct.ProdName, }; const variantValues = await dispatch( getPurProdvaraiantdata(data) ).unwrap(); if (variantValues?.data?.statusCode === 1) { const variants = variantValues?.data?.data?.filter( (prod) => prod?.ProductDetail?.[0]?.ProdId === selectedProduct?.ProdId )?.[0]?.ProductDetail?.[0]?.ProdVariantDetails || []; // Add unique identifier to each variant if not present const variantsWithId = variants.map((variant, index) => ({ ...variant, ProdVariantId: variant.ProdVariantId || variant.StockDetails?.[0]?.InwardDtlId || `${selectedProduct.ProdId}_${variant.ProdVariantName}_${index}`, })); if (variantsWithId.length > 1) { setVariantDetails(variantsWithId); setSelectedVariants([]); // Reset selected variants setShowVariantModal(true); } else if (variantsWithId.length === 1) { addProductToTable(selectedProduct, variantsWithId[0]); } else { addProductToTable(selectedProduct); } } else { addProductToTable(selectedProduct); } } catch (error) { console.error('Error fetching variants:', error); addProductToTable(selectedProduct); } }; const handleFieldChange = (field, index, value) => { setTableData((prev) => { const updated = [...prev]; const actualItem = filteredTableData[index]; const actualIndex = prev?.findIndex( (p) => p.ProdId === actualItem.ProdId && p.ProdVariantId === actualItem.ProdVariantId ); if (actualIndex !== -1) { updated[actualIndex][field] = value; } return updated; }); }; const handleVariantCheckboxChange = (variantId, checked) => { setSelectedVariants((prev) => { if (checked) { return [...prev, variantId]; } else { return prev.filter((id) => id !== variantId); } }); }; const handleAddSelectedVariants = () => { if (selectedVariants.length === 0) { setMessageType('warning'); setMessageData('Please select at least one variant'); return; } if (selectedProductForVariant) { let addedCount = 0; selectedVariants.forEach((variantId) => { const variant = variantDetails.find( (v) => v.ProdVariantId === variantId ); if (variant) { const success = addProductToTable(selectedProductForVariant, variant); if (success) addedCount++; } if (addedCount > 0) { setMessageType('success'); setMessageData( `${variant.ProdVariantName} added to transfer successfully` ); } }); } setShowVariantModal(false); setVariantDetails([]); setSelectedProductForVariant(null); setSelectedVariants([]); if (scanner) { setTimeout(() => { productInputRef.current?.focus(); setProductSearchText(''); }, 100); } }; const handleDeleteRow = (index) => { const productToDelete = filteredTableData[index]; setTableData((prev) => prev?.filter( (p) => !( p.ProdId === productToDelete.ProdId && p.ProdVariantId === productToDelete.ProdVariantId ) ) ); setMessageType('success'); setMessageData(`${productToDelete.ProdName} removed successfully`); }; const onFinish = async () => { if (tableData.length === 0) { setMessageType('warning'); setMessageData('Please add at least one product'); return; } const invalidRow = tableData.find( (item) => !item.ReceivedQty || Number(item.ReceivedQty) <= 0 ); if (invalidRow) { setMessageType('warning'); setMessageData('Required Qty must be greater than 0 for all products'); return; } if (!selectedBranchId) { setMessageType('warning'); setMessageData('Please select To Location (Branch)'); return; } const payload = { CompId: CompId, BranchId: BranchId, AppId: AppId, FromLocationId: selectedComponent === 'Create' ? selectedBranchId : BranchId, ToLocationId: selectedComponent === 'Create' ? BranchId : selectedBranchId, RequestStatus: selectedComponent, RequestStock: tableData?.map((item) => ({ ProdId: item.ProdId, InwardDtlId: item.InwardDtlId, ProdVariantName:item.ProdVariantName, RequestedQty: Number(item.ReceivedQty), ProdVariantName: item.ProdVariantName, })), CreatedBy: UserId, }; const response = await dispatch(postRequestStock(payload)).unwrap(); if (response?.data?.statusCode === 1) { setMessageType('success'); setMessageData(response?.data?.response); setTableData(''); setSelectedBranchId(''); navigate(`${subDirectory}setting/stock-transferlist`); } else { setMessageType('error'); setMessageData(response?.data?.response); } }; useEffect(() => { if (status && status !== selectedComponent) { setselectedComponent(status); } }, [status]); const ComponentSelect = (value) => { setselectedComponent(value); }; const handleScannerToggle = () => { setScanner((prev) => { const next = !prev; if (next) { setIsManualSelection(false); setTimeout(() => productInputRef.current?.focus(), 0); } return next; }); }; return (
{/* */} {/*
{status === 'Create' &&
ComponentSelect(status)} > {status} Transfer
} {status === 'Request' &&
ComponentSelect(status)} > {status} Transfer
}
*/}
handleProductSelect(value, option) } onChange={handleProductSearch} filterOption={(input, option) => option.label.toLowerCase().includes(input.toLowerCase()) } disabled={isProcessing} /> {isMobile ? (
{ debouncedScannerSearch(value); }} />
) : ( )}
Branch} className="field-DropDown" options={branchData?.map((option) => ({ value: option.BrId, label: option.BrName, }))} onChangeFunction={(e) => branchDropDownChange(e)} valueData={selectedBranchId} />
{/*
setSearchText(e.target.value)} className="transfer-search-input" />
*/}
{selectedComponent === 'Create' && } {filteredTableData.length === 0 ? ( ) : ( filteredTableData?.map((record, index) => ( {selectedComponent === 'Create' && ( )} )) )}
S.No Product Name Product Variant UOM NameBalance QtyRequired Qty Action
No products added yet.
{index + 1} {record.ProdName} {record.ProdVariantName || 'N/A'} {record.UomName}{record.OverAllQty} handleFieldChange( 'ReceivedQty', index, e.target.value.replace(/[^0-9.]/g, '') ) } /> handleDeleteRow(index)} />
} buttonText={status} color="901D77" htmlType="submit" />
{ setShowVariantModal(false); setVariantDetails([]); setSelectedProductForVariant(null); setSelectedVariants([]); if (scanner) { setTimeout(() => { productInputRef.current?.focus(); setProductSearchText(''); }, 100); } }} onOk={handleAddSelectedVariants} okText="Add Selected Variants" cancelText="Cancel" width={500} >
{variantDetails && variantDetails.length > 0 ? (

Select variant(s) for{' '} {selectedProductForVariant?.ProdName}

{variantDetails.map((variant) => (
handleVariantCheckboxChange( variant.ProdVariantId, !selectedVariants.includes(variant.ProdVariantId) ) } >
handleVariantCheckboxChange( variant.ProdVariantId, e.target.checked ) } onClick={(e) => e.stopPropagation()} style={{ width: '18px', height: '18px', cursor: 'pointer', accentColor: '#901D77', }} />

{variant.ProdVariantName}

MRP: ₹{variant.StockDetails?.[0]?.MRP || 0} | Sell Price: ₹{variant.StockDetails?.[0]?.SellPrice || 0}

))}

Selected: {selectedVariants.length} variant(s)

) : (

No variants available

)}
); }; export default CreateTransfer;