Android_Retail/src/Pages/Product/ExcelUpload.jsx

3711 lines
107 KiB
React
Raw Normal View History

2026-03-23 16:08:11 +05:30
import React, { useState, useRef, useEffect, useContext } from 'react';
2026-02-09 10:32:03 +05:30
import { useDispatch, useSelector } from 'react-redux';
2026-03-23 16:08:11 +05:30
// import { read, utils } from 'xlsx';
2026-02-09 10:32:03 +05:30
import ExcelJS from 'exceljs';
2026-03-23 16:08:11 +05:30
import { Table, Form, Input, Tooltip, Modal, Select } from 'antd';
2026-01-27 18:27:29 +05:30
import {
2026-03-23 16:08:11 +05:30
emptyExcelData,
excelDataSelector,
excelFileSelector,
excelFileErrorSelector,
fileInputRefSelector,
uploadExcel,
2026-02-09 10:32:03 +05:30
} from '../../Features/ExcelUploadPage/ExcelUploadPage.js';
import {
2026-03-23 16:08:11 +05:30
getProductData,
onlineimages,
postFieldSetup,
getFieldSetupData,
2026-02-09 10:32:03 +05:30
} from '../../Features/ProductPage/ProductPage.js';
import Imageupload from '../../Components/Forms/Upload.jsx';
import { BiSolidFileExport } from 'react-icons/bi';
import { CiExport } from 'react-icons/ci';
import { uploadImage } from '../../Features/upload/upload.js';
import { TbWorldSearch } from 'react-icons/tb';
import { DefaultModal } from '../../Components/Modal/DefaultModal.jsx';
import '../../Styles/Product/excel.scss';
import { AiFillDelete } from 'react-icons/ai';
import Excelupldimg from '../../Images/Excel.png';
import { FiDelete, FiDownload } from 'react-icons/fi';
import { getSession } from '../../Services/Others.js';
import { ArrowRightOutlined, CloseCircleOutlined } from '@ant-design/icons';
import stringSimilarity from 'string-similarity';
import {
2026-03-23 16:08:11 +05:30
getCommonAppPreference,
ApplicationPreferences,
getApplicationDetails,
2026-02-09 10:32:03 +05:30
} from '../../Features/BrachLogin/BranchLogin.js';
import { MdOutlineAppRegistration } from 'react-icons/md';
import FormHeader from '../PageComponents/FormHeader.jsx';
import { DropDowns } from '../../Components/Forms/DropDown.jsx';
import Buttons from '../../Components/Forms/Buttons.jsx';
import { IoClose } from 'react-icons/io5';
import { Messages } from '../../Components/Notifications/Messages.jsx';
import useWhyDidYouUpdate from '../../Services/findrerender.js';
2026-01-27 18:27:29 +05:30
const getApplicationSampleData = (appName) => {
2026-03-23 16:08:11 +05:30
const sampleDataMap = {
Restaurant: {
productName: 'Chicken Biryani',
quantity: 1,
uom: 'PLT',
salesPrice: 250,
mrp: 280,
category: 'Non-Vegetarian',
subCategory: 'Biryani',
brand: 'Chef Special',
variantName: 'Large',
tax: 'CGST+SGST - 5%',
hsn: '21069030',
additionalSamples: [
{
productName: 'Masala Dosa',
quantity: 1,
uom: 'PLT',
salesPrice: 120,
category: 'Vegetarian',
subCategory: 'South Indian',
2026-02-09 10:32:03 +05:30
},
2026-03-23 16:08:11 +05:30
{
productName: 'Paneer Butter Masala',
quantity: 1,
uom: 'PLT',
salesPrice: 200,
category: 'Vegetarian',
subCategory: 'North Indian',
},
],
},
Bakery: {
productName: 'Chocolate Cake',
quantity: 1,
uom: 'PCS',
salesPrice: 500,
mrp: 550,
category: 'Cakes',
subCategory: 'Birthday Cakes',
brand: 'Fresh Bakes',
variantName: '1 Kg',
tax: 'CGST+SGST - 5%',
hsn: '19053100',
additionalSamples: [
{
productName: 'Croissant',
quantity: 1,
uom: 'PCS',
salesPrice: 45,
category: 'Pastries',
subCategory: 'French Pastries',
},
{
productName: 'Whole Wheat Bread',
quantity: 1,
uom: 'LOAF',
salesPrice: 35,
category: 'Breads',
subCategory: 'Wheat Breads',
},
],
},
'Electrical, Electronics and Computers': {
productName: 'LED Bulb 9W',
quantity: 1,
uom: 'PCS',
salesPrice: 120,
mrp: 150,
category: 'Lighting',
subCategory: 'LED Bulbs',
brand: 'Philips',
variantName: 'Cool White',
tax: 'CGST+SGST - 18%',
hsn: '85395000',
additionalSamples: [
{
productName: 'Mobile Charger',
quantity: 1,
uom: 'PCS',
salesPrice: 299,
category: 'Mobile Accessories',
subCategory: 'Chargers',
},
{
productName: 'Extension Board 4 Socket',
quantity: 1,
uom: 'PCS',
salesPrice: 350,
category: 'Electrical Accessories',
subCategory: 'Power Strips',
},
],
},
'Mobile Phone and Accessories': {
productName: 'Smartphone Screen Guard',
quantity: 1,
uom: 'PCS',
salesPrice: 199,
mrp: 299,
category: 'Screen Protection',
subCategory: 'Tempered Glass',
brand: 'TechGuard',
variantName: 'iPhone 14',
tax: 'CGST+SGST - 18%',
hsn: '70071900',
additionalSamples: [
{
productName: 'Phone Case',
quantity: 1,
uom: 'PCS',
salesPrice: 149,
category: 'Protection',
subCategory: 'Back Covers',
},
{
productName: 'Wireless Earbuds',
quantity: 1,
uom: 'PAIR',
salesPrice: 1599,
category: 'Audio',
subCategory: 'Bluetooth Earphones',
},
],
},
Grocery: {
productName: 'Basmati Rice',
quantity: 1,
uom: 'KG',
salesPrice: 85,
mrp: 90,
category: 'Food Grains',
subCategory: 'Rice',
brand: 'India Gate',
variantName: '1 Kg Pack',
tax: 'NIL - 0%',
hsn: '10063020',
additionalSamples: [
{
productName: 'Toor Dal',
quantity: 1,
uom: 'KG',
salesPrice: 120,
category: 'Pulses',
subCategory: 'Arhar Dal',
},
{
productName: 'Refined Oil',
quantity: 1,
uom: 'LITER',
salesPrice: 110,
category: 'Cooking Oil',
subCategory: 'Sunflower Oil',
},
],
},
'Departmental Stores': {
productName: 'Shampoo',
quantity: 1,
uom: 'BTL',
salesPrice: 180,
mrp: 200,
category: 'Personal Care',
subCategory: 'Hair Care',
brand: 'Head & Shoulders',
variantName: '200ml',
tax: 'CGST+SGST - 18%',
hsn: '33051000',
additionalSamples: [
{
productName: 'Toothpaste',
quantity: 1,
uom: 'TUBE',
salesPrice: 45,
category: 'Oral Care',
subCategory: 'Toothpaste',
},
{
productName: 'Laundry Detergent',
quantity: 1,
uom: 'KG',
salesPrice: 85,
category: 'Household',
subCategory: 'Washing Powder',
},
],
},
Jewellery: {
productName: 'Gold Ring',
quantity: 1,
uom: 'PCS',
salesPrice: 25000,
mrp: 26000,
category: 'Gold Jewellery',
subCategory: 'Rings',
brand: 'Tanishq',
variantName: '22K Gold',
tax: 'CGST+SGST - 3%',
hsn: '71131900',
additionalSamples: [
{
productName: 'Silver Earrings',
quantity: 1,
uom: 'PAIR',
salesPrice: 1200,
category: 'Silver Jewellery',
subCategory: 'Earrings',
},
{
productName: 'Diamond Pendant',
quantity: 1,
uom: 'PCS',
salesPrice: 15000,
category: 'Diamond Jewellery',
subCategory: 'Pendants',
},
],
},
'Lifestyle and Fashion': {
productName: 'Cotton T-Shirt',
quantity: 1,
uom: 'PCS',
salesPrice: 499,
mrp: 599,
category: 'Apparel',
subCategory: 'T-Shirts',
brand: 'Nike',
variantName: 'Medium',
tax: 'CGST+SGST - 5%',
hsn: '61091000',
additionalSamples: [
{
productName: 'Jeans',
quantity: 1,
uom: 'PCS',
salesPrice: 1299,
category: 'Bottoms',
subCategory: 'Denim',
},
{
productName: 'Sneakers',
quantity: 1,
uom: 'PAIR',
salesPrice: 2499,
category: 'Footwear',
subCategory: 'Casual Shoes',
},
],
},
'Salon, Spa and Beauty Parlour': {
productName: 'Hair Cut Service',
quantity: 1,
uom: 'SERVICE',
salesPrice: 300,
mrp: 350,
category: 'Hair Services',
subCategory: 'Cutting',
brand: 'Premium Salon',
variantName: 'Regular Cut',
tax: 'CGST+SGST - 18%',
hsn: '99820000',
additionalSamples: [
{
productName: 'Facial Treatment',
quantity: 1,
uom: 'SERVICE',
salesPrice: 800,
category: 'Skin Care',
subCategory: 'Facial',
},
{
productName: 'Hair Color',
quantity: 1,
uom: 'SERVICE',
salesPrice: 1200,
category: 'Hair Services',
subCategory: 'Coloring',
},
],
},
Stationery: {
productName: 'A4 Paper Pack',
quantity: 1,
uom: 'PACK',
salesPrice: 280,
mrp: 300,
category: 'Paper Products',
subCategory: 'Copy Paper',
brand: 'JK Copier',
variantName: '500 Sheets',
tax: 'CGST+SGST - 12%',
hsn: '48025510',
additionalSamples: [
{
productName: 'Ball Pen',
quantity: 1,
uom: 'PCS',
salesPrice: 15,
category: 'Writing Instruments',
subCategory: 'Pens',
},
{
productName: 'Notebook',
quantity: 1,
uom: 'PCS',
salesPrice: 45,
category: 'Books & Notebooks',
subCategory: 'Exercise Books',
},
],
},
Pharmacy: {
productName: 'Paracetamol 500mg',
quantity: 1,
uom: 'STRIP',
salesPrice: 12,
mrp: 15,
category: 'Medicines',
subCategory: 'Fever & Pain',
brand: 'Crocin',
variantName: '10 Tablets',
tax: 'CGST+SGST - 12%',
hsn: '30049099',
additionalSamples: [
{
productName: 'Hand Sanitizer',
quantity: 1,
uom: 'BTL',
salesPrice: 65,
category: 'Healthcare',
subCategory: 'Sanitizers',
},
{
productName: 'Bandage Roll',
quantity: 1,
uom: 'PCS',
salesPrice: 25,
category: 'Medical Supplies',
subCategory: 'First Aid',
},
],
},
Furniture: {
productName: 'Wooden Dining Table',
quantity: 1,
uom: 'PCS',
salesPrice: 15000,
mrp: 18000,
category: 'Dining Furniture',
subCategory: 'Dining Tables',
brand: 'Royal Oak',
variantName: '6 Seater',
tax: 'CGST+SGST - 12%',
hsn: '94036090',
additionalSamples: [
{
productName: 'Office Chair',
quantity: 1,
uom: 'PCS',
salesPrice: 5500,
category: 'Office Furniture',
subCategory: 'Chairs',
},
{
productName: 'Queen Size Bed',
quantity: 1,
uom: 'PCS',
salesPrice: 12000,
category: 'Bedroom Furniture',
subCategory: 'Beds',
},
],
},
Fruits: {
productName: 'Fresh Apples',
quantity: 1,
uom: 'KG',
salesPrice: 180,
mrp: 200,
category: 'Fresh Fruits',
subCategory: 'Seasonal Fruits',
brand: 'Farm Fresh',
variantName: 'Kashmiri Apple',
tax: 'NIL - 0%',
hsn: '08081000',
additionalSamples: [
{
productName: 'Bananas',
quantity: 1,
uom: 'DOZEN',
salesPrice: 60,
category: 'Fresh Fruits',
subCategory: 'Tropical Fruits',
},
{
productName: 'Orange Juice',
quantity: 1,
uom: 'LITER',
salesPrice: 120,
category: 'Fruit Juices',
subCategory: 'Fresh Juice',
},
],
},
Textiles: {
productName: 'Cotton Fabric',
quantity: 1,
uom: 'METER',
salesPrice: 85,
mrp: 100,
category: 'Fabric',
subCategory: 'Cotton Fabric',
brand: 'Raymond',
variantName: 'Plain White',
tax: 'CGST+SGST - 5%',
hsn: '52081900',
additionalSamples: [
{
productName: 'Silk Saree',
quantity: 1,
uom: 'PCS',
salesPrice: 2500,
category: 'Ready Made',
subCategory: 'Sarees',
},
{
productName: 'Woolen Shawl',
quantity: 1,
uom: 'PCS',
salesPrice: 800,
category: 'Accessories',
subCategory: 'Shawls',
},
],
},
'Computer Sales Shop': {
productName: 'Gaming Laptop',
quantity: 1,
uom: 'PCS',
salesPrice: 55000,
mrp: 60000,
category: 'Laptops',
subCategory: 'Gaming Laptops',
brand: 'ASUS',
variantName: 'ROG Strix G15',
tax: 'CGST+SGST - 18%',
hsn: '84713000',
additionalSamples: [
{
productName: 'Wireless Mouse',
quantity: 1,
uom: 'PCS',
salesPrice: 1200,
category: 'Computer Accessories',
subCategory: 'Mouse',
},
{
productName: 'SSD 500GB',
quantity: 1,
uom: 'PCS',
salesPrice: 4500,
category: 'Storage',
subCategory: 'Solid State Drive',
},
],
},
};
// Return sample data for the specified app, or generic data if not found
return (
sampleDataMap[appName] || {
productName: 'Sample Product Name',
quantity: 1,
uom: 'PCS',
salesPrice: 100,
mrp: 100,
category: 'Sample Category',
subCategory: 'Sample Sub Category',
brand: 'Sample Brand',
variantName: 'Sample Variant Name',
tax: 'NIL - 0%',
hsn: '12345678',
additionalSamples: [],
}
);
2026-01-27 18:27:29 +05:30
};
const allowedExcelTypes = [
2026-03-23 16:08:11 +05:30
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', // .xlsx
'application/vnd.ms-excel', // .xls
2026-01-27 18:27:29 +05:30
];
const ProductExcel = ({
2026-03-23 16:08:11 +05:30
handleSubmit,
CategoryId,
CategoryNames,
SubcatId,
SubCatData,
AllBrandId,
Allbranddata,
Allbranddatanumfid,
Subcatnumfid,
Unit,
TaxDatas,
SuplierNames,
CategoryName1,
Catconfigid,
}) => {
useWhyDidYouUpdate('ProductExcel', {
2026-01-27 18:27:29 +05:30
handleSubmit,
CategoryId,
CategoryNames,
SubcatId,
SubCatData,
AllBrandId,
Allbranddata,
Allbranddatanumfid,
Subcatnumfid,
Unit,
TaxDatas,
SuplierNames,
CategoryName1,
Catconfigid,
2026-03-23 16:08:11 +05:30
});
const dispatch = useDispatch();
const formRef = useRef(null);
const CompId = getSession('CompId');
const BranchId = getSession('BranchId');
const AppId = getSession('AppId');
const UserId = getSession('UserId');
const fileInputRef = useRef(fileInputRefSelector);
const excelData = useSelector(excelDataSelector);
const excelFile = useSelector(excelFileSelector);
const excelFileError = useSelector(excelFileErrorSelector);
const ApplicationPreferenceData = useSelector(ApplicationPreferences);
const productBulkUploadCatId = ApplicationPreferenceData?.find(
(p) => p?.PreferredCatName?.toLowerCase() === 'product bulk upload'
)?.PreferredCatId;
const [appName, setAppName] = useState(null);
const [messageType, setMessageType] = useState(null);
const [messageData, setMessageData] = useState(null);
const [selectedFields, setSelectedFields] = useState([]);
const [tableFieldPreferences, setTableFieldPreferences] = useState([]);
const [worksheet, setWorksheet] = useState(null);
const [worksheet1, setWorksheet1] = useState(null);
const [editdelete, seteditdelete] = useState('');
const [fieldSetup, setFieldSetup] = useState(false);
const [selectedImageIndex, setSelectedImageIndex] = useState(null);
const [selectedProductName, setSelectedProductName] = useState(null);
const [imagedata, setimagedata] = useState();
const [selectedImage, setSelectedImage] = useState(null);
const [imageopen, setimageOpen] = useState();
const [onlineImage, setOnlineImage] = useState(null);
const [loading, setLoading] = useState(false);
const [message, setMessage] = useState(false);
const [Datas, setDatas] = useState();
const [currentPage, setCurrentPage] = useState(1);
const [MappingOpen, setMappingOpen] = useState(false);
const [fieldMapping, setFieldMapping] = useState({});
const [headers, setHeaders] = useState([]);
const [UploadedRawData, setUploadedRawData] = useState([]);
const [ExcelData, setExcelData] = useState([]);
const [originalUploadedRawData, setOriginalUploadedRawData] = useState([]);
const [FieldValues, setFieldValues] = useState([]);
const [OrginalData, setOrginalData] = useState([]);
const handleTableChange = (pagination, filters, sorter) => {
setCurrentPage(pagination.current);
};
console.log(tableFieldPreferences, 'tableFieldPreferences');
useEffect(() => {
if (excelData && excelData?.length > 0) {
let ExecelToJsConvertion = [];
let slicedData = excelData?.slice(1);
let filteredData = slicedData?.filter(
(item) => item?.[item?.length - 1]?.trim() !== 'sample row'
);
filteredData?.map((item, index) => {
ExecelToJsConvertion.push({
key: index,
ProductName: item?.['0'],
Quantity: item?.['1'],
UOM: item?.['2'],
SellPrice: item?.['3'],
Category: item?.['4'],
SubCategory: item?.['5'],
Brand: item?.['6'],
ProductVariantName: item?.['7'] ? item?.['7'] : 'Variant 1',
MRP: item?.['8'],
WhSalePrice: item?.['9'],
StockAvailable: item?.['10'] ? item?.['10'] : 'No',
TokenAvailable: item?.['11'] ? item?.['11'] : 'No',
ProductType: item?.['12'],
Tax: item?.['13'],
AutoGenerateQrcode: item?.['14'],
AddQrCode: item?.['15'],
OnePeiceAvailable: item?.['16'] ? item?.['16'] : 'No',
OnePeicePrice: item?.['17'],
AutoGenerateOnePieceQrcode: item?.['18'] ? item?.['18'] : 'No',
AutoGenerateOnePieceQrcodeNumber: item?.['19'],
CESS: item?.['20'],
HSN: item?.['21'],
PartNumber: item?.['22'],
Rack: item?.['23'],
ManufactureDate: item?.['24'],
ExpireDate: item?.['25'],
AvailableFrom: item?.['26'] ? item?.['26'] : '',
AvailableTo: item?.['27'] ? item?.['27'] : '',
Productimage: '',
});
});
setDatas(ExecelToJsConvertion);
// setExcelData(ExecelToJsConvertion);
// setOriginalUploadedRawData(ExecelToJsConvertion);
const style = document.createElement('style');
style.type = 'text/css';
style.innerHTML = `
2026-01-27 18:27:29 +05:30
.viewerExcelupload::-webkit-scrollbar {
display: block !important;
}
`;
2026-03-23 16:08:11 +05:30
document.head.appendChild(style);
2026-01-27 18:27:29 +05:30
2026-03-23 16:08:11 +05:30
return () => {
document.head.removeChild(style);
};
}
}, [excelData]);
2026-01-27 18:27:29 +05:30
2026-03-23 16:08:11 +05:30
useEffect(() => {
const fetchData = async () => {
if (AppId) {
const response = await dispatch(
getApplicationDetails({ AppId })
)?.unwrap();
setAppName(response?.data?.data?.[0]?.AppName);
}
};
fetchData();
}, [AppId]);
2026-01-27 18:27:29 +05:30
2026-03-23 16:08:11 +05:30
useEffect(() => {
// Call handleSubmit when Datas is updated
if (Datas) {
handleSubmit(Datas);
}
}, [Datas, handleSubmit]);
// Automatically update preview data on mapping change
const mandatoryFields = [
'ProductName',
'Quantity',
'UOM',
'SellPrice',
'Category',
];
useEffect(() => {
if (UploadedRawData.length > 0) {
const updatedMappedData = UploadedRawData.map((row, index) => {
const newRow = { key: index };
newRow['isModified'] = row?.isModified;
// ✅ Process mandatory fields (retain value even if unmapped)
mandatoryFields.forEach((field) => {
const mappedHeader = fieldMapping[field];
if (mappedHeader && row[mappedHeader] !== undefined) {
newRow[field] = row[mappedHeader];
} else {
// Fallback: try original headers (case-insensitive, whitespace-insensitive)
const fallbackHeader = headers.find(
(header) =>
header?.toLowerCase().replace(/\s+/g, '') ===
field.toLowerCase().replace(/\s+/g, '')
);
2026-01-27 18:27:29 +05:30
2026-03-23 16:08:11 +05:30
if (fallbackHeader && row[fallbackHeader] !== undefined) {
newRow[field] = row[fallbackHeader];
} else {
// Preserve existing value if present
newRow[field] = ExcelData?.[index]?.[field] ?? '';
}
}
});
2026-01-27 18:27:29 +05:30
2026-03-23 16:08:11 +05:30
// ✅ Process non-mandatory mapped fields
Object.entries(fieldMapping).forEach(([field, header]) => {
if (!mandatoryFields.includes(field)) {
newRow[field] = headers.includes(header) ? (row[header] ?? '') : '';
}
});
2026-01-27 18:27:29 +05:30
2026-03-23 16:08:11 +05:30
// ✅ Clean up unmapped columns
headers.forEach((header) => {
const isMapped = Object.values(fieldMapping).includes(header);
const mappedField = Object.keys(fieldMapping).find(
(key) => fieldMapping[key] === header
);
const isMandatory = mandatoryFields.includes(mappedField);
if (!isMapped && !(header in newRow)) {
newRow[header] = isMandatory ? (row[header] ?? '') : '';
}
});
return newRow;
});
2026-01-27 18:27:29 +05:30
2026-03-23 16:08:11 +05:30
setExcelData(updatedMappedData);
setDatas(updatedMappedData);
}
}, [fieldMapping, UploadedRawData, headers]);
2026-01-27 18:27:29 +05:30
2026-03-23 16:08:11 +05:30
useEffect(() => {
if (AppId && BranchId && CompId) {
handleExportBulkdata();
}
}, [AppId, BranchId, CompId]);
useEffect(() => {
getFieldSetup();
}, [productBulkUploadCatId]);
const getFieldSetup = async () => {
try {
const response = await dispatch(
getFieldSetupData({
AppId,
CompId,
BranchId,
categoryId: productBulkUploadCatId,
Type: 'EB',
})
).unwrap();
if (response?.data?.statusCode === 1) {
console.log(response?.data?.data?.[0]?.ConfigDtl, 'Field Setup Data');
setSelectedFields(
response?.data?.data?.[0]?.ConfigDtl?.filter(
(c) => c.ConfigId && c.Access === 'Y'
)?.map((c) => c.ConfigId) || []
);
setTableFieldPreferences(
response?.data?.data?.[0]?.ConfigDtl?.map((c) => ({
value: c.ConfigId,
label: c.ConfigName,
access: c.Access,
})) || []
);
setFieldValues(response?.data?.data?.[0]?.ConfigDtl);
} else {
setMessageType('error');
setMessageData('Failed to fetch field setup');
}
} catch (error) {
console.error('Error fetching field setup:', error);
}
};
const handleFileUpload = (e) => {
const file = e.target.files[0];
const reader = new FileReader();
if (file) {
const isAllowed = allowedExcelTypes?.includes(file.type);
if (!isAllowed) {
Modal.error({
title: 'Invalid File Type',
content: 'Only Excel files (.xls, .xlsx) are allowed.',
});
handleCancelData();
} else {
reader.readAsDataURL(file);
uploadAndProcessExcel(file);
}
} else {
dispatch(emptyExcelData());
}
};
const handleClick = () => {
// Programmatically trigger the file input click event
fileInputRef.current.click();
};
const getFieldValue = (product, field) => {
const aliasField = FIELD_ALIAS[field];
return normalize(product[field] ?? product[aliasField]);
};
const normalize = (value) => {
// Empty / invalid values
if (
value === undefined ||
value === null ||
value === '' ||
value === 'NaN-NaN-NaN'
) {
return '';
}
2026-01-27 18:27:29 +05:30
2026-03-23 16:08:11 +05:30
// Y / N flags
if (value === 'Y') return true;
if (value === 'N') return false;
// Numbers (handles "150", 150, "0")
if (
typeof value === 'number' ||
(typeof value === 'string' &&
value.trim() !== '' &&
!Number.isNaN(Number(value)))
) {
return Number(value);
}
2026-01-27 18:27:29 +05:30
2026-03-23 16:08:11 +05:30
// Default string
return String(value).trim();
};
const hashProduct = (product) =>
COMPARE_FIELDS.map((field) => getFieldValue(product, field)).join('§');
function findModifiedProducts(originalData, updatedData) {
if (!originalData?.length || !updatedData?.length) return [];
2026-01-27 18:27:29 +05:30
2026-03-23 16:08:11 +05:30
const originalHashMap = new Map();
2026-01-27 18:27:29 +05:30
2026-03-23 16:08:11 +05:30
for (const product of originalData) {
if (!product.ProdId) continue;
originalHashMap.set(product.ProdId, hashProduct(product));
}
2026-01-27 18:27:29 +05:30
2026-03-23 16:08:11 +05:30
// Step 2: Compare updated products
const modifiedProducts = [];
2026-01-27 18:27:29 +05:30
2026-03-23 16:08:11 +05:30
for (const product of updatedData) {
if (!product.ProdId) continue;
2026-01-27 18:27:29 +05:30
2026-03-23 16:08:11 +05:30
const originalHash = originalHashMap.get(product.ProdId);
if (!originalHash) continue; // new product, not modified
2026-03-23 16:08:11 +05:30
const updatedHash = hashProduct(product);
2026-01-27 18:27:29 +05:30
2026-03-23 16:08:11 +05:30
if (originalHash !== updatedHash) {
modifiedProducts.push(product);
}
}
2026-01-27 18:27:29 +05:30
2026-03-23 16:08:11 +05:30
return modifiedProducts;
}
const FIELD_ALIAS = {
'Product Variant Name': 'Product Varient Name',
};
2026-01-27 18:27:29 +05:30
2026-03-23 16:08:11 +05:30
const uploadAndProcessExcel = (file) => {
const reader = new FileReader();
2026-02-09 10:32:03 +05:30
2026-03-23 16:08:11 +05:30
reader.onload = async (e) => {
const buffer = e.target.result;
2026-02-09 10:32:03 +05:30
2026-03-23 16:08:11 +05:30
const workbook = new ExcelJS.Workbook();
await workbook.xlsx.load(buffer);
2026-02-09 10:32:03 +05:30
2026-03-23 16:08:11 +05:30
const worksheet = workbook.worksheets[0];
2026-02-09 10:32:03 +05:30
2026-03-23 16:08:11 +05:30
// 🔥 convert to same format as xlsx (array of arrays)
const jsonData = [];
worksheet.eachRow((row) => {
const rowValues = row.values.slice(1); // remove first empty index
jsonData.push(rowValues);
});
2026-02-09 10:32:03 +05:30
2026-03-23 16:08:11 +05:30
const nonEmptyRows = jsonData.filter((row) =>
row.some((cell) => cell !== '' && cell !== null && cell !== undefined)
);
2026-02-09 10:32:03 +05:30
2026-03-23 16:08:11 +05:30
const header = nonEmptyRows[0];
const rows = nonEmptyRows.slice(1);
2026-02-09 10:32:03 +05:30
2026-03-23 16:08:11 +05:30
const dateFields = [
'Available From',
'Available To',
'Manufacture Date',
'Expire Date',
'Stock Date',
];
function convertToDisplayFormat(dateString) {
if (!dateString) return '';
const date = new Date(dateString);
if (isNaN(date)) return dateString;
const day = date.getDate().toString().padStart(2, '0');
const month = (date.getMonth() + 1).toString().padStart(2, '0');
const year = date.getFullYear().toString();
return `${day}-${month}-${year}`;
}
const formattedData = rows.map((row) => {
let rowData = header.reduce((acc, col, columnIndex) => {
if (dateFields?.includes(col)) {
acc[col] = convertToDisplayFormat(row[columnIndex]);
} else {
acc[col] = row[columnIndex];
}
return acc;
}, {});
return rowData;
});
if (jsonData.length > 0) {
const extractedHeaders = Object.keys(formattedData[0] || {});
const filteredData = formattedData.filter(
(item) => !Object.values(item)?.includes('sample row')
);
2026-02-09 10:32:03 +05:30
2026-03-23 16:08:11 +05:30
const updatedData = filteredData.map((item) => ({
...item,
'Product Varient Name':
item['Product Varient Name'] === undefined
? 'Variant 1'
: item['Product Varient Name'],
'Available To':
item['Available To'] == 'NaN-NaN-NaN' ? '' : item['Available To'],
ProdId: item['ProdId'] || null,
}));
const modifiedProducts = findModifiedProducts(OrginalData, updatedData);
const modifiedIds = new Set(modifiedProducts.map((p) => p.ProdId));
const originalIds = new Set(OrginalData?.map((p) => p.ProdId) || []);
const markedData = updatedData
.map((item) => ({
...item,
isModified:
modifiedIds.has(item.ProdId) || !originalIds.has(item.ProdId),
}))
.sort((a, b) => b.isModified - a.isModified);
setUploadedRawData(markedData);
setHeaders(extractedHeaders);
autoMapFields(extractedHeaders);
dispatch(uploadExcel({ file, jsonData: nonEmptyRows }));
setWorksheet(worksheet);
setDatas([]);
setExcelData([]);
}
// ⚠️ keep your original flow (no break)
dispatch(uploadExcel({ file, jsonData: nonEmptyRows }));
setWorksheet(worksheet);
setWorksheet1(worksheet1);
setDatas(formattedData);
};
2026-02-09 10:32:03 +05:30
2026-03-23 16:08:11 +05:30
reader.readAsArrayBuffer(file);
};
// const uploadAndProcessExcel = (file) => {
// const reader = new FileReader();
// reader.onload = (e) => {
// const data = new Uint8Array(e.target.result);
// const workbook = read(data, { type: 'array' });
// const worksheet = workbook.Sheets[workbook.SheetNames[0]];
// const jsonData = utils.sheet_to_json(worksheet, {
// header: 1,
// raw: false,
// dateNF: 'DD/MM/YY',
// });
// const nonEmptyRows = jsonData.filter((row) =>
// row.some((cell) => cell !== '')
// );
// const header = nonEmptyRows[0];
// const rows = nonEmptyRows.slice(1);
// const dateFields = [
// 'Available From',
// 'Available To',
// 'Manufacture Date',
// 'Expire Date',
// 'Stock Date',
// ];
// function convertToDisplayFormat(dateString) {
// const date = new Date(dateString);
// const day = date.getDate().toString().padStart(2, '0');
// const month = (date.getMonth() + 1).toString().padStart(2, '0');
// const year = date.getFullYear().toString();
// return `${day}-${month}-${year}`;
// }
// const formattedData = rows.map((row) => {
// let rowData = header.reduce((acc, col, columnIndex) => {
// if (dateFields?.includes(col)) {
// acc[col] = convertToDisplayFormat(row[columnIndex]);
// } else {
// acc[col] = row[columnIndex];
// }
// return acc;
// }, {});
// return rowData;
// });
// if (jsonData.length > 0) {
// const extractedHeaders = Object.keys(formattedData[0]);
// const filteredData = formattedData.filter(
// (item) => !Object.values(item)?.includes('sample row')
// );
// // ✅ Add default variant name if missing
// const updatedData = filteredData.map((item) => ({
// ...item,
// 'Product Varient Name':
// item['Product Varient Name'] === undefined
// ? 'Variant 1'
// : item['Product Varient Name'],
// 'Available To':
// item['Available To'] == 'NaN-NaN-NaN' ? '' : item['Available To'],
// ProdId: item['ProdId'] || null,
// }));
// // Detect modified products
// const modifiedProducts = findModifiedProducts(OrginalData, updatedData);
// const modifiedIds = new Set(modifiedProducts.map((p) => p.ProdId));
// const originalIds = new Set(OrginalData?.map((p) => p.ProdId) || []);
// const markedData = updatedData
// .map((item) => ({
// ...item,
// isModified:
// modifiedIds.has(item.ProdId) || !originalIds.has(item.ProdId),
// }))
// .sort((a, b) => b.isModified - a.isModified);
// setUploadedRawData(markedData);
// console.log(modifiedProducts, 'modifiedProducts');
// // ✅ Set only once with final data
// setHeaders(extractedHeaders);
// autoMapFields(extractedHeaders);
// dispatch(uploadExcel({ file, jsonData: nonEmptyRows }));
// setWorksheet(worksheet);
// setDatas([]); // Clear mapped state initially
// setExcelData([]);
// }
// // setMappingOpen(true);
// dispatch(uploadExcel({ file, jsonData: nonEmptyRows }));
// setWorksheet(worksheet);
// setWorksheet1(worksheet1);
// setDatas(formattedData); // Update Datas state here
// // setExcelData(formattedData);
// };
// reader.readAsArrayBuffer(file);
// };
useEffect(() => {
imagecall();
addOnlineImage();
Fielddatas();
}, []);
const imagecall = async (ProdName) => {
if (selectedProductName !== null) {
let response = await dispatch(onlineimages(ProdName)).unwrap();
setimagedata(response?.data?.data);
}
};
const addOnlineImage = (item, index) => {
item?.ProductName && setimageOpen(true);
imagecall(item?.ProductName);
setSelectedProductName(item?.ProductName);
setSelectedImageIndex(index);
};
const updateImageUrl = (url, index, key) => {
const newData1 = [...Datas];
newData1[key] = { ...newData1[key], Productimage: url };
setDatas(newData1);
setExcelData(newData1);
};
const handleimage = () => {
setimageOpen(false);
setSelectedImageIndex(null);
};
const submitimage = async () => {
const pageNo = currentPage * 10 + selectedImageIndex - 10;
if (selectedImageIndex != null && selectedImageIndex != undefined) {
try {
if (selectedImage?.image !== undefined) {
const response = await fetch(selectedImage.image);
const datas = await response.blob();
const metadata = {
type: 'image/jpeg',
};
const file = new File([datas], 'image.jpg', metadata);
const uploadImgData = await dispatch(uploadImage(file)).unwrap();
if (uploadImgData?.data?.status == 1) {
let tempData = Datas;
const selectedIndex = tempData.findIndex(
(_, index) => index === pageNo
);
2026-02-09 10:32:03 +05:30
2026-03-23 16:08:11 +05:30
if (selectedIndex !== -1) {
tempData[selectedIndex].Productimage = uploadImgData?.data?.image;
2026-02-09 10:32:03 +05:30
}
2026-03-23 16:08:11 +05:30
setDatas(tempData);
setExcelData(tempData);
}
2026-02-09 10:32:03 +05:30
}
2026-03-23 16:08:11 +05:30
} finally {
handleimage();
}
2026-02-09 10:32:03 +05:30
}
2026-03-23 16:08:11 +05:30
};
const fieldOrder = [
'Product Name',
'Quantity',
'UOM',
'Sales Price',
'MRP',
'WholeSale Price',
'Category',
'Sub Category',
'Brand',
'Product Variant Name',
'Stock Available',
'Token Available',
'Product Type',
'Tax',
'Auto Generate QRCode',
'Add Qr Code',
'One Piece Available',
'OnePiece Price',
'Auto Generate One Piece Qrcode',
'Auto Generate One Piece Qrcode Number',
'CESS',
'HSN',
'Part Number',
'Rack',
'Manufacture Date',
'Expire Date',
'Available From',
'Available To',
];
const sortPreferredFields = (fields) => {
const fieldMap = {};
fields.forEach((field) => {
fieldMap[field.ConfigName] = field;
});
2026-01-27 18:27:29 +05:30
2026-03-23 16:08:11 +05:30
const sorted = [];
2026-01-27 18:27:29 +05:30
2026-03-23 16:08:11 +05:30
fieldOrder.forEach((name) => {
if (fieldMap[name] && fieldMap[name].Access === 'Y') {
sorted.push(fieldMap[name]);
}
});
2026-01-27 18:27:29 +05:30
2026-03-23 16:08:11 +05:30
return sorted;
};
2026-01-27 18:27:29 +05:30
2026-03-23 16:08:11 +05:30
// Usage inside your Fielddatas
const Fielddatas = async () => {
const res = await dispatch(getCommonAppPreference(AppId)).unwrap();
2026-01-27 18:27:29 +05:30
2026-03-23 16:08:11 +05:30
const productbulkUploadCatId =
res?.data?.data?.[0]?.PreferenceDetails?.find(
(p) => p?.PreferredCatName?.toLowerCase() === 'product bulk upload'
)?.PreferredCatId;
const response = await dispatch(
getFieldSetupData({
AppId,
CompId,
BranchId,
categoryId: productbulkUploadCatId,
Type: 'EB',
})
).unwrap();
const productBulkUploadFields = response?.data?.data?.[0]?.ConfigDtl || [];
const sortedFields = sortPreferredFields(productBulkUploadFields);
setFieldValues(sortedFields);
};
const handleDownload = async () => {
const workbook = new ExcelJS.Workbook();
const worksheet = workbook.addWorksheet('Sheet1');
const worksheet1 = workbook.addWorksheet('Sheet2');
const headerStyle = {
font: { bold: true },
border: {
top: { style: 'thin' },
left: { style: 'thin' },
bottom: { style: 'thin' },
right: { style: 'thin' },
},
alignment: { horizontal: 'center', vertical: 'middle' },
2026-01-27 18:27:29 +05:30
};
2026-03-23 16:08:11 +05:30
// Define all possible field configurations
const sampleData = getApplicationSampleData(appName);
console.log(sampleData, 'sampleData');
const fieldConfigs = {
ProdId: {
header: 'ProdId',
key: 'ProdId',
width: 15,
style: {
...headerStyle,
font: { bold: true, color: { argb: '#000000' } },
},
validation: null,
sampleValue: '',
headerColor: 'D3D3D3',
locked: true,
},
ProdName: {
header: 'Product Name',
key: 'ProdName',
width: 20,
Height: 20,
style: {
...headerStyle,
font: { bold: true, color: { argb: '#000000' } },
},
validation: null,
sampleValue: sampleData?.productName || 'Sample Product Name',
headerColor: 'FF2929',
},
Size: {
header: 'Quantity',
key: 'Size',
width: 10,
style: {
...headerStyle,
font: { bold: true, color: { argb: '#000000' } },
},
validation: {
type: 'custom',
allowBlank: true,
formulae: ['ISNUMBER({COLUMN}{ROW})'],
showErrorMessage: true,
errorTitle: 'Validation Error',
error: 'Please enter a Number.',
},
sampleValue: sampleData?.quantity || 1,
headerColor: 'FF2929',
},
UOM: {
header: 'UOM',
key: 'UOM',
width: 10,
style: {
...headerStyle,
font: { bold: true, color: { argb: '#000000' } },
},
validation: {
type: 'list',
allowBlank: true,
formulae: [
'"' +
(typeof Unit !== 'undefined' ? Unit.join(',') : 'KGS,PCS,LITER') +
'"',
],
},
sampleValue: sampleData?.uom || 'KGS',
headerColor: 'FF2929',
},
SellPrice: {
header: 'Sales Price',
key: 'SellPrice',
width: 15,
style: {
...headerStyle,
font: { bold: true, color: { argb: '#000000' } },
},
validation: {
type: 'custom',
allowBlank: true,
formulae: ['ISNUMBER({COLUMN}{ROW})'],
showErrorMessage: true,
errorTitle: 'Validation Error',
error: 'Please enter a Number.',
},
sampleValue: sampleData?.salesPrice || 100,
headerColor: 'FF2929',
},
ProdCat: {
header: 'Category',
key: 'ProdCat',
width: 20,
style: {
...headerStyle,
font: { bold: true, color: { argb: '#000000' } },
},
validation: {
type: 'list',
allowBlank: true,
formulae: [`Sheet2!$B$2:$B$${(CategoryNames?.length || 0) + 1}`],
},
sampleValue: sampleData?.category || 'Sample Category',
headerColor: 'FF2929',
},
ProdSubCat: {
header: 'Sub Category',
key: 'ProdSubCat',
width: 20,
style: {
...headerStyle,
font: { bold: true, color: { argb: '#000000' } },
},
validation: null, // set dynamically per row below
sampleValue: sampleData?.subCategory || 'Sample Sub Category',
headerColor: '52c41a',
},
Brand: {
header: 'Brand',
key: 'Brand',
width: 20,
style: {
...headerStyle,
font: { bold: true, color: { argb: '#000000' } },
},
validation: null, // set dynamically per row below
sampleValue: sampleData?.brand || 'Sample Brand',
headerColor: '52c41a',
},
ProdVarientName: {
header: 'Product Variant Name',
key: 'ProdVarientName',
width: 20,
Height: 20,
style: {
...headerStyle,
font: { bold: true, color: { argb: '#000000' } },
},
validation: null,
sampleValue: sampleData?.variantName || 'Sample Variant Name',
headerColor: '52c41a',
},
MRP: {
header: 'MRP',
key: 'MRP',
width: 10,
style: {
...headerStyle,
font: { bold: true, color: { argb: '#000000' } },
},
validation: {
type: 'custom',
allowBlank: true,
formulae: ['ISNUMBER({COLUMN}{ROW})'],
showErrorMessage: true,
errorTitle: 'Validation Error',
error: 'Please enter a Number.',
},
sampleValue: sampleData?.mrp || 100,
headerColor: '52c41a',
},
WhSalePrice: {
header: 'WholeSale Price',
key: 'WhSalePrice',
width: 15,
style: {
...headerStyle,
font: { bold: true, color: { argb: '#000000' } },
},
validation: {
type: 'custom',
allowBlank: true,
formulae: ['ISNUMBER({COLUMN}{ROW})'],
showErrorMessage: true,
errorTitle: 'Validation Error',
error: 'Please enter a Number.',
},
sampleValue: 100,
headerColor: '52c41a',
},
StockAvailable: {
header: 'Stock Available',
key: 'StockAvailable',
width: 20,
style: {
...headerStyle,
font: { bold: true, color: { argb: '#000000' } },
},
validation: {
type: 'list',
allowBlank: true,
formulae: ['"Yes,No"'],
},
sampleValue: 'Choose Yes or No',
headerColor: '52c41a',
},
OfferPrice: {
header: 'Token Available',
key: 'OfferPrice',
width: 15,
style: {
...headerStyle,
font: { bold: true, color: { argb: '#000000' } },
},
validation: {
type: 'list',
allowBlank: true,
formulae: ['"Yes,No"'],
},
sampleValue: 'Choose Yes or No',
headerColor: '52c41a',
},
Supplier: {
header: 'Product Type',
key: 'Supplier',
width: 20,
style: {
...headerStyle,
font: { bold: true, color: { argb: '#000000' } },
},
validation: {
type: 'list',
allowBlank: true,
formulae: ['"Product"'],
},
sampleValue: 'Product',
headerColor: '52c41a',
},
TaxId: {
header: 'Tax',
key: 'TaxId',
width: 20,
style: {
...headerStyle,
font: { bold: true, color: { argb: '#000000' } },
},
validation: null, // set via Sheet2 range reference below
sampleValue: sampleData?.tax || 'NIL - 0%',
headerColor: '52c41a',
},
AutoGenerateQr: {
header: 'Auto Generate Qrcode',
key: 'AutoGenerateQr',
width: 20,
style: {
...headerStyle,
font: { bold: true, color: { argb: '#000000' } },
},
validation: {
type: 'list',
allowBlank: true,
formulae: ['"Yes,No"'],
},
sampleValue: 'Choose Yes or No',
headerColor: '52c41a',
},
AddQrCode: {
header: 'Add Qr Code',
key: 'AddQrCode',
width: 20,
style: {
...headerStyle,
font: { bold: true, color: { argb: '#000000' } },
},
validation: null,
sampleValue: 'Sample Qr eg:12345',
headerColor: '52c41a',
},
SpecialPrice: {
header: 'One Piece Available',
key: 'SpecialPrice',
width: 20,
style: {
...headerStyle,
font: { bold: true, color: { argb: '#000000' } },
},
validation: {
type: 'list',
allowBlank: true,
formulae: ['"Yes,No"'],
},
sampleValue: 'Choose Yes or No',
headerColor: '52c41a',
},
OnePiecePrice: {
header: 'OnePiece Price',
key: 'OnePiecePrice',
width: 20,
style: {
...headerStyle,
font: { bold: true, color: { argb: '#000000' } },
},
validation: {
type: 'custom',
allowBlank: true,
formulae: ['ISNUMBER({COLUMN}{ROW})'],
showErrorMessage: true,
errorTitle: 'Validation Error',
error: 'Please enter a Number.',
},
sampleValue: 'OnePiecePrice',
headerColor: '52c41a',
},
AutoGenOnePieceQr: {
header: 'Auto Generate One Piece Qrcode',
key: 'AutoGenOnePieceQr',
width: 30,
style: {
...headerStyle,
font: { bold: true, color: { argb: '#000000' } },
},
validation: {
type: 'list',
allowBlank: true,
formulae: ['"Yes,No"'],
},
sampleValue: 'Choose Yes or No',
headerColor: '52c41a',
},
AutoGenOnePieceQrNum: {
header: 'Auto Generate One Piece Qrcode Number',
key: 'AutoGenOnePieceQrNum',
width: 40,
style: {
...headerStyle,
font: { bold: true, color: { argb: '#000000' } },
},
validation: null,
sampleValue: 'Qr Number eg:ABC123',
headerColor: '52c41a',
},
CESS: {
header: 'CESS',
key: 'CESS',
width: 20,
style: {
...headerStyle,
font: { bold: true, color: { argb: '#000000' } },
},
validation: null,
sampleValue: 'Cess eg:0.5',
headerColor: '52c41a',
},
HSNCode: {
header: 'HSN',
key: 'HSNCode',
width: 20,
style: {
...headerStyle,
font: { bold: true, color: { argb: '#000000' } },
},
validation: null,
sampleValue: 'Hsn Number eg:100190',
headerColor: '52c41a',
},
PartNumber: {
header: 'Part Number',
key: 'PartNumber',
width: 20,
style: {
...headerStyle,
font: { bold: true, color: { argb: '#000000' } },
},
validation: null,
sampleValue: 'Part Number eg:1234-5678',
headerColor: '52c41a',
},
Rack: {
header: 'Rack',
key: 'Rack',
width: 20,
style: {
...headerStyle,
font: { bold: true, color: { argb: '#000000' } },
},
validation: null,
sampleValue: 'Rack Number eg:2200302',
headerColor: '52c41a',
},
ManufDate: {
header: 'Manufacture Date',
key: 'ManufDate DATETIME',
width: 20,
style: {
...headerStyle,
font: { bold: true, color: { argb: '#000000' } },
},
validation: null,
sampleValue: new Date(),
headerColor: '52c41a',
},
ExpDate: {
header: 'Expire Date',
key: 'ExpDate DATETIME',
width: 20,
style: {
...headerStyle,
font: { bold: true, color: { argb: '#000000' } },
},
validation: null,
sampleValue: new Date(),
headerColor: '52c41a',
},
AvailableFrom: {
header: 'Available From',
key: 'AvailableFrom',
width: 20,
style: {
...headerStyle,
font: { bold: true, color: { argb: '#000000' } },
numFmt: '@',
},
validation: null,
sampleValue: '08:00:00',
headerColor: '52c41a',
isTimeField: true,
},
AvailableTo: {
header: 'Available To',
key: 'AvailableTo',
width: 20,
style: {
...headerStyle,
font: { bold: true, color: { argb: '#000000' } },
numFmt: '@',
},
validation: null,
sampleValue: '22:00:00',
headerColor: '52c41a',
isTimeField: true,
},
2026-01-27 18:27:29 +05:30
};
2026-03-23 16:08:11 +05:30
function getExcelColumnLetter(colIndex) {
let temp = '';
let letter = '';
while (colIndex > 0) {
temp = (colIndex - 1) % 26;
letter = String.fromCharCode(temp + 65) + letter;
colIndex = Math.floor((colIndex - temp - 1) / 26);
}
return letter;
}
2026-01-27 18:27:29 +05:30
2026-03-23 16:08:11 +05:30
const mandatoryFields = ['ProdName', 'Size', 'UOM', 'SellPrice', 'ProdCat'];
const apiToInternalFieldMap = {
'Product Name': 'ProdName',
Quantity: 'Size',
UOM: 'UOM',
'Sales Price': 'SellPrice',
MRP: 'MRP',
'WholeSale Price': 'WhSalePrice',
Category: 'ProdCat',
'Sub Category': 'ProdSubCat',
Brand: 'Brand',
'Product Variant Name': 'ProdVarientName',
'Token Available': 'OfferPrice',
'Stock Available': 'StockAvailable',
Tax: 'TaxId',
'Auto Generate QRCode': 'AutoGenerateQr',
'Add Qr Code': 'AddQrCode',
'One Piece Available': 'SpecialPrice',
'OnePiece Price': 'OnePiecePrice',
'Auto Generate One Piece Qrcode': 'AutoGenOnePieceQr',
'Auto Generate One Piece Qrcode Number': 'AutoGenOnePieceQrNum',
CESS: 'CESS',
HSN: 'HSNCode',
'Part Number': 'PartNumber',
Rack: 'Rack',
'Manufacture Date': 'ManufDate',
'Expire Date': 'ExpDate',
'Available From': 'AvailableFrom',
'Available To': 'AvailableTo',
'Product Type': 'Supplier',
2026-01-27 18:27:29 +05:30
};
2026-03-23 16:08:11 +05:30
// Define dependent relationships
const dependentFieldMap = {
'Auto Generate QRCode': 'Add Qr Code',
'One Piece Available': 'OnePiece Price',
'Auto Generate One Piece Qrcode': 'Auto Generate One Piece Qrcode Number',
2026-01-27 18:27:29 +05:30
};
2026-03-23 16:08:11 +05:30
// Step 1: Sort preferred fields
const sortedPreferredFields = sortPreferredFields(FieldValues); // Your existing sort function
2026-01-27 18:27:29 +05:30
2026-03-23 16:08:11 +05:30
// Step 2: Convert API names to internal keys while skipping dependent fields initially
const preferredFieldKeys = [];
const preferredFieldSet = new Set();
2026-01-27 18:27:29 +05:30
2026-03-23 16:08:11 +05:30
sortedPreferredFields.forEach((field) => {
const fieldName = field.ConfigName;
const status = field.Access;
2026-01-27 18:27:29 +05:30
2026-03-23 16:08:11 +05:30
// Skip dependent fields for now
if (Object.values(dependentFieldMap).includes(fieldName)) return;
2026-01-27 18:27:29 +05:30
2026-03-23 16:08:11 +05:30
if (status === 'Y') {
const internalKey = apiToInternalFieldMap[fieldName];
if (internalKey) {
preferredFieldKeys.push(internalKey);
preferredFieldSet.add(fieldName);
}
2026-01-27 18:27:29 +05:30
2026-03-23 16:08:11 +05:30
// If this field has a dependent field, and it's enabled, add dependent too
const dependentFieldName = dependentFieldMap[fieldName];
if (dependentFieldName) {
const dependentInternalKey =
apiToInternalFieldMap[dependentFieldName];
if (dependentInternalKey) {
preferredFieldKeys.push(dependentInternalKey);
}
2026-01-27 18:27:29 +05:30
}
2026-03-23 16:08:11 +05:30
}
});
2026-01-27 18:27:29 +05:30
2026-03-23 16:08:11 +05:30
// Step 3: Merge Mandatory + Preferred (no duplicates)
const finalFieldKeys = [
...mandatoryFields,
...preferredFieldKeys.filter((key) => !mandatoryFields.includes(key)),
2026-01-27 18:27:29 +05:30
];
2026-03-23 16:08:11 +05:30
// Step 4: Map to ExcelJS column configs
const selectedColumns = finalFieldKeys
.map((fieldKey) => {
const config = fieldConfigs[fieldKey];
if (!config) {
console.warn(`Field configuration not found for: ${fieldKey}`);
return null;
}
return config;
})
.filter(Boolean);
selectedColumns.push({
header: '__isSampleRow',
key: '__isSampleRow',
width: 10, // width can be any value
style: { font: { size: 1 }, alignment: { horizontal: 'left' } },
sampleValue: 'sample row', // Mark sample row
headerColor: 'FFFFFF',
});
2026-01-27 18:27:29 +05:30
2026-03-23 16:08:11 +05:30
worksheet.columns = selectedColumns;
2026-02-09 10:32:03 +05:30
2026-03-23 16:08:11 +05:30
worksheet.getColumn(selectedColumns.length).hidden = true;
2026-01-27 18:27:29 +05:30
2026-03-23 16:08:11 +05:30
selectedColumns.forEach((config, index) => {
const columnLetter = getExcelColumnLetter(index + 1); // ✅ FIXED
const headerCell = worksheet.getCell(`${columnLetter}1`);
headerCell.style = {
...config.style,
fill: {
type: 'pattern',
pattern: 'solid',
fgColor: { argb: config.headerColor },
},
};
});
2026-01-27 18:27:29 +05:30
2026-03-23 16:08:11 +05:30
// Add sample data in row 2 — time fields forced to text format
selectedColumns.forEach((config, index) => {
const columnLetter = getExcelColumnLetter(index + 1);
const cell = worksheet.getCell(`${columnLetter}2`);
if (config.isTimeField) {
cell.numFmt = '@';
cell.value = String(config.sampleValue);
} else {
cell.value = config.sampleValue;
}
});
2026-01-27 18:27:29 +05:30
2026-03-23 16:08:11 +05:30
// Force text format on all data rows for time fields
selectedColumns.forEach((config, index) => {
if (config.isTimeField) {
const columnLetter = getExcelColumnLetter(index + 1);
for (let r = 2; r <= 1002; r++) {
worksheet.getCell(`${columnLetter}${r}`).numFmt = '@';
2026-01-27 18:27:29 +05:30
}
2026-03-23 16:08:11 +05:30
}
});
2026-01-27 18:27:29 +05:30
2026-03-23 16:08:11 +05:30
// Add validation to columns
2026-01-27 18:27:29 +05:30
2026-03-23 16:08:11 +05:30
selectedColumns.forEach((config, colIndex) => {
if (config.validation) {
const colNumber = colIndex + 1; // Excel is 1-based
const columnLetter = getExcelColumnLetter(colNumber); // ✅ FIXED
2026-01-27 18:27:29 +05:30
2026-03-23 16:08:11 +05:30
for (let rowNumber = 2; rowNumber <= 1001; rowNumber++) {
const cell = worksheet.getCell(rowNumber, colNumber); // ✅ Use (row, col) instead of letter
let validation = { ...config.validation };
2026-01-27 18:27:29 +05:30
2026-03-23 16:08:11 +05:30
if (validation.formulae) {
validation.formulae = validation.formulae.map((formula) =>
formula
.replace('{COLUMN}', columnLetter)
.replace('{ROW}', rowNumber.toString())
);
}
2026-01-27 18:27:29 +05:30
2026-03-23 16:08:11 +05:30
cell.dataValidation = validation;
}
}
});
2026-01-27 18:27:29 +05:30
2026-03-23 16:08:11 +05:30
// // Now set protection for all rows
// // First, unlock ALL cells in the worksheet
// for (let rowNum = 1; rowNum <= worksheet.rowCount; rowNum++) {
// const row = worksheet.getRow(rowNum);
// row.eachCell({ includeEmpty: true }, (cell, colNumber) => {
// const config = selectedColumns[colNumber - 1];
// cell.protection = { locked: config?.locked || false };
// });
// }
2026-01-27 18:27:29 +05:30
2026-03-23 16:08:11 +05:30
// // Then lock ONLY the header row (row 1) and sample row (row 2)
// worksheet.getRow(1).eachCell({ includeEmpty: true }, (cell) => {
// cell.protection = { locked: true };
// });
// worksheet.getRow(2).eachCell({ includeEmpty: true }, (cell) => {
// cell.protection = { locked: true };
// });
// Add empty rows for data entry
for (let i = 1; i <= 1000; i++) {
worksheet.addRow({});
}
2026-01-27 18:27:29 +05:30
2026-03-23 16:08:11 +05:30
// Explicitly unlock all data-entry cells (rows 3+) for every column
for (let rowNum = 3; rowNum <= worksheet.rowCount; rowNum++) {
for (let colNum = 1; colNum <= selectedColumns.length; colNum++) {
const config = selectedColumns[colNum - 1];
worksheet.getCell(rowNum, colNum).protection = {
locked: config?.locked || false,
};
}
}
2026-01-27 18:27:29 +05:30
2026-03-23 16:08:11 +05:30
// Lock header row (row 1) and sample row (row 2)
for (let colNum = 1; colNum <= selectedColumns.length; colNum++) {
worksheet.getCell(1, colNum).protection = { locked: true };
worksheet.getCell(2, colNum).protection = { locked: true };
}
2026-01-27 18:27:29 +05:30
2026-03-23 16:08:11 +05:30
// Protect the worksheet
await worksheet.protect('', {
selectLockedCells: true, // Allow selecting locked cells (header/sample)
selectUnlockedCells: true, // Allow selecting unlocked cells (data rows)
formatCells: false,
formatColumns: false,
formatRows: false,
insertColumns: false,
insertRows: false,
deleteColumns: false,
deleteRows: false,
});
2026-01-27 18:27:29 +05:30
2026-03-23 16:08:11 +05:30
// Configure Sheet2 with all original columns and functionality
worksheet1.columns = [
{
header: 'Category Id',
key: 'CategoryId',
width: 15,
style: {
...headerStyle,
font: { bold: true, color: { argb: '#000000' } },
},
},
{
header: 'Category Name',
key: 'Category',
width: 30,
style: {
...headerStyle,
font: { bold: true, color: { argb: '#000000' } },
},
},
{
header: 'SUBCAT DATAS OF PARTICULAR CATEGORY',
key: 'SDPC',
width: 180,
style: {
...headerStyle,
font: { bold: true, color: { argb: '#000000' } },
},
},
{
header: 'SUBCAT ID',
key: 'SubcayID',
width: 10,
style: {
...headerStyle,
font: { bold: true, color: { argb: '#000000' } },
},
},
{
header: 'Sub Category name',
key: 'SubCategory',
width: 25,
style: {
...headerStyle,
font: { bold: true, color: { argb: '#000000' } },
},
},
{
header: '',
key: '',
width: 5,
style: {
...headerStyle,
font: { bold: true, color: { argb: '#000000' } },
},
},
{
header: 'SUBCAT NUMFID',
key: 'SubcatnumfId',
width: 20,
style: {
...headerStyle,
font: { bold: true, color: { argb: '#000000' } },
},
},
{
header: '',
key: 'smallIcon',
width: 5,
style: {
...headerStyle,
font: { bold: true, color: { argb: '#000000' } },
},
},
{
header: 'Brand ID',
key: 'BrandId',
width: 20,
style: {
...headerStyle,
font: { bold: true, color: { argb: '#000000' } },
},
},
{
header: 'Brand Name',
key: 'Brandname',
width: 20,
style: {
...headerStyle,
font: { bold: true, color: { argb: '#000000' } },
},
},
{
header: 'Brand NumfId',
key: 'BrandnumfId',
width: 20,
style: {
...headerStyle,
font: { bold: true, color: { argb: '#000000' } },
},
},
{
header: '',
key: 'smallIcon',
width: 5,
style: {
...headerStyle,
font: { bold: true, color: { argb: '#000000' } },
},
},
{
header: 'Brand Details',
key: 'Branddetails',
width: 150,
style: {
...headerStyle,
font: { bold: true, color: { argb: '#000000' } },
},
},
];
2026-01-27 18:27:29 +05:30
2026-03-23 16:08:11 +05:30
// Add data to Sheet2
for (let i = 1; i <= 100; i++) {
worksheet1.addRow({
CategoryId: '',
Category: '',
SDPC: '',
SubcayID: '',
SubCategory: '',
SubcatnumfId: '',
BrandId: '',
Brandname: '',
BrandnumfId: '',
Branddetails: '',
});
}
2026-01-27 18:27:29 +05:30
2026-03-23 16:08:11 +05:30
// All the original dropdown and validation logic for Sheet2
const dropdownOptions = {
Option1: ['Yes', 'No'],
Option2: ['Product'],
Option3: ['ValueX', 'ValueY', 'ValueZ'],
};
2026-01-27 18:27:29 +05:30
2026-03-23 16:08:11 +05:30
const selectedOptionA = 'Option1';
const selectedOptionB = 'Option2';
2026-01-27 18:27:29 +05:30
2026-03-23 16:08:11 +05:30
// Create category and brand data maps (original logic)
const categoryDataMap = {};
if (typeof CategoryId !== 'undefined') {
CategoryId.forEach((categoryId, index) => {
categoryDataMap[categoryId] =
typeof SubCatData !== 'undefined' ? SubCatData[index] : '';
});
}
2026-01-27 18:27:29 +05:30
2026-03-23 16:08:11 +05:30
const BrandDataMap = {};
if (typeof AllBrandId !== 'undefined') {
AllBrandId.forEach((AllBrandId, index) => {
BrandDataMap[AllBrandId] =
typeof SubCatData !== 'undefined' ? SubCatData[index] : '';
});
}
2026-01-27 18:27:29 +05:30
2026-03-23 16:08:11 +05:30
// Apply all original Sheet2 validations and logic
if (typeof CategoryId !== 'undefined') {
worksheet1
.getColumn('A')
.eachCell({ includeEmpty: true }, function (cell, rowNumber) {
if (rowNumber > 1) {
const dynamicValidValues = '"' + CategoryId.join(',') + '"';
const defaultCategoryId = CategoryId[rowNumber - 2];
cell.value = defaultCategoryId;
cell.dataValidation = {
type: 'list',
allowBlank: true,
formulae: [dynamicValidValues],
};
}
2026-01-27 18:27:29 +05:30
});
2026-03-23 16:08:11 +05:30
}
2026-01-27 18:27:29 +05:30
2026-03-23 16:08:11 +05:30
if (typeof CategoryNames !== 'undefined') {
worksheet1
.getColumn('B')
.eachCell({ includeEmpty: true }, function (cell, rowNumber) {
if (rowNumber > 1) {
const dynamicValidValues = '"' + CategoryNames.join(',') + '"';
const defaultCategoryName = CategoryNames[rowNumber - 2];
cell.value = defaultCategoryName;
cell.dataValidation = {
type: 'list',
allowBlank: true,
formulae: [dynamicValidValues],
};
}
});
}
2026-01-27 18:27:29 +05:30
2026-03-23 16:08:11 +05:30
if (typeof SubcatId !== 'undefined') {
worksheet1
.getColumn('D')
.eachCell({ includeEmpty: true }, function (cell, rowNumber) {
if (rowNumber > 1) {
const dynamicValidValues = '"' + SubcatId.join(',') + '"';
const defaultCategorysubName = SubcatId[rowNumber - 2];
cell.value = defaultCategorysubName;
cell.dataValidation = {
type: 'list',
allowBlank: true,
formulae: [dynamicValidValues],
};
}
});
}
2026-01-27 18:27:29 +05:30
2026-03-23 16:08:11 +05:30
if (typeof SubCatData !== 'undefined') {
worksheet1
.getColumn('E')
.eachCell({ includeEmpty: true }, function (cell, rowNumber) {
if (rowNumber > 1) {
const dynamicValidValues = '"' + SubCatData.join(',') + '"';
const defaultCategorysubName = SubCatData[rowNumber - 2];
cell.value = defaultCategorysubName;
cell.dataValidation = {
type: 'list',
allowBlank: true,
formulae: [dynamicValidValues],
};
}
});
}
2026-01-27 18:27:29 +05:30
2026-03-23 16:08:11 +05:30
if (typeof Subcatnumfid !== 'undefined') {
worksheet1
.getColumn('G')
.eachCell({ includeEmpty: true }, function (cell, rowNumber) {
if (rowNumber > 1) {
const dynamicValidValues = '"' + Subcatnumfid.join(',') + '"';
const defaultCategorysubName = Subcatnumfid[rowNumber - 2];
cell.value = defaultCategorysubName;
cell.dataValidation = {
type: 'list',
allowBlank: true,
formulae: [dynamicValidValues],
};
}
2026-01-27 18:27:29 +05:30
});
2026-03-23 16:08:11 +05:30
}
2026-01-27 18:27:29 +05:30
2026-03-23 16:08:11 +05:30
if (typeof AllBrandId !== 'undefined') {
worksheet1
.getColumn('I')
.eachCell({ includeEmpty: true }, function (cell, rowNumber) {
if (rowNumber > 1) {
const dynamicValidValues = '"' + AllBrandId.join(',') + '"';
const defaultCategorysubName = AllBrandId[rowNumber - 2];
cell.value = defaultCategorysubName;
cell.dataValidation = {
type: 'list',
allowBlank: true,
formulae: [dynamicValidValues],
};
}
});
}
2026-01-27 18:27:29 +05:30
2026-03-23 16:08:11 +05:30
if (typeof Allbranddata !== 'undefined') {
worksheet1
.getColumn('J')
.eachCell({ includeEmpty: true }, function (cell, rowNumber) {
if (rowNumber > 1) {
const dynamicValidValues = '"' + Allbranddata.join(',') + '"';
const defaultCategorysubName = Allbranddata[rowNumber - 2];
cell.value = defaultCategorysubName;
cell.dataValidation = {
type: 'list',
allowBlank: true,
formulae: [dynamicValidValues],
};
}
});
}
2026-01-27 18:27:29 +05:30
2026-03-23 16:08:11 +05:30
if (typeof Allbranddatanumfid !== 'undefined') {
worksheet1
.getColumn('K')
.eachCell({ includeEmpty: true }, function (cell, rowNumber) {
if (rowNumber > 1) {
const dynamicValidValues = '"' + Allbranddatanumfid.join(',') + '"';
const defaultCategorysubName = Allbranddatanumfid[rowNumber - 2];
cell.value = defaultCategorysubName;
cell.dataValidation = {
type: 'list',
allowBlank: true,
formulae: [dynamicValidValues],
};
}
});
}
2026-01-27 18:27:29 +05:30
2026-03-23 16:08:11 +05:30
// Write TaxDatas into Sheet2 column N for range-based dropdown (avoids 255-char limit)
if (typeof TaxDatas !== 'undefined' && TaxDatas?.length > 0) {
TaxDatas.forEach((taxItem, idx) => {
worksheet1.getCell(`N${idx + 2}`).value = taxItem
.replace(/,/g, '')
.trim();
});
}
2026-01-27 18:27:29 +05:30
2026-03-23 16:08:11 +05:30
// Apply Tax validation using Sheet2 column N range
const taxColIndex = selectedColumns.findIndex((c) => c.key === 'TaxId') + 1;
if (taxColIndex > 0 && TaxDatas?.length > 0) {
for (let rowNumber = 3; rowNumber <= 1002; rowNumber++) {
worksheet.getCell(rowNumber, taxColIndex).dataValidation = {
type: 'list',
allowBlank: true,
formulae: [`Sheet2!$N$2:$N$${TaxDatas.length + 1}`],
showErrorMessage: false,
};
}
}
2026-01-27 18:27:29 +05:30
2026-03-23 16:08:11 +05:30
// ── Dynamic SubCategory dropdown per Category ──────────────────────
// Build a map: categoryName → [subcat names]
const catSubcatMap = {};
if (CategoryNames && SubCatData && Subcatnumfid) {
CategoryNames.forEach((catName, catIdx) => {
const catId = CategoryId?.[catIdx];
const subcats = SubCatData.filter(
(_, sIdx) => Subcatnumfid[sIdx] == catId
);
catSubcatMap[catName] = subcats;
});
}
2026-01-27 18:27:29 +05:30
2026-03-23 16:08:11 +05:30
// Write each category's subcats into Sheet2 starting at column O (col 15)
// Row 1 = category name header, rows 2+ = subcat names
const catSubcatStartCol = 15; // column O
const catNameToColLetter = {};
2026-01-27 18:27:29 +05:30
2026-03-23 16:08:11 +05:30
if (CategoryNames) {
CategoryNames.forEach((catName, catIdx) => {
const colIndex = catSubcatStartCol + catIdx;
const colLetter = getExcelColumnLetter(colIndex);
catNameToColLetter[catName] = colLetter;
2026-01-27 18:27:29 +05:30
2026-03-23 16:08:11 +05:30
// Write header (category name)
worksheet1.getCell(`${colLetter}1`).value = catName;
2026-01-27 18:27:29 +05:30
2026-03-23 16:08:11 +05:30
// Write subcats for this category
const subcats = catSubcatMap[catName] || [];
subcats.forEach((subcat, sIdx) => {
worksheet1.getCell(`${colLetter}${sIdx + 2}`).value = subcat;
});
2026-01-27 18:27:29 +05:30
2026-03-23 16:08:11 +05:30
// Define a named range for this category's subcats
const safeName = catName.replace(/[^A-Za-z0-9_]/g, '_');
const rangeRef =
subcats.length > 0
? `Sheet2!$${colLetter}$2:$${colLetter}$${subcats.length + 1}`
: `Sheet2!$${colLetter}$2:$${colLetter}$2`;
2026-01-27 18:27:29 +05:30
2026-03-23 16:08:11 +05:30
workbook.definedNames.add(rangeRef, safeName);
});
}
2026-01-27 18:27:29 +05:30
2026-03-23 16:08:11 +05:30
// Find which column index ProdCat and ProdSubCat are in Sheet1
const catColIndex =
selectedColumns.findIndex((c) => c.key === 'ProdCat') + 1;
const subCatColIndex =
selectedColumns.findIndex((c) => c.key === 'ProdSubCat') + 1;
if (catColIndex > 0 && subCatColIndex > 0) {
const catColLetter = getExcelColumnLetter(catColIndex);
for (let rowNumber = 3; rowNumber <= 1002; rowNumber++) {
const cell = worksheet.getCell(rowNumber, subCatColIndex);
cell.dataValidation = {
type: 'list',
allowBlank: true,
formulae: [
`INDIRECT(SUBSTITUTE(${catColLetter}${rowNumber}," ","_"))`,
],
showErrorMessage: false,
};
}
}
2026-01-27 18:27:29 +05:30
2026-03-23 16:08:11 +05:30
// ── Dynamic Brand dropdown per SubCategory ────────────────────────
// Build a map: subcatName → [brand names] using Allbranddatanumfid (parent subcat ID)
const subcatBrandMap = {};
if (SubCatData && Allbranddata && Allbranddatanumfid) {
SubCatData.forEach((subcatName, sIdx) => {
const subcatId = SubcatId?.[sIdx];
const brands = Allbranddata.filter(
(_, bIdx) => Allbranddatanumfid[bIdx] == subcatId
);
subcatBrandMap[subcatName] = brands;
});
}
2026-03-23 16:08:11 +05:30
// Write each subcat's brands into Sheet2 starting after the category columns
const brandStartCol = catSubcatStartCol + (CategoryNames?.length || 0) + 1;
2026-03-23 16:08:11 +05:30
if (SubCatData) {
SubCatData.forEach((subcatName, sIdx) => {
const colIndex = brandStartCol + sIdx;
const colLetter = getExcelColumnLetter(colIndex);
2026-03-23 16:08:11 +05:30
worksheet1.getCell(`${colLetter}1`).value = `B_${subcatName}`;
2026-03-23 16:08:11 +05:30
const brands = subcatBrandMap[subcatName] || [];
brands.forEach((brand, bIdx) => {
worksheet1.getCell(`${colLetter}${bIdx + 2}`).value = brand;
});
2026-03-23 16:08:11 +05:30
const safeName = `B_${subcatName.replace(/[^A-Za-z0-9_]/g, '_')}`;
const rangeRef =
brands.length > 0
? `Sheet2!$${colLetter}$2:$${colLetter}$${brands.length + 1}`
: `Sheet2!$${colLetter}$2:$${colLetter}$2`;
workbook.definedNames.add(rangeRef, safeName);
});
}
2026-03-23 16:08:11 +05:30
// Apply INDIRECT brand validation based on selected SubCategory
const brandColIndex =
selectedColumns.findIndex((c) => c.key === 'Brand') + 1;
if (subCatColIndex > 0 && brandColIndex > 0) {
const subCatColLetter = getExcelColumnLetter(subCatColIndex);
for (let rowNumber = 3; rowNumber <= 1002; rowNumber++) {
const cell = worksheet.getCell(rowNumber, brandColIndex);
cell.dataValidation = {
type: 'list',
allowBlank: true,
formulae: [
`INDIRECT("B_"&SUBSTITUTE(${subCatColLetter}${rowNumber}," ","_"))`,
],
showErrorMessage: false,
};
}
}
// ─────────────────────────────────────────────────────────────────────
// Original complex logic for column C (SUBCAT DATAS)
worksheet1
.getColumn('C')
.eachCell({ includeEmpty: true }, function (cell, rowNumber) {
if (rowNumber > 1) {
const categoryId = worksheet1.getCell(`A${rowNumber}`).value;
if (categoryId !== null) {
const uniqueSubCategoryData = new Set();
const matchedData = [];
2026-03-23 16:08:11 +05:30
worksheet1
.getColumn('G')
.eachCell(
{ includeEmpty: true },
function (subCatCell, subCatRowNumber) {
if (subCatRowNumber > 1 && subCatCell.value === categoryId) {
const subCategoryName = worksheet1.getCell(
`E${subCatRowNumber}`
).value;
const subCatId = worksheet1.getCell(
`D${subCatRowNumber}`
).value;
const fullSubCategoryName = `${subCatId} - ${subCategoryName}`;
uniqueSubCategoryData.add({
subCategoryId: subCatId,
subCategoryName: subCategoryName,
});
const subCatData = categoryDataMap[categoryId]
? categoryDataMap[categoryId].subCatData
: null;
const data = {
subCategoryId: subCatId,
subCategoryName: subCategoryName,
subCatData: subCatData,
};
matchedData.push(data);
}
}
);
2026-03-23 16:08:11 +05:30
cell.value = JSON.stringify(Array.from(uniqueSubCategoryData));
}
}
2026-03-23 16:08:11 +05:30
});
// Original complex logic for column M (Brand Details)
worksheet1
.getColumn('M')
.eachCell({ includeEmpty: true }, function (cell, rowNumber) {
if (rowNumber > 1) {
const SubcatId = worksheet1.getCell(`D${rowNumber}`).value;
if (SubcatId !== null) {
const uniqueSubCategoryData = new Set();
const matchedData = [];
2026-03-23 16:08:11 +05:30
worksheet1
.getColumn('K')
.eachCell(
{ includeEmpty: true },
function (BrandCell, BrandRowNumber) {
if (BrandRowNumber > 1 && BrandCell.value === SubcatId) {
const BrandName = worksheet1.getCell(
`J${BrandRowNumber}`
).value;
const BrandId = worksheet1.getCell(
`I${BrandRowNumber}`
).value;
const fullSubCategoryName = `${BrandId} - ${BrandName}`;
uniqueSubCategoryData.add({
BrandId: BrandId,
BrandName: BrandName,
});
const BrandData = BrandDataMap[SubcatId]
? BrandDataMap[SubcatId].BrandData
: null;
const data = {
BrandId: BrandId,
BrandName: BrandName,
BrandData: BrandData,
};
matchedData.push(data);
}
}
);
2026-03-23 16:08:11 +05:30
cell.value = JSON.stringify(Array.from(uniqueSubCategoryData));
}
}
2026-03-23 16:08:11 +05:30
});
2026-03-23 16:08:11 +05:30
// Generate and download the file
const buffer = await workbook.xlsx.writeBuffer();
const blob = new Blob([buffer], {
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
});
const filename = 'Product Data.xlsx';
if (typeof window !== 'undefined') {
if (window.navigator && window.navigator.msSaveOrOpenBlob) {
window.navigator.msSaveOrOpenBlob(blob, filename);
} else {
const downloadLink = document.createElement('a');
downloadLink.href = window.URL.createObjectURL(blob);
downloadLink.download = filename;
downloadLink.click();
}
}
};
// Usage examples:
// Download only specific fields:
// handleDownload(['ProdName', 'Size']); // Only Product Name and Quantity
// handleDownload(['ProdName', 'Size', 'UOM', 'SellPrice']); // Multiple fields
// handleDownload(['ProdName', 'Size', 'UOM', 'SellPrice', 'ProdCat', 'Brand']); // More fields
// Available field names:
// 'ProdName', 'Size', 'UOM', 'SellPrice', 'ProdCat', 'ProdSubCat', 'Brand',
// 'ProdVarientName', 'MRP', 'StockAvailable', 'OfferPrice', 'Supplier', 'TaxId',
// 'AutoGenerateQr', 'AddQrCode', 'SpecialPrice', 'OnePiecePrice', 'AutoGenOnePieceQr',
// 'AutoGenOnePieceQrNum', 'CESS', 'HSNCode', 'PartNumber', 'Rack', 'ManufDate',
// 'ExpDate', 'AvailableFrom', 'AvailableTo'
const handleFileSubmit = (e) => {
e.preventDefault();
if (excelFile === null) {
excelFileError('Please select an Excel file.');
} else {
handleSubmit(Datas);
}
};
const handleFieldSetup = () => {
setFieldSetup(true);
};
const handleFieldSetupSubmit = async () => {
const postData = {
AppId: AppId,
CompId: CompId,
BranchId: BranchId,
Type: 'EB',
FormType: 'Product',
TypeId: productBulkUploadCatId,
ConfigDtl: selectedFields?.map((field) => ({
ConfigId: field,
Access: 'Y',
})),
CreatedBy: UserId,
};
2026-03-23 16:08:11 +05:30
const response = await dispatch(postFieldSetup(postData))?.unwrap();
2026-03-23 16:08:11 +05:30
if (response?.data?.statusCode === 1) {
setMessageType('success');
setMessageData(response?.data?.response);
setFieldSetup(false);
await getFieldSetup();
} else {
setMessageType('error');
setMessageData('Failed to set up fields');
}
};
const handleFieldSelect = (value) => {
setSelectedFields((prev) => {
if (prev.includes(value)) {
return prev;
}
return [value, ...prev];
});
};
2026-03-23 16:08:11 +05:30
const handleFieldRemove = (id) => {
setSelectedFields((prev) => prev.filter((field) => field !== id));
};
2026-03-23 16:08:11 +05:30
const handleCancelData = () => {
dispatch(emptyExcelData());
if (fileInputRef.current) {
fileInputRef.current.value = '';
}
};
const columns = [
{
title: 'Product Name',
dataIndex: 'ProductName',
key: 'ProductName',
editable: true,
},
{
title: 'Qty',
dataIndex: 'Quantity',
key: 'Quantity',
editable: true,
},
{
title: 'UOM',
dataIndex: 'UOM',
key: 'UOM',
editable: true,
},
{
title: 'Sales Price',
dataIndex: 'SellPrice',
key: 'SellPrice',
editable: true,
},
{
title: 'Category',
dataIndex: 'Category',
key: 'Category',
editable: true,
},
{
title: 'Sub Category',
dataIndex: 'SubCategory',
key: 'SubCategory',
editable: true,
},
{
title: 'Brand',
dataIndex: 'Brand',
key: 'Brand',
editable: true,
},
{
title: 'Variant Name',
dataIndex: 'ProductVariantName',
key: 'ProductVariantName',
editable: true,
},
{
title: 'MRP',
dataIndex: 'MRP',
key: 'MRP',
editable: true,
},
{
title: 'WholeSale Price',
dataIndex: 'WhSalePrice',
key: 'WhSalePrice',
editable: true,
},
{
title: 'Auto Qrcode',
dataIndex: 'AutoGenerateQrcode',
key: 'AutoGenerateQrcode',
editable: true,
},
{
title: 'Add QrCode',
dataIndex: 'AddQrCode',
key: 'AddQrCode',
editable: true,
},
{
title: 'Stock Available',
dataIndex: 'StockAvailable',
key: 'StockAvailable',
editable: true,
},
{
title: 'Token Available',
dataIndex: 'TokenAvailable',
key: 'TokenAvailable',
editable: true,
},
{
title: 'One Piece Available',
dataIndex: 'OnePieceAvailable',
key: 'OnePieceAvailable',
editable: true,
},
{
title: 'One Piece Price',
dataIndex: 'OnePiecePrice',
key: 'OnePiecePrice',
editable: true,
},
{
title: 'Auto Gen One Piece Qr',
dataIndex: 'AutoGenerateOnePieceQrcode',
key: 'AutoGenerateOnePieceQrcode',
editable: true,
},
{
title: 'Auto Gen One Piece Qr No',
dataIndex: 'AutoGenerateOnePieceQrcodeNumber',
key: 'AutoGenerateOnePieceQrcodeNumber',
editable: true,
},
{
title: 'Product Type',
dataIndex: 'ProductType',
key: 'ProductType',
editable: true,
},
{
title: 'Tax',
dataIndex: 'Tax',
key: 'Tax',
editable: true,
},
{
title: 'Cess',
dataIndex: 'CESS',
key: 'CESS',
editable: true,
},
{
title: 'HSN',
dataIndex: 'HSN',
key: 'HSN',
editable: true,
},
{
title: 'Part Number',
dataIndex: 'PartNumber',
key: 'PartNumber',
editable: true,
},
{
title: 'Rack',
dataIndex: 'Rack',
key: 'Rack',
editable: true,
},
{
title: 'Mfg Date',
dataIndex: 'ManufactureDate',
key: 'ManufactureDate',
editable: true,
},
{
title: 'Exp Date',
dataIndex: 'ExpireDate',
key: 'ExpireDate',
editable: true,
},
{
title: 'Avl From',
dataIndex: 'AvailableFrom',
key: 'AvailableFrom',
editable: true,
},
{
title: 'Avl To',
dataIndex: 'AvailableTo',
key: 'AvailableTo',
editable: true,
},
{
title: 'Image',
dataIndex: 'Productimage',
key: 'Productimage',
render: (ProdImage, record, index) => {
const Img = ProdImage;
return (
<>
{Img ? (
<img
src={Img}
alt={record.Productimage}
style={{ maxWidth: '50px', maxHeight: '50px' }}
/>
) : (
<>
<div className="imgUpldr" style={{ fontSize: '15px' }}>
<Imageupload
singleImage={true}
updateImageUrl={(url) =>
updateImageUrl(url, index, record.key)
2026-02-09 10:32:03 +05:30
}
2026-03-23 16:08:11 +05:30
// ImageLink={record.Productimage || ""}
ImageLink={
onlineImage
? onlineImage
: record.Productimage
? record.Productimage
: ''
2026-02-09 10:32:03 +05:30
}
2026-03-23 16:08:11 +05:30
listType="picture-card"
// onlineImage={onlineImage?onlineImage:""}
// ImageLink={record.Productimage }
// updateImageUrl={updateImageUrl}
// singleImage={true}
recordMaintainKey={record.key}
/>
<Tooltip title="Online Search">
<TbWorldSearch
style={{ fontSize: '30px' }}
onClick={() => addOnlineImage(record, index)}
/>
</Tooltip>
</div>
</>
)}
</>
);
},
},
];
2026-01-27 18:27:29 +05:30
2026-03-23 16:08:11 +05:30
const column = columns?.map((col) => {
if (!col.editable) {
return col;
}
2026-01-27 18:27:29 +05:30
2026-03-23 16:08:11 +05:30
let onClickHandler = null;
2026-01-27 18:27:29 +05:30
2026-03-23 16:08:11 +05:30
if (editdelete === 'Delete') {
onClickHandler = (record) => removeFromTable(record);
}
2026-01-27 18:27:29 +05:30
2026-03-23 16:08:11 +05:30
return {
...col,
onCell: (record) => ({
record,
editable: col.editable,
dataIndex: col.dataIndex,
title: col.title,
handleSave,
onClick: () => onClickHandler(record),
}),
2026-01-27 18:27:29 +05:30
};
2026-03-23 16:08:11 +05:30
});
2026-01-27 18:27:29 +05:30
2026-03-23 16:08:11 +05:30
const EditableContext = React.createContext(null);
2026-01-27 18:27:29 +05:30
2026-03-23 16:08:11 +05:30
const EditableRow = ({ index, dataIndex, ...props }) => {
const [form] = Form.useForm();
return (
<Form form={form} component={false}>
<EditableContext.Provider value={form}>
<tr {...props} />
</EditableContext.Provider>
</Form>
);
};
const EditableCell = ({
title,
editable,
children,
dataIndex,
record,
handleSave,
index,
...restProps
}) => {
const [editing, setEditing] = useState(false);
const inputRef = useRef(null);
const form = useContext(EditableContext);
2026-01-27 18:27:29 +05:30
2026-03-23 16:08:11 +05:30
useEffect(() => {
if (editing) {
inputRef?.current?.focus();
}
}, [editing]);
const toggleEdit = () => {
setEditing(!editing);
form.setFieldsValue({
[dataIndex]: record[dataIndex],
});
2026-02-09 10:32:03 +05:30
};
2026-01-27 18:27:29 +05:30
2026-03-23 16:08:11 +05:30
const save = async () => {
try {
const values = await form.validateFields();
toggleEdit();
handleSave({
...record,
...values,
2026-01-27 18:27:29 +05:30
});
2026-03-23 16:08:11 +05:30
} catch (errInfo) {}
2026-01-27 18:27:29 +05:30
};
2026-03-23 16:08:11 +05:30
let childNode = children;
if (editable) {
childNode = editing ? (
<Form.Item
style={{
margin: 0,
}}
name={dataIndex}
>
<Input ref={inputRef} onPressEnter={save} onBlur={save} />
</Form.Item>
) : (
<div
className="editable-cell-value-wrap"
style={{
paddingRight: 24,
}}
onClick={toggleEdit}
>
{children}
</div>
);
}
2026-01-27 18:27:29 +05:30
2026-03-23 16:08:11 +05:30
return <td {...restProps}>{childNode}</td>;
};
const components = {
body: {
row: EditableRow,
cell: EditableCell,
},
};
const handleSave = (row) => {
const newDatas = [...Datas];
const item = newDatas.filter((item) => item.key === row.key);
// const item = newDatas[index];
const UpdatedData = {
ProductName: row?.ProductName,
ProductVariantName: row?.ProductVariantName
? row?.ProductVariantName
: 'Variant 1',
Quantity: row?.Quantity,
UOM: row?.UOM,
MRP: row?.MRP,
isModified: true,
WhSalePrice: row?.WhSalePrice,
SellPrice: row?.SellPrice,
Category: row?.Category,
SubCategory: row?.SubCategory,
Brand: row?.Brand,
AutoGenerateQrcode: row?.AutoGenerateQrcode,
AddQrCode: row?.AddQrCode,
StockAvailable: row?.StockAvailable,
// NumberOfPieceInside:row?.NumberOfPieceInside,
TokenAvailable: row?.TokenAvailable,
OnePieceAvailable: row?.OnePieceAvailable,
OnePiecePrice: row?.OnePiecePrice,
AutoGenerateOnePieceQrcode: row?.AutoGenerateOnePieceQrcode,
AutoGenerateOnePieceQrcodeNumber: row?.AutoGenerateOnePieceQrcodeNumber,
ProductType: row?.ProductType,
Tax: row?.Tax,
CESS: row?.CESS,
HSN: row?.HSN,
PartNumber: row?.PartNumber,
Rack: row?.Rack,
ManufactureDate: row?.ManufactureDate,
ExpireDate: row?.ExpireDate,
AvailableFrom: row?.AvailableFrom,
AvailableTo: row?.AvailableTo,
Productimage: row?.Productimage,
key: row?.key,
2026-01-27 18:27:29 +05:30
};
2026-03-23 16:08:11 +05:30
newDatas.splice(item?.[0]?.key, 1, {
...item?.[0],
...UpdatedData,
});
setDatas(newDatas);
setExcelData(newDatas);
};
const handlechange22 = (index) => {
if (editdelete === 'Delete') {
seteditdelete('');
} else {
seteditdelete('Delete');
}
};
2026-01-27 18:27:29 +05:30
2026-03-23 16:08:11 +05:30
const removeFromTable = (record) => {
const indexToRemove = Datas.findIndex((item) => item.key === record.key);
2026-01-27 18:27:29 +05:30
2026-03-23 16:08:11 +05:30
if (indexToRemove !== -1) {
const updatedDataSource = [...Datas];
updatedDataSource.splice(indexToRemove, 1);
setDatas(updatedDataSource);
setExcelData(updatedDataSource);
}
};
const handleExportData = async () => {
setLoading(true);
let response = await dispatch(
getProductData({ CompId: CompId, BranchId: BranchId, AppId: AppId })
).unwrap();
if (response?.data?.statusCode == 1 && response?.data?.data?.length > 0) {
const formattedData = response?.data?.data?.map((item) => {
2026-01-27 18:27:29 +05:30
return {
2026-03-23 16:08:11 +05:30
ProdId: item.ProdId,
'Product Name': item.ProdName,
'Product Variant Name': item.ProdVarientName,
Quantity: item.Size,
UOM: item.UomName,
MRP: item.MRP,
'WholeSale Price': item.WhSalePrice,
'Sales Price': item.SellPrice,
Category: item.CategoryName,
'Sub Category': item.SubCategoryName,
Brand: item.BrandName,
'Auto Generate Qrcode': item.AutoGenerateQr
? item.AutoGenerateQr
: 'N',
'Add Qr Code': item.QRCode,
'Stock Available': item.StockAvailable ? item.StockAvailable : 'N',
'Token Available': item.TokenAvailable ? item.TokenAvailable : 'N',
'One Piece Available': item.OnePcsAvailable
? item.OnePcsAvailable
: 'N',
'One Piece Price': item.OnePcsPrice,
'Auto Generate One Piece Qrcode': item.AutoGenerateSingleQr
? item.AutoGenerateSingleQr
: 'N',
'Auto Generate One Piece Qrcode Number': item.OnePcQR,
'Product Type': item.ProdTypeName ? item.ProdTypeName : 'Product',
Tax: `${item.TaxName || 'NIL'} - ${item.TaxPercentage || 0}%`,
CESS: item.CESS,
HSN: item.HSNCode,
'Part Number': item.PartNumber,
Rack: item.Rack,
'Manufacture Date': null,
'Expire Date': null,
'Available From':
item.AvailableFrom != '00:00:00' ? item.AvailableFrom : null,
'Available To':
item.AvailableTo != '00:00:00' ? item.AvailableTo : null,
2026-01-27 18:27:29 +05:30
};
2026-03-23 16:08:11 +05:30
});
2026-01-27 18:27:29 +05:30
2026-03-23 16:08:11 +05:30
const workbook = new ExcelJS.Workbook();
const worksheet = workbook.addWorksheet('Products');
2026-01-27 18:27:29 +05:30
2026-03-23 16:08:11 +05:30
worksheet.columns = Object.keys(formattedData[0]).map((key) => ({
header: key,
key: key,
width: 20,
}));
2026-01-27 18:27:29 +05:30
2026-03-23 16:08:11 +05:30
formattedData.forEach((row) => worksheet.addRow(row));
2026-01-27 18:27:29 +05:30
2026-03-23 16:08:11 +05:30
worksheet.eachRow((row, rowNumber) => {
row.eachCell((cell, colNumber) => {
cell.protection = { locked: colNumber === 1 };
});
});
await worksheet.protect('', {
selectLockedCells: true,
selectUnlockedCells: true,
});
const buffer = await workbook.xlsx.writeBuffer();
const blob = new Blob([buffer], {
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
});
const downloadLink = document.createElement('a');
downloadLink.href = window.URL.createObjectURL(blob);
downloadLink.download = 'Product_Data.xlsx';
downloadLink.click();
setLoading(false);
} else {
setLoading(false);
setMessage(true);
}
};
const handleExportBulkdata = async () => {
let response = await dispatch(
getProductData({ CompId: CompId, BranchId: BranchId, AppId: AppId })
).unwrap();
if (response?.data?.statusCode == 1 && response?.data?.data?.length > 0) {
const formattedData = response?.data?.data?.map((item) => {
return {
ProdId: item.ProdId,
'Product Name': item.ProdName,
'Product Variant Name': item.ProdVarientName,
Quantity: item.Size,
UOM: item.UomName,
MRP: item.MRP,
'WholeSale Price': item.WhSalePrice,
'Sales Price': item.SellPrice,
Category: item.CategoryName,
'Sub Category': item.SubCategoryName,
Brand: item.BrandName,
'Auto Generate Qrcode': item.AutoGenerateQr
? item.AutoGenerateQr
: 'N',
'Add Qr Code': item.QRCode,
'Stock Available': item.StockAvailable ? item.StockAvailable : 'N',
'Token Available': item.TokenAvailable ? item.TokenAvailable : 'N',
'One Piece Available': item.OnePcsAvailable
? item.OnePcsAvailable
: 'N',
'One Piece Price': item.OnePcsPrice,
'Auto Generate One Piece Qrcode': item.AutoGenerateSingleQr
? item.AutoGenerateSingleQr
: 'N',
'Auto Generate One Piece Qrcode Number': item.OnePcQR,
'Product Type': item.ProdTypeName ? item.ProdTypeName : 'Product',
Tax: `${item.TaxName || 'NIL'} - ${item.TaxPercentage || 0}%`,
CESS: item.CESS,
HSN: item.HSNCode,
'Part Number': item.PartNumber,
Rack: item.Rack,
'Manufacture Date': null,
'Expire Date': null,
'Available From':
item.AvailableFrom != '00:00:00' ? item.AvailableFrom : null,
'Available To':
item.AvailableTo != '00:00:00' ? item.AvailableTo : null,
2026-01-27 18:27:29 +05:30
};
2026-03-23 16:08:11 +05:30
});
2026-01-27 18:27:29 +05:30
2026-03-23 16:08:11 +05:30
setOrginalData(formattedData);
}
};
const requiredFields = [
'ProductName',
'ProductVariantName',
'Quantity',
'UOM',
'MRP',
'SellPrice',
'WhSalePrice',
'Category',
'SubCategory',
'Brand',
'StockAvailable',
'TokenAvailable',
'ProductType',
'Tax',
'AutoGenerateQrcode',
'AddQrCode',
'OnePieceAvailable',
'OnePiecePrice',
'AutoGenerateOnePieceQrcode',
'AutoGenerateOnePieceQrcodeNumber',
'CESS',
'HSN',
'PartNumber',
'Rack',
'ManufactureDate',
'ExpireDate',
'AvailableFrom',
'AvailableTo',
];
const enhancedColumns = column.map((col) => {
if (col.dataIndex === 'Productimage') return col;
// Get all used fields *except* the current column (to allow remap)
const usedFields = Object.entries(fieldMapping)
.filter(([key]) => key !== col.dataIndex)
.map(([, value]) => value);
// Fields left for dropdown (excluding already selected ones)
const availableHeaders = headers?.filter(
(h) => !usedFields?.includes(h) && h !== '__isSampleRow'
);
2026-01-27 18:27:29 +05:30
2026-03-23 16:08:11 +05:30
const isMapped = !!fieldMapping[col.dataIndex];
return {
...col,
title: (
<div
style={{
display: 'flex',
flexDirection: 'column',
justifyContent: 'space-between',
height: '80%',
}}
>
<div style={{ display: 'flex', flexDirection: 'column' }}>
<span>{col.title}</span>
{!isMapped && (
<span style={{ color: 'red', fontSize: '11px' }}>Not mapped</span>
)}
</div>
<div style={{ position: 'relative', width: '80%', marginTop: 5 }}>
<Select
// allowClear
style={{ width: '100%' }}
placeholder="Select"
value={fieldMapping[col.dataIndex] || undefined}
onChange={(selectedField) => {
setFieldMapping((prev) => {
const updated = { ...prev };
// Remove the same field from other mappings
Object.entries(updated).forEach(([key, val]) => {
if (val === selectedField) delete updated[key];
});
if (selectedField) {
updated[col.dataIndex] = selectedField;
} else {
delete updated[col.dataIndex];
}
return updated;
});
}}
>
{availableHeaders.map((header) => (
<Select.Option key={header} value={header}>
{header}
</Select.Option>
))}
</Select>
{fieldMapping[col.dataIndex] && (
<CloseCircleOutlined
onClick={() => {
setFieldMapping((prev) => {
const updated = { ...prev };
delete updated[col.dataIndex];
return updated;
});
}}
className="SelectCancelicon"
style={{
position: 'absolute',
right: '-17px',
top: '80%',
transform: 'translateY(-50%)',
color: 'red',
fontSize: 14,
cursor: 'pointer',
zIndex: 1,
}}
/>
)}
</div>
</div>
),
render: (text, record, index) => {
const mappedField = fieldMapping[col.dataIndex];
2026-01-27 18:27:29 +05:30
2026-03-23 16:08:11 +05:30
if (
mappedField &&
originalUploadedRawData?.[index]?.[mappedField] !== undefined
) {
return originalUploadedRawData[index][mappedField];
2026-01-27 18:27:29 +05:30
}
2026-03-23 16:08:11 +05:30
return record[col.dataIndex] !== undefined ? record[col.dataIndex] : '';
},
onCell: (record) => ({
record,
editable: col.editable,
dataIndex: col.dataIndex,
title: col.title,
handleSave,
onClick: () => {
if (editdelete === 'Delete') {
removeFromTable(record);
}
2026-01-27 18:27:29 +05:30
},
2026-03-23 16:08:11 +05:30
}),
2026-01-27 18:27:29 +05:30
};
2026-03-23 16:08:11 +05:30
});
const aliasMap = {
ProductName: ['product name', 'product', 'item', 'item name', 'prodname'],
ProductVariantName: [
'variant',
'product variant',
'variant name',
'productvariant',
],
Quantity: ['qty', 'quantity', 'qty size', 'quantitysize', 'qnt'],
UOM: ['uom', 'unit', 'unit of measure'],
MRP: ['mrp', 'mrp price', 'm.r.p'],
SellPrice: ['retail', 'retail price', 'sales price', 'selling price'],
WhSalePrice: ['wholesale', 'wholesale price', 'wholesaleprice'],
Category: ['category', 'product category', 'cat'],
SubCategory: ['subcategory', 'sub category', 'sub cat', 'sub-cat'],
Tax: ['tax', 'tax percent', 'gst', 'vat'],
CESS: ['cess'],
HSN: ['hsn', 'hsn code'],
Brand: ['brand', 'brand name'],
// Add more as needed...
};
const autoMapFields = (headers) => {
const updatedMapping = {};
const cleanedHeaders = headers.map((h) =>
h?.toLowerCase().replace(/\s+/g, '')
);
2026-01-27 18:27:29 +05:30
2026-03-23 16:08:11 +05:30
requiredFields.forEach((requiredField) => {
const cleanedField = requiredField?.toLowerCase();
// 1. Try exact match (cleaned)
const match = stringSimilarity.findBestMatch(
cleanedField,
cleanedHeaders
);
const bestMatchIndex = match.bestMatchIndex;
const bestMatchScore = match.bestMatch.rating;
if (bestMatchScore > 0.6) {
updatedMapping[requiredField] = headers[bestMatchIndex];
return;
}
// 2. Try alias match
const aliases = aliasMap[requiredField] || [];
const matchedHeader = headers.find((header) =>
aliases.some(
(alias) =>
header?.toLowerCase().replace(/\s+/g, '') ===
alias.replace(/\s+/g, '')
)
);
if (matchedHeader) {
updatedMapping[requiredField] = matchedHeader;
}
});
2026-01-27 18:27:29 +05:30
2026-03-23 16:08:11 +05:30
setFieldMapping(updatedMapping);
};
const COMPARE_FIELDS = [
'Product Name',
'Quantity',
'MRP',
'Sales Price',
'WholeSale Price',
'Category',
'Sub Category',
'Brand',
'Stock Available',
];
return (
<div>
<Messages
messageType={messageType}
messageData={messageData}
onComplete={() => {
setMessageData(null);
setMessageType(null);
}}
/>
<div className="form">
<form
className="form-group"
autoComplete="off"
onSubmit={handleFileSubmit}
>
<div
style={{
display: 'flex',
justifyContent: 'space-between',
flexWrap: 'wrap',
}}
>
<p style={{ fontSize: '20px', fontWeight: '500' }}>
Upload Your Excel
</p>
2026-01-27 18:27:29 +05:30
2026-03-23 16:08:11 +05:30
<div
style={{
display: 'flex',
flexDirection: 'row',
gap: '0.5rem',
margin: '1rem 0',
}}
>
<Tooltip title="Field Setup">
2026-02-09 10:32:03 +05:30
<div
2026-03-23 16:08:11 +05:30
className="btn btn--info"
style={{
background: '#00694aff',
color: '#fff',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
padding: '5px 12px',
borderRadius: '4px',
}}
onClick={handleFieldSetup}
2026-02-09 10:32:03 +05:30
>
2026-03-23 16:08:11 +05:30
<MdOutlineAppRegistration size={19} />
2026-01-27 18:27:29 +05:30
</div>
2026-03-23 16:08:11 +05:30
</Tooltip>
<div
className="export-excel-div"
onClick={() => handleExportData()}
>
<div>
<CiExport size={20} strokeWidth={1} />
</div>
<div style={{ fontSize: '14px', letterSpacing: '0.2px' }}>
Export Excel
</div>
</div>
{/* {excelData?.length > 0 && excelData &&
2026-01-27 18:27:29 +05:30
<div className="export-excel-div" onClick={() => setMappingOpen(true)}>
<div>
<CiBoxList />
</div>
<div>
Map Your Fields
</div>
</div>
} */}
2026-03-23 16:08:11 +05:30
{loading && <div style={{ color: '#ff4d4f' }}>Loading...</div>}
{message && <div style={{ color: '#ff4d4f' }}>No Data Found</div>}
2026-01-27 18:27:29 +05:30
</div>
2026-03-23 16:08:11 +05:30
{excelData?.length > 0 && excelData && (
<div>
<Tooltip title="Delete">
<AiFillDelete
onClick={handlechange22}
style={{
fontSize: '23px',
color: editdelete ? '#52c41a' : '#1292ee',
}}
/>
</Tooltip>
</div>
)}
</div>
<div
className="download-link"
style={{
display: 'flex',
alignItems: 'flex-end',
// justifyContent: "space-between",
justifyContent:
excelData?.length > 0 ? 'space-between' : 'flex-end',
width: '100%',
}}
>
{excelData?.length > 0 && (
<div className="SheetIndiacte">
<div>
<p className="mdsheet"></p>
<span>Modified</span>
</div>
<div>
<p className="mdsheet2"></p>
<span>Not Modified</span>
</div>
</div>
)}
<Tooltip title={'Format Sheet'} placement="top">
<button onClick={handleDownload}>
<FiDownload style={{ fontSize: '20px' }} />
<span>Download Sample Sheet</span>
</button>
</Tooltip>
</div>
{(!excelData || excelData?.length === 0) && (
<>
<div
2026-02-09 10:32:03 +05:30
style={{
2026-03-23 16:08:11 +05:30
display: 'flex',
alignItems: 'center',
border: '1px dashed #000',
width: 'max-content',
padding: '10px',
borderRadius: '6px',
2026-02-09 10:32:03 +05:30
}}
2026-03-23 16:08:11 +05:30
>
<div style={{ position: 'relative', display: 'inline-block' }}>
{/* <Imageupload size="2x" /> */}
<div
style={{
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
}}
>
<img
src={Excelupldimg}
width={'80px'}
style={{ cursor: 'pointer', margin: '0 0 1rem 0' }}
onClick={handleClick}
/>
<input
type="file"
className="form-control"
onChange={handleFileUpload}
ref={fileInputRef}
style={{
position: 'absolute',
top: 0,
left: 0,
width: '100%',
height: '100%',
opacity: 0,
cursor: 'pointer',
}}
/>
<a style={{ color: '#000', fontSize: '12px' }}>
{' '}
Upload Sheet{' '}
</a>
</div>
</div>
</div>
</>
)}
{excelFileError && (
<div className="text-danger" style={{ marginTop: '5px' }}>
{excelFileError}
</div>
)}
</form>
</div>
<div
className="viewerExcelupload"
style={{ maxHeight: '90vh', overflowX: 'auto' }}
>
{excelData && excelData?.length > 0 ? (
<Table
columns={enhancedColumns}
components={components}
bordered
dataSource={ExcelData}
className="tableExcelUpload"
onChange={handleTableChange}
rowClassName={(record) =>
record.isModified ? 'modified-row' : 'old-row'
}
></Table>
) : (
<p></p>
)}
</div>
<div
style={{
display: 'flex',
alignItems: 'center',
width: '100%',
gap: '2rem',
margin: '1rem 0rem',
}}
>
{/* <Tooltip title="Format Sheet" placement="bottom">
2026-01-27 18:27:29 +05:30
<div className="download-link" onClick={handleDownload} style={{ display: 'flex', alignItems: 'center', gap: '.5rem', padding: '0.3rem 0.4rem', borderRadius: '6px', backgroundColor: 'ghostwhite' }} >
<FiDownload style={{ fontSize: '20px', }} />
<a style={{ color: '#000' }}> Download</a>
</div>
</Tooltip> */}
2026-03-23 16:08:11 +05:30
{excelData && excelData?.length > 0 && (
<div
className="cancel-link"
onClick={handleCancelData}
style={{
display: 'flex',
alignItems: 'center',
gap: '.5rem',
padding: '0.3rem 0.4rem',
borderRadius: '6px',
backgroundColor: 'ghostwhite',
}}
>
<FiDelete style={{ fontSize: '20px', color: '#1292EE' }} />
<a style={{ color: '#000' }}> Remove File</a>
</div>
)}
</div>
<DefaultModal
open={fieldSetup}
handleCancel={() => setFieldSetup(false)}
footer={false}
children={
<div className="field-setup-container">
<div className="field-setup-header">
<FormHeader
title={'Select the fields you want to include in the table'}
/>
2026-01-27 18:27:29 +05:30
</div>
2026-03-23 16:08:11 +05:30
<div className="field-setup-content">
<Form onFinish={handleFieldSetupSubmit} ref={formRef}>
<Form.Item name="fields">
<DropDowns
label="Select fields"
// valueData={selectedFields}
onChangeFunction={handleFieldSelect}
options={tableFieldPreferences?.filter(
(p) => !selectedFields?.some((s) => s === p.value)
)}
/>
</Form.Item>
<div className="field-setup-selected-fields">
{selectedFields
?.map((id) =>
tableFieldPreferences?.find((p) => p.value === id)
)
.filter(Boolean)
.map((field) => (
<div className="selected-field" key={field.value}>
<div>{field.label}</div>
<div
className="close-icon"
onClick={() => handleFieldRemove(field.value)}
>
<IoClose size={15} />
2026-01-27 18:27:29 +05:30
</div>
2026-03-23 16:08:11 +05:30
</div>
))}
</div>
<div style={{ display: 'flex', justifyContent: 'flex-end' }}>
<Buttons
buttonText="SUBMIT"
color="901D77"
icon={<ArrowRightOutlined />}
htmlType={true}
/>
</div>
</Form>
</div>
</div>
}
/>
<div className="catalogueproductImg">
<DefaultModal
title="ONLINE IMAGE UPLOADER"
width={800}
open={imageopen}
footer={true}
buttonText="Submit"
children={
<Form className="">
<div className="productonlineImg">
{imagedata?.map((item, index) => (
<div
className={`singleimg ${
selectedImage === item ? 'selected' : ''
}`}
key={index}
onClick={() => setSelectedImage(item)}
>
<img
src={item.image}
alt="no image"
style={{ height: '100%', width: '100%' }}
/>
</div>
))}
</div>
</Form>
}
handleSubmit={submitimage}
handleCancel={handleimage}
></DefaultModal>
</div>
<Modal
title="Map Your Fields"
open={MappingOpen}
// onOk={handleMappingConfirm}
onCancel={() => setMappingOpen(false)}
>
{requiredFields.map((field) => {
const usedHeaders = Object.entries(fieldMapping)
.filter(([key]) => key !== field)
.map(([, value]) => value);
const availableHeaders = headers?.filter(
(header) => !usedHeaders?.includes(header)
);
return (
<div key={field} style={{ marginBottom: 10 }}>
<label>{field}</label>
<Select
allowClear
style={{ width: '100%' }}
placeholder="Select column"
value={fieldMapping[field] || undefined}
clearIcon={
<CloseCircleOutlined style={{ color: 'red', fontSize: 20 }} />
2026-01-27 18:27:29 +05:30
}
2026-03-23 16:08:11 +05:30
onChange={(value) => {
setFieldMapping((prev) => {
const updated = { ...prev };
if (!value) {
delete updated[field];
} else {
updated[field] = value;
2026-01-27 18:27:29 +05:30
}
2026-03-23 16:08:11 +05:30
return updated;
});
}}
>
{availableHeaders.map((header) => (
<Select.Option key={header} value={header}>
{header}
</Select.Option>
))}
</Select>
2026-01-27 18:27:29 +05:30
</div>
2026-03-23 16:08:11 +05:30
);
})}
</Modal>
</div>
);
2026-01-27 18:27:29 +05:30
};
export default ProductExcel;