Merge pull request 'stockform optimization and split into components' (#92) from feature/pricebulkchanges into main
Reviewed-on: Pozomind/pozo-retail-app#92
This commit is contained in:
commit
ca56cced6e
|
|
@ -1,5 +1,4 @@
|
|||
import React, { useState, useRef, useEffect } from 'react';
|
||||
import { AppstoreOutlined, MailOutlined, SettingOutlined, } from '@ant-design/icons';
|
||||
import { useState, useRef, useEffect } from 'react';
|
||||
import { FaCaretRight } from "react-icons/fa";
|
||||
|
||||
import './PozoMenu.scss';
|
||||
|
|
@ -8,7 +7,6 @@ const PozoMenu = ({ items = [], mode = "vertical", style = {}, onClick, collapse
|
|||
const [openKeys, setOpenKeys] = useState([]);
|
||||
const [selectedKey, setSelectedKey] = useState('');
|
||||
const [dropdownPositions, setDropdownPositions] = useState({});
|
||||
console.log(dropdownPositions, "dropdownPositions")
|
||||
const [isMobile, setIsMobile] = useState(window.innerWidth <= 768);
|
||||
const menuRef = useRef();
|
||||
|
||||
|
|
@ -115,7 +113,7 @@ const PozoMenu = ({ items = [], mode = "vertical", style = {}, onClick, collapse
|
|||
// Image 2: No space on right, space on left - open to LEFT
|
||||
position.left = rect.left - dropdownWidth - gap + scrollX;
|
||||
position.direction = 'left';
|
||||
console.log('✅ Opening LEFT - no space on right');
|
||||
|
||||
} else {
|
||||
// Edge case: Limited space on both sides
|
||||
if (spaceRight >= spaceLeft) {
|
||||
|
|
@ -245,7 +243,7 @@ const PozoMenu = ({ items = [], mode = "vertical", style = {}, onClick, collapse
|
|||
|
||||
// Calculate position and add to dropdown positions
|
||||
const position = calculatePosition(event.currentTarget, parentKeys.length);
|
||||
console.log('🎯 Setting position for', itemKey, ':', position);
|
||||
|
||||
|
||||
setDropdownPositions(prev => {
|
||||
// Remove positions for closed items
|
||||
|
|
@ -254,7 +252,6 @@ const PozoMenu = ({ items = [], mode = "vertical", style = {}, onClick, collapse
|
|||
if (prev[key]) newPos[key] = prev[key];
|
||||
});
|
||||
newPos[itemKey] = position;
|
||||
console.log('🎯 Updated dropdown positions:', newPos);
|
||||
return newPos;
|
||||
});
|
||||
|
||||
|
|
@ -276,11 +273,6 @@ const PozoMenu = ({ items = [], mode = "vertical", style = {}, onClick, collapse
|
|||
});
|
||||
}
|
||||
|
||||
console.log('Menu item clicked:', {
|
||||
key: item.key,
|
||||
keyPath: [...parentKeys, item.key],
|
||||
label: item.label
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -415,8 +407,6 @@ const PozoMenu = ({ items = [], mode = "vertical", style = {}, onClick, collapse
|
|||
}, [items, openKeys]);
|
||||
|
||||
|
||||
console.log(submenuRefs, "menuRef")
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`pozo-menu pozo-menu-${mode}`}
|
||||
|
|
|
|||
|
|
@ -123,38 +123,46 @@ export const postPurchaseData = createAsyncThunk(
|
|||
export const getPurchaseData = createAsyncThunk(
|
||||
'Stock/getPurchaseData',
|
||||
async (ProdData) => {
|
||||
console.log(ProdData, 'ProdData');
|
||||
if (
|
||||
ProdData?.CompId != null &&
|
||||
ProdData?.CompId != undefined &&
|
||||
ProdData?.BranchId != null &&
|
||||
ProdData?.BranchId != undefined &&
|
||||
ProdData?.AppId != null &&
|
||||
ProdData?.AppId != undefined &&
|
||||
ProdData?.OrderFromDate &&
|
||||
ProdData?.OrderToDate
|
||||
ProdData?.AppId != null
|
||||
) {
|
||||
const params = new URLSearchParams({
|
||||
compId: ProdData.CompId,
|
||||
branchId: ProdData.BranchId,
|
||||
appId: ProdData.AppId,
|
||||
fromDate: ProdData.OrderFromDate,
|
||||
toDate: ProdData.OrderToDate,
|
||||
});
|
||||
|
||||
// ✅ Add dates only if present
|
||||
if (ProdData?.OrderFromDate) {
|
||||
params.append('fromDate', ProdData.OrderFromDate);
|
||||
}
|
||||
|
||||
if (ProdData?.OrderToDate) {
|
||||
params.append('toDate', ProdData.OrderToDate);
|
||||
}
|
||||
|
||||
// ✅ Search
|
||||
if (ProdData?.search) {
|
||||
params.append('searchText', ProdData.search);
|
||||
}
|
||||
|
||||
// ✅ Pagination
|
||||
if (ProdData?.PageNumber) {
|
||||
params.append('pageNumber', ProdData.PageNumber);
|
||||
}
|
||||
|
||||
return await axiosRetailInstanceData.get(
|
||||
return axiosRetailInstanceData.get(
|
||||
`/Purchase?${params.toString()}`
|
||||
);
|
||||
}
|
||||
|
||||
// optional safety
|
||||
return Promise.reject('Missing required parameters');
|
||||
}
|
||||
);
|
||||
|
||||
export const putStockData = createAsyncThunk(
|
||||
'Stock/postStockData',
|
||||
async (putData) => {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,151 @@
|
|||
import { Tooltip } from 'antd';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
const AddAllProductsButton = ({
|
||||
productData = [],
|
||||
purchaseData = [],
|
||||
setPurchaseData,
|
||||
form,
|
||||
setIsLoading,
|
||||
setLoadingText,
|
||||
batchSize = 100,
|
||||
SelectedPurcTaxType,
|
||||
}) => {
|
||||
// This function just returns a prepared product object, no state changes
|
||||
|
||||
const ProductDropDownChange = async (
|
||||
VariantName,
|
||||
ProdId,
|
||||
option,
|
||||
productsList,
|
||||
extractedProduct = false,
|
||||
extractedItem
|
||||
) => {
|
||||
const ProductData1 = (productsList || productData)?.find(
|
||||
(p) => p.ProdId === ProdId && p.ProdVariantName === VariantName
|
||||
);
|
||||
if (!ProductData1) return null;
|
||||
|
||||
const DefaultVariant = ProductData1?.ProdVariantPriceDetails?.find(
|
||||
(item) => item?.DefaultVariant === 'Y' && item?.ReceivedQty === 0
|
||||
)?.DefaultVariant;
|
||||
|
||||
const qty = extractedProduct
|
||||
? parseFloat(extractedItem?.parsedData?.qty) || 0
|
||||
: 0;
|
||||
const acceptedQty = extractedProduct
|
||||
? parseFloat(extractedItem?.parsedData?.qty) || 0
|
||||
: 0;
|
||||
const rate = extractedProduct
|
||||
? parseFloat(extractedItem?.parsedData?.rate) || 0
|
||||
: 0;
|
||||
|
||||
return {
|
||||
DefaultVariant,
|
||||
ProdId: ProductData1.ProdId,
|
||||
MRP: ProductData1.MRP,
|
||||
ProdName: ProductData1.ProdName,
|
||||
UomName: ProductData1.UomName,
|
||||
SellPrice: ProductData1.SellPrice,
|
||||
StockAvailable: ProductData1.StockAvailable,
|
||||
OnePcsAvailable: ProductData1.OnePcsAvailable,
|
||||
OnePcsPrice: ProductData1.OnePcsPrice,
|
||||
NoOfPcs: ProductData1.NoOfPcs,
|
||||
TaxId: ProductData1.TaxId,
|
||||
TaxPercentage: ProductData1.TaxPercentage,
|
||||
ProdVariantName: ProductData1.ProdVariantName,
|
||||
PurchaseTax: parseFloat(extractedItem?.parsedData?.tax) || 0,
|
||||
BalanceQty: qty,
|
||||
InwardPrice: rate,
|
||||
PurcDisc: 0,
|
||||
Amount: isNaN(rate) || isNaN(acceptedQty) ? 0 : rate * acceptedQty,
|
||||
WhSalePrice: 0,
|
||||
ReceivedQty: qty,
|
||||
AcceptedQty: acceptedQty,
|
||||
RejectedQty: qty - acceptedQty,
|
||||
OfferPrice: 0,
|
||||
SpecialPrice: 0,
|
||||
FreeItem: 0,
|
||||
TaxAmt: 0,
|
||||
TaxType: SelectedPurcTaxType || 0,
|
||||
PurcDiscType: 'P',
|
||||
refImage: extractedItem?.parsedData?.image,
|
||||
PurchaseHSNCode: extractedItem?.parsedData?.hsn || '',
|
||||
localId: uuidv4(),
|
||||
};
|
||||
};
|
||||
|
||||
const handleAddAll = async () => {
|
||||
try {
|
||||
setIsLoading(true);
|
||||
setLoadingText('Adding products...');
|
||||
|
||||
// allow UI paint
|
||||
await new Promise((r) => requestAnimationFrame(r));
|
||||
|
||||
const allValidProducts = [];
|
||||
const allFormValues = {};
|
||||
|
||||
for (let i = 0; i < productData.length; i += batchSize) {
|
||||
const batch = productData.slice(i, i + batchSize);
|
||||
|
||||
setLoadingText(
|
||||
`Processing ${Math.min(i + batchSize, productData.length)} / ${
|
||||
productData.length
|
||||
}`
|
||||
);
|
||||
|
||||
const batchProducts = await Promise.all(
|
||||
batch.map((product) =>
|
||||
ProductDropDownChange(
|
||||
product.ProdVariantName,
|
||||
product.ProdId,
|
||||
{
|
||||
label: `${product.ProdName} (${product.Size} ${product.UomName})${
|
||||
product.BrandName ? ` - ${product.BrandName}` : ''
|
||||
}`,
|
||||
},
|
||||
productData
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
const valid = batchProducts.filter(Boolean);
|
||||
allValidProducts.push(...valid);
|
||||
|
||||
valid.forEach((p) => {
|
||||
const id = p.localId;
|
||||
allFormValues[`SellPrice${id}`] = p.SellPrice;
|
||||
allFormValues[`PurchaseTax${id}`] = p.PurchaseTax;
|
||||
allFormValues[`BalanceQty${id}`] = p.BalanceQty;
|
||||
allFormValues[`ReceivedQty${id}`] = p.ReceivedQty;
|
||||
allFormValues[`AcceptedQty${id}`] = p.AcceptedQty;
|
||||
allFormValues[`RejectedQty${id}`] = p.RejectedQty;
|
||||
allFormValues[`InwardPrice${id}`] = p.InwardPrice;
|
||||
allFormValues[`Amount${id}`] = p.Amount;
|
||||
});
|
||||
|
||||
await new Promise((r) => setTimeout(r, 0));
|
||||
}
|
||||
|
||||
// ✅ ONE state update
|
||||
setPurchaseData((prev) => [...allValidProducts, ...prev]);
|
||||
|
||||
// ✅ ONE form update
|
||||
form?.setFieldsValue(allFormValues);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
setLoadingText('');
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Tooltip title="Add All Products" placement="top">
|
||||
<button type="button" className="addallProductTBN" onClick={handleAddAll}>
|
||||
Add All
|
||||
</button>
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
|
||||
export default AddAllProductsButton;
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
import React, { useState } from 'react';
|
||||
import { Tooltip } from '@mui/material';
|
||||
import { IoSettingsOutline } from 'react-icons/io5';
|
||||
import { DefaultModal } from '../../Components/Modal/DefaultModal.jsx';
|
||||
|
||||
|
||||
const SettingsIconWithModal = ({
|
||||
tooltipTitle = 'Add Fields',
|
||||
tableFieldPreferences = [],
|
||||
selectedAdditionalColumn = [],
|
||||
onSubmit,
|
||||
}) => {
|
||||
const [open, setOpen] = useState(false);
|
||||
const [selectedFields, setSelectedFields] = useState([]);
|
||||
const [tempSelectedColumns, setTempSelectedColumns] = useState([]);
|
||||
|
||||
const handleIconClick = () => {
|
||||
// map labels → values
|
||||
const mappedFields = selectedAdditionalColumn
|
||||
.map(
|
||||
(col) =>
|
||||
tableFieldPreferences.find((pref) => pref.label === col)?.value
|
||||
)
|
||||
.filter(Boolean);
|
||||
|
||||
setSelectedFields(mappedFields);
|
||||
setTempSelectedColumns([...selectedAdditionalColumn]);
|
||||
setOpen(true);
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
setOpen(false);
|
||||
setTempSelectedColumns([]);
|
||||
};
|
||||
|
||||
const handleApply = () => {
|
||||
onSubmit({
|
||||
selectedFields,
|
||||
tempSelectedColumns,
|
||||
closeModal: () => setOpen(false),
|
||||
resetTemp: () => setTempSelectedColumns([]),
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* ⚙️ ICON ONLY */}
|
||||
<Tooltip title={tooltipTitle} placement="left">
|
||||
<IoSettingsOutline
|
||||
style={{ cursor: 'pointer', fontSize: '20px' }}
|
||||
onClick={handleIconClick}
|
||||
/>
|
||||
</Tooltip>
|
||||
|
||||
{/* MODAL */}
|
||||
<DefaultModal
|
||||
title="Select Additional Fields"
|
||||
open={open}
|
||||
footer
|
||||
buttonText="Apply"
|
||||
destroyOnClose
|
||||
handleSubmit={handleApply}
|
||||
handleCancel={handleCancel}
|
||||
>
|
||||
<div style={{ padding: '20px' }}>
|
||||
{tableFieldPreferences.map((option) => (
|
||||
<div key={option.value} className="Customized_QuickAdd">
|
||||
<label>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={selectedFields.includes(option.value)}
|
||||
onChange={(e) => {
|
||||
const checked = e.target.checked;
|
||||
|
||||
setSelectedFields((prev) =>
|
||||
checked
|
||||
? [...prev, option.value]
|
||||
: prev.filter((v) => v !== option.value)
|
||||
);
|
||||
|
||||
setTempSelectedColumns((prev) =>
|
||||
checked
|
||||
? [...prev, option.label]
|
||||
: prev.filter((v) => v !== option.label)
|
||||
);
|
||||
}}
|
||||
style={{ marginRight: '8px' }}
|
||||
/>
|
||||
{option.label}
|
||||
</label>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</DefaultModal>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default SettingsIconWithModal;
|
||||
|
|
@ -2,7 +2,7 @@ import { useState, useEffect, useRef, useCallback } from 'react';
|
|||
import { useNavigate, useLocation } from 'react-router-dom';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { PlusOutlined, EyeOutlined, PrinterFilled } from '@ant-design/icons';
|
||||
import { Image, Pagination, Space, Table } from 'antd';
|
||||
import { Image, Pagination, Space, Table, Tooltip } from 'antd';
|
||||
import {
|
||||
changeBreadCrumb,
|
||||
getEmpAccess,
|
||||
|
|
@ -59,6 +59,7 @@ import StickerPrintTemplates from '../Product/StickerTemplates.jsx';
|
|||
import QRCodeCopiesModal from '../Product/QRCodeCopiesModal.jsx';
|
||||
import { DatePicker } from 'antd';
|
||||
import {
|
||||
formatDateForAPI,
|
||||
generateBarcode,
|
||||
generateCodeImage,
|
||||
generateQRCode,
|
||||
|
|
@ -142,7 +143,6 @@ const StockList = () => {
|
|||
const [selectedRecords, setSelectedRecords] = useState([]);
|
||||
const [activeTab, setActiveTab] = useState('stock');
|
||||
const [MultiCode, setMultiCode] = useState(true);
|
||||
const [TotalCount, setTotalCount] = useState(null);
|
||||
const [dateStrings, setDateStrings] = useState([
|
||||
dayjs().format('YYYY-MM-DD'),
|
||||
dayjs().format('YYYY-MM-DD'),
|
||||
|
|
@ -233,16 +233,15 @@ const StockList = () => {
|
|||
AppId: AppId,
|
||||
BranchId: BranchId,
|
||||
CompId: CompId,
|
||||
OrderFromDate: dates?.[0] ?? '',
|
||||
OrderToDate: dates?.[1] ?? '',
|
||||
OrderFromDate: formatDateForAPI(dates?.[0]) ?? '',
|
||||
OrderToDate: formatDateForAPI(dates?.[1]) ?? '',
|
||||
PageNumber: page,
|
||||
})
|
||||
).unwrap();
|
||||
if (Response?.data?.statusCode === 1) {
|
||||
setTotalCount(Response?.data?.TotalCount);
|
||||
return Response?.data?.data;
|
||||
} else {
|
||||
setTotalCount(0);
|
||||
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
|
@ -287,17 +286,16 @@ const StockList = () => {
|
|||
AppId: AppId,
|
||||
BranchId: BranchId,
|
||||
CompId: CompId,
|
||||
OrderFromDate: dateStrings?.[0] ?? '',
|
||||
OrderToDate: dateStrings?.[1] ?? '',
|
||||
// OrderFromDate: dateStrings?.[0] ?? '',
|
||||
// OrderToDate: dateStrings?.[1] ?? '',
|
||||
search: SearchText,
|
||||
})
|
||||
).unwrap();
|
||||
if (Response?.data?.statusCode === 1) {
|
||||
setTotalCount(Response?.data?.TotalCount);
|
||||
setpage(1);
|
||||
return Response?.data?.data;
|
||||
} else {
|
||||
setTotalCount(0);
|
||||
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
|
@ -703,9 +701,9 @@ const StockList = () => {
|
|||
dispatch(
|
||||
getSupplierData({ AppId: AppId, BranchId: BranchId, CompId: CompId })
|
||||
);
|
||||
dispatch(
|
||||
getPurchaseData({ AppId: AppId, BranchId: BranchId, CompId: CompId })
|
||||
);
|
||||
// dispatch(
|
||||
// getPurchaseData({ AppId: AppId, BranchId: BranchId, CompId: CompId })
|
||||
// );
|
||||
|
||||
let ProdData = {
|
||||
AppId: AppId,
|
||||
|
|
@ -894,25 +892,25 @@ const StockList = () => {
|
|||
PaymentAmount: PaymentAmount.toFixed(2),
|
||||
InvoiceDate: groupInfo.InwardDate
|
||||
? moment(groupInfo.InwardDate, 'YYYY-MM-DD').format(
|
||||
'YYYY-MM-DDTHH:mm:ss'
|
||||
)
|
||||
'YYYY-MM-DDTHH:mm:ss'
|
||||
)
|
||||
: null,
|
||||
SuppInvoiceNo: groupInfo.SupplierInvoiceNumber,
|
||||
SuppInvoiceDate: groupInfo.SupplierInvoiceDate
|
||||
? moment(groupInfo.SupplierInvoiceDate, 'YYYY-MM-DD').format(
|
||||
'YYYY-MM-DDTHH:mm:ss'
|
||||
)
|
||||
'YYYY-MM-DDTHH:mm:ss'
|
||||
)
|
||||
: null,
|
||||
DeliveryChallanNo: groupInfo.DeliveryChallanNo,
|
||||
DeliveryInvoiceDate: groupInfo.DeliveryInvoiceDate
|
||||
? moment(groupInfo.DeliveryInvoiceDate, 'YYYY-MM-DD').format(
|
||||
'YYYY-MM-DDTHH:mm:ss'
|
||||
)
|
||||
'YYYY-MM-DDTHH:mm:ss'
|
||||
)
|
||||
: null,
|
||||
DueDate: groupInfo.DueDate
|
||||
? moment(groupInfo.DueDate, 'YYYY-MM-DD').format(
|
||||
'YYYY-MM-DDTHH:mm:ss'
|
||||
)
|
||||
'YYYY-MM-DDTHH:mm:ss'
|
||||
)
|
||||
: null,
|
||||
InvoiceAmount: Totalamount.toFixed(2),
|
||||
TaxAmount: Totaltax.toFixed(2),
|
||||
|
|
@ -930,13 +928,13 @@ const StockList = () => {
|
|||
FreeQty: item.FreeQty,
|
||||
ManufDate: item.ManufactureDate
|
||||
? moment(item.ManufactureDate, 'YYYY-MM-DD').format(
|
||||
'YYYY-MM-DDTHH:mm:ss'
|
||||
)
|
||||
'YYYY-MM-DDTHH:mm:ss'
|
||||
)
|
||||
: null,
|
||||
ExpDate: item.ExpireDate
|
||||
? moment(item.ExpireDate, 'YYYY-MM-DD').format(
|
||||
'YYYY-MM-DDTHH:mm:ss'
|
||||
)
|
||||
'YYYY-MM-DDTHH:mm:ss'
|
||||
)
|
||||
: null,
|
||||
BatchNo: item.BatchNo,
|
||||
ModelNo: item.ModelNo,
|
||||
|
|
@ -989,14 +987,10 @@ const StockList = () => {
|
|||
const productName = checkComponent('Product Name');
|
||||
setPrintProductName(productName);
|
||||
};
|
||||
// const ProductName = Productdata ?.map((e)=>e.ProdName+' '(+e.Size+' '+e.UomName))
|
||||
const ProductName = Productdata?.map(
|
||||
(e) => `${e.ProdName} (${e.Size} ${e.UomName})`
|
||||
);
|
||||
// const ProductName = Varientdata?.map((e)=>e.ProdName)
|
||||
// const ProductName = Productdata?.map((e)=>e.ProdName)
|
||||
// const ProductSize = Productdata ?.map((e)=>e.Size)
|
||||
// const ProductUomName = Productdata ?.map((e)=>e.UomName)
|
||||
|
||||
|
||||
const SupplierNames = SupplierData?.map((e) => e.SuppName);
|
||||
|
||||
|
|
@ -1012,33 +1006,8 @@ const StockList = () => {
|
|||
const InwardDtlIds = Varientdata?.map((e) => e.InwardDtlId);
|
||||
const StockProductId = Varientdata?.map((e) => e.ProdId);
|
||||
|
||||
// let proddetail = StockData?.map((e) => {
|
||||
// return {
|
||||
// ...e,
|
||||
// ProdDetails: e.ProdDetails?.map((d) => ({
|
||||
// ...d,
|
||||
// InvoiceNo: e.InvoiceNo,
|
||||
// InvoiceDate: e.InvoiceDate,
|
||||
// InvoiceAmt: e.InvoiceAmount
|
||||
// }))
|
||||
// };
|
||||
// });
|
||||
// let subObjectsArray = [];
|
||||
// // Iterate over mainObjects and concatenate subObjects arrays
|
||||
// proddetail.forEach(obj => {
|
||||
// subObjectsArray = subObjectsArray.concat(obj.ProdDetails);
|
||||
// });
|
||||
|
||||
// function safeRound(amountStr) {
|
||||
// if (amountStr == null) return allowDecimal ? '0.00' : '0';
|
||||
|
||||
// const cleaned = String(amountStr).replace(/[^0-9.-]+/g, '');
|
||||
// const num = Number(cleaned);
|
||||
|
||||
// if (isNaN(num)) return allowDecimal ? '0.00' : '0';
|
||||
|
||||
// return allowDecimal ? num.toFixed(2) : Math.round(num).toString();
|
||||
// }
|
||||
|
||||
const onFinish = async () => {
|
||||
const template = barcodeTemplateDetails?.find(
|
||||
|
|
@ -1194,7 +1163,8 @@ const StockList = () => {
|
|||
? countData?.length
|
||||
? countData?.length
|
||||
: 0
|
||||
: TotalCount;
|
||||
: data?.[0]?.TotalCount;
|
||||
console.log(debouncedSearchText, countData, data, "kkkk", countOfProduct)
|
||||
return (
|
||||
<div className="userPageTable">
|
||||
<div className="userPageContent">
|
||||
|
|
@ -1210,14 +1180,15 @@ const StockList = () => {
|
|||
<div
|
||||
className="searchAddDiv"
|
||||
style={{ justifyContent: 'flex-start' }}
|
||||
>
|
||||
<div className="formSearch" style={{ Width: '200px' }}>
|
||||
<Search
|
||||
placeholder="Search by Invoice No"
|
||||
onSearch={onSearch}
|
||||
onSearchChange={onSearchChange}
|
||||
/>
|
||||
</div>
|
||||
><Tooltip title="Search using Invoice No,Supp Name">
|
||||
<div className="formSearch" style={{ Width: '200px' }}>
|
||||
<Search
|
||||
placeholder="Search by Invoice No"
|
||||
onSearch={onSearch}
|
||||
onSearchChange={onSearchChange}
|
||||
/>
|
||||
</div>
|
||||
</Tooltip>
|
||||
<div style={{ width: '200px' }}>
|
||||
<RangePicker
|
||||
placeholder={['Start Date', 'End Date']}
|
||||
|
|
@ -1258,7 +1229,7 @@ const StockList = () => {
|
|||
data={filteredData}
|
||||
dataSource={filteredData}
|
||||
onChange={handleChange}
|
||||
// pagination={handlePageChange}
|
||||
// pagination={handlePageChange}
|
||||
/>{' '}
|
||||
{countOfProduct > 10 && (
|
||||
<Pagination
|
||||
|
|
|
|||
|
|
@ -0,0 +1,16 @@
|
|||
import { useRef, useCallback } from 'react';
|
||||
|
||||
export const useDebounce = (fn, delay = 300) => {
|
||||
const timer = useRef(null);
|
||||
|
||||
return useCallback(
|
||||
(...args) => {
|
||||
if (timer.current) clearTimeout(timer.current);
|
||||
|
||||
timer.current = setTimeout(() => {
|
||||
fn(...args);
|
||||
}, delay);
|
||||
},
|
||||
[fn, delay]
|
||||
);
|
||||
};
|
||||
|
|
@ -0,0 +1,171 @@
|
|||
import { useRef } from 'react';
|
||||
import { useVirtualizer } from '@tanstack/react-virtual';
|
||||
|
||||
const VirtualizedTable = ({
|
||||
dataSource = [],
|
||||
columns = [],
|
||||
rowHeight = 54,
|
||||
height = '50vh',
|
||||
overscan = 10,
|
||||
rowKey = 'id',
|
||||
}) => {
|
||||
const parentRef = useRef();
|
||||
const headerRef = useRef();
|
||||
|
||||
const rowVirtualizer = useVirtualizer({
|
||||
count: dataSource.length,
|
||||
getScrollElement: () => parentRef.current,
|
||||
estimateSize: () => rowHeight,
|
||||
overscan,
|
||||
});
|
||||
|
||||
const handleBodyScroll = (e) => {
|
||||
if (headerRef.current) {
|
||||
headerRef.current.scrollLeft = e.target.scrollLeft;
|
||||
}
|
||||
};
|
||||
|
||||
const virtualItems = rowVirtualizer.getVirtualItems();
|
||||
const totalSize = rowVirtualizer.getTotalSize();
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
border: '1px solid #d1d1d1',
|
||||
borderRadius: '4px',
|
||||
width: '100%',
|
||||
overflow: 'hidden',
|
||||
}}
|
||||
>
|
||||
{/* Header */}
|
||||
<div ref={headerRef} style={{ overflowX: 'hidden', overflowY: 'hidden' }}>
|
||||
<table
|
||||
style={{
|
||||
borderCollapse: 'collapse',
|
||||
width: '100%',
|
||||
tableLayout: 'auto',
|
||||
}}
|
||||
>
|
||||
<thead style={{ background: '#2c88ee', color: '#fff' }}>
|
||||
<tr className="proReceiptTD">
|
||||
{columns.map((col) => (
|
||||
<th
|
||||
key={col.key || col.dataIndex}
|
||||
style={{
|
||||
padding: '10px 6px',
|
||||
textAlign: col.align || 'left',
|
||||
fontWeight: 600,
|
||||
fontSize: '13px',
|
||||
borderRight: '1px solid rgba(255,255,255,0.3)',
|
||||
boxSizing: 'border-box',
|
||||
width: col.width || 'auto',
|
||||
minWidth: col.width || 80,
|
||||
whiteSpace: 'nowrap',
|
||||
}}
|
||||
>
|
||||
{col.title}
|
||||
</th>
|
||||
))}
|
||||
</tr>
|
||||
</thead>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{/* Body */}
|
||||
<div
|
||||
ref={parentRef}
|
||||
onScroll={handleBodyScroll}
|
||||
style={{
|
||||
height,
|
||||
overflowY: 'auto',
|
||||
overflowX: 'auto',
|
||||
position: 'relative',
|
||||
scrollbarWidth: 'thin',
|
||||
}}
|
||||
>
|
||||
<div style={{ height: `${totalSize}px`, position: 'relative' }}>
|
||||
{virtualItems.map((virtualRow) => {
|
||||
const record = dataSource[virtualRow.index];
|
||||
const key = record?.[rowKey] || virtualRow.index;
|
||||
|
||||
return (
|
||||
<div
|
||||
className="UppertablePR"
|
||||
key={key}
|
||||
style={{
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
left: 0,
|
||||
width: '100%',
|
||||
height: `${rowHeight}px`,
|
||||
transform: `translateY(${virtualRow.start}px)`,
|
||||
}}
|
||||
>
|
||||
<table
|
||||
style={{
|
||||
borderCollapse: 'collapse',
|
||||
width: '100%',
|
||||
tableLayout: 'auto',
|
||||
height: '100%',
|
||||
}}
|
||||
>
|
||||
<tbody>
|
||||
<tr
|
||||
className="proReceiptTD"
|
||||
style={{
|
||||
backgroundColor:
|
||||
virtualRow.index % 2 === 0 ? '#e2e2e2' : '#fff',
|
||||
borderBottom: '1px solid #f0f0f0',
|
||||
}}
|
||||
>
|
||||
{columns.map((col) => (
|
||||
<td
|
||||
className="tdProductrecipt"
|
||||
key={col.key || col.dataIndex}
|
||||
style={{
|
||||
padding: '6px',
|
||||
borderRight: '1px solid #f0f0f0',
|
||||
fontSize: '12px',
|
||||
fontWeight: 500,
|
||||
boxSizing: 'border-box',
|
||||
width: col.width || 'auto',
|
||||
minWidth: col.width || 80,
|
||||
textAlign: col.align || 'left',
|
||||
verticalAlign: 'middle',
|
||||
}}
|
||||
>
|
||||
{col.render
|
||||
? col.render(
|
||||
record?.[col.dataIndex],
|
||||
record,
|
||||
virtualRow.index
|
||||
)
|
||||
: record?.[col.dataIndex]}
|
||||
</td>
|
||||
))}
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{dataSource.length === 0 && (
|
||||
<div
|
||||
style={{
|
||||
textAlign: 'center',
|
||||
padding: '20px',
|
||||
color: '#999',
|
||||
fontSize: '14px',
|
||||
}}
|
||||
>
|
||||
No data
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default VirtualizedTable;
|
||||
|
|
@ -36,7 +36,6 @@
|
|||
}
|
||||
|
||||
.Product-table {
|
||||
|
||||
table {
|
||||
border-spacing: unset;
|
||||
}
|
||||
|
|
@ -88,7 +87,7 @@
|
|||
width: 180px !important;
|
||||
}
|
||||
|
||||
>div:nth-child(2) {
|
||||
> div:nth-child(2) {
|
||||
p {
|
||||
display: none !important;
|
||||
}
|
||||
|
|
@ -146,7 +145,7 @@
|
|||
max-width: 130px !important;
|
||||
}
|
||||
|
||||
>td:nth-child(3) {
|
||||
> td:nth-child(3) {
|
||||
.ant-input {
|
||||
width: max-content !important;
|
||||
max-width: 70px !important;
|
||||
|
|
@ -154,7 +153,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
>td:nth-child(5) {
|
||||
> td:nth-child(5) {
|
||||
.ant-input {
|
||||
width: max-content !important;
|
||||
max-width: 100px !important;
|
||||
|
|
@ -325,12 +324,12 @@
|
|||
}
|
||||
|
||||
.purchase-status {
|
||||
>div:nth-child(1) {
|
||||
> div:nth-child(1) {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
|
||||
>p {
|
||||
> p {
|
||||
padding-bottom: 0 !important;
|
||||
}
|
||||
}
|
||||
|
|
@ -448,7 +447,7 @@
|
|||
|
||||
.invoice-date {
|
||||
.ant-form-item-control-input-content {
|
||||
>label {
|
||||
> label {
|
||||
font-family: 'Poppins';
|
||||
font-weight: 500;
|
||||
}
|
||||
|
|
@ -474,7 +473,7 @@
|
|||
gap: 1rem;
|
||||
margin-bottom: 10px;
|
||||
|
||||
>button {
|
||||
> button {
|
||||
font-family: 'Poppins';
|
||||
background-color: #ef4444;
|
||||
color: #fff;
|
||||
|
|
@ -482,8 +481,8 @@
|
|||
align-items: center;
|
||||
|
||||
.ant-btn-icon {
|
||||
>span {
|
||||
>svg {
|
||||
> span {
|
||||
> svg {
|
||||
width: 13px;
|
||||
height: 13px;
|
||||
}
|
||||
|
|
@ -566,8 +565,6 @@
|
|||
width: 100%;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.stock-input {
|
||||
// width: 100vw;
|
||||
width: 100%;
|
||||
|
|
@ -608,8 +605,6 @@
|
|||
width: 100%;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.stock-input {
|
||||
width: 100vw;
|
||||
}
|
||||
|
|
@ -781,13 +776,12 @@
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
.addallProductTBN {
|
||||
border: none;
|
||||
outline: none;
|
||||
padding: 8px 16px;
|
||||
width: max-content;
|
||||
font-family: "Poppins";
|
||||
font-family: 'Poppins';
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
|
|
@ -822,9 +816,26 @@
|
|||
}
|
||||
|
||||
.imeiSNTable {
|
||||
|
||||
.ant-input {
|
||||
padding: 4px 4px !important;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.proReceiptTD {
|
||||
.ant-input {
|
||||
width: 100% !important;
|
||||
height: 35px !important;
|
||||
padding: 2px !important;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 500px) {
|
||||
.tdProductrecipt,
|
||||
.proReceiptTD {
|
||||
font-size: 12px !important;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue