expired libary change
This commit is contained in:
parent
a50ea556f1
commit
55255069c4
File diff suppressed because it is too large
Load Diff
29
package.json
29
package.json
|
|
@ -11,8 +11,7 @@
|
|||
"format": "prettier --write ."
|
||||
},
|
||||
"dependencies": {
|
||||
"@ant-design/plots": "^1.2.6",
|
||||
"@autocomplete/material-ui": "0.0.17",
|
||||
"@ant-design/plots": "^2.6.8",
|
||||
"@capacitor-community/speech-recognition": "^7.0.1",
|
||||
"@capacitor-mlkit/barcode-scanning": "^8.0.1",
|
||||
"@capacitor/app": "^8.0.1",
|
||||
|
|
@ -21,53 +20,49 @@
|
|||
"@dnd-kit/sortable": "^10.0.0",
|
||||
"@emotion/react": "^11.11.4",
|
||||
"@emotion/styled": "^11.11.5",
|
||||
"@material-ui/lab": "^4.0.0-alpha.61",
|
||||
"@mui/material": "^5.15.18",
|
||||
"@react-google-maps/api": "^2.20.8",
|
||||
"@reduxjs/toolkit": "^1.9.5",
|
||||
"@tanstack/react-query": "^5.90.11",
|
||||
"@tanstack/react-query-devtools": "^5.91.1",
|
||||
"@tanstack/react-virtual": "^3.13.18",
|
||||
"@types/node": "^25.5.0",
|
||||
"@zxing/library": "^0.21.3",
|
||||
"antd": "^5.17.4",
|
||||
"antd-img-crop": "^4.22.0",
|
||||
"aos": "^2.3.4",
|
||||
"axios": "^1.7.2",
|
||||
"apexcharts": "^5.10.4",
|
||||
"axios": "^1.13.6",
|
||||
"browser-image-compression": "^2.0.2",
|
||||
"classnames": "^2.5.1",
|
||||
"cropperjs": "^1.6.2",
|
||||
"crypto-js": "^4.1.1",
|
||||
"date-fns": "^4.1.0",
|
||||
"devtools-detect": "^4.0.2",
|
||||
"exceljs": "^4.3.0",
|
||||
"exceljs": "^4.4.0",
|
||||
"file-saver": "^2.0.5",
|
||||
"google-maps-react": "^2.0.6",
|
||||
"highcharts": "^11.4.3",
|
||||
"highcharts-react-official": "^3.2.1",
|
||||
"history": "^5.3.0",
|
||||
"html2canvas": "^1.4.1",
|
||||
"html2pdf": "^0.0.11",
|
||||
"html2pdf.js": "^0.10.3",
|
||||
"html5-qrcode": "^2.3.8",
|
||||
"jquery": "^3.7.1",
|
||||
"js-cookie": "^3.0.5",
|
||||
"jsbarcode": "^3.11.6",
|
||||
"jspdf": "^2.5.1",
|
||||
"jspdf": "^4.2.1",
|
||||
"lucide": "^0.577.0",
|
||||
"lucide-react": "^0.536.0",
|
||||
"material-ui": "^0.20.2",
|
||||
"moment": "^2.30.1",
|
||||
"mui-autocomplete": "^2.0.1",
|
||||
"number-to-words": "^1.2.4",
|
||||
"pdf-lib": "^1.17.1",
|
||||
"qr-scanner": "^1.4.2",
|
||||
"qrcode": "^1.5.3",
|
||||
"qrcode.react": "^4.2.0",
|
||||
"react": "^18.3.1",
|
||||
"react-apexcharts": "^2.1.0",
|
||||
"react-beautiful-dnd": "^13.1.1",
|
||||
"react-color": "^2.19.3",
|
||||
"react-countup": "^6.5.3",
|
||||
"react-device-detect": "^2.2.3",
|
||||
"react-devtools": "^5.2.0",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-icons": "^4.12.0",
|
||||
"react-image-crop": "^11.0.10",
|
||||
|
|
@ -82,25 +77,23 @@
|
|||
"recharts": "^3.5.0",
|
||||
"string-similarity": "^4.0.4",
|
||||
"tesseract.js": "^6.0.0",
|
||||
"universal-cookie": "^6.1.3",
|
||||
"universal-cookie": "^8.0.1",
|
||||
"webfontloader": "^1.6.28",
|
||||
"xlsx": "^0.18.5",
|
||||
"zustand": "^5.0.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "^18.3.3",
|
||||
"@types/react-dom": "^18.3.0",
|
||||
"@vitejs/plugin-react": "^4.3.0",
|
||||
"@vitejs/plugin-react": "^6.0.1",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-plugin-react": "^7.34.2",
|
||||
"eslint-plugin-react-hooks": "^4.6.2",
|
||||
"eslint-plugin-react-refresh": "^0.4.7",
|
||||
"prettier": "^3.5.3",
|
||||
"rollup-plugin-obfuscator": "^1.1.0",
|
||||
"rollup-plugin-terser": "^7.0.2",
|
||||
"sass": "^1.77.2",
|
||||
"terser": "^5.31.3",
|
||||
"vite": "^4.5.3",
|
||||
"vite": "^8.0.0",
|
||||
"vite-plugin-obfuscator": "^1.0.5",
|
||||
"vite-plugin-remove-console": "^2.2.0"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,10 @@
|
|||
import React, { Component } from 'react';
|
||||
import { Map, Marker, GoogleApiWrapper } from 'google-maps-react';
|
||||
import { GoogleMap, LoadScript, Marker } from '@react-google-maps/api';
|
||||
|
||||
const containerStyle = {
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
};
|
||||
|
||||
class MapView extends Component {
|
||||
constructor(props) {
|
||||
|
|
@ -9,81 +14,73 @@ class MapView extends Component {
|
|||
currentLocation: props.prevlca ? props.prevlca : false,
|
||||
};
|
||||
}
|
||||
|
||||
async componentDidMount() {
|
||||
if (Object.keys(this.state.fields.location)?.length === 0) {
|
||||
const { lat, lng } = await this.getcurrentLocation();
|
||||
this.setState((prev) => ({
|
||||
fields: {
|
||||
...prev.fields,
|
||||
location: {
|
||||
lat,
|
||||
lng,
|
||||
},
|
||||
},
|
||||
currentLocation: {
|
||||
lat,
|
||||
lng,
|
||||
location: { lat, lng },
|
||||
},
|
||||
currentLocation: { lat, lng },
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
getcurrentLocation() {
|
||||
if (navigator && navigator.geolocation) {
|
||||
return new Promise((resolve, reject) => {
|
||||
return new Promise((resolve) => {
|
||||
navigator.geolocation.getCurrentPosition((pos) => {
|
||||
const coords = pos.coords;
|
||||
resolve({
|
||||
lat: coords.latitude,
|
||||
lng: coords.longitude,
|
||||
lat: pos.coords.latitude,
|
||||
lng: pos.coords.longitude,
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
return {
|
||||
lat: 0.0,
|
||||
lng: 0,
|
||||
};
|
||||
return { lat: 0, lng: 0 };
|
||||
}
|
||||
addMarker = (location, map) => {
|
||||
|
||||
handleMapClick = (e) => {
|
||||
const lat = e.latLng.lat();
|
||||
const lng = e.latLng.lng();
|
||||
|
||||
const location = { lat, lng };
|
||||
|
||||
this.setState({
|
||||
fields: { location: { lat: location.lat(), lng: location.lng() } },
|
||||
fields: { location },
|
||||
});
|
||||
map.panTo(location);
|
||||
|
||||
if (this.props.onMarkerClick) {
|
||||
this.props.onMarkerClick(location);
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const { lat = 0, lng = 0 } = this.state.fields.location;
|
||||
// var bounds = new this.props.google.maps.LatLngBounds();
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Map
|
||||
google={this.props.google}
|
||||
style={{
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
}}
|
||||
initialCenter={{ lat: lat || 0, lng: lng || 0 }}
|
||||
<LoadScript googleMapsApiKey="AIzaSyB6w_WDy6psJ5HPX15Me1-o6CkS5jTYWnE">
|
||||
<GoogleMap
|
||||
mapContainerStyle={containerStyle}
|
||||
center={{ lat: lat || 0, lng: lng || 0 }}
|
||||
zoom={14}
|
||||
mapTypeId="roadmap"
|
||||
onClick={(t, map, c) => {
|
||||
this.addMarker(c.latLng, map);
|
||||
}}
|
||||
// bounds={bounds}
|
||||
onClick={this.handleMapClick}
|
||||
>
|
||||
{lat && lng && (
|
||||
<Marker
|
||||
tooltip={true}
|
||||
name="Your Position"
|
||||
position={this.state.fields.location}
|
||||
onClick={this.props.onMarkerClick}
|
||||
position={{ lat, lng }}
|
||||
onClick={() =>
|
||||
this?.props?.onMarkerClick &&
|
||||
this?.props?.onMarkerClick({ lat, lng })
|
||||
}
|
||||
/>
|
||||
</Map>
|
||||
</div>
|
||||
)}
|
||||
</GoogleMap>
|
||||
</LoadScript>
|
||||
);
|
||||
}
|
||||
}
|
||||
export default GoogleApiWrapper({
|
||||
apiKey: 'AIzaSyB6w_WDy6psJ5HPX15Me1-o6CkS5jTYWnE',
|
||||
})(MapView);
|
||||
|
||||
export default MapView;
|
||||
|
|
|
|||
|
|
@ -1,97 +1,97 @@
|
|||
import React, { useState } from 'react';
|
||||
import { Modal, Button } from 'antd';
|
||||
import { ImportOutlined, ExportOutlined } from '@ant-design/icons';
|
||||
import * as XLSX from 'xlsx';
|
||||
import { saveAs } from 'file-saver';
|
||||
// import React, { useState } from 'react';
|
||||
// import { Modal, Button } from 'antd';
|
||||
// import { ImportOutlined, ExportOutlined } from '@ant-design/icons';
|
||||
// import * as XLSX from 'xlsx';
|
||||
// import { saveAs } from 'file-saver';
|
||||
|
||||
const [excelData, setExcelData] = useState(null);
|
||||
const [excelFile, setExcelFile] = useState(null);
|
||||
const [excelFileError, setExcelFileError] = useState(null);
|
||||
// const [excelData, setExcelData] = useState(null);
|
||||
// const [excelFile, setExcelFile] = useState(null);
|
||||
// const [excelFileError, setExcelFileError] = useState(null);
|
||||
|
||||
const handleFile = (e) => {
|
||||
let selectedFile = e.target.files[0];
|
||||
if (selectedFile) {
|
||||
if (selectedFile && fileType.includes(selectedFile.type)) {
|
||||
let reader = new FileReader();
|
||||
reader.readAsArrayBuffer(selectedFile);
|
||||
reader.onload = (e) => {
|
||||
setExcelFileError(null);
|
||||
setExcelFile(e.target.result);
|
||||
};
|
||||
} else {
|
||||
setExcelFileError('Please select only excel file types');
|
||||
setExcelFile(null);
|
||||
}
|
||||
} else {
|
||||
console.log('Please select a file');
|
||||
}
|
||||
};
|
||||
// const handleFile = (e) => {
|
||||
// let selectedFile = e.target.files[0];
|
||||
// if (selectedFile) {
|
||||
// if (selectedFile && fileType.includes(selectedFile.type)) {
|
||||
// let reader = new FileReader();
|
||||
// reader.readAsArrayBuffer(selectedFile);
|
||||
// reader.onload = (e) => {
|
||||
// setExcelFileError(null);
|
||||
// setExcelFile(e.target.result);
|
||||
// };
|
||||
// } else {
|
||||
// setExcelFileError('Please select only excel file types');
|
||||
// setExcelFile(null);
|
||||
// }
|
||||
// } else {
|
||||
// console.log('Please select a file');
|
||||
// }
|
||||
// };
|
||||
|
||||
const handleSubmit = (e) => {
|
||||
e.preventDefault();
|
||||
if (excelFile !== null) {
|
||||
const workbook = XLSX.read(excelFile, { type: 'buffer' });
|
||||
const worksheetName = workbook.SheetNames[0];
|
||||
const worksheet = workbook.Sheets[worksheetName];
|
||||
const data = XLSX.utils.sheet_to_json(worksheet);
|
||||
setExcelData(data);
|
||||
} else {
|
||||
setExcelData(null);
|
||||
}
|
||||
};
|
||||
// const handleSubmit = (e) => {
|
||||
// e.preventDefault();
|
||||
// if (excelFile !== null) {
|
||||
// const workbook = XLSX.read(excelFile, { type: 'buffer' });
|
||||
// const worksheetName = workbook.SheetNames[0];
|
||||
// const worksheet = workbook.Sheets[worksheetName];
|
||||
// const data = XLSX.utils.sheet_to_json(worksheet);
|
||||
// setExcelData(data);
|
||||
// } else {
|
||||
// setExcelData(null);
|
||||
// }
|
||||
// };
|
||||
|
||||
const handleExport = () => {
|
||||
if (excelData !== null) {
|
||||
const worksheet = XLSX.utils.json_to_sheet(excelData);
|
||||
const workbook = XLSX.utils.book_new();
|
||||
XLSX.utils.book_append_sheet(workbook, worksheet, 'Sheet1');
|
||||
const excelBuffer = XLSX.write(workbook, {
|
||||
bookType: 'xlsx',
|
||||
type: 'array',
|
||||
});
|
||||
const excelData = new Blob([excelBuffer], {
|
||||
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
||||
});
|
||||
saveAs(excelData, 'exported_data.xlsx');
|
||||
}
|
||||
};
|
||||
// const handleExport = () => {
|
||||
// if (excelData !== null) {
|
||||
// const worksheet = XLSX.utils.json_to_sheet(excelData);
|
||||
// const workbook = XLSX.utils.book_new();
|
||||
// XLSX.utils.book_append_sheet(workbook, worksheet, 'Sheet1');
|
||||
// const excelBuffer = XLSX.write(workbook, {
|
||||
// bookType: 'xlsx',
|
||||
// type: 'array',
|
||||
// });
|
||||
// const excelData = new Blob([excelBuffer], {
|
||||
// type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
||||
// });
|
||||
// saveAs(excelData, 'exported_data.xlsx');
|
||||
// }
|
||||
// };
|
||||
|
||||
const Modals = ({ open, title, handleCancel, children, footer }) => {
|
||||
return (
|
||||
<Modal
|
||||
visible={open}
|
||||
title={title}
|
||||
onCancel={handleCancel}
|
||||
footer={
|
||||
footer
|
||||
? [
|
||||
<Button key="submit" type="primary" onClick={handleSubmit}>
|
||||
Submit
|
||||
</Button>,
|
||||
<Button key="cancel" onClick={handleCancel}>
|
||||
Cancel
|
||||
</Button>,
|
||||
]
|
||||
: footer
|
||||
}
|
||||
>
|
||||
{children}
|
||||
<div>
|
||||
<h4>Import Excel</h4>
|
||||
<input type="file" onChange={handleFile} />
|
||||
{excelFileError && <div className="error">{excelFileError}</div>}
|
||||
<Button type="primary" icon={<ImportOutlined />} onClick={handleSubmit}>
|
||||
Import
|
||||
</Button>
|
||||
</div>
|
||||
<div>
|
||||
<h4>Export Excel</h4>
|
||||
<Button type="primary" icon={<ExportOutlined />} onClick={handleExport}>
|
||||
Export
|
||||
</Button>
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
// const Modals = ({ open, title, handleCancel, children, footer }) => {
|
||||
// return (
|
||||
// <Modal
|
||||
// visible={open}
|
||||
// title={title}
|
||||
// onCancel={handleCancel}
|
||||
// footer={
|
||||
// footer
|
||||
// ? [
|
||||
// <Button key="submit" type="primary" onClick={handleSubmit}>
|
||||
// Submit
|
||||
// </Button>,
|
||||
// <Button key="cancel" onClick={handleCancel}>
|
||||
// Cancel
|
||||
// </Button>,
|
||||
// ]
|
||||
// : footer
|
||||
// }
|
||||
// >
|
||||
// {children}
|
||||
// <div>
|
||||
// <h4>Import Excel</h4>
|
||||
// <input type="file" onChange={handleFile} />
|
||||
// {excelFileError && <div className="error">{excelFileError}</div>}
|
||||
// <Button type="primary" icon={<ImportOutlined />} onClick={handleSubmit}>
|
||||
// Import
|
||||
// </Button>
|
||||
// </div>
|
||||
// <div>
|
||||
// <h4>Export Excel</h4>
|
||||
// <Button type="primary" icon={<ExportOutlined />} onClick={handleExport}>
|
||||
// Export
|
||||
// </Button>
|
||||
// </div>
|
||||
// </Modal>
|
||||
// );
|
||||
// };
|
||||
|
||||
export default Modals;
|
||||
// export default Modals;
|
||||
|
|
|
|||
|
|
@ -65,6 +65,7 @@ export const MobileMultiPdfPrint = async ({
|
|||
}) => {
|
||||
try {
|
||||
const style = await PrintStyleFunction(printerTemplateStyle, 'Sales');
|
||||
|
||||
const stylesMap = {
|
||||
'Style 1': 'PrintStyle1',
|
||||
'Style 2': 'PrintStyle2',
|
||||
|
|
@ -88,78 +89,61 @@ export const MobileMultiPdfPrint = async ({
|
|||
const selectedStyle = stylesMap[printerTemplateStyle] || 'RePrint';
|
||||
const Printsize = printDatas?.PageSize || 'A4';
|
||||
|
||||
// Create or clear the container
|
||||
let container = document.getElementById('print-merged');
|
||||
if (!container) {
|
||||
container = document.createElement('div');
|
||||
container.id = 'print-merged';
|
||||
container.style.display = 'none';
|
||||
document.body.appendChild(container);
|
||||
} else {
|
||||
container.innerHTML = '';
|
||||
}
|
||||
|
||||
// Process in batches for large files
|
||||
const BATCH_SIZE = 5;
|
||||
const totalOrders = PrintOrderDetails.length;
|
||||
let processedOrders = 0;
|
||||
let allBlobs = [];
|
||||
|
||||
for (let i = 0; i < totalOrders; i += BATCH_SIZE) {
|
||||
const batch = PrintOrderDetails.slice(i, i + BATCH_SIZE);
|
||||
|
||||
// Clear container for each batch
|
||||
container.innerHTML = '';
|
||||
|
||||
// Process each order in the current batch
|
||||
batch.forEach((_, index) => {
|
||||
const globalIndex = i + index;
|
||||
// 🔥 LOOP EACH INVOICE (MAIN FIX)
|
||||
for (let i = 0; i < totalOrders; i++) {
|
||||
const element = document.getElementById(
|
||||
`${selectedStyle}-${globalIndex}`
|
||||
`${selectedStyle}-${i}`
|
||||
);
|
||||
if (element) {
|
||||
const cloned = element.cloneNode(true);
|
||||
|
||||
// Add page break between orders (except the last one)
|
||||
if (globalIndex < totalOrders - 1) {
|
||||
const pageBreak = document.createElement('div');
|
||||
pageBreak.style.pageBreakAfter = 'always';
|
||||
container.appendChild(cloned);
|
||||
container.appendChild(pageBreak);
|
||||
} else {
|
||||
container.appendChild(cloned);
|
||||
if (!element) {
|
||||
console.warn('⚠️ Element not found:', `${selectedStyle}-${i}`);
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
console.warn(
|
||||
'⚠️ Element not found:',
|
||||
`${selectedStyle}-${globalIndex}`
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
// Generate PDF for this batch
|
||||
// 🔥 TEMP WRAPPER (IMPORTANT)
|
||||
const wrapper = document.createElement('div');
|
||||
wrapper.id = `temp-${i}`;
|
||||
wrapper.style.width = '794px';
|
||||
wrapper.style.background = '#fff';
|
||||
|
||||
wrapper.innerHTML = `
|
||||
${style}
|
||||
${element.innerHTML}
|
||||
`;
|
||||
|
||||
document.body.appendChild(wrapper);
|
||||
|
||||
try {
|
||||
const pdfBlob = await pdfDiv('print-merged', style);
|
||||
allBlobs.push(pdfBlob);
|
||||
// 👉 ONE invoice → ONE PDF
|
||||
const blob = await pdfDiv(wrapper.id);
|
||||
|
||||
processedOrders += batch.length;
|
||||
const progress = Math.round((processedOrders / totalOrders) * 100);
|
||||
allBlobs.push(blob);
|
||||
|
||||
} catch (err) {
|
||||
console.error('PDF error:', err);
|
||||
}
|
||||
|
||||
document.body.removeChild(wrapper);
|
||||
|
||||
// 🔥 Progress update
|
||||
const progress = Math.round(((i + 1) / totalOrders) * 100);
|
||||
onProgress(progress);
|
||||
} catch (error) {
|
||||
console.error('Error generating PDF batch:', error);
|
||||
throw new Error(
|
||||
`Failed to generate PDF for batch starting at index ${i}`
|
||||
);
|
||||
}
|
||||
|
||||
// 🔥 small delay for stability (important for large data)
|
||||
await new Promise((r) => setTimeout(r, 5));
|
||||
}
|
||||
|
||||
// ✅ Merge all PDF blobs into one
|
||||
// 🔥 MERGE ALL PDFs
|
||||
const combinedPdfBlob = await mergePdfBlobs(allBlobs);
|
||||
const base64Pdf = await blobToBase64(combinedPdfBlob);
|
||||
|
||||
// Handle different print actions
|
||||
// 🔥 DOWNLOAD
|
||||
if (PrintComing === 'download') {
|
||||
const blobUrl = createSecureBlobUrl(combinedPdfBlob);
|
||||
|
||||
const link = document.createElement('a');
|
||||
link.href = blobUrl;
|
||||
link.download = 'Receipts.pdf';
|
||||
|
|
@ -172,71 +156,44 @@ export const MobileMultiPdfPrint = async ({
|
|||
}, 100);
|
||||
}
|
||||
|
||||
// Return blob if requested (after download completes)
|
||||
// 🔥 RETURN BLOB
|
||||
if (returnBlob && PrintComing === 'download') {
|
||||
setPrintOrderDetails([]);
|
||||
if (container) {
|
||||
document.body.removeChild(container);
|
||||
}
|
||||
return { success: true, pdfBlob: combinedPdfBlob };
|
||||
}
|
||||
|
||||
// if (PrintComing === "whatsapp") {
|
||||
// const orderIds = PrintOrderDetails.map((row) => row?.OrderId).filter(Boolean);
|
||||
// const documentUrl = `${MainHomeUrl}viewbill?code=${orderIds}`;
|
||||
// const waUrl =
|
||||
// "https://web.whatsapp.com/send?text=" +
|
||||
// encodeURIComponent("Here are your receipts 👉 " + documentUrl);
|
||||
// window.open(waUrl, "_blank");
|
||||
// }
|
||||
|
||||
// 🔥 WHATSAPP
|
||||
if (PrintComing === 'whatsapp') {
|
||||
const orderIdsArray = PrintOrderDetails.map((row) => row?.OrderId).filter(
|
||||
Boolean
|
||||
);
|
||||
const orderIdsArray = PrintOrderDetails.map((row) => row?.OrderId).filter(Boolean);
|
||||
|
||||
if (orderIdsArray.length === 0) {
|
||||
console.warn('⚠️ No order IDs found for WhatsApp sharing.');
|
||||
return;
|
||||
}
|
||||
if (orderIdsArray.length === 0) return;
|
||||
|
||||
// 2️⃣ Encrypt order IDs
|
||||
const encrypted = encryptObject(orderIdsArray);
|
||||
|
||||
// 3️⃣ Prepare payload for backend
|
||||
const postData = {
|
||||
Url: encrypted,
|
||||
CreatedBy: UserId, // Make sure UserId is passed correctly to your function
|
||||
CreatedBy: UserId,
|
||||
};
|
||||
|
||||
try {
|
||||
// 4️⃣ Call backend to get a ReferenceNo
|
||||
const res = await dispatch(PublicQrCodepost(postData)).unwrap();
|
||||
console.log('Backend response:', res);
|
||||
|
||||
// 5️⃣ Extract ReferenceNo (adjust based on your backend response structure)
|
||||
// Example: it might be res.data.ReferenceNo or res.ReferenceNo
|
||||
const urlData = res?.data?.ReferenceNo || res?.ReferenceNo;
|
||||
if (!urlData) return;
|
||||
|
||||
if (!urlData) {
|
||||
console.error('❌ No ReferenceNo returned from backend');
|
||||
return;
|
||||
}
|
||||
|
||||
// 6️⃣ Build the shareable URL
|
||||
const documentUrl = `${MainHomeUrl}viewbill?code=${urlData}`;
|
||||
|
||||
// 7️⃣ Open WhatsApp Web with URL
|
||||
const waUrl =
|
||||
'https://web.whatsapp.com/send?text=' +
|
||||
encodeURIComponent('Here are your receipts 👉 ' + documentUrl);
|
||||
|
||||
window.open(waUrl, '_blank');
|
||||
} catch (err) {
|
||||
console.error('❌ WhatsApp sharing failed:', err);
|
||||
console.error('❌ WhatsApp error:', err);
|
||||
}
|
||||
}
|
||||
|
||||
// 🔥 EMAIL
|
||||
if (PrintComing === 'email') {
|
||||
try {
|
||||
const payload = {
|
||||
|
|
@ -247,13 +204,13 @@ export const MobileMultiPdfPrint = async ({
|
|||
};
|
||||
|
||||
const res = await dispatch(Emailsend(payload))?.unwrap();
|
||||
console.log('✅ Email sent response:', res?.data || res);
|
||||
return res;
|
||||
} catch (err) {
|
||||
console.error('❌ Email error:', err);
|
||||
}
|
||||
}
|
||||
|
||||
// 🔥 PRINT VIEW
|
||||
if (!['download', 'whatsapp', 'email'].includes(PrintComing)) {
|
||||
const blobUrl = createSecureBlobUrl(combinedPdfBlob);
|
||||
|
||||
|
|
@ -261,17 +218,10 @@ export const MobileMultiPdfPrint = async ({
|
|||
if (newWindow) {
|
||||
newWindow.document.write(`
|
||||
<html>
|
||||
<head>
|
||||
<title>Receipts</title>
|
||||
<style>
|
||||
body { margin: 0; display: flex; justify-content: center; align-items: center; height: 100vh; background: #f0f0f0; }
|
||||
iframe { width: 100%; height: 100%; border: none; }
|
||||
.print-button { position: fixed; top: 20px; right: 20px; z-index: 1000; padding: 10px 15px; background: #4CAF50; color: white; border: none; border-radius: 4px; cursor: pointer; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<button class="print-button" onclick="window.print()">Print</button>
|
||||
<iframe src="${blobUrl}" type="application/pdf"></iframe>
|
||||
<body style="margin:0">
|
||||
<button onclick="window.print()"
|
||||
style="position:fixed;top:10px;right:10px;">Print</button>
|
||||
<iframe src="${blobUrl}" style="width:100%;height:100%;border:none"></iframe>
|
||||
</body>
|
||||
</html>
|
||||
`);
|
||||
|
|
@ -280,43 +230,299 @@ export const MobileMultiPdfPrint = async ({
|
|||
window.location.href = blobUrl;
|
||||
}
|
||||
|
||||
// For mobile intent URL
|
||||
const intentUrl =
|
||||
'intent://open?' +
|
||||
'filename=' +
|
||||
encodeURIComponent('Receipts.pdf') +
|
||||
'&size=' +
|
||||
combinedPdfBlob.size +
|
||||
'filename=' + encodeURIComponent('Receipts.pdf') +
|
||||
'&size=' + combinedPdfBlob.size +
|
||||
'&type=application/pdf' +
|
||||
'&data=' +
|
||||
encodeURIComponent(base64Pdf) +
|
||||
'&PrintTypeS=' +
|
||||
encodeURIComponent(Printsize) +
|
||||
'&PrintTypeE' +
|
||||
'&data=' + encodeURIComponent(base64Pdf) +
|
||||
'&PrintTypeS=' + encodeURIComponent(Printsize) +
|
||||
'#Intent;scheme=pozoprinter;package=com.example.pozoprinter;end';
|
||||
|
||||
setTimeout(() => {
|
||||
try {
|
||||
window.location.href = intentUrl;
|
||||
} catch (e) {
|
||||
console.warn('Could not open intent URL:', e);
|
||||
}
|
||||
} catch {console.log("some errors")}
|
||||
}, 500);
|
||||
}
|
||||
|
||||
// Clean up
|
||||
// 🔥 CLEANUP
|
||||
setPrintOrderDetails([]);
|
||||
if (container) {
|
||||
document.body.removeChild(container);
|
||||
}
|
||||
|
||||
return { success: true, message: 'PDF processed successfully' };
|
||||
|
||||
} catch (error) {
|
||||
console.error('MobileMultiPdfPrint error:', error);
|
||||
return { success: false, message: error.message };
|
||||
}
|
||||
};
|
||||
|
||||
// export const MobileMultiPdfPrint = async ({
|
||||
// printerTemplateStyle = 'Style 13',
|
||||
// printDatas = {},
|
||||
// PrintOrderDetails = [],
|
||||
// setPrintOrderDetails = () => {},
|
||||
// PrintComing = '',
|
||||
// emailId = '',
|
||||
// dispatch,
|
||||
// UserId = '',
|
||||
// onProgress = () => {},
|
||||
// returnBlob = false,
|
||||
// }) => {
|
||||
// try {
|
||||
// const style = await PrintStyleFunction(printerTemplateStyle, 'Sales');
|
||||
// const stylesMap = {
|
||||
// 'Style 1': 'PrintStyle1',
|
||||
// 'Style 2': 'PrintStyle2',
|
||||
// 'Style 3': 'PrintStyle3',
|
||||
// 'Style 4': 'PrintStyle4',
|
||||
// 'Style 5': 'PrintStyle5',
|
||||
// 'Style 6': 'PrintStyle6',
|
||||
// 'Style 7': 'PrintStyle7',
|
||||
// 'Style 8': 'PrintStyle8',
|
||||
// 'Style 9': 'PrintStyle9',
|
||||
// 'Style 10': 'PrintStyle10',
|
||||
// 'Style 11': 'PrintStyle11',
|
||||
// 'Style 12': 'PrintStyle12',
|
||||
// 'Style 13': 'RePrint',
|
||||
// A4: 'PrintStyleA4',
|
||||
// A5: 'PrintStyleA5',
|
||||
// A4Standard: 'A4Standard',
|
||||
// TaxInvoice: 'TaxInvoice',
|
||||
// };
|
||||
|
||||
// const selectedStyle = stylesMap[printerTemplateStyle] || 'RePrint';
|
||||
// const Printsize = printDatas?.PageSize || 'A4';
|
||||
|
||||
// // Create or clear the container
|
||||
// let container = document.getElementById('print-merged');
|
||||
// if (!container) {
|
||||
// container = document.createElement('div');
|
||||
// container.id = 'print-merged';
|
||||
// container.style.display = 'none';
|
||||
// document.body.appendChild(container);
|
||||
// } else {
|
||||
// container.innerHTML = '';
|
||||
// }
|
||||
|
||||
// // Process in batches for large files
|
||||
// const BATCH_SIZE = 5;
|
||||
// const totalOrders = PrintOrderDetails.length;
|
||||
// let processedOrders = 0;
|
||||
// let allBlobs = [];
|
||||
|
||||
// for (let i = 0; i < totalOrders; i += BATCH_SIZE) {
|
||||
// const batch = PrintOrderDetails.slice(i, i + BATCH_SIZE);
|
||||
|
||||
// // Clear container for each batch
|
||||
// container.innerHTML = '';
|
||||
|
||||
// // Process each order in the current batch
|
||||
// batch.forEach((_, index) => {
|
||||
// const globalIndex = i + index;
|
||||
// const element = document.getElementById(
|
||||
// `${selectedStyle}-${globalIndex}`
|
||||
// );
|
||||
// if (element) {
|
||||
// const cloned = element.cloneNode(true);
|
||||
|
||||
// // Add page break between orders (except the last one)
|
||||
// if (globalIndex < totalOrders - 1) {
|
||||
// const pageBreak = document.createElement('div');
|
||||
// pageBreak.style.pageBreakAfter = 'always';
|
||||
// container.appendChild(cloned);
|
||||
// container.appendChild(pageBreak);
|
||||
// } else {
|
||||
// container.appendChild(cloned);
|
||||
// }
|
||||
// } else {
|
||||
// console.warn(
|
||||
// '⚠️ Element not found:',
|
||||
// `${selectedStyle}-${globalIndex}`
|
||||
// );
|
||||
// }
|
||||
// });
|
||||
|
||||
// // Generate PDF for this batch
|
||||
// try {
|
||||
// const pdfBlob = await pdfDiv('print-merged', style);
|
||||
// allBlobs.push(pdfBlob);
|
||||
|
||||
// processedOrders += batch.length;
|
||||
// const progress = Math.round((processedOrders / totalOrders) * 100);
|
||||
// onProgress(progress);
|
||||
// } catch (error) {
|
||||
// console.error('Error generating PDF batch:', error);
|
||||
// throw new Error(
|
||||
// `Failed to generate PDF for batch starting at index ${i}`
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
|
||||
// // ✅ Merge all PDF blobs into one
|
||||
// const combinedPdfBlob = await mergePdfBlobs(allBlobs);
|
||||
// const base64Pdf = await blobToBase64(combinedPdfBlob);
|
||||
|
||||
// // Handle different print actions
|
||||
// if (PrintComing === 'download') {
|
||||
// const blobUrl = createSecureBlobUrl(combinedPdfBlob);
|
||||
// const link = document.createElement('a');
|
||||
// link.href = blobUrl;
|
||||
// link.download = 'Receipts.pdf';
|
||||
// document.body.appendChild(link);
|
||||
// link.click();
|
||||
|
||||
// setTimeout(() => {
|
||||
// document.body.removeChild(link);
|
||||
// revokeSecureBlobUrl(blobUrl);
|
||||
// }, 100);
|
||||
// }
|
||||
|
||||
// // Return blob if requested (after download completes)
|
||||
// if (returnBlob && PrintComing === 'download') {
|
||||
// setPrintOrderDetails([]);
|
||||
// if (container) {
|
||||
// document.body.removeChild(container);
|
||||
// }
|
||||
// return { success: true, pdfBlob: combinedPdfBlob };
|
||||
// }
|
||||
|
||||
// // if (PrintComing === "whatsapp") {
|
||||
// // const orderIds = PrintOrderDetails.map((row) => row?.OrderId).filter(Boolean);
|
||||
// // const documentUrl = `${MainHomeUrl}viewbill?code=${orderIds}`;
|
||||
// // const waUrl =
|
||||
// // "https://web.whatsapp.com/send?text=" +
|
||||
// // encodeURIComponent("Here are your receipts 👉 " + documentUrl);
|
||||
// // window.open(waUrl, "_blank");
|
||||
// // }
|
||||
|
||||
// if (PrintComing === 'whatsapp') {
|
||||
// const orderIdsArray = PrintOrderDetails.map((row) => row?.OrderId).filter(
|
||||
// Boolean
|
||||
// );
|
||||
|
||||
// if (orderIdsArray.length === 0) {
|
||||
// console.warn('⚠️ No order IDs found for WhatsApp sharing.');
|
||||
// return;
|
||||
// }
|
||||
|
||||
// // 2️⃣ Encrypt order IDs
|
||||
// const encrypted = encryptObject(orderIdsArray);
|
||||
|
||||
// // 3️⃣ Prepare payload for backend
|
||||
// const postData = {
|
||||
// Url: encrypted,
|
||||
// CreatedBy: UserId, // Make sure UserId is passed correctly to your function
|
||||
// };
|
||||
|
||||
// try {
|
||||
// // 4️⃣ Call backend to get a ReferenceNo
|
||||
// const res = await dispatch(PublicQrCodepost(postData)).unwrap();
|
||||
// console.log('Backend response:', res);
|
||||
|
||||
// // 5️⃣ Extract ReferenceNo (adjust based on your backend response structure)
|
||||
// // Example: it might be res.data.ReferenceNo or res.ReferenceNo
|
||||
// const urlData = res?.data?.ReferenceNo || res?.ReferenceNo;
|
||||
|
||||
// if (!urlData) {
|
||||
// console.error('❌ No ReferenceNo returned from backend');
|
||||
// return;
|
||||
// }
|
||||
|
||||
// // 6️⃣ Build the shareable URL
|
||||
// const documentUrl = `${MainHomeUrl}viewbill?code=${urlData}`;
|
||||
|
||||
// // 7️⃣ Open WhatsApp Web with URL
|
||||
// const waUrl =
|
||||
// 'https://web.whatsapp.com/send?text=' +
|
||||
// encodeURIComponent('Here are your receipts 👉 ' + documentUrl);
|
||||
|
||||
// window.open(waUrl, '_blank');
|
||||
// } catch (err) {
|
||||
// console.error('❌ WhatsApp sharing failed:', err);
|
||||
// }
|
||||
// }
|
||||
|
||||
// if (PrintComing === 'email') {
|
||||
// try {
|
||||
// const payload = {
|
||||
// fileBlob: combinedPdfBlob,
|
||||
// toEmail: emailId,
|
||||
// type: 'Sales',
|
||||
// messageTemplatesList: [],
|
||||
// };
|
||||
|
||||
// const res = await dispatch(Emailsend(payload))?.unwrap();
|
||||
// console.log('✅ Email sent response:', res?.data || res);
|
||||
// return res;
|
||||
// } catch (err) {
|
||||
// console.error('❌ Email error:', err);
|
||||
// }
|
||||
// }
|
||||
|
||||
// if (!['download', 'whatsapp', 'email'].includes(PrintComing)) {
|
||||
// const blobUrl = createSecureBlobUrl(combinedPdfBlob);
|
||||
|
||||
// const newWindow = window.open('', '_blank');
|
||||
// if (newWindow) {
|
||||
// newWindow.document.write(`
|
||||
// <html>
|
||||
// <head>
|
||||
// <title>Receipts</title>
|
||||
// <style>
|
||||
// body { margin: 0; display: flex; justify-content: center; align-items: center; height: 100vh; background: #f0f0f0; }
|
||||
// iframe { width: 100%; height: 100%; border: none; }
|
||||
// .print-button { position: fixed; top: 20px; right: 20px; z-index: 1000; padding: 10px 15px; background: #4CAF50; color: white; border: none; border-radius: 4px; cursor: pointer; }
|
||||
// </style>
|
||||
// </head>
|
||||
// <body>
|
||||
// <button class="print-button" onclick="window.print()">Print</button>
|
||||
// <iframe src="${blobUrl}" type="application/pdf"></iframe>
|
||||
// </body>
|
||||
// </html>
|
||||
// `);
|
||||
// newWindow.document.close();
|
||||
// } else {
|
||||
// window.location.href = blobUrl;
|
||||
// }
|
||||
|
||||
// // For mobile intent URL
|
||||
// const intentUrl =
|
||||
// 'intent://open?' +
|
||||
// 'filename=' +
|
||||
// encodeURIComponent('Receipts.pdf') +
|
||||
// '&size=' +
|
||||
// combinedPdfBlob.size +
|
||||
// '&type=application/pdf' +
|
||||
// '&data=' +
|
||||
// encodeURIComponent(base64Pdf) +
|
||||
// '&PrintTypeS=' +
|
||||
// encodeURIComponent(Printsize) +
|
||||
// '&PrintTypeE' +
|
||||
// '#Intent;scheme=pozoprinter;package=com.example.pozoprinter;end';
|
||||
|
||||
// setTimeout(() => {
|
||||
// try {
|
||||
// window.location.href = intentUrl;
|
||||
// } catch (e) {
|
||||
// console.warn('Could not open intent URL:', e);
|
||||
// }
|
||||
// }, 500);
|
||||
// }
|
||||
|
||||
// // Clean up
|
||||
// setPrintOrderDetails([]);
|
||||
// if (container) {
|
||||
// document.body.removeChild(container);
|
||||
// }
|
||||
|
||||
// return { success: true, message: 'PDF processed successfully' };
|
||||
// } catch (error) {
|
||||
// console.error('MobileMultiPdfPrint error:', error);
|
||||
// return { success: false, message: error.message };
|
||||
// }
|
||||
// };
|
||||
|
||||
// Progress component to show processing status
|
||||
export const PdfProcessingProgress = ({ progress, isProcessing }) => {
|
||||
if (!isProcessing) return null;
|
||||
|
|
|
|||
|
|
@ -1,10 +1,9 @@
|
|||
import React, { useEffect, useState } from 'react';
|
||||
import { useState } from 'react';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { ShopOutlined, ShoppingOutlined, UpOutlined } from '@ant-design/icons';
|
||||
import { UpOutlined } from '@ant-design/icons';
|
||||
import {
|
||||
ChangeDineInDefaultFn,
|
||||
ChangeSelectedCustDisable,
|
||||
GlobalBookingType,
|
||||
GlobalChairLevelServiceDefault,
|
||||
GlobalDefaultBookingType,
|
||||
|
|
@ -13,24 +12,20 @@ import {
|
|||
Globalunpaidflow,
|
||||
changeBookingType,
|
||||
changeChairLevelServiceDefault,
|
||||
changeCustomerID,
|
||||
changeDefaultBookingType,
|
||||
changeEstimateBooking,
|
||||
changeOrderCardDetails,
|
||||
changeOrderType,
|
||||
changeReorderHoldDetails,
|
||||
changeReorderProductDetails,
|
||||
changeSelectedCustId,
|
||||
changeSelectedOption,
|
||||
changeSelectedTableDetails,
|
||||
} from '../../../../Features/BookingScreen/BookingData/BookingData';
|
||||
import PozoDineInIcon from './Pozo retail icons/PozoDineIn.jsx';
|
||||
import TakeAway from './Pozo retail icons/PozoTakeAwayIcon';
|
||||
import { ChangeTotalAmount } from '../../../../Features/ExteraCharges/ExtraCharges.js';
|
||||
import TooltipWrapper from '../../../../Components/Tooltip/Tooltip';
|
||||
import { isMobile } from 'react-device-detect';
|
||||
import { Badge, Popover, Space, Switch, Tooltip } from 'antd';
|
||||
import { color } from 'highcharts';
|
||||
import { Popover, Space, Switch, Tooltip } from 'antd';
|
||||
|
||||
import './BSNavBarTable.scss';
|
||||
import { RiRefreshLine } from "react-icons/ri";
|
||||
|
||||
|
|
@ -72,7 +67,7 @@ const BSNavBarTable = () => {
|
|||
await dispatch(changeReorderHoldDetails({}));
|
||||
await dispatch(changeReorderProductDetails([]));
|
||||
await dispatch(changeOrderCardDetails([]));
|
||||
dispatch(changeunpaidflow(false));
|
||||
// dispatch(changeunpaidflow(false));
|
||||
}
|
||||
// await dispatch(changeReorderHoldDetails({}))
|
||||
// await dispatch(ChangeTotalAmount([]))
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import React, { useRef, useEffect, useState, useContext } from 'react';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { Table, Button, Form, Input, Tooltip, Modal } from 'antd';
|
||||
import { read, utils } from 'xlsx';
|
||||
import { Table, Form, Input, Tooltip, Modal } from 'antd';
|
||||
// import { read, utils } from 'xlsx';
|
||||
import ExcelJS from 'exceljs';
|
||||
import {
|
||||
emptyExcelData,
|
||||
|
|
@ -32,6 +32,7 @@ const ConfigExcel = ({ handleSubmit, CategoryNames, SubCatData }) => {
|
|||
const [worksheet1, setWorksheet1] = useState(null);
|
||||
const [editdelete, seteditdelete] = useState('');
|
||||
const [Datas, setDatas] = useState();
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
|
||||
const [radioValue, setRadioValue] = useState('Category Name');
|
||||
|
||||
|
|
@ -146,58 +147,137 @@ const ConfigExcel = ({ handleSubmit, CategoryNames, SubCatData }) => {
|
|||
// 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);
|
||||
reader.onload = async (e) => {
|
||||
try {
|
||||
const buffer = e.target.result;
|
||||
|
||||
const workbook = read(data, { type: 'array' });
|
||||
const worksheet = workbook.Sheets[workbook.SheetNames[0]];
|
||||
const workbook = new ExcelJS.Workbook();
|
||||
await workbook.xlsx.load(buffer);
|
||||
|
||||
const jsonData = utils.sheet_to_json(worksheet, {
|
||||
header: 1,
|
||||
raw: false,
|
||||
dateNF: 'DD/MM/YY',
|
||||
const worksheet = workbook.worksheets[0];
|
||||
|
||||
const jsonData = [];
|
||||
|
||||
worksheet.eachRow((row) => {
|
||||
const rowData = [];
|
||||
|
||||
row.eachCell({ includeEmpty: true }, (cell) => {
|
||||
let value = cell.value;
|
||||
|
||||
// handle different ExcelJS value types
|
||||
if (value && typeof value === 'object') {
|
||||
if (value.text) value = value.text;
|
||||
else if (value.result) value = value.result;
|
||||
else if (value.richText) {
|
||||
value = value.richText.map((t) => t.text).join('');
|
||||
}
|
||||
}
|
||||
|
||||
rowData.push(value ?? '');
|
||||
});
|
||||
|
||||
jsonData.push(rowData);
|
||||
});
|
||||
|
||||
// remove empty rows (same as before)
|
||||
const nonEmptyRows = jsonData.filter((row) =>
|
||||
row.some((cell) => cell !== '')
|
||||
);
|
||||
|
||||
const header = nonEmptyRows[0];
|
||||
const rows = nonEmptyRows.slice(1);
|
||||
|
||||
const dateFields = ['Manufacture Date', 'Expire Date', 'Stock Date'];
|
||||
|
||||
function convertToDisplayFormat(dateString) {
|
||||
const 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}`;
|
||||
}
|
||||
if (isNaN(date)) return '';
|
||||
return `${String(date.getDate()).padStart(2, '0')}-${String(
|
||||
date.getMonth() + 1
|
||||
).padStart(2, '0')}-${date.getFullYear()}`;
|
||||
};
|
||||
|
||||
const formattedData = rows.map((row) => {
|
||||
let rowData = header.reduce((acc, col, columnIndex) => {
|
||||
return header.reduce((acc, col, index) => {
|
||||
if (dateFields.includes(col)) {
|
||||
acc[col] = convertToDisplayFormat(row[columnIndex]);
|
||||
acc[col] = convertToDisplayFormat(row[index]);
|
||||
} else {
|
||||
acc[col] = row[columnIndex];
|
||||
acc[col] = row[index];
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
return rowData;
|
||||
});
|
||||
|
||||
// SAME AS BEFORE (no break)
|
||||
dispatch(uploadExcel({ file, jsonData: nonEmptyRows }));
|
||||
setWorksheet1(worksheet1);
|
||||
setDatas(formattedData);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
Modal.error({
|
||||
title: 'Error',
|
||||
content: 'Failed to process Excel file',
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
reader.readAsArrayBuffer(file);
|
||||
};
|
||||
// const uploadAndProcessExcel = (file) => {
|
||||
// const reader = new FileReader();
|
||||
|
||||
// reader.onload = (e) => {
|
||||
// const data = new Uint8Array(e.target.result);
|
||||
|
||||
// const workbook = read(data, { type: 'array' });
|
||||
// const worksheet = workbook.Sheets[workbook.SheetNames[0]];
|
||||
|
||||
// const jsonData = utils.sheet_to_json(worksheet, {
|
||||
// header: 1,
|
||||
// raw: false,
|
||||
// dateNF: 'DD/MM/YY',
|
||||
// });
|
||||
|
||||
// const nonEmptyRows = jsonData.filter((row) =>
|
||||
// row.some((cell) => cell !== '')
|
||||
// );
|
||||
// const header = nonEmptyRows[0];
|
||||
// const rows = nonEmptyRows.slice(1);
|
||||
|
||||
// const dateFields = ['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;
|
||||
// });
|
||||
|
||||
// dispatch(uploadExcel({ file, jsonData: nonEmptyRows }));
|
||||
// setWorksheet1(worksheet1);
|
||||
// setDatas(formattedData);
|
||||
// };
|
||||
|
||||
// reader.readAsArrayBuffer(file);
|
||||
// };
|
||||
|
||||
const handleDownload = async () => {
|
||||
const workbook = new ExcelJS.Workbook();
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
|
||||
|
||||
import { useMemo } from 'react';
|
||||
import Highcharts from 'highcharts';
|
||||
import HighchartsReact from 'highcharts-react-official';
|
||||
import Chart from 'react-apexcharts';
|
||||
import './PieChart.scss';
|
||||
|
||||
const DynamicPieChart = ({ chartData }) => {
|
||||
|
|
@ -37,16 +38,35 @@ const DynamicPieChart = ({ chartData }) => {
|
|||
);
|
||||
}
|
||||
|
||||
// 🔥 Convert Highcharts format → ApexCharts format
|
||||
const series = chartData.map((item) => item.y);
|
||||
const labels = chartData.map((item) => item.name);
|
||||
|
||||
const options = {
|
||||
chart: {
|
||||
type: 'pie',
|
||||
},
|
||||
labels: labels,
|
||||
title: {
|
||||
text: `Overall Amount : ${safeRound(overallValue)}`,
|
||||
align: 'center',
|
||||
},
|
||||
tooltip: {
|
||||
y: {
|
||||
formatter: (val) => safeRound(val),
|
||||
},
|
||||
},
|
||||
legend: {
|
||||
position: 'bottom',
|
||||
},
|
||||
dataLabels: {
|
||||
formatter: (val) => `${val.toFixed(1)}%`,
|
||||
},
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={{ width: 'inherit' }}>
|
||||
<HighchartsReact
|
||||
highcharts={Highcharts}
|
||||
options={{
|
||||
chart: { type: 'pie' },
|
||||
title: { text: `Overall Amount : ${safeRound(overallValue)}` },
|
||||
series: [{ name: 'Amount', data: chartData }],
|
||||
}}
|
||||
/>
|
||||
<div style={{ width: '100%' }}>
|
||||
<Chart options={options} series={series} type="pie" height={300} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -5,11 +5,9 @@ import { changeKioskOrderDetails } from '../../../Features/BookingScreen/Booking
|
|||
import '../../../Styles/Kiosk/OrderCart/OrderCart2.scss';
|
||||
import PozoDineInIcon from '../KioskIcons/KioskDineIn';
|
||||
import TakeAway from '../KioskIcons/KioskTakeAway';
|
||||
import { color } from 'highcharts';
|
||||
import { calculateOverAllQtyLimit } from '../../../utils/calculations';
|
||||
import { PreferenceData } from '../../../Features/BookingScreen/BookingData/BookingData';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { message } from 'antd';
|
||||
import { useState } from 'react';
|
||||
import { IoIosWarning } from 'react-icons/io';
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import React, { useState, useRef, useEffect, useContext, useMemo } from 'react';
|
||||
import React, { useState, useRef, useEffect, useContext } from 'react';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { read, utils } from 'xlsx';
|
||||
// import { read, utils } from 'xlsx';
|
||||
import ExcelJS from 'exceljs';
|
||||
import { Table, Form, Input, Button, Tooltip, Modal, Select } from 'antd';
|
||||
import { Table, Form, Input, Tooltip, Modal, Select } from 'antd';
|
||||
import {
|
||||
emptyExcelData,
|
||||
excelDataSelector,
|
||||
|
|
@ -26,12 +26,9 @@ 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 {
|
||||
|
|
@ -606,8 +603,6 @@ const ProductExcel = ({
|
|||
const [loading, setLoading] = useState(false);
|
||||
const [message, setMessage] = useState(false);
|
||||
const [Datas, setDatas] = useState();
|
||||
console.log(Datas, 'Datas32323232');
|
||||
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
const [MappingOpen, setMappingOpen] = useState(false);
|
||||
const [fieldMapping, setFieldMapping] = useState({});
|
||||
|
|
@ -770,40 +765,6 @@ const ProductExcel = ({
|
|||
handleExportBulkdata();
|
||||
}
|
||||
}, [AppId, BranchId, CompId]);
|
||||
// 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();
|
||||
|
|
@ -845,7 +806,6 @@ const ProductExcel = ({
|
|||
};
|
||||
|
||||
const handleFileUpload = (e) => {
|
||||
|
||||
const file = e.target.files[0];
|
||||
const reader = new FileReader();
|
||||
|
||||
|
|
@ -905,7 +865,6 @@ const ProductExcel = ({
|
|||
const hashProduct = (product) =>
|
||||
COMPARE_FIELDS.map((field) => getFieldValue(product, field)).join('§');
|
||||
function findModifiedProducts(originalData, updatedData) {
|
||||
|
||||
if (!originalData?.length || !updatedData?.length) return [];
|
||||
|
||||
const originalHashMap = new Map();
|
||||
|
|
@ -940,26 +899,31 @@ const ProductExcel = ({
|
|||
const uploadAndProcessExcel = (file) => {
|
||||
const reader = new FileReader();
|
||||
|
||||
reader.onload = (e) => {
|
||||
const data = new Uint8Array(e.target.result);
|
||||
reader.onload = async (e) => {
|
||||
const buffer = e.target.result;
|
||||
|
||||
const workbook = read(data, { type: 'array' });
|
||||
const worksheet = workbook.Sheets[workbook.SheetNames[0]];
|
||||
const workbook = new ExcelJS.Workbook();
|
||||
await workbook.xlsx.load(buffer);
|
||||
|
||||
const jsonData = utils.sheet_to_json(worksheet, {
|
||||
header: 1,
|
||||
raw: false,
|
||||
dateNF: 'DD/MM/YY',
|
||||
const worksheet = workbook.worksheets[0];
|
||||
|
||||
// 🔥 convert to same format as xlsx (array of arrays)
|
||||
const jsonData = [];
|
||||
worksheet.eachRow((row) => {
|
||||
const rowValues = row.values.slice(1); // remove first empty index
|
||||
jsonData.push(rowValues);
|
||||
});
|
||||
|
||||
const nonEmptyRows = jsonData.filter((row) =>
|
||||
row.some((cell) => cell !== '')
|
||||
row.some((cell) => cell !== '' && cell !== null && cell !== undefined)
|
||||
);
|
||||
|
||||
const header = nonEmptyRows[0];
|
||||
const rows = nonEmptyRows.slice(1);
|
||||
|
||||
const dateFields = [
|
||||
'Available From',
|
||||
'Available To',
|
||||
'Manufacture Date',
|
||||
'Expire Date',
|
||||
'Stock Date',
|
||||
|
|
@ -967,21 +931,10 @@ const ProductExcel = ({
|
|||
|
||||
function convertToDisplayFormat(dateString) {
|
||||
if (!dateString) return '';
|
||||
// Handle DD/MM/YY or DD/MM/YYYY (from Excel dateNF format)
|
||||
const slashParts = String(dateString).split('/');
|
||||
if (slashParts.length === 3) {
|
||||
const [d, m, y] = slashParts;
|
||||
const fullYear = y.length === 2 ? '20' + y : y;
|
||||
return `${d.padStart(2,'0')}-${m.padStart(2,'0')}-${fullYear}`;
|
||||
}
|
||||
// Handle DD-MM-YYYY already in correct format
|
||||
const dashParts = String(dateString).split('-');
|
||||
if (dashParts.length === 3 && dashParts[2].length === 4) {
|
||||
return dateString; // already DD-MM-YYYY
|
||||
}
|
||||
// Fallback: try native Date parse (YYYY-MM-DD etc)
|
||||
|
||||
const date = new Date(dateString);
|
||||
if (isNaN(date.getTime())) return '';
|
||||
if (isNaN(date)) return dateString;
|
||||
|
||||
const day = date.getDate().toString().padStart(2, '0');
|
||||
const month = (date.getMonth() + 1).toString().padStart(2, '0');
|
||||
const year = date.getFullYear().toString();
|
||||
|
|
@ -997,40 +950,31 @@ const ProductExcel = ({
|
|||
}
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
return rowData;
|
||||
});
|
||||
|
||||
if (jsonData.length > 0) {
|
||||
const extractedHeaders = Object.keys(formattedData[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'],
|
||||
'Manufacture Date':
|
||||
item['Manufacture Date'] == 'NaN-NaN-NaN' ? '' : item['Manufacture Date'],
|
||||
'Expire Date':
|
||||
item['Expire Date'] == 'NaN-NaN-NaN' ? '' : item['Expire Date'],
|
||||
'Available From':
|
||||
item['Available From'] == 'NaN-NaN-NaN' ? '' : item['Available From'],
|
||||
'Available To':
|
||||
item['Available To'] == 'NaN-NaN-NaN' ? '' : item['Available To'],
|
||||
ProdId: item['ProdId'] || null,
|
||||
}));
|
||||
|
||||
// Detect modified products
|
||||
|
||||
const modifiedProducts = findModifiedProducts(OrginalData, updatedData);
|
||||
const modifiedIds = new Set(modifiedProducts.map((p) => p.ProdId));
|
||||
const originalIds = new Set(OrginalData?.map((p) => p.ProdId) || []);
|
||||
|
||||
const markedData = updatedData
|
||||
.map((item) => ({
|
||||
...item,
|
||||
|
|
@ -1038,30 +982,135 @@ const ProductExcel = ({
|
|||
modifiedIds.has(item.ProdId) || !originalIds.has(item.ProdId),
|
||||
}))
|
||||
.sort((a, b) => b.isModified - a.isModified);
|
||||
|
||||
setUploadedRawData(markedData);
|
||||
console.log(modifiedProducts, 'modifiedProducts');
|
||||
|
||||
// ✅ Set only once with final data
|
||||
setHeaders(extractedHeaders);
|
||||
|
||||
autoMapFields(extractedHeaders);
|
||||
|
||||
dispatch(uploadExcel({ file, jsonData: nonEmptyRows }));
|
||||
|
||||
setWorksheet(worksheet);
|
||||
setDatas([]); // Clear mapped state initially
|
||||
setDatas([]);
|
||||
setExcelData([]);
|
||||
}
|
||||
|
||||
// setMappingOpen(true);
|
||||
// ⚠️ keep your original flow (no break)
|
||||
dispatch(uploadExcel({ file, jsonData: nonEmptyRows }));
|
||||
setWorksheet(worksheet);
|
||||
setWorksheet1(worksheet1);
|
||||
setDatas(formattedData); // Update Datas state here
|
||||
// setExcelData(formattedData);
|
||||
setDatas(formattedData);
|
||||
};
|
||||
|
||||
reader.readAsArrayBuffer(file);
|
||||
};
|
||||
|
||||
// const uploadAndProcessExcel = (file) => {
|
||||
// const reader = new FileReader();
|
||||
|
||||
// reader.onload = (e) => {
|
||||
// const data = new Uint8Array(e.target.result);
|
||||
|
||||
// const workbook = read(data, { type: 'array' });
|
||||
// const worksheet = workbook.Sheets[workbook.SheetNames[0]];
|
||||
|
||||
// const jsonData = utils.sheet_to_json(worksheet, {
|
||||
// header: 1,
|
||||
// raw: false,
|
||||
// dateNF: 'DD/MM/YY',
|
||||
// });
|
||||
|
||||
// const nonEmptyRows = jsonData.filter((row) =>
|
||||
// row.some((cell) => cell !== '')
|
||||
// );
|
||||
|
||||
// const header = nonEmptyRows[0];
|
||||
// const rows = nonEmptyRows.slice(1);
|
||||
|
||||
// const dateFields = [
|
||||
// 'Available From',
|
||||
// 'Available To',
|
||||
// 'Manufacture Date',
|
||||
// 'Expire Date',
|
||||
// 'Stock Date',
|
||||
// ];
|
||||
|
||||
// function convertToDisplayFormat(dateString) {
|
||||
// const date = new Date(dateString);
|
||||
// const day = date.getDate().toString().padStart(2, '0');
|
||||
// const month = (date.getMonth() + 1).toString().padStart(2, '0');
|
||||
// const year = date.getFullYear().toString();
|
||||
// return `${day}-${month}-${year}`;
|
||||
// }
|
||||
|
||||
// const formattedData = rows.map((row) => {
|
||||
// let rowData = header.reduce((acc, col, columnIndex) => {
|
||||
// if (dateFields?.includes(col)) {
|
||||
// acc[col] = convertToDisplayFormat(row[columnIndex]);
|
||||
// } else {
|
||||
// acc[col] = row[columnIndex];
|
||||
// }
|
||||
// return acc;
|
||||
// }, {});
|
||||
|
||||
// return rowData;
|
||||
// });
|
||||
|
||||
// if (jsonData.length > 0) {
|
||||
// const extractedHeaders = Object.keys(formattedData[0]);
|
||||
|
||||
// const filteredData = formattedData.filter(
|
||||
// (item) => !Object.values(item)?.includes('sample row')
|
||||
// );
|
||||
|
||||
// // ✅ Add default variant name if missing
|
||||
// const updatedData = filteredData.map((item) => ({
|
||||
// ...item,
|
||||
// 'Product Varient Name':
|
||||
// item['Product Varient Name'] === undefined
|
||||
// ? 'Variant 1'
|
||||
// : item['Product Varient Name'],
|
||||
// 'Available To':
|
||||
// item['Available To'] == 'NaN-NaN-NaN' ? '' : item['Available To'],
|
||||
// ProdId: item['ProdId'] || null,
|
||||
// }));
|
||||
|
||||
// // Detect modified products
|
||||
|
||||
// const modifiedProducts = findModifiedProducts(OrginalData, updatedData);
|
||||
// const modifiedIds = new Set(modifiedProducts.map((p) => p.ProdId));
|
||||
// const originalIds = new Set(OrginalData?.map((p) => p.ProdId) || []);
|
||||
// const markedData = updatedData
|
||||
// .map((item) => ({
|
||||
// ...item,
|
||||
// isModified:
|
||||
// modifiedIds.has(item.ProdId) || !originalIds.has(item.ProdId),
|
||||
// }))
|
||||
// .sort((a, b) => b.isModified - a.isModified);
|
||||
// setUploadedRawData(markedData);
|
||||
// console.log(modifiedProducts, 'modifiedProducts');
|
||||
|
||||
// // ✅ Set only once with final data
|
||||
// setHeaders(extractedHeaders);
|
||||
|
||||
// autoMapFields(extractedHeaders);
|
||||
// dispatch(uploadExcel({ file, jsonData: nonEmptyRows }));
|
||||
// setWorksheet(worksheet);
|
||||
// setDatas([]); // Clear mapped state initially
|
||||
// setExcelData([]);
|
||||
// }
|
||||
|
||||
// // setMappingOpen(true);
|
||||
// dispatch(uploadExcel({ file, jsonData: nonEmptyRows }));
|
||||
// setWorksheet(worksheet);
|
||||
// setWorksheet1(worksheet1);
|
||||
// setDatas(formattedData); // Update Datas state here
|
||||
// // setExcelData(formattedData);
|
||||
// };
|
||||
|
||||
// reader.readAsArrayBuffer(file);
|
||||
// };
|
||||
|
||||
useEffect(() => {
|
||||
imagecall();
|
||||
addOnlineImage();
|
||||
|
|
@ -1083,9 +1132,9 @@ const ProductExcel = ({
|
|||
};
|
||||
|
||||
const updateImageUrl = (url, index, key) => {
|
||||
|
||||
const newData1 = [...Datas];
|
||||
newData1[key] = { ...newData1[key], Productimage: url };
|
||||
|
||||
setDatas(newData1);
|
||||
setExcelData(newData1);
|
||||
};
|
||||
|
|
@ -1826,24 +1875,24 @@ const ProductExcel = ({
|
|||
}
|
||||
});
|
||||
|
||||
// // Now set protection for all rows
|
||||
// // First, unlock ALL cells in the worksheet
|
||||
// for (let rowNum = 1; rowNum <= worksheet.rowCount; rowNum++) {
|
||||
// const row = worksheet.getRow(rowNum);
|
||||
// row.eachCell({ includeEmpty: true }, (cell, colNumber) => {
|
||||
// const config = selectedColumns[colNumber - 1];
|
||||
// cell.protection = { locked: config?.locked || false };
|
||||
// });
|
||||
// }
|
||||
// // Now set protection for all rows
|
||||
// // First, unlock ALL cells in the worksheet
|
||||
// for (let rowNum = 1; rowNum <= worksheet.rowCount; rowNum++) {
|
||||
// const row = worksheet.getRow(rowNum);
|
||||
// row.eachCell({ includeEmpty: true }, (cell, colNumber) => {
|
||||
// const config = selectedColumns[colNumber - 1];
|
||||
// cell.protection = { locked: config?.locked || false };
|
||||
// });
|
||||
// }
|
||||
|
||||
// // Then lock ONLY the header row (row 1) and sample row (row 2)
|
||||
// worksheet.getRow(1).eachCell({ includeEmpty: true }, (cell) => {
|
||||
// cell.protection = { locked: true };
|
||||
// });
|
||||
// // 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 };
|
||||
// });
|
||||
// worksheet.getRow(2).eachCell({ includeEmpty: true }, (cell) => {
|
||||
// cell.protection = { locked: true };
|
||||
// });
|
||||
// Add empty rows for data entry
|
||||
for (let i = 1; i <= 1000; i++) {
|
||||
worksheet.addRow({});
|
||||
|
|
@ -1853,7 +1902,9 @@ const ProductExcel = ({
|
|||
for (let rowNum = 3; rowNum <= worksheet.rowCount; rowNum++) {
|
||||
for (let colNum = 1; colNum <= selectedColumns.length; colNum++) {
|
||||
const config = selectedColumns[colNum - 1];
|
||||
worksheet.getCell(rowNum, colNum).protection = { locked: config?.locked || false };
|
||||
worksheet.getCell(rowNum, colNum).protection = {
|
||||
locked: config?.locked || false,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2188,7 +2239,9 @@ const ProductExcel = ({
|
|||
// Write TaxDatas into Sheet2 column N for range-based dropdown (avoids 255-char limit)
|
||||
if (typeof TaxDatas !== 'undefined' && TaxDatas?.length > 0) {
|
||||
TaxDatas.forEach((taxItem, idx) => {
|
||||
worksheet1.getCell(`N${idx + 2}`).value = taxItem.replace(/,/g, '').trim();
|
||||
worksheet1.getCell(`N${idx + 2}`).value = taxItem
|
||||
.replace(/,/g, '')
|
||||
.trim();
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -2240,7 +2293,8 @@ const ProductExcel = ({
|
|||
|
||||
// Define a named range for this category's subcats
|
||||
const safeName = catName.replace(/[^A-Za-z0-9_]/g, '_');
|
||||
const rangeRef = subcats.length > 0
|
||||
const rangeRef =
|
||||
subcats.length > 0
|
||||
? `Sheet2!$${colLetter}$2:$${colLetter}$${subcats.length + 1}`
|
||||
: `Sheet2!$${colLetter}$2:$${colLetter}$2`;
|
||||
|
||||
|
|
@ -2249,8 +2303,10 @@ const ProductExcel = ({
|
|||
}
|
||||
|
||||
// Find which column index ProdCat and ProdSubCat are in Sheet1
|
||||
const catColIndex = selectedColumns.findIndex((c) => c.key === 'ProdCat') + 1;
|
||||
const subCatColIndex = selectedColumns.findIndex((c) => c.key === 'ProdSubCat') + 1;
|
||||
const catColIndex =
|
||||
selectedColumns.findIndex((c) => c.key === 'ProdCat') + 1;
|
||||
const subCatColIndex =
|
||||
selectedColumns.findIndex((c) => c.key === 'ProdSubCat') + 1;
|
||||
|
||||
if (catColIndex > 0 && subCatColIndex > 0) {
|
||||
const catColLetter = getExcelColumnLetter(catColIndex);
|
||||
|
|
@ -2260,7 +2316,9 @@ const ProductExcel = ({
|
|||
cell.dataValidation = {
|
||||
type: 'list',
|
||||
allowBlank: true,
|
||||
formulae: [`INDIRECT(SUBSTITUTE(${catColLetter}${rowNumber}," ","_"))`],
|
||||
formulae: [
|
||||
`INDIRECT(SUBSTITUTE(${catColLetter}${rowNumber}," ","_"))`,
|
||||
],
|
||||
showErrorMessage: false,
|
||||
};
|
||||
}
|
||||
|
|
@ -2295,7 +2353,8 @@ const ProductExcel = ({
|
|||
});
|
||||
|
||||
const safeName = `B_${subcatName.replace(/[^A-Za-z0-9_]/g, '_')}`;
|
||||
const rangeRef = brands.length > 0
|
||||
const rangeRef =
|
||||
brands.length > 0
|
||||
? `Sheet2!$${colLetter}$2:$${colLetter}$${brands.length + 1}`
|
||||
: `Sheet2!$${colLetter}$2:$${colLetter}$2`;
|
||||
workbook.definedNames.add(rangeRef, safeName);
|
||||
|
|
@ -2303,7 +2362,8 @@ const ProductExcel = ({
|
|||
}
|
||||
|
||||
// Apply INDIRECT brand validation based on selected SubCategory
|
||||
const brandColIndex = selectedColumns.findIndex((c) => c.key === 'Brand') + 1;
|
||||
const brandColIndex =
|
||||
selectedColumns.findIndex((c) => c.key === 'Brand') + 1;
|
||||
if (subCatColIndex > 0 && brandColIndex > 0) {
|
||||
const subCatColLetter = getExcelColumnLetter(subCatColIndex);
|
||||
for (let rowNumber = 3; rowNumber <= 1002; rowNumber++) {
|
||||
|
|
@ -2311,7 +2371,9 @@ const ProductExcel = ({
|
|||
cell.dataValidation = {
|
||||
type: 'list',
|
||||
allowBlank: true,
|
||||
formulae: [`INDIRECT("B_"&SUBSTITUTE(${subCatColLetter}${rowNumber}," ","_"))`],
|
||||
formulae: [
|
||||
`INDIRECT("B_"&SUBSTITUTE(${subCatColLetter}${rowNumber}," ","_"))`,
|
||||
],
|
||||
showErrorMessage: false,
|
||||
};
|
||||
}
|
||||
|
|
@ -2696,7 +2758,9 @@ const ProductExcel = ({
|
|||
<div className="imgUpldr" style={{ fontSize: '15px' }}>
|
||||
<Imageupload
|
||||
singleImage={true}
|
||||
updateImageUrl={(url) => updateImageUrl(url, index, record.key)}
|
||||
updateImageUrl={(url) =>
|
||||
updateImageUrl(url, index, record.key)
|
||||
}
|
||||
// ImageLink={record.Productimage || ""}
|
||||
ImageLink={
|
||||
onlineImage
|
||||
|
|
@ -2800,7 +2864,7 @@ const ProductExcel = ({
|
|||
...record,
|
||||
...values,
|
||||
});
|
||||
} catch (errInfo) { }
|
||||
} catch (errInfo) {}
|
||||
};
|
||||
|
||||
let childNode = children;
|
||||
|
|
@ -3364,19 +3428,19 @@ const ProductExcel = ({
|
|||
display: 'flex',
|
||||
alignItems: 'flex-end',
|
||||
// justifyContent: "space-between",
|
||||
justifyContent: excelData?.length > 0 ? "space-between" : "flex-end",
|
||||
width: "100%"
|
||||
justifyContent:
|
||||
excelData?.length > 0 ? 'space-between' : 'flex-end',
|
||||
width: '100%',
|
||||
}}
|
||||
>
|
||||
{excelData?.length > 0 && (
|
||||
|
||||
<div className='SheetIndiacte'>
|
||||
<div className="SheetIndiacte">
|
||||
<div>
|
||||
<p className='mdsheet'></p>
|
||||
<p className="mdsheet"></p>
|
||||
<span>Modified</span>
|
||||
</div>
|
||||
<div>
|
||||
<p className='mdsheet2'></p>
|
||||
<p className="mdsheet2"></p>
|
||||
<span>Not Modified</span>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -3570,7 +3634,8 @@ const ProductExcel = ({
|
|||
<div className="productonlineImg">
|
||||
{imagedata?.map((item, index) => (
|
||||
<div
|
||||
className={`singleimg ${selectedImage === item ? 'selected' : ''
|
||||
className={`singleimg ${
|
||||
selectedImage === item ? 'selected' : ''
|
||||
}`}
|
||||
key={index}
|
||||
onClick={() => setSelectedImage(item)}
|
||||
|
|
|
|||
|
|
@ -1596,7 +1596,12 @@ const ProductList = () => {
|
|||
}
|
||||
};
|
||||
const getPreference = async () => {
|
||||
const data = { AppId: AppId, CompId: CompId, BranchId: BranchId,UserId: UserId };
|
||||
const data = {
|
||||
AppId: AppId,
|
||||
CompId: CompId,
|
||||
BranchId: BranchId,
|
||||
UserId: UserId,
|
||||
};
|
||||
const { data: res } = await dispatch(getPreferenceData(data)).unwrap();
|
||||
const decimalSetting = res?.data?.[0]?.SettingDtlDetails?.find(
|
||||
(setting) =>
|
||||
|
|
@ -2029,9 +2034,6 @@ const ProductList = () => {
|
|||
formRef?.current?.resetFields();
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
const handleResetExcelData = useCallback(() => {
|
||||
dispatch(emptyExcelData());
|
||||
}, []);
|
||||
|
|
@ -2051,9 +2053,7 @@ const ProductList = () => {
|
|||
const handleSubmit = useCallback(async () => {
|
||||
setBulkpost(true);
|
||||
|
||||
const FilterData = Exceldatasubmit?.filter(
|
||||
a => a.ProductName !== ''
|
||||
);
|
||||
const FilterData = Exceldatasubmit?.filter((a) => a.ProductName !== '');
|
||||
|
||||
if (!FilterData || FilterData.length === 0) {
|
||||
setBulkpost(false);
|
||||
|
|
@ -2062,7 +2062,7 @@ const ProductList = () => {
|
|||
return;
|
||||
}
|
||||
|
||||
const formattedData = FilterData.map(data => ({
|
||||
const formattedData = FilterData.map((data) => ({
|
||||
AppId: getSession('AppId'),
|
||||
CompId: getSession('CompId'),
|
||||
BranchId: getSession('BranchId'),
|
||||
|
|
@ -2139,7 +2139,7 @@ const ProductList = () => {
|
|||
BranchId,
|
||||
AppId,
|
||||
handleResetExcelData,
|
||||
handleCancel
|
||||
handleCancel,
|
||||
]);
|
||||
|
||||
const Unit = useMemo(
|
||||
|
|
@ -2197,10 +2197,7 @@ const ProductList = () => {
|
|||
[SupplierData]
|
||||
);
|
||||
|
||||
const TaxDatas = useMemo(
|
||||
() => TaxData?.map((e) => e.TaxIdName),
|
||||
[TaxData]
|
||||
);
|
||||
const TaxDatas = useMemo(() => TaxData?.map((e) => e.TaxIdName), [TaxData]);
|
||||
|
||||
const TaxData1 = useMemo(
|
||||
() => TaxData?.map((e) => e.TaxPercentage),
|
||||
|
|
@ -2209,9 +2206,7 @@ const ProductList = () => {
|
|||
|
||||
const combinedTaxData = useMemo(
|
||||
() =>
|
||||
TaxDatas?.map(
|
||||
(taxIdName, index) => `${taxIdName} - ${TaxData1[index]}%`
|
||||
),
|
||||
TaxDatas?.map((taxIdName, index) => `${taxIdName} - ${TaxData1[index]}%`),
|
||||
[TaxDatas, TaxData1]
|
||||
);
|
||||
const Deletebulk = async () => {
|
||||
|
|
@ -2706,7 +2701,9 @@ const ProductList = () => {
|
|||
generateQRCodeCopy={(code) =>
|
||||
generateQRCodeCopy(QrandbarcodeDatas, code)
|
||||
}
|
||||
generateCodeImage={(code, codeType = 'Q') => generateCodeImage(code, codeType, QrandbarcodeDatas)}
|
||||
generateCodeImage={(code, codeType = 'Q') =>
|
||||
generateCodeImage(code, codeType, QrandbarcodeDatas)
|
||||
}
|
||||
imageTagBarcodeAndQR={imageTagBarcodeAndQR}
|
||||
dropdownValue={dropdownValue}
|
||||
barcodeTemplateDetails={barcodeTemplateDetails}
|
||||
|
|
@ -2739,7 +2736,7 @@ const ProductList = () => {
|
|||
/>
|
||||
{Bulkpost && (
|
||||
<div className="Bulkuploadpost">
|
||||
<div align="center" class="fondLoader">
|
||||
<div align="center" className="fondLoader">
|
||||
<div className="contener_general">
|
||||
<div className="contener_mixte">
|
||||
<div className="ballcolor ball_1"> </div>
|
||||
|
|
@ -2812,7 +2809,7 @@ const ProductList = () => {
|
|||
}}
|
||||
onlineImage={onlineImage}
|
||||
ImageLink={rowRecord?.ProdLogo || null}
|
||||
updateImageUrl={imageModalRowIndex ? updateImageUrl : () => { }}
|
||||
updateImageUrl={imageModalRowIndex ? updateImageUrl : () => {}}
|
||||
handleClose={handleImageModalClose}
|
||||
/>
|
||||
<button
|
||||
|
|
|
|||
|
|
@ -1,6 +1,13 @@
|
|||
import React, { useState, useRef, useEffect, useContext, useCallback, useMemo } from 'react';
|
||||
import React, {
|
||||
useState,
|
||||
useRef,
|
||||
useEffect,
|
||||
useContext,
|
||||
useCallback,
|
||||
useMemo,
|
||||
} from 'react';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { read, utils } from 'xlsx';
|
||||
// import { read, utils } from 'xlsx';
|
||||
import ExcelJS from 'exceljs';
|
||||
import { Table, Form, Input, Tooltip } from 'antd';
|
||||
import { AiFillDelete } from 'react-icons/ai';
|
||||
|
|
@ -8,7 +15,6 @@ import { FiDelete, FiDownload } from 'react-icons/fi';
|
|||
import {
|
||||
emptyExcelData,
|
||||
excelDataSelector,
|
||||
excelFileSelector,
|
||||
excelFileErrorSelector,
|
||||
uploadExcel,
|
||||
} from '../../Features/ExcelUploadPage/ExcelUploadPage.js';
|
||||
|
|
@ -38,22 +44,6 @@ const EXCEL_COLUMNS = [
|
|||
{ header: 'Variant Name', key: 'VariantName', width: 20, field: '4' },
|
||||
];
|
||||
|
||||
const HEADER_STYLE = {
|
||||
font: { bold: true },
|
||||
border: {
|
||||
top: { style: 'thin' },
|
||||
left: { style: 'thin' },
|
||||
bottom: { style: 'thin' },
|
||||
right: { style: 'thin' },
|
||||
},
|
||||
alignment: { horizontal: 'center', vertical: 'middle' },
|
||||
};
|
||||
|
||||
const HEADER_FILL = {
|
||||
type: 'pattern',
|
||||
pattern: 'solid',
|
||||
fgColor: { argb: 'FF2929' },
|
||||
};
|
||||
|
||||
// Utility Functions
|
||||
const isValidExcelFile = (file) => ALLOWED_EXCEL_TYPES.includes(file.type);
|
||||
|
|
@ -187,14 +177,22 @@ const PurchaseExcel = ({ handleSubmit }) => {
|
|||
|
||||
const type = supplierData.Type;
|
||||
const LocationType =
|
||||
type === 'Supplier' ? 'S' : type === 'Branch' ? 'B' : type === 'WareHouse' ? 'W' : '';
|
||||
type === 'Supplier'
|
||||
? 'S'
|
||||
: type === 'Branch'
|
||||
? 'B'
|
||||
: type === 'WareHouse'
|
||||
? 'W'
|
||||
: '';
|
||||
|
||||
const response = await dispatch(
|
||||
getSupplaierIdwithTypeBasedProducts({
|
||||
CompId: LocationType === 'S' ? CompId : supplierData?.SuppCompId,
|
||||
AppId: LocationType === 'S' ? AppId : supplierData?.SuppAppId,
|
||||
BranchId: LocationType === 'S' ? BranchId : supplierData?.SuppBranchId,
|
||||
SuppId: LocationType === 'S' ? supplierId : supplierData?.SuppBranchId,
|
||||
BranchId:
|
||||
LocationType === 'S' ? BranchId : supplierData?.SuppBranchId,
|
||||
SuppId:
|
||||
LocationType === 'S' ? supplierId : supplierData?.SuppBranchId,
|
||||
LocationType,
|
||||
})
|
||||
)?.unwrap();
|
||||
|
|
@ -205,17 +203,17 @@ const PurchaseExcel = ({ handleSubmit }) => {
|
|||
} else {
|
||||
setSupplierProducts([]);
|
||||
setMessageData('No products are mapped to this supplier.');
|
||||
setMessageType('warning')
|
||||
setMessageType('warning');
|
||||
}
|
||||
} else {
|
||||
setSupplierProducts([]);
|
||||
setMessageData('Failed to fetch supplier products.');
|
||||
setMessageType('error')
|
||||
setMessageType('error');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error fetching supplier products:', error);
|
||||
setMessageData('An error occurred while fetching products.');
|
||||
setMessageType('error')
|
||||
setMessageType('error');
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -227,23 +225,55 @@ const PurchaseExcel = ({ handleSubmit }) => {
|
|||
}, [data, handleSubmit]);
|
||||
|
||||
// File upload handler
|
||||
// const uploadAndProcessExcel = useCallback(
|
||||
// (file) => {
|
||||
// const reader = new FileReader();
|
||||
|
||||
// reader.onload = (e) => {
|
||||
// const arrayBuffer = new Uint8Array(e.target.result);
|
||||
// const workbook = read(arrayBuffer, { 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 !== ''));
|
||||
|
||||
// dispatch(uploadExcel({ file, jsonData: nonEmptyRows }));
|
||||
// };
|
||||
|
||||
// reader.readAsArrayBuffer(file);
|
||||
// },
|
||||
// [dispatch]
|
||||
// );
|
||||
|
||||
const uploadAndProcessExcel = useCallback(
|
||||
(file) => {
|
||||
const reader = new FileReader();
|
||||
|
||||
reader.onload = (e) => {
|
||||
const arrayBuffer = new Uint8Array(e.target.result);
|
||||
const workbook = read(arrayBuffer, { type: 'array' });
|
||||
const worksheet = workbook.Sheets[workbook.SheetNames[0]];
|
||||
reader.onload = async (e) => {
|
||||
const buffer = e.target.result;
|
||||
|
||||
const jsonData = utils.sheet_to_json(worksheet, {
|
||||
header: 1,
|
||||
raw: false,
|
||||
dateNF: 'DD/MM/YY',
|
||||
const workbook = new ExcelJS.Workbook();
|
||||
await workbook.xlsx.load(buffer);
|
||||
|
||||
const worksheet = workbook.worksheets[0];
|
||||
|
||||
// 🔥 same structure as sheet_to_json({ header: 1 })
|
||||
const jsonData = [];
|
||||
worksheet.eachRow((row) => {
|
||||
const rowValues = row.values.slice(1); // remove first empty index
|
||||
jsonData.push(rowValues);
|
||||
});
|
||||
|
||||
const nonEmptyRows = jsonData.filter((row) => row.some((cell) => cell !== ''));
|
||||
const nonEmptyRows = jsonData.filter((row) =>
|
||||
row.some((cell) => cell !== '' && cell !== null && cell !== undefined)
|
||||
);
|
||||
|
||||
// ✅ unchanged
|
||||
dispatch(uploadExcel({ file, jsonData: nonEmptyRows }));
|
||||
};
|
||||
|
||||
|
|
@ -263,7 +293,7 @@ const PurchaseExcel = ({ handleSubmit }) => {
|
|||
|
||||
if (!isValidExcelFile(file)) {
|
||||
setMessageData('Only Excel files (.xls, .xlsx) are allowed.');
|
||||
setMessageType('error')
|
||||
setMessageType('error');
|
||||
handleCancelData();
|
||||
return;
|
||||
}
|
||||
|
|
@ -290,16 +320,20 @@ const PurchaseExcel = ({ handleSubmit }) => {
|
|||
|
||||
// Download Excel template with supplier-specific products and dynamic variants
|
||||
const handleDownload = useCallback(async () => {
|
||||
if (!supplierProducts || supplierProducts.length === 0 || !selectedSupplierData) {
|
||||
if (
|
||||
!supplierProducts ||
|
||||
supplierProducts.length === 0 ||
|
||||
!selectedSupplierData
|
||||
) {
|
||||
setMessageData('No products available to generate Excel.');
|
||||
setMessageType('warning')
|
||||
setMessageType('warning');
|
||||
return;
|
||||
}
|
||||
|
||||
const workbook = new ExcelJS.Workbook();
|
||||
const worksheet = workbook.addWorksheet("Sheet1");
|
||||
const hiddenSheet = workbook.addWorksheet("ProductVariantMap");
|
||||
const validationSheet = workbook.addWorksheet("ValidationHelper");
|
||||
const worksheet = workbook.addWorksheet('Sheet1');
|
||||
const hiddenSheet = workbook.addWorksheet('ProductVariantMap');
|
||||
const validationSheet = workbook.addWorksheet('ValidationHelper');
|
||||
|
||||
// __define-ocg__ - defining column headers for Excel generation
|
||||
worksheet.columns = EXCEL_COLUMNS.map(({ header, key, width }) => ({
|
||||
|
|
@ -313,11 +347,11 @@ const PurchaseExcel = ({ handleSubmit }) => {
|
|||
cell.fill = {
|
||||
type: 'pattern',
|
||||
pattern: 'solid',
|
||||
fgColor: { argb: 'FF2929' } // Red background
|
||||
fgColor: { argb: 'FF2929' }, // Red background
|
||||
};
|
||||
cell.font = {
|
||||
bold: true,
|
||||
color: { argb: '000000' } // White text
|
||||
color: { argb: '000000' }, // White text
|
||||
};
|
||||
cell.border = {
|
||||
top: { style: 'thin' },
|
||||
|
|
@ -327,14 +361,14 @@ const PurchaseExcel = ({ handleSubmit }) => {
|
|||
};
|
||||
cell.alignment = {
|
||||
horizontal: 'center',
|
||||
vertical: 'middle'
|
||||
vertical: 'middle',
|
||||
};
|
||||
});
|
||||
|
||||
hiddenSheet.addRow(["ProductName", "VariantName"]);
|
||||
hiddenSheet.addRow(['ProductName', 'VariantName']);
|
||||
|
||||
// Add validation helper columns
|
||||
validationSheet.addRow(["IsDuplicate"]);
|
||||
validationSheet.addRow(['IsDuplicate']);
|
||||
|
||||
const supplierDisplayName = getSupplierDisplayName(selectedSupplierData);
|
||||
const productNames = supplierProducts.map((p) => p.ProdName);
|
||||
|
|
@ -347,7 +381,7 @@ const PurchaseExcel = ({ handleSubmit }) => {
|
|||
const uniqueVariantNames = [
|
||||
...new Set(
|
||||
(product.ProdVariantPriceDetails || [])
|
||||
.filter((v) => v.ActiveStatus === "A")
|
||||
.filter((v) => v.ActiveStatus === 'A')
|
||||
.map((v) => v.ProdVariantName)
|
||||
),
|
||||
];
|
||||
|
|
@ -362,10 +396,9 @@ const PurchaseExcel = ({ handleSubmit }) => {
|
|||
|
||||
const endRow = currentRow - 1;
|
||||
|
||||
const safeName = product.ProdName
|
||||
.trim()
|
||||
.replace(/[^A-Za-z0-9_]/g, "_")
|
||||
.replace(/^(\d)/, "_$1");
|
||||
const safeName = product.ProdName.trim()
|
||||
.replace(/[^A-Za-z0-9_]/g, '_')
|
||||
.replace(/^(\d)/, '_$1');
|
||||
|
||||
const rangeRef = `ProductVariantMap!$B$${startRow}:$B$${endRow}`;
|
||||
productRanges[safeName] = rangeRef;
|
||||
|
|
@ -378,21 +411,23 @@ const PurchaseExcel = ({ handleSubmit }) => {
|
|||
for (let i = 0; i < 50; i++) worksheet.addRow({});
|
||||
|
||||
// Find ProductName column index
|
||||
const productNameColIndex = EXCEL_COLUMNS.findIndex(col => col.key === "ProductName");
|
||||
const productNameColIndex = EXCEL_COLUMNS.findIndex(
|
||||
(col) => col.key === 'ProductName'
|
||||
);
|
||||
const productNameColLetter = String.fromCharCode(65 + productNameColIndex);
|
||||
|
||||
// Step 3: Add a helper column for duplicate detection (will be hidden)
|
||||
const helperColIndex = EXCEL_COLUMNS.length;
|
||||
const helperColLetter = String.fromCharCode(65 + helperColIndex);
|
||||
|
||||
worksheet.getColumn(helperColLetter).header = "DuplicateCheck";
|
||||
worksheet.getColumn(helperColLetter).header = 'DuplicateCheck';
|
||||
worksheet.getColumn(helperColLetter).width = 5;
|
||||
|
||||
// Add formula to detect duplicates in helper column
|
||||
for (let rowNumber = 2; rowNumber <= 51; rowNumber++) {
|
||||
const helperCell = worksheet.getCell(`${helperColLetter}${rowNumber}`);
|
||||
helperCell.value = {
|
||||
formula: `IF(${productNameColLetter}${rowNumber}="","",COUNTIF($${productNameColLetter}$2:$${productNameColLetter}$51,${productNameColLetter}${rowNumber})>1)`
|
||||
formula: `IF(${productNameColLetter}${rowNumber}="","",COUNTIF($${productNameColLetter}$2:$${productNameColLetter}$51,${productNameColLetter}${rowNumber})>1)`,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -400,22 +435,24 @@ const PurchaseExcel = ({ handleSubmit }) => {
|
|||
EXCEL_COLUMNS.forEach(({ header, key }, index) => {
|
||||
const columnLetter = String.fromCharCode(65 + index);
|
||||
|
||||
worksheet.getColumn(columnLetter).eachCell({ includeEmpty: true }, (cell, rowNumber) => {
|
||||
worksheet
|
||||
.getColumn(columnLetter)
|
||||
.eachCell({ includeEmpty: true }, (cell, rowNumber) => {
|
||||
if (rowNumber <= 1) return;
|
||||
|
||||
if (key === "Supplier") {
|
||||
if (key === 'Supplier') {
|
||||
if (rowNumber === 2) cell.value = supplierDisplayName;
|
||||
cell.dataValidation = {
|
||||
type: "list",
|
||||
type: 'list',
|
||||
allowBlank: false,
|
||||
formulae: [`"${supplierDisplayName}"`],
|
||||
};
|
||||
} else if (key === "ProductName") {
|
||||
} else if (key === 'ProductName') {
|
||||
// Keep the dropdown list
|
||||
cell.dataValidation = {
|
||||
type: "list",
|
||||
type: 'list',
|
||||
allowBlank: true,
|
||||
formulae: [`"${productNames.join(",")}"`],
|
||||
formulae: [`"${productNames.join(',')}"`],
|
||||
};
|
||||
|
||||
// Add conditional formatting to highlight duplicates
|
||||
|
|
@ -429,47 +466,48 @@ const PurchaseExcel = ({ handleSubmit }) => {
|
|||
fill: {
|
||||
type: 'pattern',
|
||||
pattern: 'solid',
|
||||
bgColor: { argb: 'FFFF6B6B' }
|
||||
bgColor: { argb: 'FFFF6B6B' },
|
||||
},
|
||||
font: {
|
||||
color: { argb: 'FFFFFFFF' },
|
||||
bold: true
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
bold: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
// Add a warning note
|
||||
cell.note = "⚠️ WARNING: Each product should only be selected once. Duplicates will be highlighted in red.";
|
||||
|
||||
} else if (key === "VariantName") {
|
||||
cell.note =
|
||||
'⚠️ WARNING: Each product should only be selected once. Duplicates will be highlighted in red.';
|
||||
} else if (key === 'VariantName') {
|
||||
const productCell = `${productNameColLetter}${rowNumber}`;
|
||||
const formula = `INDIRECT(SUBSTITUTE(SUBSTITUTE(SUBSTITUTE(SUBSTITUTE(SUBSTITUTE(${productCell}," ","_"),"-","_"),"&","_"),"(","_"),")","_"))`;
|
||||
|
||||
cell.dataValidation = {
|
||||
type: "list",
|
||||
type: 'list',
|
||||
allowBlank: true,
|
||||
formulae: [formula],
|
||||
};
|
||||
cell.note = "Select product first; variants depend on chosen product.";
|
||||
} else if (key === "Quantity") {
|
||||
cell.note =
|
||||
'Select product first; variants depend on chosen product.';
|
||||
} else if (key === 'Quantity') {
|
||||
cell.dataValidation = {
|
||||
type: "custom",
|
||||
type: 'custom',
|
||||
allowBlank: true,
|
||||
formulae: [`ISNUMBER(${columnLetter}${rowNumber})`],
|
||||
showErrorMessage: true,
|
||||
errorTitle: "Validation Error",
|
||||
error: "Enter a valid number.",
|
||||
errorTitle: 'Validation Error',
|
||||
error: 'Enter a valid number.',
|
||||
};
|
||||
} else if (key === "QrCode") {
|
||||
} else if (key === 'QrCode') {
|
||||
cell.dataValidation = {
|
||||
type: "custom",
|
||||
type: 'custom',
|
||||
allowBlank: false,
|
||||
formulae: [`ISNUMBER(${columnLetter}${rowNumber})`],
|
||||
showErrorMessage: true,
|
||||
errorTitle: "Validation Error",
|
||||
error: "Enter a valid number.",
|
||||
errorTitle: 'Validation Error',
|
||||
error: 'Enter a valid number.',
|
||||
};
|
||||
}
|
||||
});
|
||||
|
|
@ -479,17 +517,17 @@ const PurchaseExcel = ({ handleSubmit }) => {
|
|||
worksheet.getColumn(helperColLetter).hidden = true;
|
||||
|
||||
// Step 6: Hide the mapping sheets
|
||||
hiddenSheet.state = "veryHidden";
|
||||
validationSheet.state = "veryHidden";
|
||||
hiddenSheet.state = 'veryHidden';
|
||||
validationSheet.state = 'veryHidden';
|
||||
|
||||
// Step 7: Export file
|
||||
const buffer = await workbook.xlsx.writeBuffer();
|
||||
const blob = new Blob([buffer], {
|
||||
type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
||||
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
||||
});
|
||||
|
||||
const filename = `PurchaseOrder_${supplierDisplayName}.xlsx`;
|
||||
const link = document.createElement("a");
|
||||
const link = document.createElement('a');
|
||||
link.href = URL.createObjectURL(blob);
|
||||
link.download = filename;
|
||||
link.click();
|
||||
|
|
@ -499,7 +537,6 @@ const PurchaseExcel = ({ handleSubmit }) => {
|
|||
handleModalCancel();
|
||||
}, [supplierProducts, selectedSupplierData, getSupplierDisplayName]);
|
||||
|
||||
|
||||
const handleCancelData = useCallback(() => {
|
||||
dispatch(emptyExcelData());
|
||||
if (fileInputRef.current) {
|
||||
|
|
@ -601,7 +638,7 @@ const PurchaseExcel = ({ handleSubmit }) => {
|
|||
} catch (error) {
|
||||
console.error('Error fetching suppliers:', error);
|
||||
setMessageData('Failed to fetch suppliers.');
|
||||
setMessageType('error')
|
||||
setMessageType('error');
|
||||
}
|
||||
}, [CompId, AppId, BranchId, dispatch]);
|
||||
|
||||
|
|
@ -620,7 +657,7 @@ const PurchaseExcel = ({ handleSubmit }) => {
|
|||
const handleSupplierBasedProducts = useCallback(async () => {
|
||||
if (!selectedSupplier) {
|
||||
setMessageData('Please select a supplier.');
|
||||
setMessageType('warning')
|
||||
setMessageType('warning');
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -629,7 +666,14 @@ const PurchaseExcel = ({ handleSubmit }) => {
|
|||
|
||||
return (
|
||||
<div className="container">
|
||||
<Messages messageType={messageType} messageData={messageData} onComplete={() => { setMessageData(null); setMessageType(null); }} />
|
||||
<Messages
|
||||
messageType={messageType}
|
||||
messageData={messageData}
|
||||
onComplete={() => {
|
||||
setMessageData(null);
|
||||
setMessageType(null);
|
||||
}}
|
||||
/>
|
||||
<div className="form">
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
|
||||
<label>
|
||||
|
|
@ -698,7 +742,10 @@ const PurchaseExcel = ({ handleSubmit }) => {
|
|||
)}
|
||||
</div>
|
||||
|
||||
<div className="viewerExcelupload" style={{ maxHeight: '500px', overflow: 'auto' }}>
|
||||
<div
|
||||
className="viewerExcelupload"
|
||||
style={{ maxHeight: '500px', overflow: 'auto' }}
|
||||
>
|
||||
{hasData && (
|
||||
<Table
|
||||
columns={columns}
|
||||
|
|
|
|||
|
|
@ -15,16 +15,12 @@ import {
|
|||
PurchaseOrderMailPost,
|
||||
} from '../../Features/PurchaseOrder/PurchaseOrder';
|
||||
import { Breadcrumb } from 'antd';
|
||||
import { styled } from '@mui/system';
|
||||
|
||||
import {
|
||||
breadCrumbSelector,
|
||||
changeBreadCrumb,
|
||||
} from '../../Features/AppPage/CenterPage';
|
||||
|
||||
const Input = styled('input')({
|
||||
display: 'none',
|
||||
});
|
||||
|
||||
const subDirectory = import.meta.env.BASE_URL;
|
||||
|
||||
const PurchaseOrderMail = () => {
|
||||
|
|
@ -599,7 +595,7 @@ const PurchaseOrderMail = () => {
|
|||
]}
|
||||
>
|
||||
<InputField
|
||||
label={<label class="required">Receiver Name</label>}
|
||||
label={<label className="required">Receiver Name</label>}
|
||||
autoComplete="off"
|
||||
onChange={(e) => {
|
||||
setdeliveryName(e?.target?.value);
|
||||
|
|
@ -631,7 +627,7 @@ const PurchaseOrderMail = () => {
|
|||
]}
|
||||
>
|
||||
<InputField
|
||||
label={<label class="required">Mobile Number</label>}
|
||||
label={<label className="required">Mobile Number</label>}
|
||||
autoComplete="off"
|
||||
maxLength={10}
|
||||
onChange={(e) => {
|
||||
|
|
@ -661,7 +657,7 @@ const PurchaseOrderMail = () => {
|
|||
]}
|
||||
>
|
||||
<InputField
|
||||
label={<label class="required">Shipping Address</label>}
|
||||
label={<label className="required">Shipping Address</label>}
|
||||
autoComplete="off"
|
||||
onChange={(e) => {
|
||||
setdeliveryAddress(e?.target?.value);
|
||||
|
|
@ -688,7 +684,7 @@ const PurchaseOrderMail = () => {
|
|||
]}
|
||||
>
|
||||
<InputField
|
||||
label={<label class="required">City / Town</label>}
|
||||
label={<label className="required">City / Town</label>}
|
||||
autoComplete="off"
|
||||
onChange={(e) => {
|
||||
setdeliverycity(e?.target?.value);
|
||||
|
|
@ -715,7 +711,7 @@ const PurchaseOrderMail = () => {
|
|||
]}
|
||||
>
|
||||
<InputField
|
||||
label={<label class="required">State </label>}
|
||||
label={<label className="required">State </label>}
|
||||
autoComplete="off"
|
||||
onChange={(e) => {
|
||||
setdeliveryState(e?.target?.value);
|
||||
|
|
@ -745,7 +741,7 @@ const PurchaseOrderMail = () => {
|
|||
]}
|
||||
>
|
||||
<InputField
|
||||
label={<label class="required">PIN Code</label>}
|
||||
label={<label className="required">PIN Code</label>}
|
||||
autoComplete="off"
|
||||
onChange={(e) => {
|
||||
setdeliveryZip(e?.target?.value);
|
||||
|
|
|
|||
|
|
@ -6,9 +6,9 @@ import React, {
|
|||
useCallback,
|
||||
} from 'react';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { read, utils } from 'xlsx';
|
||||
// import { read, utils } from 'xlsx';
|
||||
import ExcelJS from 'exceljs';
|
||||
import { Table, Form, Input, Button, Tooltip, Modal } from 'antd';
|
||||
import { Table, Form, Input, Tooltip, Modal } from 'antd';
|
||||
import {
|
||||
emptyExcelData,
|
||||
excelDataSelector,
|
||||
|
|
@ -272,28 +272,36 @@ const StockExcel = ({
|
|||
const uploadAndProcessExcel = (file) => {
|
||||
const reader = new FileReader();
|
||||
|
||||
reader.onload = (e) => {
|
||||
const data = new Uint8Array(e.target.result);
|
||||
reader.onload = async (e) => {
|
||||
const buffer = e.target.result;
|
||||
|
||||
const workbook = read(data, { type: 'array' });
|
||||
const worksheet = workbook.Sheets[workbook.SheetNames[0]];
|
||||
const workbook = new ExcelJS.Workbook();
|
||||
await workbook.xlsx.load(buffer);
|
||||
|
||||
const jsonData = utils.sheet_to_json(worksheet, {
|
||||
header: 1,
|
||||
raw: false,
|
||||
dateNF: 'DD/MM/YY',
|
||||
const worksheet = workbook.worksheets[0];
|
||||
|
||||
// 🔥 same structure as xlsx (array of arrays)
|
||||
const jsonData = [];
|
||||
worksheet.eachRow((row) => {
|
||||
const rowValues = row.values.slice(1); // remove empty index
|
||||
jsonData.push(rowValues);
|
||||
});
|
||||
|
||||
const nonEmptyRows = jsonData.filter((row) =>
|
||||
row.some((cell) => cell !== '')
|
||||
row.some((cell) => cell !== '' && cell !== null && cell !== undefined)
|
||||
);
|
||||
|
||||
const header = nonEmptyRows[0];
|
||||
const rows = nonEmptyRows.slice(1);
|
||||
|
||||
const dateFields = ['Manufacture Date', 'Expire Date', 'Stock Date'];
|
||||
|
||||
function convertToDisplayFormat(dateString) {
|
||||
if (!dateString) return '';
|
||||
|
||||
const date = new Date(dateString);
|
||||
if (isNaN(date)) return dateString;
|
||||
|
||||
const day = date.getDate().toString().padStart(2, '0');
|
||||
const month = (date.getMonth() + 1).toString().padStart(2, '0');
|
||||
const year = date.getFullYear().toString();
|
||||
|
|
@ -307,13 +315,12 @@ const StockExcel = ({
|
|||
} else {
|
||||
acc[col] = row[columnIndex];
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
return rowData;
|
||||
});
|
||||
|
||||
// ✅ unchanged logic
|
||||
dispatch(uploadExcel({ file, jsonData: nonEmptyRows }));
|
||||
setWorksheet1(worksheet1);
|
||||
setDatas(formattedData);
|
||||
|
|
@ -322,6 +329,59 @@ const StockExcel = ({
|
|||
reader.readAsArrayBuffer(file);
|
||||
};
|
||||
|
||||
// const uploadAndProcessExcel = (file) => {
|
||||
// const reader = new FileReader();
|
||||
|
||||
// reader.onload = (e) => {
|
||||
// const data = new Uint8Array(e.target.result);
|
||||
|
||||
// const workbook = read(data, { type: 'array' });
|
||||
// const worksheet = workbook.Sheets[workbook.SheetNames[0]];
|
||||
|
||||
// const jsonData = utils.sheet_to_json(worksheet, {
|
||||
// header: 1,
|
||||
// raw: false,
|
||||
// dateNF: 'DD/MM/YY',
|
||||
// });
|
||||
|
||||
// const nonEmptyRows = jsonData.filter((row) =>
|
||||
// row.some((cell) => cell !== '')
|
||||
// );
|
||||
// const header = nonEmptyRows[0];
|
||||
// const rows = nonEmptyRows.slice(1);
|
||||
|
||||
// const dateFields = ['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;
|
||||
// });
|
||||
|
||||
// dispatch(uploadExcel({ file, jsonData: nonEmptyRows }));
|
||||
// setWorksheet1(worksheet1);
|
||||
// setDatas(formattedData);
|
||||
// };
|
||||
|
||||
// reader.readAsArrayBuffer(file);
|
||||
// };
|
||||
|
||||
// ...existing code...
|
||||
const handleDownload = async () => {
|
||||
try {
|
||||
|
|
@ -651,7 +711,7 @@ const StockExcel = ({
|
|||
cell.dataValidation = {
|
||||
type: 'list',
|
||||
formulae: ['"Own,With Paid"'],
|
||||
allowBlank: true
|
||||
allowBlank: true,
|
||||
};
|
||||
|
||||
if (!cell.value) {
|
||||
|
|
@ -1239,7 +1299,7 @@ const StockExcel = ({
|
|||
...record,
|
||||
...values,
|
||||
});
|
||||
} catch (errInfo) { }
|
||||
} catch (errInfo) {}
|
||||
};
|
||||
|
||||
let childNode = children;
|
||||
|
|
|
|||
|
|
@ -48,9 +48,8 @@ import {
|
|||
} from '../../Features/ExcelUploadPage/ExcelUploadPage.js';
|
||||
const subDirectory = import.meta.env.BASE_URL;
|
||||
import ExcelJS from 'exceljs';
|
||||
import { read, utils } from 'xlsx';
|
||||
// import { read, utils } from 'xlsx';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { InputField } from '../../Components/Forms/InputField.jsx';
|
||||
import { MdOutlineAppRegistration } from 'react-icons/md';
|
||||
import {
|
||||
getFieldSetupData,
|
||||
|
|
@ -737,7 +736,12 @@ const StockPriceUpdate = () => {
|
|||
fetchData();
|
||||
}, []);
|
||||
const getPreference = async () => {
|
||||
const data = { AppId: AppId, CompId: CompId, BranchId: BranchId,UserId: UserId };
|
||||
const data = {
|
||||
AppId: AppId,
|
||||
CompId: CompId,
|
||||
BranchId: BranchId,
|
||||
UserId: UserId,
|
||||
};
|
||||
const { data: res } = await dispatch(getPreferenceData(data)).unwrap();
|
||||
const decimalSetting = res?.data?.[0]?.SettingDtlDetails?.find(
|
||||
(setting) =>
|
||||
|
|
@ -1820,15 +1824,17 @@ const StockPriceUpdate = () => {
|
|||
|
||||
excelDataRef.current?.setFieldsValue(formValues);
|
||||
};
|
||||
const extractPriceChangeDataFromExcel = async (arrayBuffer) => {
|
||||
const workbook = new ExcelJS.Workbook();
|
||||
await workbook.xlsx.load(arrayBuffer);
|
||||
|
||||
const extractPriceChangeDataFromExcel = (arrayBuffer) => {
|
||||
const workbook = read(arrayBuffer, { type: 'array' });
|
||||
const worksheet = workbook.Sheets[workbook.SheetNames[0]];
|
||||
const worksheet = workbook.worksheets[0];
|
||||
|
||||
const rows = utils.sheet_to_json(worksheet, {
|
||||
header: 1,
|
||||
raw: false,
|
||||
defval: '',
|
||||
// 🔥 same structure as xlsx (array of arrays)
|
||||
const rows = [];
|
||||
worksheet.eachRow((row) => {
|
||||
const rowValues = row.values.slice(1); // remove empty index
|
||||
rows.push(rowValues.map((cell) => cell ?? '')); // match defval: ''
|
||||
});
|
||||
|
||||
if (!rows.length) return [];
|
||||
|
|
@ -1901,7 +1907,7 @@ const StockPriceUpdate = () => {
|
|||
? Number(getVal(row, 'Old Wholesale Price'))
|
||||
: null,
|
||||
|
||||
// 🔹 New prices (normalized)
|
||||
// 🔹 New prices
|
||||
NewMRP: newMRP,
|
||||
NewSellPrice: newSP,
|
||||
NewWholeSalePrice: newWholesale,
|
||||
|
|
@ -1911,6 +1917,96 @@ const StockPriceUpdate = () => {
|
|||
|
||||
return result;
|
||||
};
|
||||
// const extractPriceChangeDataFromExcel = (arrayBuffer) => {
|
||||
// const workbook = read(arrayBuffer, { type: 'array' });
|
||||
// const worksheet = workbook.Sheets[workbook.SheetNames[0]];
|
||||
|
||||
// const rows = utils.sheet_to_json(worksheet, {
|
||||
// header: 1,
|
||||
// raw: false,
|
||||
// defval: '',
|
||||
// });
|
||||
|
||||
// if (!rows.length) return [];
|
||||
|
||||
// const header = rows[0];
|
||||
// setHeaders(header);
|
||||
// const dataRows = rows.slice(1);
|
||||
|
||||
// // Helper to safely get column value
|
||||
// const getVal = (row, colName) => {
|
||||
// const idx = header.indexOf(colName);
|
||||
// return idx !== -1 ? row[idx] : '';
|
||||
// };
|
||||
|
||||
// const result = dataRows
|
||||
// .map((row) => {
|
||||
// const prodId = getVal(row, 'ProdId')?.toString().trim();
|
||||
// if (!prodId) return null;
|
||||
|
||||
// const newMRP =
|
||||
// getVal(row, 'New MRP') !== '' ? Number(getVal(row, 'New MRP')) : null;
|
||||
|
||||
// const newSP =
|
||||
// getVal(row, 'New SP') !== '' ? Number(getVal(row, 'New SP')) : null;
|
||||
|
||||
// const newWholesale =
|
||||
// getVal(row, 'New Wholesale Price') !== ''
|
||||
// ? Number(getVal(row, 'New Wholesale Price'))
|
||||
// : null;
|
||||
|
||||
// // ❌ Must have at least one change
|
||||
// if (newMRP === null && newSP === null && newWholesale === null) {
|
||||
// return null;
|
||||
// }
|
||||
|
||||
// return {
|
||||
// // 🔹 Product identity
|
||||
// localId: uuidv4(),
|
||||
// ProdId: prodId,
|
||||
// InwardDtlId: getVal(row, 'InwardDtlId')
|
||||
// ? getVal(row, 'InwardDtlId').toString()
|
||||
// : null,
|
||||
|
||||
// // 🔹 Product info
|
||||
// VariantName: getVal(row, 'Variant Name') || null,
|
||||
// BatchRef: getVal(row, 'Batch No') || null,
|
||||
// ProductName: getVal(row, 'Product Name') || null,
|
||||
// Size: getVal(row, 'Size') || null,
|
||||
// UOMName: getVal(row, 'UOMName') || null,
|
||||
// StockMaintainence: getVal(row, 'Stock Maintainence') || null,
|
||||
|
||||
// // 🔹 Stock / dates
|
||||
// StockDate: getVal(row, 'Stock Date') || null,
|
||||
// ExpDate: getVal(row, 'Exp Date') || null,
|
||||
// downloadType: getVal(row, 'downloadType') || null,
|
||||
// Stock:
|
||||
// getVal(row, 'Stock') !== '' ? Number(getVal(row, 'Stock')) : null,
|
||||
|
||||
// // 🔹 Old prices
|
||||
// OldMRP:
|
||||
// getVal(row, 'Old MRP') !== ''
|
||||
// ? Number(getVal(row, 'Old MRP'))
|
||||
// : null,
|
||||
|
||||
// OldSP:
|
||||
// getVal(row, 'Old SP') !== '' ? Number(getVal(row, 'Old SP')) : null,
|
||||
|
||||
// OldWholeSalePrice:
|
||||
// getVal(row, 'Old Wholesale Price') !== ''
|
||||
// ? Number(getVal(row, 'Old Wholesale Price'))
|
||||
// : null,
|
||||
|
||||
// // 🔹 New prices (normalized)
|
||||
// NewMRP: newMRP,
|
||||
// NewSellPrice: newSP,
|
||||
// NewWholeSalePrice: newWholesale,
|
||||
// };
|
||||
// })
|
||||
// .filter(Boolean);
|
||||
|
||||
// return result;
|
||||
// };
|
||||
|
||||
const handleCancelData = () => {
|
||||
dispatch(emptyExcelData());
|
||||
|
|
|
|||
|
|
@ -1,28 +1,28 @@
|
|||
import React, { useCallback, useMemo, useRef, useState } from "react";
|
||||
import { read, utils } from "xlsx";
|
||||
import ExcelJS from "exceljs";
|
||||
import { Button } from "antd";
|
||||
import { MdRefresh, MdInfo } from "react-icons/md";
|
||||
import { RiFileExcel2Fill } from "react-icons/ri";
|
||||
import { Messages } from "../../Components/Notifications/Messages.jsx";
|
||||
import { getSession } from "../../Services/Others.js";
|
||||
import "./OpeningStockExcel.scss"
|
||||
import { useCallback, useMemo, useRef, useState } from 'react';
|
||||
// import { read, utils } from "xlsx";
|
||||
import ExcelJS from 'exceljs';
|
||||
import { Button } from 'antd';
|
||||
import { MdRefresh, MdInfo } from 'react-icons/md';
|
||||
import { RiFileExcel2Fill } from 'react-icons/ri';
|
||||
import { Messages } from '../../Components/Notifications/Messages.jsx';
|
||||
import { getSession } from '../../Services/Others.js';
|
||||
import './OpeningStockExcel.scss';
|
||||
|
||||
const TEMPLATE_HEADERS = [
|
||||
{ key: "ProdName", header: "Product Name" },
|
||||
{ key: "TotalQty", header: "Total Qty" },
|
||||
{ key: "TotalPiece", header: "Total Pieces" },
|
||||
{ key: 'ProdName', header: 'Product Name' },
|
||||
{ key: 'TotalQty', header: 'Total Qty' },
|
||||
{ key: 'TotalPiece', header: 'Total Pieces' },
|
||||
];
|
||||
|
||||
const OpeningStockExcel = ({ products = [], onApply, onCancel }) => {
|
||||
const UserId = getSession("UserId");
|
||||
const UserId = getSession('UserId');
|
||||
const fileInputRef = useRef(null);
|
||||
const [previewRows, setPreviewRows] = useState([]);
|
||||
const [messageType, setMessageType] = useState(null);
|
||||
const [messageData, setMessageData] = useState(null);
|
||||
const [isParsing, setIsParsing] = useState(false);
|
||||
const [inst, setInst] = useState(false)
|
||||
console.log(previewRows, "previewRows")
|
||||
const [inst, setInst] = useState(false);
|
||||
console.log(previewRows, 'previewRows');
|
||||
const onComplete = useCallback(() => {
|
||||
setMessageData(null);
|
||||
setMessageType(null);
|
||||
|
|
@ -31,9 +31,9 @@ const OpeningStockExcel = ({ products = [], onApply, onCancel }) => {
|
|||
const templateColumns = useMemo(
|
||||
() => [
|
||||
{
|
||||
title: "Product Name",
|
||||
dataIndex: "productDisplayName",
|
||||
key: "productDisplayName",
|
||||
title: 'Product Name',
|
||||
dataIndex: 'productDisplayName',
|
||||
key: 'productDisplayName',
|
||||
width: 200,
|
||||
},
|
||||
// {
|
||||
|
|
@ -43,36 +43,35 @@ const OpeningStockExcel = ({ products = [], onApply, onCancel }) => {
|
|||
// width: 180,
|
||||
// },
|
||||
{
|
||||
title: "Total Qty",
|
||||
dataIndex: "TotalQty",
|
||||
key: "TotalQty",
|
||||
title: 'Total Qty',
|
||||
dataIndex: 'TotalQty',
|
||||
key: 'TotalQty',
|
||||
width: 50,
|
||||
},
|
||||
{
|
||||
title: "Total Pcs",
|
||||
dataIndex: "TotalPiece",
|
||||
key: "TotalPiece",
|
||||
title: 'Total Pcs',
|
||||
dataIndex: 'TotalPiece',
|
||||
key: 'TotalPiece',
|
||||
width: 50,
|
||||
render: (value, row) =>
|
||||
row?.OnePcsAvailable === "Y" ? value : "N/A",
|
||||
render: (value, row) => (row?.OnePcsAvailable === 'Y' ? value : 'N/A'),
|
||||
},
|
||||
],
|
||||
[]
|
||||
);
|
||||
|
||||
const handleInstrction = () => {
|
||||
setInst(!inst)
|
||||
}
|
||||
setInst(!inst);
|
||||
};
|
||||
const handleDownloadTemplate = useCallback(async () => {
|
||||
if (!products?.length) {
|
||||
setMessageType("warning");
|
||||
setMessageData("No products available to include in the template.");
|
||||
setMessageType('warning');
|
||||
setMessageData('No products available to include in the template.');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const workbook = new ExcelJS.Workbook();
|
||||
const worksheet = workbook.addWorksheet("Opening Stock");
|
||||
const worksheet = workbook.addWorksheet('Opening Stock');
|
||||
|
||||
const headerRow = worksheet.addRow(
|
||||
TEMPLATE_HEADERS.map((item) => item.header)
|
||||
|
|
@ -80,33 +79,29 @@ const OpeningStockExcel = ({ products = [], onApply, onCancel }) => {
|
|||
headerRow.eachCell((cell) => {
|
||||
cell.protection = { locked: true };
|
||||
cell.fill = {
|
||||
type: "pattern",
|
||||
pattern: "solid",
|
||||
fgColor: { argb: "FF0F6CB6" },
|
||||
type: 'pattern',
|
||||
pattern: 'solid',
|
||||
fgColor: { argb: 'FF0F6CB6' },
|
||||
};
|
||||
cell.font = {
|
||||
color: { argb: "FFFFFFFF" },
|
||||
color: { argb: 'FFFFFFFF' },
|
||||
bold: true,
|
||||
};
|
||||
cell.alignment = { vertical: "middle", horizontal: "center" };
|
||||
cell.alignment = { vertical: 'middle', horizontal: 'center' };
|
||||
cell.border = {
|
||||
top: { style: "thin", color: { argb: "FFFFFFFF" } },
|
||||
left: { style: "thin", color: { argb: "FFFFFFFF" } },
|
||||
bottom: { style: "thin", color: { argb: "FFFFFFFF" } },
|
||||
right: { style: "thin", color: { argb: "FFFFFFFF" } },
|
||||
top: { style: 'thin', color: { argb: 'FFFFFFFF' } },
|
||||
left: { style: 'thin', color: { argb: 'FFFFFFFF' } },
|
||||
bottom: { style: 'thin', color: { argb: 'FFFFFFFF' } },
|
||||
right: { style: 'thin', color: { argb: 'FFFFFFFF' } },
|
||||
};
|
||||
});
|
||||
|
||||
products.forEach((product) => {
|
||||
const displayName = product?.ProdVariantName
|
||||
? `${product?.ProdName ?? ""} (${product?.ProdVariantName ?? ""})`
|
||||
: product?.ProdName ?? "";
|
||||
? `${product?.ProdName ?? ''} (${product?.ProdVariantName ?? ''})`
|
||||
: (product?.ProdName ?? '');
|
||||
|
||||
const newRow = worksheet.addRow([
|
||||
displayName,
|
||||
"",
|
||||
"",
|
||||
]);
|
||||
const newRow = worksheet.addRow([displayName, '', '']);
|
||||
|
||||
const rowNumber = newRow.number;
|
||||
const productCell = worksheet.getCell(rowNumber, 1);
|
||||
|
|
@ -114,61 +109,61 @@ const OpeningStockExcel = ({ products = [], onApply, onCancel }) => {
|
|||
|
||||
const qtyCell = worksheet.getCell(rowNumber, 2);
|
||||
qtyCell.dataValidation = {
|
||||
type: "decimal",
|
||||
operator: "greaterThanOrEqual",
|
||||
type: 'decimal',
|
||||
operator: 'greaterThanOrEqual',
|
||||
allowBlank: true,
|
||||
showErrorMessage: true,
|
||||
showInputMessage: true,
|
||||
formulae: [0],
|
||||
errorTitle: "Invalid Quantity",
|
||||
error: "Please enter a numeric value greater than or equal to 0.",
|
||||
inputTitle: "Total Qty",
|
||||
inputMessage: "Enter the total quantity (numeric).",
|
||||
errorTitle: 'Invalid Quantity',
|
||||
error: 'Please enter a numeric value greater than or equal to 0.',
|
||||
inputTitle: 'Total Qty',
|
||||
inputMessage: 'Enter the total quantity (numeric).',
|
||||
};
|
||||
qtyCell.protection = { locked: false };
|
||||
|
||||
const pieceCell = worksheet.getCell(rowNumber, 3);
|
||||
const allowPieces = product?.OnePcsAvailable === "Y";
|
||||
const allowPieces = product?.OnePcsAvailable === 'Y';
|
||||
|
||||
if (allowPieces) {
|
||||
pieceCell.dataValidation = {
|
||||
type: "decimal",
|
||||
operator: "greaterThanOrEqual",
|
||||
type: 'decimal',
|
||||
operator: 'greaterThanOrEqual',
|
||||
allowBlank: true,
|
||||
showErrorMessage: true,
|
||||
showInputMessage: true,
|
||||
formulae: [0],
|
||||
errorTitle: "Invalid Total Pieces",
|
||||
error: "Please enter a numeric value greater than or equal to 0.",
|
||||
inputTitle: "Total Pieces",
|
||||
inputMessage: "Enter the total pieces (numeric).",
|
||||
errorTitle: 'Invalid Total Pieces',
|
||||
error: 'Please enter a numeric value greater than or equal to 0.',
|
||||
inputTitle: 'Total Pieces',
|
||||
inputMessage: 'Enter the total pieces (numeric).',
|
||||
};
|
||||
pieceCell.protection = { locked: false };
|
||||
} else {
|
||||
pieceCell.dataValidation = {
|
||||
type: "custom",
|
||||
type: 'custom',
|
||||
allowBlank: true,
|
||||
showErrorMessage: true,
|
||||
showInputMessage: true,
|
||||
formulae: [`=LEN(TRIM(C${rowNumber}))=0`],
|
||||
errorTitle: "Not Allowed",
|
||||
errorTitle: 'Not Allowed',
|
||||
error:
|
||||
"Total Pieces entry is not allowed for this product. Leave this cell empty.",
|
||||
inputTitle: "Total Pieces",
|
||||
'Total Pieces entry is not allowed for this product. Leave this cell empty.',
|
||||
inputTitle: 'Total Pieces',
|
||||
inputMessage:
|
||||
"Total Pieces is not allowed for this product and must remain empty.",
|
||||
'Total Pieces is not allowed for this product and must remain empty.',
|
||||
};
|
||||
pieceCell.fill = {
|
||||
type: "pattern",
|
||||
pattern: "solid",
|
||||
fgColor: { argb: "FFEFEFEF" },
|
||||
type: 'pattern',
|
||||
pattern: 'solid',
|
||||
fgColor: { argb: 'FFEFEFEF' },
|
||||
};
|
||||
pieceCell.font = {
|
||||
color: { argb: "FF6E6E6E" },
|
||||
color: { argb: 'FF6E6E6E' },
|
||||
italic: true,
|
||||
};
|
||||
pieceCell.note =
|
||||
"Total Pieces entry is disabled because single-piece handling is not available for this product.";
|
||||
'Total Pieces entry is disabled because single-piece handling is not available for this product.';
|
||||
pieceCell.protection = { locked: true };
|
||||
}
|
||||
});
|
||||
|
|
@ -185,7 +180,7 @@ const OpeningStockExcel = ({ products = [], onApply, onCancel }) => {
|
|||
});
|
||||
|
||||
try {
|
||||
await worksheet.protect("OpeningStockTemplate", {
|
||||
await worksheet.protect('OpeningStockTemplate', {
|
||||
selectLockedCells: true,
|
||||
selectUnlockedCells: true,
|
||||
formatCells: false,
|
||||
|
|
@ -197,143 +192,306 @@ const OpeningStockExcel = ({ products = [], onApply, onCancel }) => {
|
|||
deleteRows: false,
|
||||
});
|
||||
} catch (protectError) {
|
||||
console.warn("Worksheet protection not supported in this environment:", protectError);
|
||||
setMessageType("warning");
|
||||
console.warn(
|
||||
'Worksheet protection not supported in this environment:',
|
||||
protectError
|
||||
);
|
||||
setMessageType('warning');
|
||||
setMessageData(
|
||||
"Template protection is not supported in this browser. The sheet will still download without protection."
|
||||
'Template protection is not supported in this browser. The sheet will still download without protection.'
|
||||
);
|
||||
}
|
||||
|
||||
const buffer = await workbook.xlsx.writeBuffer();
|
||||
const blob = new Blob([buffer], {
|
||||
type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
||||
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
||||
});
|
||||
|
||||
const link = document.createElement("a");
|
||||
const link = document.createElement('a');
|
||||
link.href = URL.createObjectURL(blob);
|
||||
link.download = "opening_stock_template.xlsx";
|
||||
link.download = 'opening_stock_template.xlsx';
|
||||
link.click();
|
||||
URL.revokeObjectURL(link.href);
|
||||
} catch (error) {
|
||||
console.error("Template download failed:", error);
|
||||
setMessageType("error");
|
||||
setMessageData("Failed to generate the template. Please try again.");
|
||||
console.error('Template download failed:', error);
|
||||
setMessageType('error');
|
||||
setMessageData('Failed to generate the template. Please try again.');
|
||||
}
|
||||
}, [products]);
|
||||
|
||||
// const parseExcelFile = useCallback(
|
||||
// async (file) => {
|
||||
// if (!file) return;
|
||||
|
||||
// setIsParsing(true);
|
||||
// try {
|
||||
// const arrayBuffer = await file.arrayBuffer();
|
||||
// const workbook = read(arrayBuffer, { type: "array" });
|
||||
// const worksheet = workbook.Sheets[workbook.SheetNames[0]];
|
||||
// const rows = utils.sheet_to_json(worksheet, { header: 1, defval: "" });
|
||||
|
||||
// if (!rows.length) {
|
||||
// setMessageType("warning");
|
||||
// setMessageData("Uploaded file is empty.");
|
||||
// setPreviewRows([]);
|
||||
// return;
|
||||
// }
|
||||
|
||||
// const [headerRow, ...dataRows] = rows;
|
||||
// const headerMap = TEMPLATE_HEADERS.reduce(
|
||||
// (acc, item) => ({
|
||||
// ...acc,
|
||||
// [item.key]: headerRow.findIndex(
|
||||
// (header) =>
|
||||
// header?.toString().trim().toLowerCase() ===
|
||||
// item.header.toLowerCase()
|
||||
// ),
|
||||
// }),
|
||||
// {}
|
||||
// );
|
||||
|
||||
// const missingHeaders = Object.entries(headerMap)
|
||||
// .filter(([, index]) => index === -1)
|
||||
// .map(([key]) => key);
|
||||
|
||||
// if (missingHeaders.length) {
|
||||
// setMessageType("error");
|
||||
// setMessageData(
|
||||
// `Missing required column(s) in the Excel file: ${missingHeaders.join(
|
||||
// ", "
|
||||
// )}`
|
||||
// );
|
||||
// setPreviewRows([]);
|
||||
// return;
|
||||
// }
|
||||
|
||||
// const productIndex = products.reduce((acc, product) => {
|
||||
// const displayName = product?.ProdVariantName
|
||||
// ? `${product?.ProdName ?? ""} (${product?.ProdVariantName ?? ""})`
|
||||
// : product?.ProdName ?? "";
|
||||
// if (displayName) {
|
||||
// acc[displayName.trim().toLowerCase()] = product;
|
||||
// }
|
||||
// return acc;
|
||||
// }, {});
|
||||
|
||||
// const parsedRows = [];
|
||||
// const issues = [];
|
||||
|
||||
// dataRows.forEach((row, rowIndex) => {
|
||||
// const prodNameCell = row[headerMap.ProdName];
|
||||
// const qtyCell = row[headerMap.TotalQty];
|
||||
// const pieceCell = row[headerMap.TotalPiece];
|
||||
|
||||
// const productNameValue = prodNameCell?.toString().trim();
|
||||
// if (!productNameValue) {
|
||||
// if (qtyCell || pieceCell) {
|
||||
// issues.push(
|
||||
// `Row ${rowIndex + 2}: Product Name is empty while quantities are provided.`
|
||||
// );
|
||||
// }
|
||||
// return;
|
||||
// }
|
||||
|
||||
// const productMatch =
|
||||
// productIndex[productNameValue.trim().toLowerCase()];
|
||||
// if (!productMatch) {
|
||||
// issues.push(
|
||||
// `Row ${rowIndex + 2}: Product "${productNameValue}" is not found in available products.`
|
||||
// );
|
||||
// return;
|
||||
// }
|
||||
|
||||
// const totalQty = qtyCell?.toString().trim();
|
||||
// const totalPiece = pieceCell?.toString().trim();
|
||||
|
||||
// const hasValues =
|
||||
// (totalQty && totalQty.length > 0) ||
|
||||
// (totalPiece && totalPiece.length > 0);
|
||||
|
||||
// if (!hasValues) {
|
||||
// return;
|
||||
// }
|
||||
|
||||
// const isQtyValid =
|
||||
// !totalQty || /^[0-9]+(\.[0-9]+)?$/.test(totalQty.trim());
|
||||
// const isPieceValid =
|
||||
// !totalPiece || /^[0-9]+(\.[0-9]+)?$/.test(totalPiece.trim());
|
||||
|
||||
// if (!isQtyValid || !isPieceValid) {
|
||||
// issues.push(
|
||||
// `Row ${rowIndex + 2}: Total Qty or Total Pieces is not a valid number.`
|
||||
// );
|
||||
// return;
|
||||
// }
|
||||
|
||||
// const allowPieces = productMatch?.OnePcsAvailable === "Y";
|
||||
// let cleanedPieces = totalPiece ?? "";
|
||||
// if (!allowPieces && cleanedPieces) {
|
||||
// issues.push(
|
||||
// `Row ${rowIndex + 2}: Total Pieces is not allowed for "${productNameValue}" and the value has been ignored.`
|
||||
// );
|
||||
// cleanedPieces = "";
|
||||
// }
|
||||
|
||||
// parsedRows.push({
|
||||
// key: productMatch.ProdId,
|
||||
// ProdId: productMatch.ProdId,
|
||||
// ProdName: productMatch.ProdName,
|
||||
// ProdVariantName: productMatch.ProdVariantName,
|
||||
// productDisplayName: productNameValue,
|
||||
// TotalQty: totalQty ?? "",
|
||||
// TotalPiece: cleanedPieces,
|
||||
// OnePcsAvailable: productMatch?.OnePcsAvailable,
|
||||
// });
|
||||
// });
|
||||
|
||||
// if (!parsedRows.length) {
|
||||
// setMessageType("warning");
|
||||
// setMessageData(
|
||||
// issues.length
|
||||
// ? issues.join(" ")
|
||||
// : "No valid rows found in the uploaded file."
|
||||
// );
|
||||
// setPreviewRows([]);
|
||||
// return;
|
||||
// }
|
||||
|
||||
// if (issues.length) {
|
||||
// setMessageType("warning");
|
||||
// setMessageData(
|
||||
// `Some rows were skipped:\n${issues.join("\n")}`
|
||||
// );
|
||||
// } else {
|
||||
// setMessageType("success");
|
||||
// setMessageData(
|
||||
// `Successfully parsed ${parsedRows.length} row(s) from the uploaded file.`
|
||||
// );
|
||||
// }
|
||||
|
||||
// setPreviewRows(parsedRows);
|
||||
// } catch (error) {
|
||||
// console.error("Failed to parse Excel file:", error);
|
||||
// setMessageType("error");
|
||||
// setMessageData(
|
||||
// "Failed to read the uploaded file. Please ensure it is a valid Excel document."
|
||||
// );
|
||||
// setPreviewRows([]);
|
||||
// } finally {
|
||||
// setIsParsing(false);
|
||||
// }
|
||||
// },
|
||||
// [products]
|
||||
// );
|
||||
|
||||
const parseExcelFile = useCallback(
|
||||
async (file) => {
|
||||
if (!file) return;
|
||||
|
||||
setIsParsing(true);
|
||||
|
||||
try {
|
||||
const workbook = new ExcelJS.Workbook();
|
||||
const arrayBuffer = await file.arrayBuffer();
|
||||
const workbook = read(arrayBuffer, { type: "array" });
|
||||
const worksheet = workbook.Sheets[workbook.SheetNames[0]];
|
||||
const rows = utils.sheet_to_json(worksheet, { header: 1, defval: "" });
|
||||
|
||||
await workbook.xlsx.load(arrayBuffer);
|
||||
|
||||
const worksheet = workbook.worksheets[0];
|
||||
if (!worksheet) throw new Error('No sheet found');
|
||||
|
||||
const rows = [];
|
||||
|
||||
worksheet.eachRow((row, rowNumber) => {
|
||||
rows.push(row.values.slice(1)); // remove first empty index
|
||||
});
|
||||
|
||||
if (!rows.length) {
|
||||
setMessageType("warning");
|
||||
setMessageData("Uploaded file is empty.");
|
||||
setMessageType('warning');
|
||||
setMessageData('Uploaded file is empty.');
|
||||
setPreviewRows([]);
|
||||
return;
|
||||
}
|
||||
|
||||
const [headerRow, ...dataRows] = rows;
|
||||
const headerMap = TEMPLATE_HEADERS.reduce(
|
||||
(acc, item) => ({
|
||||
...acc,
|
||||
[item.key]: headerRow.findIndex(
|
||||
(header) =>
|
||||
header?.toString().trim().toLowerCase() ===
|
||||
item.header.toLowerCase()
|
||||
),
|
||||
}),
|
||||
{}
|
||||
const [headerRow = [], ...dataRows] = rows;
|
||||
|
||||
const normalizedHeaders = headerRow.map((h) =>
|
||||
h?.toString().trim().toLowerCase()
|
||||
);
|
||||
|
||||
const headerMap = {};
|
||||
TEMPLATE_HEADERS.forEach((item) => {
|
||||
headerMap[item.key] = normalizedHeaders.indexOf(
|
||||
item.header.toLowerCase()
|
||||
);
|
||||
});
|
||||
|
||||
const missingHeaders = Object.entries(headerMap)
|
||||
.filter(([, index]) => index === -1)
|
||||
.map(([key]) => key);
|
||||
|
||||
if (missingHeaders.length) {
|
||||
setMessageType("error");
|
||||
setMessageType('error');
|
||||
setMessageData(
|
||||
`Missing required column(s) in the Excel file: ${missingHeaders.join(
|
||||
", "
|
||||
)}`
|
||||
`Missing required column(s): ${missingHeaders.join(', ')}`
|
||||
);
|
||||
setPreviewRows([]);
|
||||
return;
|
||||
}
|
||||
|
||||
const productIndex = products.reduce((acc, product) => {
|
||||
// 🔥 Product index
|
||||
const productIndex = {};
|
||||
for (const product of products) {
|
||||
const displayName = product?.ProdVariantName
|
||||
? `${product?.ProdName ?? ""} (${product?.ProdVariantName ?? ""})`
|
||||
: product?.ProdName ?? "";
|
||||
? `${product?.ProdName ?? ''} (${product?.ProdVariantName ?? ''})`
|
||||
: (product?.ProdName ?? '');
|
||||
|
||||
if (displayName) {
|
||||
acc[displayName.trim().toLowerCase()] = product;
|
||||
productIndex[displayName.trim().toLowerCase()] = product;
|
||||
}
|
||||
}
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
const parsedRows = [];
|
||||
const issues = [];
|
||||
|
||||
dataRows.forEach((row, rowIndex) => {
|
||||
const prodNameCell = row[headerMap.ProdName];
|
||||
const qtyCell = row[headerMap.TotalQty];
|
||||
const pieceCell = row[headerMap.TotalPiece];
|
||||
const rowNum = rowIndex + 2;
|
||||
|
||||
const productNameValue = row[headerMap.ProdName]?.toString().trim();
|
||||
const totalQty = row[headerMap.TotalQty]?.toString().trim();
|
||||
const totalPiece = row[headerMap.TotalPiece]?.toString().trim();
|
||||
|
||||
const productNameValue = prodNameCell?.toString().trim();
|
||||
if (!productNameValue) {
|
||||
if (qtyCell || pieceCell) {
|
||||
issues.push(
|
||||
`Row ${rowIndex + 2}: Product Name is empty while quantities are provided.`
|
||||
);
|
||||
if (totalQty || totalPiece) {
|
||||
issues.push(`Row ${rowNum}: Product Name is empty.`);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const productMatch =
|
||||
productIndex[productNameValue.trim().toLowerCase()];
|
||||
const productMatch = productIndex[productNameValue.toLowerCase()];
|
||||
|
||||
if (!productMatch) {
|
||||
issues.push(
|
||||
`Row ${rowIndex + 2}: Product "${productNameValue}" is not found in available products.`
|
||||
`Row ${rowNum}: Product "${productNameValue}" not found.`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const totalQty = qtyCell?.toString().trim();
|
||||
const totalPiece = pieceCell?.toString().trim();
|
||||
if (!totalQty && !totalPiece) return;
|
||||
|
||||
const hasValues =
|
||||
(totalQty && totalQty.length > 0) ||
|
||||
(totalPiece && totalPiece.length > 0);
|
||||
const isValidNumber = (val) =>
|
||||
!val || /^[0-9]+(\.[0-9]+)?$/.test(val);
|
||||
|
||||
if (!hasValues) {
|
||||
if (!isValidNumber(totalQty) || !isValidNumber(totalPiece)) {
|
||||
issues.push(`Row ${rowNum}: Invalid number.`);
|
||||
return;
|
||||
}
|
||||
|
||||
const isQtyValid =
|
||||
!totalQty || /^[0-9]+(\.[0-9]+)?$/.test(totalQty.trim());
|
||||
const isPieceValid =
|
||||
!totalPiece || /^[0-9]+(\.[0-9]+)?$/.test(totalPiece.trim());
|
||||
|
||||
if (!isQtyValid || !isPieceValid) {
|
||||
let cleanedPieces = totalPiece || '';
|
||||
if (productMatch?.OnePcsAvailable !== 'Y' && cleanedPieces) {
|
||||
issues.push(
|
||||
`Row ${rowIndex + 2}: Total Qty or Total Pieces is not a valid number.`
|
||||
`Row ${rowNum}: Pieces not allowed for "${productNameValue}". Ignored.`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const allowPieces = productMatch?.OnePcsAvailable === "Y";
|
||||
let cleanedPieces = totalPiece ?? "";
|
||||
if (!allowPieces && cleanedPieces) {
|
||||
issues.push(
|
||||
`Row ${rowIndex + 2}: Total Pieces is not allowed for "${productNameValue}" and the value has been ignored.`
|
||||
);
|
||||
cleanedPieces = "";
|
||||
cleanedPieces = '';
|
||||
}
|
||||
|
||||
parsedRows.push({
|
||||
|
|
@ -342,42 +500,33 @@ const OpeningStockExcel = ({ products = [], onApply, onCancel }) => {
|
|||
ProdName: productMatch.ProdName,
|
||||
ProdVariantName: productMatch.ProdVariantName,
|
||||
productDisplayName: productNameValue,
|
||||
TotalQty: totalQty ?? "",
|
||||
TotalQty: totalQty || '',
|
||||
TotalPiece: cleanedPieces,
|
||||
OnePcsAvailable: productMatch?.OnePcsAvailable,
|
||||
});
|
||||
});
|
||||
|
||||
if (!parsedRows.length) {
|
||||
setMessageType("warning");
|
||||
setMessageType('warning');
|
||||
setMessageData(
|
||||
issues.length
|
||||
? issues.join(" ")
|
||||
: "No valid rows found in the uploaded file."
|
||||
issues.length ? issues.join('\n') : 'No valid rows found.'
|
||||
);
|
||||
setPreviewRows([]);
|
||||
return;
|
||||
}
|
||||
|
||||
if (issues.length) {
|
||||
setMessageType("warning");
|
||||
setMessageType(issues.length ? 'warning' : 'success');
|
||||
setMessageData(
|
||||
`Some rows were skipped:\n${issues.join("\n")}`
|
||||
issues.length
|
||||
? `Some rows skipped:\n${issues.join('\n')}`
|
||||
: `Parsed ${parsedRows.length} row(s).`
|
||||
);
|
||||
} else {
|
||||
setMessageType("success");
|
||||
setMessageData(
|
||||
`Successfully parsed ${parsedRows.length} row(s) from the uploaded file.`
|
||||
);
|
||||
}
|
||||
|
||||
setPreviewRows(parsedRows);
|
||||
} catch (error) {
|
||||
console.error("Failed to parse Excel file:", error);
|
||||
setMessageType("error");
|
||||
setMessageData(
|
||||
"Failed to read the uploaded file. Please ensure it is a valid Excel document."
|
||||
);
|
||||
console.error('Excel parse error:', error);
|
||||
setMessageType('error');
|
||||
setMessageData('Invalid Excel file.');
|
||||
setPreviewRows([]);
|
||||
} finally {
|
||||
setIsParsing(false);
|
||||
|
|
@ -396,8 +545,8 @@ const OpeningStockExcel = ({ products = [], onApply, onCancel }) => {
|
|||
|
||||
const handleApply = useCallback(() => {
|
||||
if (!previewRows.length) {
|
||||
setMessageType("warning");
|
||||
setMessageData("No parsed rows available to apply.");
|
||||
setMessageType('warning');
|
||||
setMessageData('No parsed rows available to apply.');
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -405,17 +554,19 @@ const OpeningStockExcel = ({ products = [], onApply, onCancel }) => {
|
|||
previewRows.map((row) => ({
|
||||
ProdId: row.ProdId,
|
||||
ProdVariantName: row?.ProdVariantName,
|
||||
OnePcsAvailable: row.OnePcsAvailable || "N",
|
||||
"CreatedBy": UserId,
|
||||
OnePcsAvailable: row.OnePcsAvailable || 'N',
|
||||
CreatedBy: UserId,
|
||||
TotalQty:
|
||||
row.TotalQty !== undefined && row.TotalQty !== null
|
||||
? row.TotalQty.toString()
|
||||
: "",
|
||||
: '',
|
||||
TotalPcs:
|
||||
row.OnePcsAvailable === "Y" && row.TotalPiece !== undefined && row.TotalPiece !== null
|
||||
row.OnePcsAvailable === 'Y' &&
|
||||
row.TotalPiece !== undefined &&
|
||||
row.TotalPiece !== null
|
||||
? row.TotalPiece.toString()
|
||||
: 0,
|
||||
InwardDetails: []
|
||||
InwardDetails: [],
|
||||
}))
|
||||
);
|
||||
}, [onApply, previewRows]);
|
||||
|
|
@ -425,7 +576,7 @@ const OpeningStockExcel = ({ products = [], onApply, onCancel }) => {
|
|||
setMessageData(null);
|
||||
setMessageType(null);
|
||||
if (fileInputRef.current) {
|
||||
fileInputRef.current.value = "";
|
||||
fileInputRef.current.value = '';
|
||||
}
|
||||
}, []);
|
||||
|
||||
|
|
@ -471,7 +622,10 @@ const OpeningStockExcel = ({ products = [], onApply, onCancel }) => {
|
|||
)}
|
||||
</div>
|
||||
|
||||
<div onClick={handleInstrction} className="openingStock-excel-instruction-toggle">
|
||||
<div
|
||||
onClick={handleInstrction}
|
||||
className="openingStock-excel-instruction-toggle"
|
||||
>
|
||||
<MdInfo className="openingStock-excel-info-icon" />
|
||||
Instructions
|
||||
</div>
|
||||
|
|
@ -479,7 +633,8 @@ const OpeningStockExcel = ({ products = [], onApply, onCancel }) => {
|
|||
{inst && (
|
||||
<div className="openingStock-excel-instructions">
|
||||
<div className="openingStock-excel-instruction-item">
|
||||
1. Download the template and provide the quantities for each product.
|
||||
1. Download the template and provide the quantities for each
|
||||
product.
|
||||
</div>
|
||||
<div className="openingStock-excel-instruction-item">
|
||||
2. Only update the `Total Qty` and `Total Pieces` columns.
|
||||
|
|
@ -505,15 +660,17 @@ const OpeningStockExcel = ({ products = [], onApply, onCancel }) => {
|
|||
<tr key={row.key}>
|
||||
<td>{row.productDisplayName}</td>
|
||||
<td>{row.TotalQty}</td>
|
||||
<td>{row?.OnePcsAvailable === "Y" ? row.TotalPiece : "N/A"}</td>
|
||||
<td>
|
||||
{row?.OnePcsAvailable === 'Y' ? row.TotalPiece : 'N/A'}
|
||||
</td>
|
||||
</tr>
|
||||
))
|
||||
) : (
|
||||
<tr>
|
||||
<td colSpan="3" className="openingStock-excel-empty-state">
|
||||
{isParsing
|
||||
? "Parsing uploaded file..."
|
||||
: "No data parsed yet. Upload a filled Excel file to preview changes."}
|
||||
? 'Parsing uploaded file...'
|
||||
: 'No data parsed yet. Upload a filled Excel file to preview changes.'}
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
|
|
@ -522,7 +679,6 @@ const OpeningStockExcel = ({ products = [], onApply, onCancel }) => {
|
|||
</div>
|
||||
|
||||
<div className="openingStock-excel-footer">
|
||||
|
||||
<Button
|
||||
className="openingStock-excel-apply-btn"
|
||||
type="primary"
|
||||
|
|
@ -538,4 +694,3 @@ const OpeningStockExcel = ({ products = [], onApply, onCancel }) => {
|
|||
};
|
||||
|
||||
export default OpeningStockExcel;
|
||||
|
||||
|
|
|
|||
|
|
@ -25,7 +25,6 @@ const commonUrl = import.meta.env.ENV_COMMON_BASE_URL;
|
|||
const subDirectory = import.meta.env.BASE_URL;
|
||||
|
||||
const ProtectedRoutes = ({ routesConfig }) => {
|
||||
|
||||
useTemplate(
|
||||
getSession('CompId'),
|
||||
getSession('BranchId'),
|
||||
|
|
@ -91,7 +90,7 @@ const ProtectedRoutes = ({ routesConfig }) => {
|
|||
AppId: AppId,
|
||||
CompId: CompId,
|
||||
BranchId: BranchId,
|
||||
UserId:UserId
|
||||
UserId: UserId,
|
||||
};
|
||||
const response = await dispatch(gettableData(DineinData)).unwrap();
|
||||
if (response?.data?.statusCode === 1) {
|
||||
|
|
|
|||
|
|
@ -4,7 +4,9 @@ import moment from 'moment';
|
|||
import jsPDF from 'jspdf';
|
||||
import { changePrintStatus } from '../Features/BookingScreen/BookingData/BookingData';
|
||||
import store from '../app/store.js';
|
||||
import html2pdf from 'html2pdf.js';
|
||||
// import html2pdf from 'html2pdf.js';
|
||||
|
||||
import html2canvas from 'html2canvas';
|
||||
|
||||
const cookies = new Cookies();
|
||||
const SECRET_KEY = import.meta.env.ENV_SECRET_KEY;
|
||||
|
|
@ -82,42 +84,40 @@ export const decryptedValuesFun = (value) => {
|
|||
}
|
||||
};
|
||||
|
||||
export const encryptObject=(obj)=> {
|
||||
export const encryptObject = (obj) => {
|
||||
const jsonString = JSON.stringify(obj);
|
||||
const encrypted = CryptoJS.AES.encrypt(jsonString, SECRET_KEY).toString();
|
||||
|
||||
// Make Base64 string URL-safe
|
||||
const urlSafe = encrypted
|
||||
.replace(/\+/g, "-")
|
||||
.replace(/\//g, "_")
|
||||
.replace(/=+$/, "");
|
||||
.replace(/\+/g, '-')
|
||||
.replace(/\//g, '_')
|
||||
.replace(/=+$/, '');
|
||||
|
||||
return urlSafe;
|
||||
}
|
||||
};
|
||||
|
||||
// Decrypt URL-safe AES string to original object
|
||||
export const decryptToObject=(encryptedUrlSafeStr) =>{
|
||||
// Decrypt URL-safe AES string to original object
|
||||
export const decryptToObject = (encryptedUrlSafeStr) => {
|
||||
try {
|
||||
if (!encryptedUrlSafeStr) return null;
|
||||
|
||||
// Restore base64 format
|
||||
let base64 = encryptedUrlSafeStr
|
||||
.replace(/-/g, "+")
|
||||
.replace(/_/g, "/");
|
||||
let base64 = encryptedUrlSafeStr.replace(/-/g, '+').replace(/_/g, '/');
|
||||
|
||||
// Add missing padding
|
||||
while (base64.length % 4 !== 0) {
|
||||
base64 += "=";
|
||||
base64 += '=';
|
||||
}
|
||||
|
||||
const bytes = CryptoJS.AES.decrypt(base64, SECRET_KEY);
|
||||
const decrypted = bytes.toString(CryptoJS.enc.Utf8);
|
||||
return JSON.parse(decrypted);
|
||||
} catch (err) {
|
||||
console.error("Decryption failed:", err);
|
||||
console.error('Decryption failed:', err);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
};
|
||||
export const TokendecryptedValuesFun = (value) => {
|
||||
if (!value) {
|
||||
return null;
|
||||
|
|
@ -173,7 +173,7 @@ export async function ClearCookie() {
|
|||
}
|
||||
|
||||
export async function printDiv(id, style) {
|
||||
console.log("printDiv",id, style)
|
||||
console.log('printDiv', id, style);
|
||||
return new Promise(async (resolve, reject) => {
|
||||
const content = document.getElementById(id);
|
||||
|
||||
|
|
@ -251,21 +251,20 @@ export function dateFormatChange(dateString) {
|
|||
export function dateFormatChange1(dateString) {
|
||||
return moment(dateString, [
|
||||
'YYYY-MM-DDTHH:mm:ss.SSS',
|
||||
'YYYY-MM-DDTHH:mm:ss'
|
||||
'YYYY-MM-DDTHH:mm:ss',
|
||||
]).format('DD-MMM-YYYY hh:mm A');
|
||||
}
|
||||
|
||||
export function ExtractDateFormate(dateString) {
|
||||
if (!dateString) return "";
|
||||
if (!dateString) return '';
|
||||
const date = new Date(dateString);
|
||||
if (isNaN(date)) return "";
|
||||
if (isNaN(date)) return '';
|
||||
const day = date.getDate();
|
||||
const month = date.toLocaleString("en-US", { month: "short" });
|
||||
const month = date.toLocaleString('en-US', { month: 'short' });
|
||||
const year = date.getFullYear();
|
||||
return `${day}-${month}-${year}`;
|
||||
}
|
||||
|
||||
|
||||
// export function dateTimeFormatChange(dateString) {
|
||||
// let date = moment(dateString, ['YYYY-MM-DDTHH:mm:ss']).format('DD-MM-YYYY hh:mm:ss A');
|
||||
// let finaldate = date.replace('T', ' ');
|
||||
|
|
@ -273,7 +272,9 @@ export function ExtractDateFormate(dateString) {
|
|||
// }
|
||||
|
||||
export function dateTimeFormatChange(dateString) {
|
||||
return moment(dateString, ['YYYY-MM-DDTHH:mm:ss']).format('DD-MMM-YYYY hh:mm A');
|
||||
return moment(dateString, ['YYYY-MM-DDTHH:mm:ss']).format(
|
||||
'DD-MMM-YYYY hh:mm A'
|
||||
);
|
||||
}
|
||||
|
||||
export function convertTo12HourFormat(inputDate) {
|
||||
|
|
@ -301,8 +302,69 @@ export function handleGeneratePDF(id) {
|
|||
});
|
||||
}
|
||||
}
|
||||
// export async function pdfDiv(id, style) {
|
||||
// return new Promise(async (resolve, reject) => {
|
||||
|
||||
export const pdfDiv = async (id, style = '') => {
|
||||
const content = document.getElementById(id);
|
||||
|
||||
if (!content) {
|
||||
throw new Error(`Element with ID "${id}" not found`);
|
||||
}
|
||||
|
||||
// 🔥 wrapper
|
||||
const wrapper = document.createElement('div');
|
||||
wrapper.style.width = '794px';
|
||||
wrapper.style.background = '#fff';
|
||||
|
||||
wrapper.innerHTML = `
|
||||
${style}
|
||||
${content.innerHTML}
|
||||
`;
|
||||
|
||||
document.body.appendChild(wrapper);
|
||||
|
||||
try {
|
||||
const canvas = await html2canvas(wrapper, {
|
||||
scale: 2,
|
||||
useCORS: true,
|
||||
});
|
||||
|
||||
const pdf = new jsPDF('p', 'mm', 'a4');
|
||||
|
||||
const imgWidth = 210;
|
||||
const pageHeight = 297;
|
||||
|
||||
const imgHeight = (canvas.height * imgWidth) / canvas.width;
|
||||
|
||||
const imgData = canvas.toDataURL('image/jpeg', 1.0);
|
||||
|
||||
let heightLeft = imgHeight;
|
||||
let position = 0;
|
||||
|
||||
// ✅ First page
|
||||
pdf.addImage(imgData, 'JPEG', 0, position, imgWidth, imgHeight);
|
||||
heightLeft -= pageHeight;
|
||||
|
||||
// ✅ Correct page splitting
|
||||
while (heightLeft > 0) {
|
||||
position = -(imgHeight - heightLeft); // 🔥 FIX HERE
|
||||
|
||||
pdf.addPage();
|
||||
pdf.addImage(imgData, 'JPEG', 0, position, imgWidth, imgHeight);
|
||||
|
||||
heightLeft -= pageHeight;
|
||||
}
|
||||
|
||||
document.body.removeChild(wrapper);
|
||||
|
||||
return pdf.output('blob');
|
||||
} catch (err) {
|
||||
document.body.removeChild(wrapper);
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
|
||||
// export const pdfDiv = async (id, style = '') => {
|
||||
// return new Promise(async(resolve, reject) => {
|
||||
// const content = document.getElementById(id);
|
||||
|
||||
// if (!content) {
|
||||
|
|
@ -311,87 +373,39 @@ export function handleGeneratePDF(id) {
|
|||
// }
|
||||
|
||||
// try {
|
||||
// // Create a wrapper to apply styles
|
||||
// // Create wrapper div
|
||||
// const wrapper = document.createElement('div');
|
||||
// wrapper.innerHTML = `
|
||||
// ${style}
|
||||
// ${content.innerHTML}
|
||||
// `;
|
||||
|
||||
// // Generate PDF Blob
|
||||
// // PDF options
|
||||
// const opt = {
|
||||
// margin: 0.5,
|
||||
// image: { type: 'jpeg', quality: 0.98 },
|
||||
// html2canvas: { scale: 2, useCORS: true },
|
||||
// jsPDF: { unit: 'mm', format: 'a4', orientation: 'portrait' },
|
||||
// margin: 0,
|
||||
// image: { type: 'jpeg', quality: 0.9 },
|
||||
// html2canvas: {
|
||||
// scale: 2,
|
||||
// useCORS: true,
|
||||
// letterRendering: true,
|
||||
// },
|
||||
// jsPDF: {
|
||||
// unit: 'mm',
|
||||
// format: 'a4',
|
||||
// orientation: 'portrait',
|
||||
// },
|
||||
// pagebreak: {
|
||||
// mode: ['avoid-all', 'css', 'legacy'],
|
||||
// before: '.page-break-before',
|
||||
// after: '.page-break-after',
|
||||
// avoid: '.page-break-avoid'
|
||||
// }
|
||||
// },
|
||||
// };
|
||||
|
||||
// const pdfBlob = await html2pdf().set(opt).from(wrapper).outputPdf('blob');
|
||||
// resolve(pdfBlob);
|
||||
// } catch (error) {
|
||||
// reject(error);
|
||||
// const blob = await html2pdf().set(opt).from(wrapper).outputPdf('blob');
|
||||
// resolve(blob);
|
||||
// } catch (err) {
|
||||
// reject(err);
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
|
||||
// export async function blobToBase64(blob) {
|
||||
// return new Promise((resolve, reject) => {
|
||||
// const reader = new FileReader();
|
||||
// reader.onloadend = () => resolve(reader.result.split(',')[1]);
|
||||
// reader.onerror = reject;
|
||||
// reader.readAsDataURL(blob);
|
||||
// });
|
||||
// }
|
||||
// New pdfDiv and blobToBase64 functions
|
||||
export const pdfDiv = async (id, style = "") => {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
const content = document.getElementById(id);
|
||||
|
||||
if (!content) {
|
||||
reject(new Error(`Element with ID "${id}" not found`));
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// Create wrapper div
|
||||
const wrapper = document.createElement("div");
|
||||
wrapper.innerHTML = `
|
||||
${style}
|
||||
${content.innerHTML}
|
||||
`;
|
||||
|
||||
// PDF options
|
||||
const opt = {
|
||||
margin: 0,
|
||||
image: { type: "jpeg", quality: 0.9 },
|
||||
html2canvas: {
|
||||
scale: 2,
|
||||
useCORS: true,
|
||||
letterRendering: true,
|
||||
},
|
||||
jsPDF: {
|
||||
unit: "mm",
|
||||
format: "a4",
|
||||
orientation: "portrait",
|
||||
},
|
||||
pagebreak: {
|
||||
mode: ["avoid-all", "css", "legacy"],
|
||||
},
|
||||
};
|
||||
|
||||
const blob = await html2pdf().set(opt).from(wrapper).outputPdf("blob");
|
||||
resolve(blob);
|
||||
} catch (err) {
|
||||
reject(err);
|
||||
}
|
||||
});
|
||||
};
|
||||
// };
|
||||
|
||||
export const blobToBase64 = (blob) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
|
|
@ -399,7 +413,7 @@ export const blobToBase64 = (blob) => {
|
|||
reader.onloadend = () => {
|
||||
const bytes = new Uint8Array(reader.result);
|
||||
let binary = '';
|
||||
bytes.forEach((b) => binary += String.fromCharCode(b));
|
||||
bytes.forEach((b) => (binary += String.fromCharCode(b)));
|
||||
resolve(btoa(binary));
|
||||
};
|
||||
reader.onerror = reject;
|
||||
|
|
@ -424,5 +438,3 @@ export const validateSafeInput = (value) => {
|
|||
|
||||
return Promise.resolve();
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
8151
src/check.jsx
8151
src/check.jsx
File diff suppressed because it is too large
Load Diff
|
|
@ -1,34 +1,27 @@
|
|||
import { defineConfig } from "vite";
|
||||
import react from "@vitejs/plugin-react";
|
||||
import { terser } from "rollup-plugin-terser";
|
||||
|
||||
export default defineConfig(({ command, mode }) => {
|
||||
if (mode === "production") {
|
||||
return {
|
||||
plugins: [
|
||||
react(),
|
||||
// terser({ // Add terser plugin for production
|
||||
// compress: {
|
||||
// drop_console: true, // This removes all console.* statements
|
||||
// },
|
||||
// }),
|
||||
],
|
||||
base: "/apps/retail/",
|
||||
envDir: "src",
|
||||
envPrefix: "ENV_",
|
||||
};
|
||||
} else {
|
||||
export default defineConfig(({ mode }) => {
|
||||
const isProd = mode === "production";
|
||||
|
||||
return {
|
||||
plugins: [react()],
|
||||
|
||||
base: isProd ? "/apps/retail/" : "/",
|
||||
|
||||
envDir: "src",
|
||||
envPrefix: "ENV_",
|
||||
|
||||
server: {
|
||||
host: "192.168.1.34",
|
||||
host: true, // 🔥 auto detect your IP
|
||||
port: process.env.PORT || 3015,
|
||||
},
|
||||
optimizeDeps: {
|
||||
exclude: ["console.log"],
|
||||
|
||||
build: {
|
||||
minify: "esbuild",
|
||||
esbuild: {
|
||||
drop: isProd ? ["console", "debugger"] : [],
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in New Issue