2940 lines
108 KiB
React
2940 lines
108 KiB
React
|
|
import React, { useState, useRef, useEffect, useContext, useMemo } from "react";
|
||
|
|
import { useDispatch, useSelector } from "react-redux";
|
||
|
|
import { read, utils } from "xlsx";
|
||
|
|
import ExcelJS from "exceljs";
|
||
|
|
import { Table, Form, Input, Button, Tooltip, Modal, Select } from "antd";
|
||
|
|
import {
|
||
|
|
emptyExcelData,
|
||
|
|
excelDataSelector,
|
||
|
|
excelFileSelector,
|
||
|
|
excelFileErrorSelector,
|
||
|
|
fileInputRefSelector,
|
||
|
|
uploadExcel,
|
||
|
|
} from "../../Features/ExcelUploadPage/ExcelUploadPage.js";
|
||
|
|
import { getProductData, onlineimages, postFieldSetup, getFieldSetupData } 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 upldimg from "../../Images/upldimg.png";
|
||
|
|
import Excelupldimg from "../../Images/Excel.png";
|
||
|
|
import { FiDelete, FiDownload } from "react-icons/fi";
|
||
|
|
import { getSession } from "../../Services/Others.js";
|
||
|
|
import * as XLSX from "xlsx";
|
||
|
|
import { CiBoxList } from "react-icons/ci";
|
||
|
|
import { ArrowRightOutlined, CloseCircleOutlined } from '@ant-design/icons'
|
||
|
|
import stringSimilarity from "string-similarity";
|
||
|
|
import { getCommonAppPreference, ApplicationPreferences, getApplicationDetails } 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";
|
||
|
|
|
||
|
|
const getApplicationSampleData = (appName) => {
|
||
|
|
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'
|
||
|
|
},
|
||
|
|
{
|
||
|
|
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: []
|
||
|
|
};
|
||
|
|
};
|
||
|
|
|
||
|
|
|
||
|
|
const allowedExcelTypes = [
|
||
|
|
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', // .xlsx
|
||
|
|
'application/vnd.ms-excel' // .xls
|
||
|
|
];
|
||
|
|
|
||
|
|
const ProductExcel = ({
|
||
|
|
handleSubmit,
|
||
|
|
CategoryId,
|
||
|
|
CategoryNames,
|
||
|
|
SubcatId,
|
||
|
|
SubCatData,
|
||
|
|
AllBrandId,
|
||
|
|
Allbranddata,
|
||
|
|
Allbranddatanumfid,
|
||
|
|
Subcatnumfid,
|
||
|
|
Unit,
|
||
|
|
TaxDatas,
|
||
|
|
SuplierNames,
|
||
|
|
CategoryName1,
|
||
|
|
Catconfigid,
|
||
|
|
}) => {
|
||
|
|
|
||
|
|
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 handleTableChange = (pagination, filters, sorter) => {
|
||
|
|
setCurrentPage(pagination.current);
|
||
|
|
};
|
||
|
|
|
||
|
|
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 = `
|
||
|
|
.viewerExcelupload::-webkit-scrollbar {
|
||
|
|
display: block !important;
|
||
|
|
}
|
||
|
|
`;
|
||
|
|
document.head.appendChild(style);
|
||
|
|
|
||
|
|
return () => {
|
||
|
|
document.head.removeChild(style);
|
||
|
|
};
|
||
|
|
|
||
|
|
}
|
||
|
|
}, [excelData]);
|
||
|
|
|
||
|
|
useEffect(() => {
|
||
|
|
const fetchData = async () => {
|
||
|
|
if (AppId) {
|
||
|
|
const response = await dispatch(getApplicationDetails({ AppId }))?.unwrap();
|
||
|
|
setAppName(response?.data?.data?.[0]?.AppName)
|
||
|
|
}
|
||
|
|
};
|
||
|
|
fetchData();
|
||
|
|
}, [AppId])
|
||
|
|
|
||
|
|
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 };
|
||
|
|
|
||
|
|
// ✅ 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, '')
|
||
|
|
);
|
||
|
|
|
||
|
|
if (fallbackHeader && row[fallbackHeader] !== undefined) {
|
||
|
|
newRow[field] = row[fallbackHeader];
|
||
|
|
} else {
|
||
|
|
// Preserve existing value if present
|
||
|
|
newRow[field] = ExcelData?.[index]?.[field] ?? '';
|
||
|
|
}
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
|
||
|
|
// ✅ Process non-mandatory mapped fields
|
||
|
|
Object.entries(fieldMapping).forEach(([field, header]) => {
|
||
|
|
if (!mandatoryFields.includes(field)) {
|
||
|
|
newRow[field] = headers.includes(header) ? row[header] ?? '' : '';
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
// ✅ 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;
|
||
|
|
});
|
||
|
|
|
||
|
|
setExcelData(updatedMappedData);
|
||
|
|
setDatas(updatedMappedData);
|
||
|
|
}
|
||
|
|
}, [fieldMapping, UploadedRawData, headers]);
|
||
|
|
|
||
|
|
|
||
|
|
// useEffect(() => {
|
||
|
|
// if (UploadedRawData.length > 0) {
|
||
|
|
// const updatedMappedData = UploadedRawData.map((row, index) => {
|
||
|
|
// const newRow = { key: index };
|
||
|
|
|
||
|
|
// requiredFields.forEach((requiredField) => {
|
||
|
|
// const mappedHeader = fieldMapping[requiredField];
|
||
|
|
|
||
|
|
// if (mappedHeader && row.hasOwnProperty(mappedHeader)) {
|
||
|
|
// // ✅ Use only the mapped header if it exists in row
|
||
|
|
// newRow[requiredField] = row[mappedHeader];
|
||
|
|
// } else {
|
||
|
|
// // 🚫 Do not fallback — keep it clean
|
||
|
|
// newRow[requiredField] = '';
|
||
|
|
// }
|
||
|
|
// });
|
||
|
|
|
||
|
|
// // Optional: add unmapped fields if needed
|
||
|
|
// headers.forEach((header) => {
|
||
|
|
// const isMapped = Object.values(fieldMapping).includes(header);
|
||
|
|
// const mappedKey = Object.keys(fieldMapping).find(key => fieldMapping[key] === header);
|
||
|
|
|
||
|
|
// if (!isMapped && !(header in newRow) && !requiredFields.includes(mappedKey)) {
|
||
|
|
// newRow[header] = row[header] ?? '';
|
||
|
|
// }
|
||
|
|
// });
|
||
|
|
|
||
|
|
// return newRow;
|
||
|
|
// });
|
||
|
|
|
||
|
|
// setDatas(updatedMappedData);
|
||
|
|
// setExcelData(updatedMappedData);
|
||
|
|
// }
|
||
|
|
// }, [fieldMapping, UploadedRawData, headers]);
|
||
|
|
|
||
|
|
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 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"],
|
||
|
|
}));
|
||
|
|
|
||
|
|
// ✅ Set only once with final data
|
||
|
|
setHeaders(extractedHeaders);
|
||
|
|
setUploadedRawData(updatedData); // Final, cleaned and enriched data
|
||
|
|
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
|
||
|
|
);
|
||
|
|
|
||
|
|
if (selectedIndex !== -1) {
|
||
|
|
tempData[selectedIndex].Productimage = uploadImgData?.data?.image;
|
||
|
|
}
|
||
|
|
setDatas(tempData);
|
||
|
|
setExcelData(tempData);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
} finally {
|
||
|
|
handleimage();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
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;
|
||
|
|
});
|
||
|
|
|
||
|
|
const sorted = [];
|
||
|
|
|
||
|
|
fieldOrder.forEach(name => {
|
||
|
|
if (fieldMap[name] && fieldMap[name].Access === "Y") {
|
||
|
|
sorted.push(fieldMap[name]);
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
return sorted;
|
||
|
|
};
|
||
|
|
|
||
|
|
// Usage inside your Fielddatas
|
||
|
|
const Fielddatas = async () => {
|
||
|
|
|
||
|
|
const res = await dispatch(getCommonAppPreference(AppId)).unwrap();
|
||
|
|
|
||
|
|
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" },
|
||
|
|
};
|
||
|
|
|
||
|
|
// Define all possible field configurations
|
||
|
|
const sampleData = getApplicationSampleData(appName);
|
||
|
|
console.log(sampleData, "sampleData")
|
||
|
|
const fieldConfigs = {
|
||
|
|
'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: ['"' + (typeof CategoryNames !== 'undefined' ? CategoryNames.join(",") : "Sample Category") + '"'],
|
||
|
|
},
|
||
|
|
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,
|
||
|
|
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,
|
||
|
|
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: {
|
||
|
|
type: "list",
|
||
|
|
allowBlank: true,
|
||
|
|
formulae: ['"' + (typeof TaxDatas !== 'undefined' ? TaxDatas.map(item => item.replace(/,/g, "").replace("-", "-").trim()).join(",") : "NIL - 0%") + '"'],
|
||
|
|
},
|
||
|
|
sampleValue: "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: "StockAvailable",
|
||
|
|
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: "StockDate",
|
||
|
|
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: "StockDate",
|
||
|
|
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 TIME",
|
||
|
|
width: 20,
|
||
|
|
style: { ...headerStyle, font: { bold: true, color: { argb: "#000000" } } },
|
||
|
|
validation: null,
|
||
|
|
sampleValue: new Date(),
|
||
|
|
headerColor: "52c41a"
|
||
|
|
},
|
||
|
|
'AvailableTo': {
|
||
|
|
header: "Available To",
|
||
|
|
key: "AvailableTo TIME",
|
||
|
|
width: 20,
|
||
|
|
style: { ...headerStyle, font: { bold: true, color: { argb: "#000000" } } },
|
||
|
|
validation: null,
|
||
|
|
sampleValue: new Date(),
|
||
|
|
headerColor: "52c41a"
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
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;
|
||
|
|
}
|
||
|
|
|
||
|
|
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"
|
||
|
|
};
|
||
|
|
|
||
|
|
// 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"
|
||
|
|
};
|
||
|
|
|
||
|
|
// Step 1: Sort preferred fields
|
||
|
|
const sortedPreferredFields = sortPreferredFields(FieldValues); // Your existing sort function
|
||
|
|
|
||
|
|
// Step 2: Convert API names to internal keys while skipping dependent fields initially
|
||
|
|
const preferredFieldKeys = [];
|
||
|
|
const preferredFieldSet = new Set();
|
||
|
|
|
||
|
|
sortedPreferredFields.forEach(field => {
|
||
|
|
const fieldName = field.ConfigName;
|
||
|
|
const status = field.Access;
|
||
|
|
|
||
|
|
// Skip dependent fields for now
|
||
|
|
if (Object.values(dependentFieldMap).includes(fieldName)) return;
|
||
|
|
|
||
|
|
if (status === "Y") {
|
||
|
|
const internalKey = apiToInternalFieldMap[fieldName];
|
||
|
|
if (internalKey) {
|
||
|
|
preferredFieldKeys.push(internalKey);
|
||
|
|
preferredFieldSet.add(fieldName);
|
||
|
|
}
|
||
|
|
|
||
|
|
// 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);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
// Step 3: Merge Mandatory + Preferred (no duplicates)
|
||
|
|
const finalFieldKeys = [...mandatoryFields, ...preferredFieldKeys.filter(key => !mandatoryFields.includes(key))];
|
||
|
|
|
||
|
|
// 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"
|
||
|
|
});
|
||
|
|
|
||
|
|
worksheet.columns = selectedColumns;
|
||
|
|
|
||
|
|
worksheet.getColumn(selectedColumns.length).hidden = true;
|
||
|
|
|
||
|
|
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 }
|
||
|
|
}
|
||
|
|
};
|
||
|
|
});
|
||
|
|
|
||
|
|
// Add sample data in row 2
|
||
|
|
|
||
|
|
selectedColumns.forEach((config, index) => {
|
||
|
|
const columnLetter = getExcelColumnLetter(index + 1); // ✅ FIXED
|
||
|
|
worksheet.getCell(`${columnLetter}2`).value = config.sampleValue;
|
||
|
|
});
|
||
|
|
|
||
|
|
// Add validation to columns
|
||
|
|
|
||
|
|
selectedColumns.forEach((config, colIndex) => {
|
||
|
|
if (config.validation) {
|
||
|
|
const colNumber = colIndex + 1; // Excel is 1-based
|
||
|
|
const columnLetter = getExcelColumnLetter(colNumber); // ✅ FIXED
|
||
|
|
|
||
|
|
for (let rowNumber = 2; rowNumber <= 1001; rowNumber++) {
|
||
|
|
const cell = worksheet.getCell(rowNumber, colNumber); // ✅ Use (row, col) instead of letter
|
||
|
|
let validation = { ...config.validation };
|
||
|
|
|
||
|
|
if (validation.formulae) {
|
||
|
|
validation.formulae = validation.formulae.map(formula =>
|
||
|
|
formula.replace('{COLUMN}', columnLetter).replace('{ROW}', rowNumber.toString())
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
cell.dataValidation = validation;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
|
||
|
|
// Add empty rows for data entry
|
||
|
|
for (let i = 1; i <= 1000; i++) {
|
||
|
|
worksheet.addRow({});
|
||
|
|
}
|
||
|
|
|
||
|
|
// 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) => {
|
||
|
|
cell.protection = { locked: false };
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
// 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 };
|
||
|
|
});
|
||
|
|
|
||
|
|
// 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
|
||
|
|
});
|
||
|
|
|
||
|
|
// 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" } } },
|
||
|
|
},
|
||
|
|
];
|
||
|
|
|
||
|
|
// Add data to Sheet2
|
||
|
|
for (let i = 1; i <= 100; i++) {
|
||
|
|
worksheet1.addRow({
|
||
|
|
CategoryId: "",
|
||
|
|
Category: "",
|
||
|
|
SDPC: "",
|
||
|
|
SubcayID: "",
|
||
|
|
SubCategory: "",
|
||
|
|
SubcatnumfId: "",
|
||
|
|
BrandId: "",
|
||
|
|
Brandname: "",
|
||
|
|
BrandnumfId: "",
|
||
|
|
Branddetails: "",
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
// All the original dropdown and validation logic for Sheet2
|
||
|
|
const dropdownOptions = {
|
||
|
|
Option1: ["Yes", "No"],
|
||
|
|
Option2: ["Product"],
|
||
|
|
Option3: ["ValueX", "ValueY", "ValueZ"],
|
||
|
|
};
|
||
|
|
|
||
|
|
const selectedOptionA = "Option1";
|
||
|
|
const selectedOptionB = "Option2";
|
||
|
|
|
||
|
|
// 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] : '';
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
const BrandDataMap = {};
|
||
|
|
if (typeof AllBrandId !== 'undefined') {
|
||
|
|
AllBrandId.forEach((AllBrandId, index) => {
|
||
|
|
BrandDataMap[AllBrandId] = typeof SubCatData !== 'undefined' ? SubCatData[index] : '';
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
// 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],
|
||
|
|
};
|
||
|
|
}
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
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],
|
||
|
|
};
|
||
|
|
}
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
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],
|
||
|
|
};
|
||
|
|
}
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
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],
|
||
|
|
};
|
||
|
|
}
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
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],
|
||
|
|
};
|
||
|
|
}
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
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],
|
||
|
|
};
|
||
|
|
}
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
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],
|
||
|
|
};
|
||
|
|
}
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
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],
|
||
|
|
};
|
||
|
|
}
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
// 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 = [];
|
||
|
|
|
||
|
|
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);
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
cell.value = JSON.stringify(Array.from(uniqueSubCategoryData));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
// 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 = [];
|
||
|
|
|
||
|
|
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);
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
cell.value = JSON.stringify(Array.from(uniqueSubCategoryData));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
// 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
|
||
|
|
}
|
||
|
|
|
||
|
|
const response = await dispatch(postFieldSetup(postData))?.unwrap();
|
||
|
|
|
||
|
|
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];
|
||
|
|
});
|
||
|
|
};
|
||
|
|
|
||
|
|
const handleFieldRemove = (id) => {
|
||
|
|
setSelectedFields((prev) => prev.filter((field) => field !== id));
|
||
|
|
};
|
||
|
|
|
||
|
|
|
||
|
|
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)}
|
||
|
|
// ImageLink={record.Productimage || ""}
|
||
|
|
ImageLink={
|
||
|
|
onlineImage
|
||
|
|
? onlineImage
|
||
|
|
: record.Productimage
|
||
|
|
? record.Productimage
|
||
|
|
: ""
|
||
|
|
}
|
||
|
|
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>
|
||
|
|
</>
|
||
|
|
)}
|
||
|
|
</>
|
||
|
|
);
|
||
|
|
},
|
||
|
|
},
|
||
|
|
];
|
||
|
|
|
||
|
|
|
||
|
|
const column = columns?.map((col) => {
|
||
|
|
if (!col.editable) {
|
||
|
|
return col;
|
||
|
|
}
|
||
|
|
|
||
|
|
let onClickHandler = null;
|
||
|
|
|
||
|
|
if (editdelete === "Delete") {
|
||
|
|
onClickHandler = (record) => removeFromTable(record);
|
||
|
|
}
|
||
|
|
|
||
|
|
return {
|
||
|
|
...col,
|
||
|
|
onCell: (record) => ({
|
||
|
|
record,
|
||
|
|
editable: col.editable,
|
||
|
|
dataIndex: col.dataIndex,
|
||
|
|
title: col.title,
|
||
|
|
handleSave,
|
||
|
|
onClick: () => onClickHandler(record),
|
||
|
|
}),
|
||
|
|
};
|
||
|
|
});
|
||
|
|
|
||
|
|
const EditableContext = React.createContext(null);
|
||
|
|
|
||
|
|
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);
|
||
|
|
|
||
|
|
useEffect(() => {
|
||
|
|
if (editing) {
|
||
|
|
inputRef?.current?.focus();
|
||
|
|
}
|
||
|
|
}, [editing]);
|
||
|
|
|
||
|
|
const toggleEdit = () => {
|
||
|
|
setEditing(!editing);
|
||
|
|
form.setFieldsValue({
|
||
|
|
[dataIndex]: record[dataIndex],
|
||
|
|
});
|
||
|
|
};
|
||
|
|
|
||
|
|
const save = async () => {
|
||
|
|
try {
|
||
|
|
const values = await form.validateFields();
|
||
|
|
toggleEdit();
|
||
|
|
handleSave({
|
||
|
|
...record,
|
||
|
|
...values,
|
||
|
|
});
|
||
|
|
} catch (errInfo) { }
|
||
|
|
};
|
||
|
|
|
||
|
|
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>
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
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,
|
||
|
|
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,
|
||
|
|
|
||
|
|
};
|
||
|
|
newDatas.splice(item?.[0]?.key, 1, {
|
||
|
|
...item?.[0],
|
||
|
|
...UpdatedData,
|
||
|
|
});
|
||
|
|
setDatas(newDatas);
|
||
|
|
setExcelData(newDatas);
|
||
|
|
};
|
||
|
|
|
||
|
|
const handlechange22 = (index) => {
|
||
|
|
if (editdelete === "Delete") {
|
||
|
|
seteditdelete("");
|
||
|
|
} else {
|
||
|
|
seteditdelete("Delete");
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
const removeFromTable = (record) => {
|
||
|
|
const indexToRemove = Datas.findIndex((item) => item.key === record.key);
|
||
|
|
|
||
|
|
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 => {
|
||
|
|
return {
|
||
|
|
"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}%`, // Ensures a valid tax format
|
||
|
|
"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
|
||
|
|
};
|
||
|
|
});
|
||
|
|
|
||
|
|
const ws = XLSX.utils.json_to_sheet(formattedData);
|
||
|
|
|
||
|
|
// Create a new workbook and append the worksheet
|
||
|
|
const wb = XLSX.utils.book_new();
|
||
|
|
XLSX.utils.book_append_sheet(wb, ws, "Products");
|
||
|
|
setLoading(false)
|
||
|
|
// Trigger the file download
|
||
|
|
XLSX.writeFile(wb, "Product_Data.xlsx");
|
||
|
|
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
setLoading(false)
|
||
|
|
setMessage(true)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
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 = columns.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");
|
||
|
|
|
||
|
|
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];
|
||
|
|
|
||
|
|
if (mappedField && originalUploadedRawData?.[index]?.[mappedField] !== undefined) {
|
||
|
|
return originalUploadedRawData[index][mappedField];
|
||
|
|
}
|
||
|
|
|
||
|
|
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);
|
||
|
|
}
|
||
|
|
},
|
||
|
|
}),
|
||
|
|
};
|
||
|
|
});
|
||
|
|
|
||
|
|
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, ''));
|
||
|
|
|
||
|
|
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;
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
setFieldMapping(updatedMapping);
|
||
|
|
};
|
||
|
|
|
||
|
|
|
||
|
|
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>
|
||
|
|
|
||
|
|
<div style={{ display: "flex", flexDirection: "row", gap: "0.5rem", margin: "1rem 0" }}>
|
||
|
|
<Tooltip title="Field Setup">
|
||
|
|
<div className="btn btn--info" style={{
|
||
|
|
background: "#00694aff",
|
||
|
|
color: "#fff",
|
||
|
|
display: "flex",
|
||
|
|
alignItems: "center",
|
||
|
|
justifyContent: "center",
|
||
|
|
padding: "5px 12px",
|
||
|
|
borderRadius: "4px"
|
||
|
|
}} onClick={handleFieldSetup}><MdOutlineAppRegistration size={19} /></div>
|
||
|
|
</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 &&
|
||
|
|
<div className="export-excel-div" onClick={() => setMappingOpen(true)}>
|
||
|
|
<div>
|
||
|
|
<CiBoxList />
|
||
|
|
</div>
|
||
|
|
<div>
|
||
|
|
Map Your Fields
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
} */}
|
||
|
|
{loading &&
|
||
|
|
<div style={{ color: "#ff4d4f" }}>Loading...</div>
|
||
|
|
}
|
||
|
|
{message &&
|
||
|
|
<div style={{ color: "#ff4d4f" }}>No Data Found</div>
|
||
|
|
}
|
||
|
|
</div>
|
||
|
|
|
||
|
|
{excelData?.length > 0 && excelData &&
|
||
|
|
<div >
|
||
|
|
<Tooltip title="Delete">
|
||
|
|
<AiFillDelete
|
||
|
|
onClick={handlechange22} style={{
|
||
|
|
fontSize: "23px", color: editdelete ? "#52c41a" : "#1292ee",
|
||
|
|
}} />
|
||
|
|
</Tooltip>
|
||
|
|
</div>
|
||
|
|
}
|
||
|
|
</div>
|
||
|
|
<Tooltip
|
||
|
|
title={"Format Sheet"}
|
||
|
|
placement="bottom"
|
||
|
|
>
|
||
|
|
<div
|
||
|
|
className="download-link"
|
||
|
|
style={{
|
||
|
|
display: 'flex',
|
||
|
|
alignItems: "flex-end",
|
||
|
|
justifyContent: "flex-end",
|
||
|
|
}}
|
||
|
|
>
|
||
|
|
<button onClick={handleDownload}>
|
||
|
|
<FiDownload style={{ fontSize: '20px' }} />
|
||
|
|
<span >Download Sample Sheet</span>
|
||
|
|
</button>
|
||
|
|
</div>
|
||
|
|
</Tooltip>
|
||
|
|
|
||
|
|
{(!excelData || excelData?.length === 0) && (
|
||
|
|
<>
|
||
|
|
<div style={{
|
||
|
|
display: 'flex', alignItems: 'center', border: "1px dashed #000",
|
||
|
|
width: "max-content", padding: "10px", borderRadius: "6px"
|
||
|
|
}} >
|
||
|
|
<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}
|
||
|
|
></Table>
|
||
|
|
) :
|
||
|
|
(
|
||
|
|
<p></p>
|
||
|
|
)}
|
||
|
|
</div>
|
||
|
|
<div style={{ display: 'flex', alignItems: 'center', width: '100%', gap: '2rem', margin: '1rem 0rem' }}>
|
||
|
|
|
||
|
|
{/* <Tooltip title="Format Sheet" placement="bottom">
|
||
|
|
<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> */}
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
{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'} />
|
||
|
|
</div>
|
||
|
|
<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} />
|
||
|
|
</div>
|
||
|
|
</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 }} />}
|
||
|
|
onChange={(value) => {
|
||
|
|
setFieldMapping((prev) => {
|
||
|
|
const updated = { ...prev };
|
||
|
|
if (!value) {
|
||
|
|
delete updated[field];
|
||
|
|
} else {
|
||
|
|
updated[field] = value;
|
||
|
|
}
|
||
|
|
return updated;
|
||
|
|
});
|
||
|
|
}}
|
||
|
|
>
|
||
|
|
{availableHeaders.map((header) => (
|
||
|
|
<Select.Option key={header} value={header}>
|
||
|
|
{header}
|
||
|
|
</Select.Option>
|
||
|
|
))}
|
||
|
|
</Select>
|
||
|
|
</div>
|
||
|
|
);
|
||
|
|
})}
|
||
|
|
</Modal>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
);
|
||
|
|
|
||
|
|
};
|
||
|
|
|
||
|
|
export default ProductExcel;
|