Merge pull request 'expired libary change' (#298) from Mohan into main
Reviewed-on: Pozomind/pozo-retail-app#298
This commit is contained in:
commit
1ac1570823
File diff suppressed because it is too large
Load Diff
29
package.json
29
package.json
|
|
@ -11,8 +11,7 @@
|
||||||
"format": "prettier --write ."
|
"format": "prettier --write ."
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ant-design/plots": "^1.2.6",
|
"@ant-design/plots": "^2.6.8",
|
||||||
"@autocomplete/material-ui": "0.0.17",
|
|
||||||
"@capacitor-community/speech-recognition": "^7.0.1",
|
"@capacitor-community/speech-recognition": "^7.0.1",
|
||||||
"@capacitor-mlkit/barcode-scanning": "^8.0.1",
|
"@capacitor-mlkit/barcode-scanning": "^8.0.1",
|
||||||
"@capacitor/app": "^8.0.1",
|
"@capacitor/app": "^8.0.1",
|
||||||
|
|
@ -21,53 +20,49 @@
|
||||||
"@dnd-kit/sortable": "^10.0.0",
|
"@dnd-kit/sortable": "^10.0.0",
|
||||||
"@emotion/react": "^11.11.4",
|
"@emotion/react": "^11.11.4",
|
||||||
"@emotion/styled": "^11.11.5",
|
"@emotion/styled": "^11.11.5",
|
||||||
"@material-ui/lab": "^4.0.0-alpha.61",
|
|
||||||
"@mui/material": "^5.15.18",
|
"@mui/material": "^5.15.18",
|
||||||
|
"@react-google-maps/api": "^2.20.8",
|
||||||
"@reduxjs/toolkit": "^1.9.5",
|
"@reduxjs/toolkit": "^1.9.5",
|
||||||
"@tanstack/react-query": "^5.90.11",
|
"@tanstack/react-query": "^5.90.11",
|
||||||
"@tanstack/react-query-devtools": "^5.91.1",
|
"@tanstack/react-query-devtools": "^5.91.1",
|
||||||
"@tanstack/react-virtual": "^3.13.18",
|
"@tanstack/react-virtual": "^3.13.18",
|
||||||
|
"@types/node": "^25.5.0",
|
||||||
"@zxing/library": "^0.21.3",
|
"@zxing/library": "^0.21.3",
|
||||||
"antd": "^5.17.4",
|
"antd": "^5.17.4",
|
||||||
"antd-img-crop": "^4.22.0",
|
"antd-img-crop": "^4.22.0",
|
||||||
"aos": "^2.3.4",
|
"aos": "^2.3.4",
|
||||||
"axios": "^1.7.2",
|
"apexcharts": "^5.10.4",
|
||||||
|
"axios": "^1.13.6",
|
||||||
"browser-image-compression": "^2.0.2",
|
"browser-image-compression": "^2.0.2",
|
||||||
"classnames": "^2.5.1",
|
"classnames": "^2.5.1",
|
||||||
"cropperjs": "^1.6.2",
|
"cropperjs": "^1.6.2",
|
||||||
"crypto-js": "^4.1.1",
|
"crypto-js": "^4.1.1",
|
||||||
"date-fns": "^4.1.0",
|
"date-fns": "^4.1.0",
|
||||||
"devtools-detect": "^4.0.2",
|
"devtools-detect": "^4.0.2",
|
||||||
"exceljs": "^4.3.0",
|
"exceljs": "^4.4.0",
|
||||||
"file-saver": "^2.0.5",
|
"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",
|
"history": "^5.3.0",
|
||||||
"html2canvas": "^1.4.1",
|
"html2canvas": "^1.4.1",
|
||||||
"html2pdf": "^0.0.11",
|
"html2pdf": "^0.0.11",
|
||||||
"html2pdf.js": "^0.10.3",
|
|
||||||
"html5-qrcode": "^2.3.8",
|
"html5-qrcode": "^2.3.8",
|
||||||
"jquery": "^3.7.1",
|
"jquery": "^3.7.1",
|
||||||
"js-cookie": "^3.0.5",
|
"js-cookie": "^3.0.5",
|
||||||
"jsbarcode": "^3.11.6",
|
"jsbarcode": "^3.11.6",
|
||||||
"jspdf": "^2.5.1",
|
"jspdf": "^4.2.1",
|
||||||
"lucide": "^0.577.0",
|
"lucide": "^0.577.0",
|
||||||
"lucide-react": "^0.536.0",
|
"lucide-react": "^0.536.0",
|
||||||
"material-ui": "^0.20.2",
|
|
||||||
"moment": "^2.30.1",
|
"moment": "^2.30.1",
|
||||||
"mui-autocomplete": "^2.0.1",
|
|
||||||
"number-to-words": "^1.2.4",
|
"number-to-words": "^1.2.4",
|
||||||
"pdf-lib": "^1.17.1",
|
"pdf-lib": "^1.17.1",
|
||||||
"qr-scanner": "^1.4.2",
|
"qr-scanner": "^1.4.2",
|
||||||
"qrcode": "^1.5.3",
|
"qrcode": "^1.5.3",
|
||||||
"qrcode.react": "^4.2.0",
|
"qrcode.react": "^4.2.0",
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
|
"react-apexcharts": "^2.1.0",
|
||||||
"react-beautiful-dnd": "^13.1.1",
|
"react-beautiful-dnd": "^13.1.1",
|
||||||
"react-color": "^2.19.3",
|
"react-color": "^2.19.3",
|
||||||
"react-countup": "^6.5.3",
|
"react-countup": "^6.5.3",
|
||||||
"react-device-detect": "^2.2.3",
|
"react-device-detect": "^2.2.3",
|
||||||
"react-devtools": "^5.2.0",
|
|
||||||
"react-dom": "^18.3.1",
|
"react-dom": "^18.3.1",
|
||||||
"react-icons": "^4.12.0",
|
"react-icons": "^4.12.0",
|
||||||
"react-image-crop": "^11.0.10",
|
"react-image-crop": "^11.0.10",
|
||||||
|
|
@ -82,25 +77,23 @@
|
||||||
"recharts": "^3.5.0",
|
"recharts": "^3.5.0",
|
||||||
"string-similarity": "^4.0.4",
|
"string-similarity": "^4.0.4",
|
||||||
"tesseract.js": "^6.0.0",
|
"tesseract.js": "^6.0.0",
|
||||||
"universal-cookie": "^6.1.3",
|
"universal-cookie": "^8.0.1",
|
||||||
"webfontloader": "^1.6.28",
|
"webfontloader": "^1.6.28",
|
||||||
"xlsx": "^0.18.5",
|
|
||||||
"zustand": "^5.0.8"
|
"zustand": "^5.0.8"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/react": "^18.3.3",
|
"@types/react": "^18.3.3",
|
||||||
"@types/react-dom": "^18.3.0",
|
"@types/react-dom": "^18.3.0",
|
||||||
"@vitejs/plugin-react": "^4.3.0",
|
"@vitejs/plugin-react": "^6.0.1",
|
||||||
"eslint": "^8.57.0",
|
"eslint": "^8.57.0",
|
||||||
"eslint-plugin-react": "^7.34.2",
|
"eslint-plugin-react": "^7.34.2",
|
||||||
"eslint-plugin-react-hooks": "^4.6.2",
|
"eslint-plugin-react-hooks": "^4.6.2",
|
||||||
"eslint-plugin-react-refresh": "^0.4.7",
|
"eslint-plugin-react-refresh": "^0.4.7",
|
||||||
"prettier": "^3.5.3",
|
"prettier": "^3.5.3",
|
||||||
"rollup-plugin-obfuscator": "^1.1.0",
|
"rollup-plugin-obfuscator": "^1.1.0",
|
||||||
"rollup-plugin-terser": "^7.0.2",
|
|
||||||
"sass": "^1.77.2",
|
"sass": "^1.77.2",
|
||||||
"terser": "^5.31.3",
|
"terser": "^5.31.3",
|
||||||
"vite": "^4.5.3",
|
"vite": "^8.0.0",
|
||||||
"vite-plugin-obfuscator": "^1.0.5",
|
"vite-plugin-obfuscator": "^1.0.5",
|
||||||
"vite-plugin-remove-console": "^2.2.0"
|
"vite-plugin-remove-console": "^2.2.0"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,10 @@
|
||||||
import React, { Component } from 'react';
|
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 {
|
class MapView extends Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
|
|
@ -9,81 +14,73 @@ class MapView extends Component {
|
||||||
currentLocation: props.prevlca ? props.prevlca : false,
|
currentLocation: props.prevlca ? props.prevlca : false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async componentDidMount() {
|
async componentDidMount() {
|
||||||
if (Object.keys(this.state.fields.location)?.length === 0) {
|
if (Object.keys(this.state.fields.location)?.length === 0) {
|
||||||
const { lat, lng } = await this.getcurrentLocation();
|
const { lat, lng } = await this.getcurrentLocation();
|
||||||
this.setState((prev) => ({
|
this.setState((prev) => ({
|
||||||
fields: {
|
fields: {
|
||||||
...prev.fields,
|
...prev.fields,
|
||||||
location: {
|
location: { lat, lng },
|
||||||
lat,
|
|
||||||
lng,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
currentLocation: {
|
|
||||||
lat,
|
|
||||||
lng,
|
|
||||||
},
|
},
|
||||||
|
currentLocation: { lat, lng },
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getcurrentLocation() {
|
getcurrentLocation() {
|
||||||
if (navigator && navigator.geolocation) {
|
if (navigator && navigator.geolocation) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve) => {
|
||||||
navigator.geolocation.getCurrentPosition((pos) => {
|
navigator.geolocation.getCurrentPosition((pos) => {
|
||||||
const coords = pos.coords;
|
|
||||||
resolve({
|
resolve({
|
||||||
lat: coords.latitude,
|
lat: pos.coords.latitude,
|
||||||
lng: coords.longitude,
|
lng: pos.coords.longitude,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return {
|
return { lat: 0, lng: 0 };
|
||||||
lat: 0.0,
|
|
||||||
lng: 0,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
addMarker = (location, map) => {
|
|
||||||
|
handleMapClick = (e) => {
|
||||||
|
const lat = e.latLng.lat();
|
||||||
|
const lng = e.latLng.lng();
|
||||||
|
|
||||||
|
const location = { lat, lng };
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
fields: { location: { lat: location.lat(), lng: location.lng() } },
|
fields: { location },
|
||||||
});
|
});
|
||||||
map.panTo(location);
|
|
||||||
this.props.onMarkerClick(location);
|
if (this.props.onMarkerClick) {
|
||||||
|
this.props.onMarkerClick(location);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { lat = 0, lng = 0 } = this.state.fields.location;
|
const { lat = 0, lng = 0 } = this.state.fields.location;
|
||||||
// var bounds = new this.props.google.maps.LatLngBounds();
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<LoadScript googleMapsApiKey="AIzaSyB6w_WDy6psJ5HPX15Me1-o6CkS5jTYWnE">
|
||||||
<Map
|
<GoogleMap
|
||||||
google={this.props.google}
|
mapContainerStyle={containerStyle}
|
||||||
style={{
|
|
||||||
width: '100%',
|
|
||||||
height: '100%',
|
|
||||||
}}
|
|
||||||
initialCenter={{ lat: lat || 0, lng: lng || 0 }}
|
|
||||||
center={{ lat: lat || 0, lng: lng || 0 }}
|
center={{ lat: lat || 0, lng: lng || 0 }}
|
||||||
zoom={14}
|
zoom={14}
|
||||||
mapTypeId="roadmap"
|
onClick={this.handleMapClick}
|
||||||
onClick={(t, map, c) => {
|
|
||||||
this.addMarker(c.latLng, map);
|
|
||||||
}}
|
|
||||||
// bounds={bounds}
|
|
||||||
>
|
>
|
||||||
<Marker
|
{lat && lng && (
|
||||||
tooltip={true}
|
<Marker
|
||||||
name="Your Position"
|
position={{ lat, lng }}
|
||||||
position={this.state.fields.location}
|
onClick={() =>
|
||||||
onClick={this.props.onMarkerClick}
|
this?.props?.onMarkerClick &&
|
||||||
/>
|
this?.props?.onMarkerClick({ lat, lng })
|
||||||
</Map>
|
}
|
||||||
</div>
|
/>
|
||||||
|
)}
|
||||||
|
</GoogleMap>
|
||||||
|
</LoadScript>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export default GoogleApiWrapper({
|
|
||||||
apiKey: 'AIzaSyB6w_WDy6psJ5HPX15Me1-o6CkS5jTYWnE',
|
export default MapView;
|
||||||
})(MapView);
|
|
||||||
|
|
|
||||||
|
|
@ -1,97 +1,97 @@
|
||||||
import React, { useState } from 'react';
|
// import React, { useState } from 'react';
|
||||||
import { Modal, Button } from 'antd';
|
// import { Modal, Button } from 'antd';
|
||||||
import { ImportOutlined, ExportOutlined } from '@ant-design/icons';
|
// import { ImportOutlined, ExportOutlined } from '@ant-design/icons';
|
||||||
import * as XLSX from 'xlsx';
|
// import * as XLSX from 'xlsx';
|
||||||
import { saveAs } from 'file-saver';
|
// import { saveAs } from 'file-saver';
|
||||||
|
|
||||||
const [excelData, setExcelData] = useState(null);
|
// const [excelData, setExcelData] = useState(null);
|
||||||
const [excelFile, setExcelFile] = useState(null);
|
// const [excelFile, setExcelFile] = useState(null);
|
||||||
const [excelFileError, setExcelFileError] = useState(null);
|
// const [excelFileError, setExcelFileError] = useState(null);
|
||||||
|
|
||||||
const handleFile = (e) => {
|
// const handleFile = (e) => {
|
||||||
let selectedFile = e.target.files[0];
|
// let selectedFile = e.target.files[0];
|
||||||
if (selectedFile) {
|
// if (selectedFile) {
|
||||||
if (selectedFile && fileType.includes(selectedFile.type)) {
|
// if (selectedFile && fileType.includes(selectedFile.type)) {
|
||||||
let reader = new FileReader();
|
// let reader = new FileReader();
|
||||||
reader.readAsArrayBuffer(selectedFile);
|
// reader.readAsArrayBuffer(selectedFile);
|
||||||
reader.onload = (e) => {
|
// reader.onload = (e) => {
|
||||||
setExcelFileError(null);
|
// setExcelFileError(null);
|
||||||
setExcelFile(e.target.result);
|
// setExcelFile(e.target.result);
|
||||||
};
|
// };
|
||||||
} else {
|
// } else {
|
||||||
setExcelFileError('Please select only excel file types');
|
// setExcelFileError('Please select only excel file types');
|
||||||
setExcelFile(null);
|
// setExcelFile(null);
|
||||||
}
|
// }
|
||||||
} else {
|
// } else {
|
||||||
console.log('Please select a file');
|
// console.log('Please select a file');
|
||||||
}
|
// }
|
||||||
};
|
// };
|
||||||
|
|
||||||
const handleSubmit = (e) => {
|
// const handleSubmit = (e) => {
|
||||||
e.preventDefault();
|
// e.preventDefault();
|
||||||
if (excelFile !== null) {
|
// if (excelFile !== null) {
|
||||||
const workbook = XLSX.read(excelFile, { type: 'buffer' });
|
// const workbook = XLSX.read(excelFile, { type: 'buffer' });
|
||||||
const worksheetName = workbook.SheetNames[0];
|
// const worksheetName = workbook.SheetNames[0];
|
||||||
const worksheet = workbook.Sheets[worksheetName];
|
// const worksheet = workbook.Sheets[worksheetName];
|
||||||
const data = XLSX.utils.sheet_to_json(worksheet);
|
// const data = XLSX.utils.sheet_to_json(worksheet);
|
||||||
setExcelData(data);
|
// setExcelData(data);
|
||||||
} else {
|
// } else {
|
||||||
setExcelData(null);
|
// setExcelData(null);
|
||||||
}
|
// }
|
||||||
};
|
// };
|
||||||
|
|
||||||
const handleExport = () => {
|
// const handleExport = () => {
|
||||||
if (excelData !== null) {
|
// if (excelData !== null) {
|
||||||
const worksheet = XLSX.utils.json_to_sheet(excelData);
|
// const worksheet = XLSX.utils.json_to_sheet(excelData);
|
||||||
const workbook = XLSX.utils.book_new();
|
// const workbook = XLSX.utils.book_new();
|
||||||
XLSX.utils.book_append_sheet(workbook, worksheet, 'Sheet1');
|
// XLSX.utils.book_append_sheet(workbook, worksheet, 'Sheet1');
|
||||||
const excelBuffer = XLSX.write(workbook, {
|
// const excelBuffer = XLSX.write(workbook, {
|
||||||
bookType: 'xlsx',
|
// bookType: 'xlsx',
|
||||||
type: 'array',
|
// type: 'array',
|
||||||
});
|
// });
|
||||||
const excelData = new Blob([excelBuffer], {
|
// const excelData = new Blob([excelBuffer], {
|
||||||
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
// type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
||||||
});
|
// });
|
||||||
saveAs(excelData, 'exported_data.xlsx');
|
// saveAs(excelData, 'exported_data.xlsx');
|
||||||
}
|
// }
|
||||||
};
|
// };
|
||||||
|
|
||||||
const Modals = ({ open, title, handleCancel, children, footer }) => {
|
// const Modals = ({ open, title, handleCancel, children, footer }) => {
|
||||||
return (
|
// return (
|
||||||
<Modal
|
// <Modal
|
||||||
visible={open}
|
// visible={open}
|
||||||
title={title}
|
// title={title}
|
||||||
onCancel={handleCancel}
|
// onCancel={handleCancel}
|
||||||
footer={
|
// footer={
|
||||||
footer
|
// footer
|
||||||
? [
|
// ? [
|
||||||
<Button key="submit" type="primary" onClick={handleSubmit}>
|
// <Button key="submit" type="primary" onClick={handleSubmit}>
|
||||||
Submit
|
// Submit
|
||||||
</Button>,
|
// </Button>,
|
||||||
<Button key="cancel" onClick={handleCancel}>
|
// <Button key="cancel" onClick={handleCancel}>
|
||||||
Cancel
|
// Cancel
|
||||||
</Button>,
|
// </Button>,
|
||||||
]
|
// ]
|
||||||
: footer
|
// : footer
|
||||||
}
|
// }
|
||||||
>
|
// >
|
||||||
{children}
|
// {children}
|
||||||
<div>
|
// <div>
|
||||||
<h4>Import Excel</h4>
|
// <h4>Import Excel</h4>
|
||||||
<input type="file" onChange={handleFile} />
|
// <input type="file" onChange={handleFile} />
|
||||||
{excelFileError && <div className="error">{excelFileError}</div>}
|
// {excelFileError && <div className="error">{excelFileError}</div>}
|
||||||
<Button type="primary" icon={<ImportOutlined />} onClick={handleSubmit}>
|
// <Button type="primary" icon={<ImportOutlined />} onClick={handleSubmit}>
|
||||||
Import
|
// Import
|
||||||
</Button>
|
// </Button>
|
||||||
</div>
|
// </div>
|
||||||
<div>
|
// <div>
|
||||||
<h4>Export Excel</h4>
|
// <h4>Export Excel</h4>
|
||||||
<Button type="primary" icon={<ExportOutlined />} onClick={handleExport}>
|
// <Button type="primary" icon={<ExportOutlined />} onClick={handleExport}>
|
||||||
Export
|
// Export
|
||||||
</Button>
|
// </Button>
|
||||||
</div>
|
// </div>
|
||||||
</Modal>
|
// </Modal>
|
||||||
);
|
// );
|
||||||
};
|
// };
|
||||||
|
|
||||||
export default Modals;
|
// export default Modals;
|
||||||
|
|
|
||||||
|
|
@ -65,6 +65,7 @@ export const MobileMultiPdfPrint = async ({
|
||||||
}) => {
|
}) => {
|
||||||
try {
|
try {
|
||||||
const style = await PrintStyleFunction(printerTemplateStyle, 'Sales');
|
const style = await PrintStyleFunction(printerTemplateStyle, 'Sales');
|
||||||
|
|
||||||
const stylesMap = {
|
const stylesMap = {
|
||||||
'Style 1': 'PrintStyle1',
|
'Style 1': 'PrintStyle1',
|
||||||
'Style 2': 'PrintStyle2',
|
'Style 2': 'PrintStyle2',
|
||||||
|
|
@ -88,78 +89,61 @@ export const MobileMultiPdfPrint = async ({
|
||||||
const selectedStyle = stylesMap[printerTemplateStyle] || 'RePrint';
|
const selectedStyle = stylesMap[printerTemplateStyle] || 'RePrint';
|
||||||
const Printsize = printDatas?.PageSize || 'A4';
|
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;
|
const totalOrders = PrintOrderDetails.length;
|
||||||
let processedOrders = 0;
|
|
||||||
let allBlobs = [];
|
let allBlobs = [];
|
||||||
|
|
||||||
for (let i = 0; i < totalOrders; i += BATCH_SIZE) {
|
// 🔥 LOOP EACH INVOICE (MAIN FIX)
|
||||||
const batch = PrintOrderDetails.slice(i, i + BATCH_SIZE);
|
for (let i = 0; i < totalOrders; i++) {
|
||||||
|
const element = document.getElementById(
|
||||||
|
`${selectedStyle}-${i}`
|
||||||
|
);
|
||||||
|
|
||||||
// Clear container for each batch
|
if (!element) {
|
||||||
container.innerHTML = '';
|
console.warn('⚠️ Element not found:', `${selectedStyle}-${i}`);
|
||||||
|
continue;
|
||||||
// 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}`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 🔥 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 {
|
||||||
|
// 👉 ONE invoice → ONE PDF
|
||||||
|
const blob = await pdfDiv(wrapper.id);
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
// 🔥 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 combinedPdfBlob = await mergePdfBlobs(allBlobs);
|
||||||
const base64Pdf = await blobToBase64(combinedPdfBlob);
|
const base64Pdf = await blobToBase64(combinedPdfBlob);
|
||||||
|
|
||||||
// Handle different print actions
|
// 🔥 DOWNLOAD
|
||||||
if (PrintComing === 'download') {
|
if (PrintComing === 'download') {
|
||||||
const blobUrl = createSecureBlobUrl(combinedPdfBlob);
|
const blobUrl = createSecureBlobUrl(combinedPdfBlob);
|
||||||
|
|
||||||
const link = document.createElement('a');
|
const link = document.createElement('a');
|
||||||
link.href = blobUrl;
|
link.href = blobUrl;
|
||||||
link.download = 'Receipts.pdf';
|
link.download = 'Receipts.pdf';
|
||||||
|
|
@ -171,72 +155,45 @@ export const MobileMultiPdfPrint = async ({
|
||||||
revokeSecureBlobUrl(blobUrl);
|
revokeSecureBlobUrl(blobUrl);
|
||||||
}, 100);
|
}, 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return blob if requested (after download completes)
|
// 🔥 RETURN BLOB
|
||||||
if (returnBlob && PrintComing === 'download') {
|
if (returnBlob && PrintComing === 'download') {
|
||||||
setPrintOrderDetails([]);
|
setPrintOrderDetails([]);
|
||||||
if (container) {
|
|
||||||
document.body.removeChild(container);
|
|
||||||
}
|
|
||||||
return { success: true, pdfBlob: combinedPdfBlob };
|
return { success: true, pdfBlob: combinedPdfBlob };
|
||||||
}
|
}
|
||||||
|
|
||||||
// if (PrintComing === "whatsapp") {
|
// 🔥 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') {
|
if (PrintComing === 'whatsapp') {
|
||||||
const orderIdsArray = PrintOrderDetails.map((row) => row?.OrderId).filter(
|
const orderIdsArray = PrintOrderDetails.map((row) => row?.OrderId).filter(Boolean);
|
||||||
Boolean
|
|
||||||
);
|
|
||||||
|
|
||||||
if (orderIdsArray.length === 0) {
|
if (orderIdsArray.length === 0) return;
|
||||||
console.warn('⚠️ No order IDs found for WhatsApp sharing.');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2️⃣ Encrypt order IDs
|
|
||||||
const encrypted = encryptObject(orderIdsArray);
|
const encrypted = encryptObject(orderIdsArray);
|
||||||
|
|
||||||
// 3️⃣ Prepare payload for backend
|
|
||||||
const postData = {
|
const postData = {
|
||||||
Url: encrypted,
|
Url: encrypted,
|
||||||
CreatedBy: UserId, // Make sure UserId is passed correctly to your function
|
CreatedBy: UserId,
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 4️⃣ Call backend to get a ReferenceNo
|
|
||||||
const res = await dispatch(PublicQrCodepost(postData)).unwrap();
|
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;
|
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}`;
|
const documentUrl = `${MainHomeUrl}viewbill?code=${urlData}`;
|
||||||
|
|
||||||
// 7️⃣ Open WhatsApp Web with URL
|
|
||||||
const waUrl =
|
const waUrl =
|
||||||
'https://web.whatsapp.com/send?text=' +
|
'https://web.whatsapp.com/send?text=' +
|
||||||
encodeURIComponent('Here are your receipts 👉 ' + documentUrl);
|
encodeURIComponent('Here are your receipts 👉 ' + documentUrl);
|
||||||
|
|
||||||
window.open(waUrl, '_blank');
|
window.open(waUrl, '_blank');
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('❌ WhatsApp sharing failed:', err);
|
console.error('❌ WhatsApp error:', err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 🔥 EMAIL
|
||||||
if (PrintComing === 'email') {
|
if (PrintComing === 'email') {
|
||||||
try {
|
try {
|
||||||
const payload = {
|
const payload = {
|
||||||
|
|
@ -247,13 +204,13 @@ export const MobileMultiPdfPrint = async ({
|
||||||
};
|
};
|
||||||
|
|
||||||
const res = await dispatch(Emailsend(payload))?.unwrap();
|
const res = await dispatch(Emailsend(payload))?.unwrap();
|
||||||
console.log('✅ Email sent response:', res?.data || res);
|
|
||||||
return res;
|
return res;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('❌ Email error:', err);
|
console.error('❌ Email error:', err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 🔥 PRINT VIEW
|
||||||
if (!['download', 'whatsapp', 'email'].includes(PrintComing)) {
|
if (!['download', 'whatsapp', 'email'].includes(PrintComing)) {
|
||||||
const blobUrl = createSecureBlobUrl(combinedPdfBlob);
|
const blobUrl = createSecureBlobUrl(combinedPdfBlob);
|
||||||
|
|
||||||
|
|
@ -261,17 +218,10 @@ export const MobileMultiPdfPrint = async ({
|
||||||
if (newWindow) {
|
if (newWindow) {
|
||||||
newWindow.document.write(`
|
newWindow.document.write(`
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<body style="margin:0">
|
||||||
<title>Receipts</title>
|
<button onclick="window.print()"
|
||||||
<style>
|
style="position:fixed;top:10px;right:10px;">Print</button>
|
||||||
body { margin: 0; display: flex; justify-content: center; align-items: center; height: 100vh; background: #f0f0f0; }
|
<iframe src="${blobUrl}" style="width:100%;height:100%;border:none"></iframe>
|
||||||
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>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
`);
|
`);
|
||||||
|
|
@ -280,43 +230,299 @@ export const MobileMultiPdfPrint = async ({
|
||||||
window.location.href = blobUrl;
|
window.location.href = blobUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
// For mobile intent URL
|
|
||||||
const intentUrl =
|
const intentUrl =
|
||||||
'intent://open?' +
|
'intent://open?' +
|
||||||
'filename=' +
|
'filename=' + encodeURIComponent('Receipts.pdf') +
|
||||||
encodeURIComponent('Receipts.pdf') +
|
'&size=' + combinedPdfBlob.size +
|
||||||
'&size=' +
|
|
||||||
combinedPdfBlob.size +
|
|
||||||
'&type=application/pdf' +
|
'&type=application/pdf' +
|
||||||
'&data=' +
|
'&data=' + encodeURIComponent(base64Pdf) +
|
||||||
encodeURIComponent(base64Pdf) +
|
'&PrintTypeS=' + encodeURIComponent(Printsize) +
|
||||||
'&PrintTypeS=' +
|
|
||||||
encodeURIComponent(Printsize) +
|
|
||||||
'&PrintTypeE' +
|
|
||||||
'#Intent;scheme=pozoprinter;package=com.example.pozoprinter;end';
|
'#Intent;scheme=pozoprinter;package=com.example.pozoprinter;end';
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
try {
|
try {
|
||||||
window.location.href = intentUrl;
|
window.location.href = intentUrl;
|
||||||
} catch (e) {
|
} catch {console.log("some errors")}
|
||||||
console.warn('Could not open intent URL:', e);
|
|
||||||
}
|
|
||||||
}, 500);
|
}, 500);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clean up
|
// 🔥 CLEANUP
|
||||||
setPrintOrderDetails([]);
|
setPrintOrderDetails([]);
|
||||||
if (container) {
|
|
||||||
document.body.removeChild(container);
|
|
||||||
}
|
|
||||||
|
|
||||||
return { success: true, message: 'PDF processed successfully' };
|
return { success: true, message: 'PDF processed successfully' };
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('MobileMultiPdfPrint error:', error);
|
console.error('MobileMultiPdfPrint error:', error);
|
||||||
return { success: false, message: error.message };
|
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
|
// Progress component to show processing status
|
||||||
export const PdfProcessingProgress = ({ progress, isProcessing }) => {
|
export const PdfProcessingProgress = ({ progress, isProcessing }) => {
|
||||||
if (!isProcessing) return null;
|
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 { useDispatch, useSelector } from 'react-redux';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import { ShopOutlined, ShoppingOutlined, UpOutlined } from '@ant-design/icons';
|
import { UpOutlined } from '@ant-design/icons';
|
||||||
import {
|
import {
|
||||||
ChangeDineInDefaultFn,
|
ChangeDineInDefaultFn,
|
||||||
ChangeSelectedCustDisable,
|
|
||||||
GlobalBookingType,
|
GlobalBookingType,
|
||||||
GlobalChairLevelServiceDefault,
|
GlobalChairLevelServiceDefault,
|
||||||
GlobalDefaultBookingType,
|
GlobalDefaultBookingType,
|
||||||
|
|
@ -13,24 +12,20 @@ import {
|
||||||
Globalunpaidflow,
|
Globalunpaidflow,
|
||||||
changeBookingType,
|
changeBookingType,
|
||||||
changeChairLevelServiceDefault,
|
changeChairLevelServiceDefault,
|
||||||
changeCustomerID,
|
|
||||||
changeDefaultBookingType,
|
changeDefaultBookingType,
|
||||||
changeEstimateBooking,
|
changeEstimateBooking,
|
||||||
changeOrderCardDetails,
|
changeOrderCardDetails,
|
||||||
changeOrderType,
|
changeOrderType,
|
||||||
changeReorderHoldDetails,
|
changeReorderHoldDetails,
|
||||||
changeReorderProductDetails,
|
changeReorderProductDetails,
|
||||||
changeSelectedCustId,
|
|
||||||
changeSelectedOption,
|
|
||||||
changeSelectedTableDetails,
|
changeSelectedTableDetails,
|
||||||
} from '../../../../Features/BookingScreen/BookingData/BookingData';
|
} from '../../../../Features/BookingScreen/BookingData/BookingData';
|
||||||
import PozoDineInIcon from './Pozo retail icons/PozoDineIn.jsx';
|
import PozoDineInIcon from './Pozo retail icons/PozoDineIn.jsx';
|
||||||
import TakeAway from './Pozo retail icons/PozoTakeAwayIcon';
|
import TakeAway from './Pozo retail icons/PozoTakeAwayIcon';
|
||||||
import { ChangeTotalAmount } from '../../../../Features/ExteraCharges/ExtraCharges.js';
|
|
||||||
import TooltipWrapper from '../../../../Components/Tooltip/Tooltip';
|
import TooltipWrapper from '../../../../Components/Tooltip/Tooltip';
|
||||||
import { isMobile } from 'react-device-detect';
|
import { isMobile } from 'react-device-detect';
|
||||||
import { Badge, Popover, Space, Switch, Tooltip } from 'antd';
|
import { Popover, Space, Switch, Tooltip } from 'antd';
|
||||||
import { color } from 'highcharts';
|
|
||||||
import './BSNavBarTable.scss';
|
import './BSNavBarTable.scss';
|
||||||
import { RiRefreshLine } from "react-icons/ri";
|
import { RiRefreshLine } from "react-icons/ri";
|
||||||
|
|
||||||
|
|
@ -72,7 +67,7 @@ const BSNavBarTable = () => {
|
||||||
await dispatch(changeReorderHoldDetails({}));
|
await dispatch(changeReorderHoldDetails({}));
|
||||||
await dispatch(changeReorderProductDetails([]));
|
await dispatch(changeReorderProductDetails([]));
|
||||||
await dispatch(changeOrderCardDetails([]));
|
await dispatch(changeOrderCardDetails([]));
|
||||||
dispatch(changeunpaidflow(false));
|
// dispatch(changeunpaidflow(false));
|
||||||
}
|
}
|
||||||
// await dispatch(changeReorderHoldDetails({}))
|
// await dispatch(changeReorderHoldDetails({}))
|
||||||
// await dispatch(ChangeTotalAmount([]))
|
// await dispatch(ChangeTotalAmount([]))
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import React, { useRef, useEffect, useState, useContext } from 'react';
|
import React, { useRef, useEffect, useState, useContext } from 'react';
|
||||||
import { useDispatch, useSelector } from 'react-redux';
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
import { Table, Button, Form, Input, Tooltip, Modal } from 'antd';
|
import { Table, Form, Input, Tooltip, Modal } from 'antd';
|
||||||
import { read, utils } from 'xlsx';
|
// import { read, utils } from 'xlsx';
|
||||||
import ExcelJS from 'exceljs';
|
import ExcelJS from 'exceljs';
|
||||||
import {
|
import {
|
||||||
emptyExcelData,
|
emptyExcelData,
|
||||||
|
|
@ -32,6 +32,7 @@ const ConfigExcel = ({ handleSubmit, CategoryNames, SubCatData }) => {
|
||||||
const [worksheet1, setWorksheet1] = useState(null);
|
const [worksheet1, setWorksheet1] = useState(null);
|
||||||
const [editdelete, seteditdelete] = useState('');
|
const [editdelete, seteditdelete] = useState('');
|
||||||
const [Datas, setDatas] = useState();
|
const [Datas, setDatas] = useState();
|
||||||
|
const [currentPage, setCurrentPage] = useState(1);
|
||||||
|
|
||||||
const [radioValue, setRadioValue] = useState('Category Name');
|
const [radioValue, setRadioValue] = useState('Category Name');
|
||||||
|
|
||||||
|
|
@ -146,58 +147,137 @@ const ConfigExcel = ({ handleSubmit, CategoryNames, SubCatData }) => {
|
||||||
// Programmatically trigger the file input click event
|
// Programmatically trigger the file input click event
|
||||||
fileInputRef.current.click();
|
fileInputRef.current.click();
|
||||||
};
|
};
|
||||||
|
|
||||||
const uploadAndProcessExcel = (file) => {
|
const uploadAndProcessExcel = (file) => {
|
||||||
const reader = new FileReader();
|
const reader = new FileReader();
|
||||||
|
|
||||||
reader.onload = (e) => {
|
reader.onload = async (e) => {
|
||||||
const data = new Uint8Array(e.target.result);
|
try {
|
||||||
|
const buffer = e.target.result;
|
||||||
|
|
||||||
const workbook = read(data, { type: 'array' });
|
const workbook = new ExcelJS.Workbook();
|
||||||
const worksheet = workbook.Sheets[workbook.SheetNames[0]];
|
await workbook.xlsx.load(buffer);
|
||||||
|
|
||||||
const jsonData = utils.sheet_to_json(worksheet, {
|
const worksheet = workbook.worksheets[0];
|
||||||
header: 1,
|
|
||||||
raw: false,
|
|
||||||
dateNF: 'DD/MM/YY',
|
|
||||||
});
|
|
||||||
|
|
||||||
const nonEmptyRows = jsonData.filter((row) =>
|
const jsonData = [];
|
||||||
row.some((cell) => cell !== '')
|
|
||||||
);
|
|
||||||
const header = nonEmptyRows[0];
|
|
||||||
const rows = nonEmptyRows.slice(1);
|
|
||||||
|
|
||||||
const dateFields = ['Manufacture Date', 'Expire Date', 'Stock Date'];
|
worksheet.eachRow((row) => {
|
||||||
|
const rowData = [];
|
||||||
|
|
||||||
function convertToDisplayFormat(dateString) {
|
row.eachCell({ includeEmpty: true }, (cell) => {
|
||||||
const date = new Date(dateString);
|
let value = cell.value;
|
||||||
const day = date.getDate().toString().padStart(2, '0');
|
|
||||||
const month = (date.getMonth() + 1).toString().padStart(2, '0');
|
// handle different ExcelJS value types
|
||||||
const year = date.getFullYear().toString();
|
if (value && typeof value === 'object') {
|
||||||
return `${day}-${month}-${year}`;
|
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'];
|
||||||
|
|
||||||
|
const convertToDisplayFormat = (dateString) => {
|
||||||
|
const date = new Date(dateString);
|
||||||
|
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) => {
|
||||||
|
return header.reduce((acc, col, index) => {
|
||||||
|
if (dateFields.includes(col)) {
|
||||||
|
acc[col] = convertToDisplayFormat(row[index]);
|
||||||
|
} else {
|
||||||
|
acc[col] = row[index];
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
});
|
||||||
|
|
||||||
|
// SAME AS BEFORE (no break)
|
||||||
|
dispatch(uploadExcel({ file, jsonData: nonEmptyRows }));
|
||||||
|
setDatas(formattedData);
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
Modal.error({
|
||||||
|
title: 'Error',
|
||||||
|
content: 'Failed to process Excel file',
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
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 handleDownload = async () => {
|
||||||
const workbook = new ExcelJS.Workbook();
|
const workbook = new ExcelJS.Workbook();
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
|
|
||||||
|
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import Highcharts from 'highcharts';
|
import Chart from 'react-apexcharts';
|
||||||
import HighchartsReact from 'highcharts-react-official';
|
|
||||||
import './PieChart.scss';
|
import './PieChart.scss';
|
||||||
|
|
||||||
const DynamicPieChart = ({ chartData }) => {
|
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 (
|
return (
|
||||||
<div style={{ width: 'inherit' }}>
|
<div style={{ width: '100%' }}>
|
||||||
<HighchartsReact
|
<Chart options={options} series={series} type="pie" height={300} />
|
||||||
highcharts={Highcharts}
|
|
||||||
options={{
|
|
||||||
chart: { type: 'pie' },
|
|
||||||
title: { text: `Overall Amount : ${safeRound(overallValue)}` },
|
|
||||||
series: [{ name: 'Amount', data: chartData }],
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -5,11 +5,9 @@ import { changeKioskOrderDetails } from '../../../Features/BookingScreen/Booking
|
||||||
import '../../../Styles/Kiosk/OrderCart/OrderCart2.scss';
|
import '../../../Styles/Kiosk/OrderCart/OrderCart2.scss';
|
||||||
import PozoDineInIcon from '../KioskIcons/KioskDineIn';
|
import PozoDineInIcon from '../KioskIcons/KioskDineIn';
|
||||||
import TakeAway from '../KioskIcons/KioskTakeAway';
|
import TakeAway from '../KioskIcons/KioskTakeAway';
|
||||||
import { color } from 'highcharts';
|
|
||||||
import { calculateOverAllQtyLimit } from '../../../utils/calculations';
|
import { calculateOverAllQtyLimit } from '../../../utils/calculations';
|
||||||
import { PreferenceData } from '../../../Features/BookingScreen/BookingData/BookingData';
|
import { PreferenceData } from '../../../Features/BookingScreen/BookingData/BookingData';
|
||||||
import { useSelector } from 'react-redux';
|
import { useSelector } from 'react-redux';
|
||||||
import { message } from 'antd';
|
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { IoIosWarning } from 'react-icons/io';
|
import { IoIosWarning } from 'react-icons/io';
|
||||||
|
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -158,7 +158,7 @@ const ProductList = () => {
|
||||||
UomTypePreference?.some?.(
|
UomTypePreference?.some?.(
|
||||||
(e) =>
|
(e) =>
|
||||||
e?.PreferredSubCatName?.toLowerCase() ==
|
e?.PreferredSubCatName?.toLowerCase() ==
|
||||||
item?.ConfigName?.toLowerCase() && e?.PreferredStatus == 'Y'
|
item?.ConfigName?.toLowerCase() && e?.PreferredStatus == 'Y'
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -965,19 +965,19 @@ const ProductList = () => {
|
||||||
},
|
},
|
||||||
...(brandColumn && subCategory
|
...(brandColumn && subCategory
|
||||||
? [
|
? [
|
||||||
{
|
{
|
||||||
title: 'Brand',
|
title: 'Brand',
|
||||||
dataIndex: 'BrandName',
|
dataIndex: 'BrandName',
|
||||||
key: 'BrandName',
|
key: 'BrandName',
|
||||||
columnKey: 'BrandName',
|
columnKey: 'BrandName',
|
||||||
width: '100px',
|
width: '100px',
|
||||||
render: (text) => <a style={{ color: 'black' }}>{text}</a>,
|
render: (text) => <a style={{ color: 'black' }}>{text}</a>,
|
||||||
sorter: (a, b) => a?.BrandName?.localeCompare(b?.BrandName),
|
sorter: (a, b) => a?.BrandName?.localeCompare(b?.BrandName),
|
||||||
sortOrder:
|
sortOrder:
|
||||||
sortedInfo.columnKey === 'BrandName' ? sortedInfo.order : null,
|
sortedInfo.columnKey === 'BrandName' ? sortedInfo.order : null,
|
||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
: []),
|
: []),
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
@ -996,127 +996,127 @@ const ProductList = () => {
|
||||||
|
|
||||||
...(qrCodeColumn
|
...(qrCodeColumn
|
||||||
? [
|
? [
|
||||||
{
|
{
|
||||||
title: 'QRCode',
|
title: 'QRCode',
|
||||||
dataIndex: 'QRCode',
|
dataIndex: 'QRCode',
|
||||||
key: 'QRCode',
|
key: 'QRCode',
|
||||||
width: '100px',
|
width: '100px',
|
||||||
align: 'center',
|
align: 'center',
|
||||||
render: (text) => (
|
render: (text) => (
|
||||||
<a style={{ color: 'black' }}>{text ? text : '-'}</a>
|
<a style={{ color: 'black' }}>{text ? text : '-'}</a>
|
||||||
),
|
),
|
||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: (
|
title: (
|
||||||
<Tooltip title=" printing multiple QR codes">
|
<Tooltip title=" printing multiple QR codes">
|
||||||
<Space>
|
<Space>
|
||||||
{MultiCode && (
|
{MultiCode && (
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
checked={
|
|
||||||
selectedRecords.length > 0 &&
|
|
||||||
productData
|
|
||||||
.filter((p) => p?.QRCode)
|
|
||||||
.every((p) =>
|
|
||||||
selectedRecords.some((r) => r.ProdId === p.ProdId)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
onChange={(e) => {
|
|
||||||
const productsWithQR = productData.filter(
|
|
||||||
(p) => p?.QRCode
|
|
||||||
);
|
|
||||||
if (e.target.checked) {
|
|
||||||
const newSelections = productsWithQR.filter(
|
|
||||||
(p) =>
|
|
||||||
!selectedRecords.some(
|
|
||||||
(r) => r.ProdId === p.ProdId
|
|
||||||
)
|
|
||||||
);
|
|
||||||
setSelectedRecords((prev) => [
|
|
||||||
...prev,
|
|
||||||
...newSelections,
|
|
||||||
]);
|
|
||||||
// Create QR codes for newly selected products
|
|
||||||
CreateQrcodesBatch(newSelections, true);
|
|
||||||
} else {
|
|
||||||
// Remove QR codes for deselected products
|
|
||||||
CreateQrcodesBatch(productsWithQR, false);
|
|
||||||
setSelectedRecords((prev) =>
|
|
||||||
prev.filter(
|
|
||||||
(r) =>
|
|
||||||
!productsWithQR.some(
|
|
||||||
(p) => p.ProdId === r.ProdId
|
|
||||||
)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
<span>Print QRCode</span>
|
|
||||||
<CheckSquareOutlined
|
|
||||||
style={{
|
|
||||||
fontSize: '18px',
|
|
||||||
cursor: 'pointer',
|
|
||||||
color: MultiCode ? '#1890ff' : '#666',
|
|
||||||
}}
|
|
||||||
onClick={() => {
|
|
||||||
setMultiCode((prev) => {
|
|
||||||
const newValue = !prev;
|
|
||||||
if (!newValue) {
|
|
||||||
setSelectedRecords([]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return newValue;
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Space>
|
|
||||||
</Tooltip>
|
|
||||||
),
|
|
||||||
key: 'Print',
|
|
||||||
dataIndex: 'Print',
|
|
||||||
width: '100px',
|
|
||||||
align: 'center',
|
|
||||||
render: (_, record) =>
|
|
||||||
productData.length >= 1 ? (
|
|
||||||
<Space size="middle">
|
|
||||||
{MultiCode ? (
|
|
||||||
record?.QRCode ? (
|
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={selectedRecords.some(
|
checked={
|
||||||
(r) => r.ProdId == record.ProdId
|
selectedRecords.length > 0 &&
|
||||||
)}
|
productData
|
||||||
|
.filter((p) => p?.QRCode)
|
||||||
|
.every((p) =>
|
||||||
|
selectedRecords.some((r) => r.ProdId === p.ProdId)
|
||||||
|
)
|
||||||
|
}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
handleCheckboxChange(record, e.target.checked);
|
const productsWithQR = productData.filter(
|
||||||
CreateQrcode(
|
(p) => p?.QRCode
|
||||||
e.target.checked,
|
|
||||||
record.QRCode,
|
|
||||||
record?.ProdName,
|
|
||||||
record?.StockAvailable,
|
|
||||||
record?.TotalBalanceQty
|
|
||||||
);
|
);
|
||||||
|
if (e.target.checked) {
|
||||||
|
const newSelections = productsWithQR.filter(
|
||||||
|
(p) =>
|
||||||
|
!selectedRecords.some(
|
||||||
|
(r) => r.ProdId === p.ProdId
|
||||||
|
)
|
||||||
|
);
|
||||||
|
setSelectedRecords((prev) => [
|
||||||
|
...prev,
|
||||||
|
...newSelections,
|
||||||
|
]);
|
||||||
|
// Create QR codes for newly selected products
|
||||||
|
CreateQrcodesBatch(newSelections, true);
|
||||||
|
} else {
|
||||||
|
// Remove QR codes for deselected products
|
||||||
|
CreateQrcodesBatch(productsWithQR, false);
|
||||||
|
setSelectedRecords((prev) =>
|
||||||
|
prev.filter(
|
||||||
|
(r) =>
|
||||||
|
!productsWithQR.some(
|
||||||
|
(p) => p.ProdId === r.ProdId
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
)}
|
||||||
|
<span>Print QRCode</span>
|
||||||
|
<CheckSquareOutlined
|
||||||
|
style={{
|
||||||
|
fontSize: '18px',
|
||||||
|
cursor: 'pointer',
|
||||||
|
color: MultiCode ? '#1890ff' : '#666',
|
||||||
|
}}
|
||||||
|
onClick={() => {
|
||||||
|
setMultiCode((prev) => {
|
||||||
|
const newValue = !prev;
|
||||||
|
if (!newValue) {
|
||||||
|
setSelectedRecords([]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return newValue;
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Space>
|
||||||
|
</Tooltip>
|
||||||
|
),
|
||||||
|
key: 'Print',
|
||||||
|
dataIndex: 'Print',
|
||||||
|
width: '100px',
|
||||||
|
align: 'center',
|
||||||
|
render: (_, record) =>
|
||||||
|
productData.length >= 1 ? (
|
||||||
|
<Space size="middle">
|
||||||
|
{MultiCode ? (
|
||||||
|
record?.QRCode ? (
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
checked={selectedRecords.some(
|
||||||
|
(r) => r.ProdId == record.ProdId
|
||||||
|
)}
|
||||||
|
onChange={(e) => {
|
||||||
|
handleCheckboxChange(record, e.target.checked);
|
||||||
|
CreateQrcode(
|
||||||
|
e.target.checked,
|
||||||
|
record.QRCode,
|
||||||
|
record?.ProdName,
|
||||||
|
record?.StockAvailable,
|
||||||
|
record?.TotalBalanceQty
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
'-'
|
||||||
|
)
|
||||||
|
) : record?.QRCode ? (
|
||||||
|
<a>
|
||||||
|
<PrinterFilled
|
||||||
|
style={{ fontSize: '20px' }}
|
||||||
|
onClick={() => Qrcode(record)}
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
) : (
|
) : (
|
||||||
'-'
|
'-'
|
||||||
)
|
)}
|
||||||
) : record?.QRCode ? (
|
</Space>
|
||||||
<a>
|
) : null,
|
||||||
<PrinterFilled
|
},
|
||||||
style={{ fontSize: '20px' }}
|
]
|
||||||
onClick={() => Qrcode(record)}
|
|
||||||
/>
|
|
||||||
</a>
|
|
||||||
) : (
|
|
||||||
'-'
|
|
||||||
)}
|
|
||||||
</Space>
|
|
||||||
) : null,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
: []),
|
: []),
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
@ -1134,11 +1134,11 @@ const ProductList = () => {
|
||||||
style={{ color: '#1292EE' }}
|
style={{ color: '#1292EE' }}
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
UserType === 'Super Admin' ||
|
UserType === 'Super Admin' ||
|
||||||
(UserType === 'Super Admin User' &&
|
(UserType === 'Super Admin User' &&
|
||||||
SAAccessCommonMaster?.UpdateAccess === 'Y') ||
|
SAAccessCommonMaster?.UpdateAccess === 'Y') ||
|
||||||
(UserType === 'Employee' &&
|
(UserType === 'Employee' &&
|
||||||
empData?.UpdateAccess === 'Y') ||
|
empData?.UpdateAccess === 'Y') ||
|
||||||
UserType === 'Admin'
|
UserType === 'Admin'
|
||||||
? actionsFormatter(record, index)
|
? actionsFormatter(record, index)
|
||||||
: null
|
: null
|
||||||
}
|
}
|
||||||
|
|
@ -1155,11 +1155,11 @@ const ProductList = () => {
|
||||||
}}
|
}}
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
UserType === 'Super Admin' ||
|
UserType === 'Super Admin' ||
|
||||||
(UserType === 'Super Admin User' &&
|
(UserType === 'Super Admin User' &&
|
||||||
SAAccessCommonMaster?.DeleteAccess === 'Y') ||
|
SAAccessCommonMaster?.DeleteAccess === 'Y') ||
|
||||||
(UserType === 'Employee' &&
|
(UserType === 'Employee' &&
|
||||||
empData?.DeleteAccess === 'Y') ||
|
empData?.DeleteAccess === 'Y') ||
|
||||||
UserType === 'Admin'
|
UserType === 'Admin'
|
||||||
? statusFormatter(record)
|
? statusFormatter(record)
|
||||||
: null
|
: null
|
||||||
}
|
}
|
||||||
|
|
@ -1171,11 +1171,11 @@ const ProductList = () => {
|
||||||
}}
|
}}
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
UserType === 'Super Admin' ||
|
UserType === 'Super Admin' ||
|
||||||
(UserType === 'Super Admin User' &&
|
(UserType === 'Super Admin User' &&
|
||||||
SAAccessCommonMaster?.DeleteAccess === 'Y') ||
|
SAAccessCommonMaster?.DeleteAccess === 'Y') ||
|
||||||
(UserType === 'Employee' &&
|
(UserType === 'Employee' &&
|
||||||
empData?.DeleteAccess === 'Y') ||
|
empData?.DeleteAccess === 'Y') ||
|
||||||
UserType === 'Admin'
|
UserType === 'Admin'
|
||||||
? statusFormatter(record)
|
? statusFormatter(record)
|
||||||
: null
|
: null
|
||||||
}
|
}
|
||||||
|
|
@ -1596,7 +1596,12 @@ const ProductList = () => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const getPreference = async () => {
|
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 { data: res } = await dispatch(getPreferenceData(data)).unwrap();
|
||||||
const decimalSetting = res?.data?.[0]?.SettingDtlDetails?.find(
|
const decimalSetting = res?.data?.[0]?.SettingDtlDetails?.find(
|
||||||
(setting) =>
|
(setting) =>
|
||||||
|
|
@ -2029,9 +2034,6 @@ const ProductList = () => {
|
||||||
formRef?.current?.resetFields();
|
formRef?.current?.resetFields();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const handleResetExcelData = useCallback(() => {
|
const handleResetExcelData = useCallback(() => {
|
||||||
dispatch(emptyExcelData());
|
dispatch(emptyExcelData());
|
||||||
}, []);
|
}, []);
|
||||||
|
|
@ -2051,9 +2053,7 @@ const ProductList = () => {
|
||||||
const handleSubmit = useCallback(async () => {
|
const handleSubmit = useCallback(async () => {
|
||||||
setBulkpost(true);
|
setBulkpost(true);
|
||||||
|
|
||||||
const FilterData = Exceldatasubmit?.filter(
|
const FilterData = Exceldatasubmit?.filter((a) => a.ProductName !== '');
|
||||||
a => a.ProductName !== ''
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!FilterData || FilterData.length === 0) {
|
if (!FilterData || FilterData.length === 0) {
|
||||||
setBulkpost(false);
|
setBulkpost(false);
|
||||||
|
|
@ -2062,7 +2062,7 @@ const ProductList = () => {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const formattedData = FilterData.map(data => ({
|
const formattedData = FilterData.map((data) => ({
|
||||||
AppId: getSession('AppId'),
|
AppId: getSession('AppId'),
|
||||||
CompId: getSession('CompId'),
|
CompId: getSession('CompId'),
|
||||||
BranchId: getSession('BranchId'),
|
BranchId: getSession('BranchId'),
|
||||||
|
|
@ -2139,7 +2139,7 @@ const ProductList = () => {
|
||||||
BranchId,
|
BranchId,
|
||||||
AppId,
|
AppId,
|
||||||
handleResetExcelData,
|
handleResetExcelData,
|
||||||
handleCancel
|
handleCancel,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const Unit = useMemo(
|
const Unit = useMemo(
|
||||||
|
|
@ -2197,10 +2197,7 @@ const ProductList = () => {
|
||||||
[SupplierData]
|
[SupplierData]
|
||||||
);
|
);
|
||||||
|
|
||||||
const TaxDatas = useMemo(
|
const TaxDatas = useMemo(() => TaxData?.map((e) => e.TaxIdName), [TaxData]);
|
||||||
() => TaxData?.map((e) => e.TaxIdName),
|
|
||||||
[TaxData]
|
|
||||||
);
|
|
||||||
|
|
||||||
const TaxData1 = useMemo(
|
const TaxData1 = useMemo(
|
||||||
() => TaxData?.map((e) => e.TaxPercentage),
|
() => TaxData?.map((e) => e.TaxPercentage),
|
||||||
|
|
@ -2209,9 +2206,7 @@ const ProductList = () => {
|
||||||
|
|
||||||
const combinedTaxData = useMemo(
|
const combinedTaxData = useMemo(
|
||||||
() =>
|
() =>
|
||||||
TaxDatas?.map(
|
TaxDatas?.map((taxIdName, index) => `${taxIdName} - ${TaxData1[index]}%`),
|
||||||
(taxIdName, index) => `${taxIdName} - ${TaxData1[index]}%`
|
|
||||||
),
|
|
||||||
[TaxDatas, TaxData1]
|
[TaxDatas, TaxData1]
|
||||||
);
|
);
|
||||||
const Deletebulk = async () => {
|
const Deletebulk = async () => {
|
||||||
|
|
@ -2706,7 +2701,9 @@ const ProductList = () => {
|
||||||
generateQRCodeCopy={(code) =>
|
generateQRCodeCopy={(code) =>
|
||||||
generateQRCodeCopy(QrandbarcodeDatas, code)
|
generateQRCodeCopy(QrandbarcodeDatas, code)
|
||||||
}
|
}
|
||||||
generateCodeImage={(code, codeType = 'Q') => generateCodeImage(code, codeType, QrandbarcodeDatas)}
|
generateCodeImage={(code, codeType = 'Q') =>
|
||||||
|
generateCodeImage(code, codeType, QrandbarcodeDatas)
|
||||||
|
}
|
||||||
imageTagBarcodeAndQR={imageTagBarcodeAndQR}
|
imageTagBarcodeAndQR={imageTagBarcodeAndQR}
|
||||||
dropdownValue={dropdownValue}
|
dropdownValue={dropdownValue}
|
||||||
barcodeTemplateDetails={barcodeTemplateDetails}
|
barcodeTemplateDetails={barcodeTemplateDetails}
|
||||||
|
|
@ -2739,7 +2736,7 @@ const ProductList = () => {
|
||||||
/>
|
/>
|
||||||
{Bulkpost && (
|
{Bulkpost && (
|
||||||
<div className="Bulkuploadpost">
|
<div className="Bulkuploadpost">
|
||||||
<div align="center" class="fondLoader">
|
<div align="center" className="fondLoader">
|
||||||
<div className="contener_general">
|
<div className="contener_general">
|
||||||
<div className="contener_mixte">
|
<div className="contener_mixte">
|
||||||
<div className="ballcolor ball_1"> </div>
|
<div className="ballcolor ball_1"> </div>
|
||||||
|
|
@ -2812,7 +2809,7 @@ const ProductList = () => {
|
||||||
}}
|
}}
|
||||||
onlineImage={onlineImage}
|
onlineImage={onlineImage}
|
||||||
ImageLink={rowRecord?.ProdLogo || null}
|
ImageLink={rowRecord?.ProdLogo || null}
|
||||||
updateImageUrl={imageModalRowIndex ? updateImageUrl : () => { }}
|
updateImageUrl={imageModalRowIndex ? updateImageUrl : () => {}}
|
||||||
handleClose={handleImageModalClose}
|
handleClose={handleImageModalClose}
|
||||||
/>
|
/>
|
||||||
<button
|
<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 { useDispatch, useSelector } from 'react-redux';
|
||||||
import { read, utils } from 'xlsx';
|
// import { read, utils } from 'xlsx';
|
||||||
import ExcelJS from 'exceljs';
|
import ExcelJS from 'exceljs';
|
||||||
import { Table, Form, Input, Tooltip } from 'antd';
|
import { Table, Form, Input, Tooltip } from 'antd';
|
||||||
import { AiFillDelete } from 'react-icons/ai';
|
import { AiFillDelete } from 'react-icons/ai';
|
||||||
|
|
@ -8,7 +15,6 @@ import { FiDelete, FiDownload } from 'react-icons/fi';
|
||||||
import {
|
import {
|
||||||
emptyExcelData,
|
emptyExcelData,
|
||||||
excelDataSelector,
|
excelDataSelector,
|
||||||
excelFileSelector,
|
|
||||||
excelFileErrorSelector,
|
excelFileErrorSelector,
|
||||||
uploadExcel,
|
uploadExcel,
|
||||||
} from '../../Features/ExcelUploadPage/ExcelUploadPage.js';
|
} from '../../Features/ExcelUploadPage/ExcelUploadPage.js';
|
||||||
|
|
@ -38,22 +44,6 @@ const EXCEL_COLUMNS = [
|
||||||
{ header: 'Variant Name', key: 'VariantName', width: 20, field: '4' },
|
{ 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
|
// Utility Functions
|
||||||
const isValidExcelFile = (file) => ALLOWED_EXCEL_TYPES.includes(file.type);
|
const isValidExcelFile = (file) => ALLOWED_EXCEL_TYPES.includes(file.type);
|
||||||
|
|
@ -187,14 +177,22 @@ const PurchaseExcel = ({ handleSubmit }) => {
|
||||||
|
|
||||||
const type = supplierData.Type;
|
const type = supplierData.Type;
|
||||||
const LocationType =
|
const LocationType =
|
||||||
type === 'Supplier' ? 'S' : type === 'Branch' ? 'B' : type === 'WareHouse' ? 'W' : '';
|
type === 'Supplier'
|
||||||
|
? 'S'
|
||||||
|
: type === 'Branch'
|
||||||
|
? 'B'
|
||||||
|
: type === 'WareHouse'
|
||||||
|
? 'W'
|
||||||
|
: '';
|
||||||
|
|
||||||
const response = await dispatch(
|
const response = await dispatch(
|
||||||
getSupplaierIdwithTypeBasedProducts({
|
getSupplaierIdwithTypeBasedProducts({
|
||||||
CompId: LocationType === 'S' ? CompId : supplierData?.SuppCompId,
|
CompId: LocationType === 'S' ? CompId : supplierData?.SuppCompId,
|
||||||
AppId: LocationType === 'S' ? AppId : supplierData?.SuppAppId,
|
AppId: LocationType === 'S' ? AppId : supplierData?.SuppAppId,
|
||||||
BranchId: LocationType === 'S' ? BranchId : supplierData?.SuppBranchId,
|
BranchId:
|
||||||
SuppId: LocationType === 'S' ? supplierId : supplierData?.SuppBranchId,
|
LocationType === 'S' ? BranchId : supplierData?.SuppBranchId,
|
||||||
|
SuppId:
|
||||||
|
LocationType === 'S' ? supplierId : supplierData?.SuppBranchId,
|
||||||
LocationType,
|
LocationType,
|
||||||
})
|
})
|
||||||
)?.unwrap();
|
)?.unwrap();
|
||||||
|
|
@ -205,17 +203,17 @@ const PurchaseExcel = ({ handleSubmit }) => {
|
||||||
} else {
|
} else {
|
||||||
setSupplierProducts([]);
|
setSupplierProducts([]);
|
||||||
setMessageData('No products are mapped to this supplier.');
|
setMessageData('No products are mapped to this supplier.');
|
||||||
setMessageType('warning')
|
setMessageType('warning');
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
setSupplierProducts([]);
|
setSupplierProducts([]);
|
||||||
setMessageData('Failed to fetch supplier products.');
|
setMessageData('Failed to fetch supplier products.');
|
||||||
setMessageType('error')
|
setMessageType('error');
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error fetching supplier products:', error);
|
console.error('Error fetching supplier products:', error);
|
||||||
setMessageData('An error occurred while fetching products.');
|
setMessageData('An error occurred while fetching products.');
|
||||||
setMessageType('error')
|
setMessageType('error');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -227,23 +225,55 @@ const PurchaseExcel = ({ handleSubmit }) => {
|
||||||
}, [data, handleSubmit]);
|
}, [data, handleSubmit]);
|
||||||
|
|
||||||
// File upload handler
|
// 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(
|
const uploadAndProcessExcel = useCallback(
|
||||||
(file) => {
|
(file) => {
|
||||||
const reader = new FileReader();
|
const reader = new FileReader();
|
||||||
|
|
||||||
reader.onload = (e) => {
|
reader.onload = async (e) => {
|
||||||
const arrayBuffer = new Uint8Array(e.target.result);
|
const buffer = e.target.result;
|
||||||
const workbook = read(arrayBuffer, { type: 'array' });
|
|
||||||
const worksheet = workbook.Sheets[workbook.SheetNames[0]];
|
|
||||||
|
|
||||||
const jsonData = utils.sheet_to_json(worksheet, {
|
const workbook = new ExcelJS.Workbook();
|
||||||
header: 1,
|
await workbook.xlsx.load(buffer);
|
||||||
raw: false,
|
|
||||||
dateNF: 'DD/MM/YY',
|
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 }));
|
dispatch(uploadExcel({ file, jsonData: nonEmptyRows }));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -263,7 +293,7 @@ const PurchaseExcel = ({ handleSubmit }) => {
|
||||||
|
|
||||||
if (!isValidExcelFile(file)) {
|
if (!isValidExcelFile(file)) {
|
||||||
setMessageData('Only Excel files (.xls, .xlsx) are allowed.');
|
setMessageData('Only Excel files (.xls, .xlsx) are allowed.');
|
||||||
setMessageType('error')
|
setMessageType('error');
|
||||||
handleCancelData();
|
handleCancelData();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -290,16 +320,20 @@ const PurchaseExcel = ({ handleSubmit }) => {
|
||||||
|
|
||||||
// Download Excel template with supplier-specific products and dynamic variants
|
// Download Excel template with supplier-specific products and dynamic variants
|
||||||
const handleDownload = useCallback(async () => {
|
const handleDownload = useCallback(async () => {
|
||||||
if (!supplierProducts || supplierProducts.length === 0 || !selectedSupplierData) {
|
if (
|
||||||
|
!supplierProducts ||
|
||||||
|
supplierProducts.length === 0 ||
|
||||||
|
!selectedSupplierData
|
||||||
|
) {
|
||||||
setMessageData('No products available to generate Excel.');
|
setMessageData('No products available to generate Excel.');
|
||||||
setMessageType('warning')
|
setMessageType('warning');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const workbook = new ExcelJS.Workbook();
|
const workbook = new ExcelJS.Workbook();
|
||||||
const worksheet = workbook.addWorksheet("Sheet1");
|
const worksheet = workbook.addWorksheet('Sheet1');
|
||||||
const hiddenSheet = workbook.addWorksheet("ProductVariantMap");
|
const hiddenSheet = workbook.addWorksheet('ProductVariantMap');
|
||||||
const validationSheet = workbook.addWorksheet("ValidationHelper");
|
const validationSheet = workbook.addWorksheet('ValidationHelper');
|
||||||
|
|
||||||
// __define-ocg__ - defining column headers for Excel generation
|
// __define-ocg__ - defining column headers for Excel generation
|
||||||
worksheet.columns = EXCEL_COLUMNS.map(({ header, key, width }) => ({
|
worksheet.columns = EXCEL_COLUMNS.map(({ header, key, width }) => ({
|
||||||
|
|
@ -313,11 +347,11 @@ const PurchaseExcel = ({ handleSubmit }) => {
|
||||||
cell.fill = {
|
cell.fill = {
|
||||||
type: 'pattern',
|
type: 'pattern',
|
||||||
pattern: 'solid',
|
pattern: 'solid',
|
||||||
fgColor: { argb: 'FF2929' } // Red background
|
fgColor: { argb: 'FF2929' }, // Red background
|
||||||
};
|
};
|
||||||
cell.font = {
|
cell.font = {
|
||||||
bold: true,
|
bold: true,
|
||||||
color: { argb: '000000' } // White text
|
color: { argb: '000000' }, // White text
|
||||||
};
|
};
|
||||||
cell.border = {
|
cell.border = {
|
||||||
top: { style: 'thin' },
|
top: { style: 'thin' },
|
||||||
|
|
@ -327,14 +361,14 @@ const PurchaseExcel = ({ handleSubmit }) => {
|
||||||
};
|
};
|
||||||
cell.alignment = {
|
cell.alignment = {
|
||||||
horizontal: 'center',
|
horizontal: 'center',
|
||||||
vertical: 'middle'
|
vertical: 'middle',
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
hiddenSheet.addRow(["ProductName", "VariantName"]);
|
hiddenSheet.addRow(['ProductName', 'VariantName']);
|
||||||
|
|
||||||
// Add validation helper columns
|
// Add validation helper columns
|
||||||
validationSheet.addRow(["IsDuplicate"]);
|
validationSheet.addRow(['IsDuplicate']);
|
||||||
|
|
||||||
const supplierDisplayName = getSupplierDisplayName(selectedSupplierData);
|
const supplierDisplayName = getSupplierDisplayName(selectedSupplierData);
|
||||||
const productNames = supplierProducts.map((p) => p.ProdName);
|
const productNames = supplierProducts.map((p) => p.ProdName);
|
||||||
|
|
@ -347,7 +381,7 @@ const PurchaseExcel = ({ handleSubmit }) => {
|
||||||
const uniqueVariantNames = [
|
const uniqueVariantNames = [
|
||||||
...new Set(
|
...new Set(
|
||||||
(product.ProdVariantPriceDetails || [])
|
(product.ProdVariantPriceDetails || [])
|
||||||
.filter((v) => v.ActiveStatus === "A")
|
.filter((v) => v.ActiveStatus === 'A')
|
||||||
.map((v) => v.ProdVariantName)
|
.map((v) => v.ProdVariantName)
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
|
|
@ -362,10 +396,9 @@ const PurchaseExcel = ({ handleSubmit }) => {
|
||||||
|
|
||||||
const endRow = currentRow - 1;
|
const endRow = currentRow - 1;
|
||||||
|
|
||||||
const safeName = product.ProdName
|
const safeName = product.ProdName.trim()
|
||||||
.trim()
|
.replace(/[^A-Za-z0-9_]/g, '_')
|
||||||
.replace(/[^A-Za-z0-9_]/g, "_")
|
.replace(/^(\d)/, '_$1');
|
||||||
.replace(/^(\d)/, "_$1");
|
|
||||||
|
|
||||||
const rangeRef = `ProductVariantMap!$B$${startRow}:$B$${endRow}`;
|
const rangeRef = `ProductVariantMap!$B$${startRow}:$B$${endRow}`;
|
||||||
productRanges[safeName] = rangeRef;
|
productRanges[safeName] = rangeRef;
|
||||||
|
|
@ -378,21 +411,23 @@ const PurchaseExcel = ({ handleSubmit }) => {
|
||||||
for (let i = 0; i < 50; i++) worksheet.addRow({});
|
for (let i = 0; i < 50; i++) worksheet.addRow({});
|
||||||
|
|
||||||
// Find ProductName column index
|
// 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);
|
const productNameColLetter = String.fromCharCode(65 + productNameColIndex);
|
||||||
|
|
||||||
// Step 3: Add a helper column for duplicate detection (will be hidden)
|
// Step 3: Add a helper column for duplicate detection (will be hidden)
|
||||||
const helperColIndex = EXCEL_COLUMNS.length;
|
const helperColIndex = EXCEL_COLUMNS.length;
|
||||||
const helperColLetter = String.fromCharCode(65 + helperColIndex);
|
const helperColLetter = String.fromCharCode(65 + helperColIndex);
|
||||||
|
|
||||||
worksheet.getColumn(helperColLetter).header = "DuplicateCheck";
|
worksheet.getColumn(helperColLetter).header = 'DuplicateCheck';
|
||||||
worksheet.getColumn(helperColLetter).width = 5;
|
worksheet.getColumn(helperColLetter).width = 5;
|
||||||
|
|
||||||
// Add formula to detect duplicates in helper column
|
// Add formula to detect duplicates in helper column
|
||||||
for (let rowNumber = 2; rowNumber <= 51; rowNumber++) {
|
for (let rowNumber = 2; rowNumber <= 51; rowNumber++) {
|
||||||
const helperCell = worksheet.getCell(`${helperColLetter}${rowNumber}`);
|
const helperCell = worksheet.getCell(`${helperColLetter}${rowNumber}`);
|
||||||
helperCell.value = {
|
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,96 +435,99 @@ const PurchaseExcel = ({ handleSubmit }) => {
|
||||||
EXCEL_COLUMNS.forEach(({ header, key }, index) => {
|
EXCEL_COLUMNS.forEach(({ header, key }, index) => {
|
||||||
const columnLetter = String.fromCharCode(65 + index);
|
const columnLetter = String.fromCharCode(65 + index);
|
||||||
|
|
||||||
worksheet.getColumn(columnLetter).eachCell({ includeEmpty: true }, (cell, rowNumber) => {
|
worksheet
|
||||||
if (rowNumber <= 1) return;
|
.getColumn(columnLetter)
|
||||||
|
.eachCell({ includeEmpty: true }, (cell, rowNumber) => {
|
||||||
|
if (rowNumber <= 1) return;
|
||||||
|
|
||||||
if (key === "Supplier") {
|
if (key === 'Supplier') {
|
||||||
if (rowNumber === 2) cell.value = supplierDisplayName;
|
if (rowNumber === 2) cell.value = supplierDisplayName;
|
||||||
cell.dataValidation = {
|
cell.dataValidation = {
|
||||||
type: "list",
|
type: 'list',
|
||||||
allowBlank: false,
|
allowBlank: false,
|
||||||
formulae: [`"${supplierDisplayName}"`],
|
formulae: [`"${supplierDisplayName}"`],
|
||||||
};
|
};
|
||||||
} else if (key === "ProductName") {
|
} else if (key === 'ProductName') {
|
||||||
// Keep the dropdown list
|
// Keep the dropdown list
|
||||||
cell.dataValidation = {
|
cell.dataValidation = {
|
||||||
type: "list",
|
type: 'list',
|
||||||
allowBlank: true,
|
allowBlank: true,
|
||||||
formulae: [`"${productNames.join(",")}"`],
|
formulae: [`"${productNames.join(',')}"`],
|
||||||
};
|
};
|
||||||
|
|
||||||
// Add conditional formatting to highlight duplicates
|
// Add conditional formatting to highlight duplicates
|
||||||
worksheet.addConditionalFormatting({
|
worksheet.addConditionalFormatting({
|
||||||
ref: `${productNameColLetter}${rowNumber}`,
|
ref: `${productNameColLetter}${rowNumber}`,
|
||||||
rules: [
|
rules: [
|
||||||
{
|
{
|
||||||
type: 'expression',
|
type: 'expression',
|
||||||
formulae: [`${helperColLetter}${rowNumber}=TRUE`],
|
formulae: [`${helperColLetter}${rowNumber}=TRUE`],
|
||||||
style: {
|
style: {
|
||||||
fill: {
|
fill: {
|
||||||
type: 'pattern',
|
type: 'pattern',
|
||||||
pattern: 'solid',
|
pattern: 'solid',
|
||||||
bgColor: { argb: 'FFFF6B6B' }
|
bgColor: { argb: 'FFFF6B6B' },
|
||||||
|
},
|
||||||
|
font: {
|
||||||
|
color: { argb: 'FFFFFFFF' },
|
||||||
|
bold: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
font: {
|
},
|
||||||
color: { argb: 'FFFFFFFF' },
|
],
|
||||||
bold: true
|
});
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
|
|
||||||
// Add a warning note
|
// Add a warning note
|
||||||
cell.note = "⚠️ WARNING: Each product should only be selected once. Duplicates will be highlighted in red.";
|
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}," ","_"),"-","_"),"&","_"),"(","_"),")","_"))`;
|
||||||
|
|
||||||
} else if (key === "VariantName") {
|
cell.dataValidation = {
|
||||||
const productCell = `${productNameColLetter}${rowNumber}`;
|
type: 'list',
|
||||||
const formula = `INDIRECT(SUBSTITUTE(SUBSTITUTE(SUBSTITUTE(SUBSTITUTE(SUBSTITUTE(${productCell}," ","_"),"-","_"),"&","_"),"(","_"),")","_"))`;
|
allowBlank: true,
|
||||||
|
formulae: [formula],
|
||||||
cell.dataValidation = {
|
};
|
||||||
type: "list",
|
cell.note =
|
||||||
allowBlank: true,
|
'Select product first; variants depend on chosen product.';
|
||||||
formulae: [formula],
|
} else if (key === 'Quantity') {
|
||||||
};
|
cell.dataValidation = {
|
||||||
cell.note = "Select product first; variants depend on chosen product.";
|
type: 'custom',
|
||||||
} else if (key === "Quantity") {
|
allowBlank: true,
|
||||||
cell.dataValidation = {
|
formulae: [`ISNUMBER(${columnLetter}${rowNumber})`],
|
||||||
type: "custom",
|
showErrorMessage: true,
|
||||||
allowBlank: true,
|
errorTitle: 'Validation Error',
|
||||||
formulae: [`ISNUMBER(${columnLetter}${rowNumber})`],
|
error: 'Enter a valid number.',
|
||||||
showErrorMessage: true,
|
};
|
||||||
errorTitle: "Validation Error",
|
} else if (key === 'QrCode') {
|
||||||
error: "Enter a valid number.",
|
cell.dataValidation = {
|
||||||
};
|
type: 'custom',
|
||||||
} else if (key === "QrCode") {
|
allowBlank: false,
|
||||||
cell.dataValidation = {
|
formulae: [`ISNUMBER(${columnLetter}${rowNumber})`],
|
||||||
type: "custom",
|
showErrorMessage: true,
|
||||||
allowBlank: false,
|
errorTitle: 'Validation Error',
|
||||||
formulae: [`ISNUMBER(${columnLetter}${rowNumber})`],
|
error: 'Enter a valid number.',
|
||||||
showErrorMessage: true,
|
};
|
||||||
errorTitle: "Validation Error",
|
}
|
||||||
error: "Enter a valid number.",
|
});
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Step 5: Hide the helper column
|
// Step 5: Hide the helper column
|
||||||
worksheet.getColumn(helperColLetter).hidden = true;
|
worksheet.getColumn(helperColLetter).hidden = true;
|
||||||
|
|
||||||
// Step 6: Hide the mapping sheets
|
// Step 6: Hide the mapping sheets
|
||||||
hiddenSheet.state = "veryHidden";
|
hiddenSheet.state = 'veryHidden';
|
||||||
validationSheet.state = "veryHidden";
|
validationSheet.state = 'veryHidden';
|
||||||
|
|
||||||
// Step 7: Export file
|
// Step 7: Export file
|
||||||
const buffer = await workbook.xlsx.writeBuffer();
|
const buffer = await workbook.xlsx.writeBuffer();
|
||||||
const blob = new Blob([buffer], {
|
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 filename = `PurchaseOrder_${supplierDisplayName}.xlsx`;
|
||||||
const link = document.createElement("a");
|
const link = document.createElement('a');
|
||||||
link.href = URL.createObjectURL(blob);
|
link.href = URL.createObjectURL(blob);
|
||||||
link.download = filename;
|
link.download = filename;
|
||||||
link.click();
|
link.click();
|
||||||
|
|
@ -499,7 +537,6 @@ const PurchaseExcel = ({ handleSubmit }) => {
|
||||||
handleModalCancel();
|
handleModalCancel();
|
||||||
}, [supplierProducts, selectedSupplierData, getSupplierDisplayName]);
|
}, [supplierProducts, selectedSupplierData, getSupplierDisplayName]);
|
||||||
|
|
||||||
|
|
||||||
const handleCancelData = useCallback(() => {
|
const handleCancelData = useCallback(() => {
|
||||||
dispatch(emptyExcelData());
|
dispatch(emptyExcelData());
|
||||||
if (fileInputRef.current) {
|
if (fileInputRef.current) {
|
||||||
|
|
@ -601,7 +638,7 @@ const PurchaseExcel = ({ handleSubmit }) => {
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error fetching suppliers:', error);
|
console.error('Error fetching suppliers:', error);
|
||||||
setMessageData('Failed to fetch suppliers.');
|
setMessageData('Failed to fetch suppliers.');
|
||||||
setMessageType('error')
|
setMessageType('error');
|
||||||
}
|
}
|
||||||
}, [CompId, AppId, BranchId, dispatch]);
|
}, [CompId, AppId, BranchId, dispatch]);
|
||||||
|
|
||||||
|
|
@ -620,7 +657,7 @@ const PurchaseExcel = ({ handleSubmit }) => {
|
||||||
const handleSupplierBasedProducts = useCallback(async () => {
|
const handleSupplierBasedProducts = useCallback(async () => {
|
||||||
if (!selectedSupplier) {
|
if (!selectedSupplier) {
|
||||||
setMessageData('Please select a supplier.');
|
setMessageData('Please select a supplier.');
|
||||||
setMessageType('warning')
|
setMessageType('warning');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -629,7 +666,14 @@ const PurchaseExcel = ({ handleSubmit }) => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="container">
|
<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 className="form">
|
||||||
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
|
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
|
||||||
<label>
|
<label>
|
||||||
|
|
@ -698,7 +742,10 @@ const PurchaseExcel = ({ handleSubmit }) => {
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="viewerExcelupload" style={{ maxHeight: '500px', overflow: 'auto' }}>
|
<div
|
||||||
|
className="viewerExcelupload"
|
||||||
|
style={{ maxHeight: '500px', overflow: 'auto' }}
|
||||||
|
>
|
||||||
{hasData && (
|
{hasData && (
|
||||||
<Table
|
<Table
|
||||||
columns={columns}
|
columns={columns}
|
||||||
|
|
@ -808,4 +855,4 @@ const PurchaseExcel = ({ handleSubmit }) => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default PurchaseExcel;
|
export default PurchaseExcel;
|
||||||
|
|
|
||||||
|
|
@ -15,16 +15,12 @@ import {
|
||||||
PurchaseOrderMailPost,
|
PurchaseOrderMailPost,
|
||||||
} from '../../Features/PurchaseOrder/PurchaseOrder';
|
} from '../../Features/PurchaseOrder/PurchaseOrder';
|
||||||
import { Breadcrumb } from 'antd';
|
import { Breadcrumb } from 'antd';
|
||||||
import { styled } from '@mui/system';
|
|
||||||
import {
|
import {
|
||||||
breadCrumbSelector,
|
breadCrumbSelector,
|
||||||
changeBreadCrumb,
|
changeBreadCrumb,
|
||||||
} from '../../Features/AppPage/CenterPage';
|
} from '../../Features/AppPage/CenterPage';
|
||||||
|
|
||||||
const Input = styled('input')({
|
|
||||||
display: 'none',
|
|
||||||
});
|
|
||||||
|
|
||||||
const subDirectory = import.meta.env.BASE_URL;
|
const subDirectory = import.meta.env.BASE_URL;
|
||||||
|
|
||||||
const PurchaseOrderMail = () => {
|
const PurchaseOrderMail = () => {
|
||||||
|
|
@ -599,7 +595,7 @@ const PurchaseOrderMail = () => {
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<InputField
|
<InputField
|
||||||
label={<label class="required">Receiver Name</label>}
|
label={<label className="required">Receiver Name</label>}
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
setdeliveryName(e?.target?.value);
|
setdeliveryName(e?.target?.value);
|
||||||
|
|
@ -631,7 +627,7 @@ const PurchaseOrderMail = () => {
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<InputField
|
<InputField
|
||||||
label={<label class="required">Mobile Number</label>}
|
label={<label className="required">Mobile Number</label>}
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
maxLength={10}
|
maxLength={10}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
|
|
@ -661,7 +657,7 @@ const PurchaseOrderMail = () => {
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<InputField
|
<InputField
|
||||||
label={<label class="required">Shipping Address</label>}
|
label={<label className="required">Shipping Address</label>}
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
setdeliveryAddress(e?.target?.value);
|
setdeliveryAddress(e?.target?.value);
|
||||||
|
|
@ -688,7 +684,7 @@ const PurchaseOrderMail = () => {
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<InputField
|
<InputField
|
||||||
label={<label class="required">City / Town</label>}
|
label={<label className="required">City / Town</label>}
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
setdeliverycity(e?.target?.value);
|
setdeliverycity(e?.target?.value);
|
||||||
|
|
@ -715,7 +711,7 @@ const PurchaseOrderMail = () => {
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<InputField
|
<InputField
|
||||||
label={<label class="required">State </label>}
|
label={<label className="required">State </label>}
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
setdeliveryState(e?.target?.value);
|
setdeliveryState(e?.target?.value);
|
||||||
|
|
@ -745,7 +741,7 @@ const PurchaseOrderMail = () => {
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<InputField
|
<InputField
|
||||||
label={<label class="required">PIN Code</label>}
|
label={<label className="required">PIN Code</label>}
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
setdeliveryZip(e?.target?.value);
|
setdeliveryZip(e?.target?.value);
|
||||||
|
|
|
||||||
|
|
@ -6,9 +6,9 @@ import React, {
|
||||||
useCallback,
|
useCallback,
|
||||||
} from 'react';
|
} from 'react';
|
||||||
import { useDispatch, useSelector } from 'react-redux';
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
import { read, utils } from 'xlsx';
|
// import { read, utils } from 'xlsx';
|
||||||
import ExcelJS from 'exceljs';
|
import ExcelJS from 'exceljs';
|
||||||
import { Table, Form, Input, Button, Tooltip, Modal } from 'antd';
|
import { Table, Form, Input, Tooltip, Modal } from 'antd';
|
||||||
import {
|
import {
|
||||||
emptyExcelData,
|
emptyExcelData,
|
||||||
excelDataSelector,
|
excelDataSelector,
|
||||||
|
|
@ -272,28 +272,36 @@ const StockExcel = ({
|
||||||
const uploadAndProcessExcel = (file) => {
|
const uploadAndProcessExcel = (file) => {
|
||||||
const reader = new FileReader();
|
const reader = new FileReader();
|
||||||
|
|
||||||
reader.onload = (e) => {
|
reader.onload = async (e) => {
|
||||||
const data = new Uint8Array(e.target.result);
|
const buffer = e.target.result;
|
||||||
|
|
||||||
const workbook = read(data, { type: 'array' });
|
const workbook = new ExcelJS.Workbook();
|
||||||
const worksheet = workbook.Sheets[workbook.SheetNames[0]];
|
await workbook.xlsx.load(buffer);
|
||||||
|
|
||||||
const jsonData = utils.sheet_to_json(worksheet, {
|
const worksheet = workbook.worksheets[0];
|
||||||
header: 1,
|
|
||||||
raw: false,
|
// 🔥 same structure as xlsx (array of arrays)
|
||||||
dateNF: 'DD/MM/YY',
|
const jsonData = [];
|
||||||
|
worksheet.eachRow((row) => {
|
||||||
|
const rowValues = row.values.slice(1); // remove empty index
|
||||||
|
jsonData.push(rowValues);
|
||||||
});
|
});
|
||||||
|
|
||||||
const nonEmptyRows = jsonData.filter((row) =>
|
const nonEmptyRows = jsonData.filter((row) =>
|
||||||
row.some((cell) => cell !== '')
|
row.some((cell) => cell !== '' && cell !== null && cell !== undefined)
|
||||||
);
|
);
|
||||||
|
|
||||||
const header = nonEmptyRows[0];
|
const header = nonEmptyRows[0];
|
||||||
const rows = nonEmptyRows.slice(1);
|
const rows = nonEmptyRows.slice(1);
|
||||||
|
|
||||||
const dateFields = ['Manufacture Date', 'Expire Date', 'Stock Date'];
|
const dateFields = ['Manufacture Date', 'Expire Date', 'Stock Date'];
|
||||||
|
|
||||||
function convertToDisplayFormat(dateString) {
|
function convertToDisplayFormat(dateString) {
|
||||||
|
if (!dateString) return '';
|
||||||
|
|
||||||
const date = new Date(dateString);
|
const date = new Date(dateString);
|
||||||
|
if (isNaN(date)) return dateString;
|
||||||
|
|
||||||
const day = date.getDate().toString().padStart(2, '0');
|
const day = date.getDate().toString().padStart(2, '0');
|
||||||
const month = (date.getMonth() + 1).toString().padStart(2, '0');
|
const month = (date.getMonth() + 1).toString().padStart(2, '0');
|
||||||
const year = date.getFullYear().toString();
|
const year = date.getFullYear().toString();
|
||||||
|
|
@ -307,13 +315,12 @@ const StockExcel = ({
|
||||||
} else {
|
} else {
|
||||||
acc[col] = row[columnIndex];
|
acc[col] = row[columnIndex];
|
||||||
}
|
}
|
||||||
|
|
||||||
return acc;
|
return acc;
|
||||||
}, {});
|
}, {});
|
||||||
|
|
||||||
return rowData;
|
return rowData;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ✅ unchanged logic
|
||||||
dispatch(uploadExcel({ file, jsonData: nonEmptyRows }));
|
dispatch(uploadExcel({ file, jsonData: nonEmptyRows }));
|
||||||
setWorksheet1(worksheet1);
|
setWorksheet1(worksheet1);
|
||||||
setDatas(formattedData);
|
setDatas(formattedData);
|
||||||
|
|
@ -322,6 +329,59 @@ const StockExcel = ({
|
||||||
reader.readAsArrayBuffer(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);
|
||||||
|
// };
|
||||||
|
|
||||||
// ...existing code...
|
// ...existing code...
|
||||||
const handleDownload = async () => {
|
const handleDownload = async () => {
|
||||||
try {
|
try {
|
||||||
|
|
@ -441,8 +501,8 @@ const StockExcel = ({
|
||||||
: c.key === 'Quantity'
|
: c.key === 'Quantity'
|
||||||
? 10
|
? 10
|
||||||
: c.key === 'Supplier' ||
|
: c.key === 'Supplier' ||
|
||||||
c.key === 'InvoiceDeliveryChallan' ||
|
c.key === 'InvoiceDeliveryChallan' ||
|
||||||
c.key === 'SupplierInvoiceNumber'
|
c.key === 'SupplierInvoiceNumber'
|
||||||
? 30
|
? 30
|
||||||
: 20,
|
: 20,
|
||||||
}));
|
}));
|
||||||
|
|
@ -651,7 +711,7 @@ const StockExcel = ({
|
||||||
cell.dataValidation = {
|
cell.dataValidation = {
|
||||||
type: 'list',
|
type: 'list',
|
||||||
formulae: ['"Own,With Paid"'],
|
formulae: ['"Own,With Paid"'],
|
||||||
allowBlank: true
|
allowBlank: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!cell.value) {
|
if (!cell.value) {
|
||||||
|
|
@ -1239,7 +1299,7 @@ const StockExcel = ({
|
||||||
...record,
|
...record,
|
||||||
...values,
|
...values,
|
||||||
});
|
});
|
||||||
} catch (errInfo) { }
|
} catch (errInfo) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
let childNode = children;
|
let childNode = children;
|
||||||
|
|
|
||||||
|
|
@ -48,9 +48,8 @@ import {
|
||||||
} from '../../Features/ExcelUploadPage/ExcelUploadPage.js';
|
} from '../../Features/ExcelUploadPage/ExcelUploadPage.js';
|
||||||
const subDirectory = import.meta.env.BASE_URL;
|
const subDirectory = import.meta.env.BASE_URL;
|
||||||
import ExcelJS from 'exceljs';
|
import ExcelJS from 'exceljs';
|
||||||
import { read, utils } from 'xlsx';
|
// import { read, utils } from 'xlsx';
|
||||||
import { v4 as uuidv4 } from 'uuid';
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
import { InputField } from '../../Components/Forms/InputField.jsx';
|
|
||||||
import { MdOutlineAppRegistration } from 'react-icons/md';
|
import { MdOutlineAppRegistration } from 'react-icons/md';
|
||||||
import {
|
import {
|
||||||
getFieldSetupData,
|
getFieldSetupData,
|
||||||
|
|
@ -737,7 +736,12 @@ const StockPriceUpdate = () => {
|
||||||
fetchData();
|
fetchData();
|
||||||
}, []);
|
}, []);
|
||||||
const getPreference = async () => {
|
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 { data: res } = await dispatch(getPreferenceData(data)).unwrap();
|
||||||
const decimalSetting = res?.data?.[0]?.SettingDtlDetails?.find(
|
const decimalSetting = res?.data?.[0]?.SettingDtlDetails?.find(
|
||||||
(setting) =>
|
(setting) =>
|
||||||
|
|
@ -1820,15 +1824,17 @@ const StockPriceUpdate = () => {
|
||||||
|
|
||||||
excelDataRef.current?.setFieldsValue(formValues);
|
excelDataRef.current?.setFieldsValue(formValues);
|
||||||
};
|
};
|
||||||
|
const extractPriceChangeDataFromExcel = async (arrayBuffer) => {
|
||||||
|
const workbook = new ExcelJS.Workbook();
|
||||||
|
await workbook.xlsx.load(arrayBuffer);
|
||||||
|
|
||||||
const extractPriceChangeDataFromExcel = (arrayBuffer) => {
|
const worksheet = workbook.worksheets[0];
|
||||||
const workbook = read(arrayBuffer, { type: 'array' });
|
|
||||||
const worksheet = workbook.Sheets[workbook.SheetNames[0]];
|
|
||||||
|
|
||||||
const rows = utils.sheet_to_json(worksheet, {
|
// 🔥 same structure as xlsx (array of arrays)
|
||||||
header: 1,
|
const rows = [];
|
||||||
raw: false,
|
worksheet.eachRow((row) => {
|
||||||
defval: '',
|
const rowValues = row.values.slice(1); // remove empty index
|
||||||
|
rows.push(rowValues.map((cell) => cell ?? '')); // match defval: ''
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!rows.length) return [];
|
if (!rows.length) return [];
|
||||||
|
|
@ -1901,7 +1907,7 @@ const StockPriceUpdate = () => {
|
||||||
? Number(getVal(row, 'Old Wholesale Price'))
|
? Number(getVal(row, 'Old Wholesale Price'))
|
||||||
: null,
|
: null,
|
||||||
|
|
||||||
// 🔹 New prices (normalized)
|
// 🔹 New prices
|
||||||
NewMRP: newMRP,
|
NewMRP: newMRP,
|
||||||
NewSellPrice: newSP,
|
NewSellPrice: newSP,
|
||||||
NewWholeSalePrice: newWholesale,
|
NewWholeSalePrice: newWholesale,
|
||||||
|
|
@ -1911,6 +1917,96 @@ const StockPriceUpdate = () => {
|
||||||
|
|
||||||
return result;
|
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 = () => {
|
const handleCancelData = () => {
|
||||||
dispatch(emptyExcelData());
|
dispatch(emptyExcelData());
|
||||||
|
|
|
||||||
|
|
@ -1,28 +1,28 @@
|
||||||
import React, { useCallback, useMemo, useRef, useState } from "react";
|
import { useCallback, useMemo, useRef, useState } from 'react';
|
||||||
import { read, utils } from "xlsx";
|
// import { read, utils } from "xlsx";
|
||||||
import ExcelJS from "exceljs";
|
import ExcelJS from 'exceljs';
|
||||||
import { Button } from "antd";
|
import { Button } from 'antd';
|
||||||
import { MdRefresh, MdInfo } from "react-icons/md";
|
import { MdRefresh, MdInfo } from 'react-icons/md';
|
||||||
import { RiFileExcel2Fill } from "react-icons/ri";
|
import { RiFileExcel2Fill } from 'react-icons/ri';
|
||||||
import { Messages } from "../../Components/Notifications/Messages.jsx";
|
import { Messages } from '../../Components/Notifications/Messages.jsx';
|
||||||
import { getSession } from "../../Services/Others.js";
|
import { getSession } from '../../Services/Others.js';
|
||||||
import "./OpeningStockExcel.scss"
|
import './OpeningStockExcel.scss';
|
||||||
|
|
||||||
const TEMPLATE_HEADERS = [
|
const TEMPLATE_HEADERS = [
|
||||||
{ key: "ProdName", header: "Product Name" },
|
{ key: 'ProdName', header: 'Product Name' },
|
||||||
{ key: "TotalQty", header: "Total Qty" },
|
{ key: 'TotalQty', header: 'Total Qty' },
|
||||||
{ key: "TotalPiece", header: "Total Pieces" },
|
{ key: 'TotalPiece', header: 'Total Pieces' },
|
||||||
];
|
];
|
||||||
|
|
||||||
const OpeningStockExcel = ({ products = [], onApply, onCancel }) => {
|
const OpeningStockExcel = ({ products = [], onApply, onCancel }) => {
|
||||||
const UserId = getSession("UserId");
|
const UserId = getSession('UserId');
|
||||||
const fileInputRef = useRef(null);
|
const fileInputRef = useRef(null);
|
||||||
const [previewRows, setPreviewRows] = useState([]);
|
const [previewRows, setPreviewRows] = useState([]);
|
||||||
const [messageType, setMessageType] = useState(null);
|
const [messageType, setMessageType] = useState(null);
|
||||||
const [messageData, setMessageData] = useState(null);
|
const [messageData, setMessageData] = useState(null);
|
||||||
const [isParsing, setIsParsing] = useState(false);
|
const [isParsing, setIsParsing] = useState(false);
|
||||||
const [inst, setInst] = useState(false)
|
const [inst, setInst] = useState(false);
|
||||||
console.log(previewRows, "previewRows")
|
console.log(previewRows, 'previewRows');
|
||||||
const onComplete = useCallback(() => {
|
const onComplete = useCallback(() => {
|
||||||
setMessageData(null);
|
setMessageData(null);
|
||||||
setMessageType(null);
|
setMessageType(null);
|
||||||
|
|
@ -31,9 +31,9 @@ const OpeningStockExcel = ({ products = [], onApply, onCancel }) => {
|
||||||
const templateColumns = useMemo(
|
const templateColumns = useMemo(
|
||||||
() => [
|
() => [
|
||||||
{
|
{
|
||||||
title: "Product Name",
|
title: 'Product Name',
|
||||||
dataIndex: "productDisplayName",
|
dataIndex: 'productDisplayName',
|
||||||
key: "productDisplayName",
|
key: 'productDisplayName',
|
||||||
width: 200,
|
width: 200,
|
||||||
},
|
},
|
||||||
// {
|
// {
|
||||||
|
|
@ -43,36 +43,35 @@ const OpeningStockExcel = ({ products = [], onApply, onCancel }) => {
|
||||||
// width: 180,
|
// width: 180,
|
||||||
// },
|
// },
|
||||||
{
|
{
|
||||||
title: "Total Qty",
|
title: 'Total Qty',
|
||||||
dataIndex: "TotalQty",
|
dataIndex: 'TotalQty',
|
||||||
key: "TotalQty",
|
key: 'TotalQty',
|
||||||
width: 50,
|
width: 50,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Total Pcs",
|
title: 'Total Pcs',
|
||||||
dataIndex: "TotalPiece",
|
dataIndex: 'TotalPiece',
|
||||||
key: "TotalPiece",
|
key: 'TotalPiece',
|
||||||
width: 50,
|
width: 50,
|
||||||
render: (value, row) =>
|
render: (value, row) => (row?.OnePcsAvailable === 'Y' ? value : 'N/A'),
|
||||||
row?.OnePcsAvailable === "Y" ? value : "N/A",
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
[]
|
[]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleInstrction = () => {
|
const handleInstrction = () => {
|
||||||
setInst(!inst)
|
setInst(!inst);
|
||||||
}
|
};
|
||||||
const handleDownloadTemplate = useCallback(async () => {
|
const handleDownloadTemplate = useCallback(async () => {
|
||||||
if (!products?.length) {
|
if (!products?.length) {
|
||||||
setMessageType("warning");
|
setMessageType('warning');
|
||||||
setMessageData("No products available to include in the template.");
|
setMessageData('No products available to include in the template.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const workbook = new ExcelJS.Workbook();
|
const workbook = new ExcelJS.Workbook();
|
||||||
const worksheet = workbook.addWorksheet("Opening Stock");
|
const worksheet = workbook.addWorksheet('Opening Stock');
|
||||||
|
|
||||||
const headerRow = worksheet.addRow(
|
const headerRow = worksheet.addRow(
|
||||||
TEMPLATE_HEADERS.map((item) => item.header)
|
TEMPLATE_HEADERS.map((item) => item.header)
|
||||||
|
|
@ -80,33 +79,29 @@ const OpeningStockExcel = ({ products = [], onApply, onCancel }) => {
|
||||||
headerRow.eachCell((cell) => {
|
headerRow.eachCell((cell) => {
|
||||||
cell.protection = { locked: true };
|
cell.protection = { locked: true };
|
||||||
cell.fill = {
|
cell.fill = {
|
||||||
type: "pattern",
|
type: 'pattern',
|
||||||
pattern: "solid",
|
pattern: 'solid',
|
||||||
fgColor: { argb: "FF0F6CB6" },
|
fgColor: { argb: 'FF0F6CB6' },
|
||||||
};
|
};
|
||||||
cell.font = {
|
cell.font = {
|
||||||
color: { argb: "FFFFFFFF" },
|
color: { argb: 'FFFFFFFF' },
|
||||||
bold: true,
|
bold: true,
|
||||||
};
|
};
|
||||||
cell.alignment = { vertical: "middle", horizontal: "center" };
|
cell.alignment = { vertical: 'middle', horizontal: 'center' };
|
||||||
cell.border = {
|
cell.border = {
|
||||||
top: { style: "thin", color: { argb: "FFFFFFFF" } },
|
top: { style: 'thin', color: { argb: 'FFFFFFFF' } },
|
||||||
left: { style: "thin", color: { argb: "FFFFFFFF" } },
|
left: { style: 'thin', color: { argb: 'FFFFFFFF' } },
|
||||||
bottom: { style: "thin", color: { argb: "FFFFFFFF" } },
|
bottom: { style: 'thin', color: { argb: 'FFFFFFFF' } },
|
||||||
right: { style: "thin", color: { argb: "FFFFFFFF" } },
|
right: { style: 'thin', color: { argb: 'FFFFFFFF' } },
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
products.forEach((product) => {
|
products.forEach((product) => {
|
||||||
const displayName = product?.ProdVariantName
|
const displayName = product?.ProdVariantName
|
||||||
? `${product?.ProdName ?? ""} (${product?.ProdVariantName ?? ""})`
|
? `${product?.ProdName ?? ''} (${product?.ProdVariantName ?? ''})`
|
||||||
: product?.ProdName ?? "";
|
: (product?.ProdName ?? '');
|
||||||
|
|
||||||
const newRow = worksheet.addRow([
|
const newRow = worksheet.addRow([displayName, '', '']);
|
||||||
displayName,
|
|
||||||
"",
|
|
||||||
"",
|
|
||||||
]);
|
|
||||||
|
|
||||||
const rowNumber = newRow.number;
|
const rowNumber = newRow.number;
|
||||||
const productCell = worksheet.getCell(rowNumber, 1);
|
const productCell = worksheet.getCell(rowNumber, 1);
|
||||||
|
|
@ -114,61 +109,61 @@ const OpeningStockExcel = ({ products = [], onApply, onCancel }) => {
|
||||||
|
|
||||||
const qtyCell = worksheet.getCell(rowNumber, 2);
|
const qtyCell = worksheet.getCell(rowNumber, 2);
|
||||||
qtyCell.dataValidation = {
|
qtyCell.dataValidation = {
|
||||||
type: "decimal",
|
type: 'decimal',
|
||||||
operator: "greaterThanOrEqual",
|
operator: 'greaterThanOrEqual',
|
||||||
allowBlank: true,
|
allowBlank: true,
|
||||||
showErrorMessage: true,
|
showErrorMessage: true,
|
||||||
showInputMessage: true,
|
showInputMessage: true,
|
||||||
formulae: [0],
|
formulae: [0],
|
||||||
errorTitle: "Invalid Quantity",
|
errorTitle: 'Invalid Quantity',
|
||||||
error: "Please enter a numeric value greater than or equal to 0.",
|
error: 'Please enter a numeric value greater than or equal to 0.',
|
||||||
inputTitle: "Total Qty",
|
inputTitle: 'Total Qty',
|
||||||
inputMessage: "Enter the total quantity (numeric).",
|
inputMessage: 'Enter the total quantity (numeric).',
|
||||||
};
|
};
|
||||||
qtyCell.protection = { locked: false };
|
qtyCell.protection = { locked: false };
|
||||||
|
|
||||||
const pieceCell = worksheet.getCell(rowNumber, 3);
|
const pieceCell = worksheet.getCell(rowNumber, 3);
|
||||||
const allowPieces = product?.OnePcsAvailable === "Y";
|
const allowPieces = product?.OnePcsAvailable === 'Y';
|
||||||
|
|
||||||
if (allowPieces) {
|
if (allowPieces) {
|
||||||
pieceCell.dataValidation = {
|
pieceCell.dataValidation = {
|
||||||
type: "decimal",
|
type: 'decimal',
|
||||||
operator: "greaterThanOrEqual",
|
operator: 'greaterThanOrEqual',
|
||||||
allowBlank: true,
|
allowBlank: true,
|
||||||
showErrorMessage: true,
|
showErrorMessage: true,
|
||||||
showInputMessage: true,
|
showInputMessage: true,
|
||||||
formulae: [0],
|
formulae: [0],
|
||||||
errorTitle: "Invalid Total Pieces",
|
errorTitle: 'Invalid Total Pieces',
|
||||||
error: "Please enter a numeric value greater than or equal to 0.",
|
error: 'Please enter a numeric value greater than or equal to 0.',
|
||||||
inputTitle: "Total Pieces",
|
inputTitle: 'Total Pieces',
|
||||||
inputMessage: "Enter the total pieces (numeric).",
|
inputMessage: 'Enter the total pieces (numeric).',
|
||||||
};
|
};
|
||||||
pieceCell.protection = { locked: false };
|
pieceCell.protection = { locked: false };
|
||||||
} else {
|
} else {
|
||||||
pieceCell.dataValidation = {
|
pieceCell.dataValidation = {
|
||||||
type: "custom",
|
type: 'custom',
|
||||||
allowBlank: true,
|
allowBlank: true,
|
||||||
showErrorMessage: true,
|
showErrorMessage: true,
|
||||||
showInputMessage: true,
|
showInputMessage: true,
|
||||||
formulae: [`=LEN(TRIM(C${rowNumber}))=0`],
|
formulae: [`=LEN(TRIM(C${rowNumber}))=0`],
|
||||||
errorTitle: "Not Allowed",
|
errorTitle: 'Not Allowed',
|
||||||
error:
|
error:
|
||||||
"Total Pieces entry is not allowed for this product. Leave this cell empty.",
|
'Total Pieces entry is not allowed for this product. Leave this cell empty.',
|
||||||
inputTitle: "Total Pieces",
|
inputTitle: 'Total Pieces',
|
||||||
inputMessage:
|
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 = {
|
pieceCell.fill = {
|
||||||
type: "pattern",
|
type: 'pattern',
|
||||||
pattern: "solid",
|
pattern: 'solid',
|
||||||
fgColor: { argb: "FFEFEFEF" },
|
fgColor: { argb: 'FFEFEFEF' },
|
||||||
};
|
};
|
||||||
pieceCell.font = {
|
pieceCell.font = {
|
||||||
color: { argb: "FF6E6E6E" },
|
color: { argb: 'FF6E6E6E' },
|
||||||
italic: true,
|
italic: true,
|
||||||
};
|
};
|
||||||
pieceCell.note =
|
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 };
|
pieceCell.protection = { locked: true };
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
@ -185,7 +180,7 @@ const OpeningStockExcel = ({ products = [], onApply, onCancel }) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await worksheet.protect("OpeningStockTemplate", {
|
await worksheet.protect('OpeningStockTemplate', {
|
||||||
selectLockedCells: true,
|
selectLockedCells: true,
|
||||||
selectUnlockedCells: true,
|
selectUnlockedCells: true,
|
||||||
formatCells: false,
|
formatCells: false,
|
||||||
|
|
@ -197,143 +192,306 @@ const OpeningStockExcel = ({ products = [], onApply, onCancel }) => {
|
||||||
deleteRows: false,
|
deleteRows: false,
|
||||||
});
|
});
|
||||||
} catch (protectError) {
|
} catch (protectError) {
|
||||||
console.warn("Worksheet protection not supported in this environment:", protectError);
|
console.warn(
|
||||||
setMessageType("warning");
|
'Worksheet protection not supported in this environment:',
|
||||||
|
protectError
|
||||||
|
);
|
||||||
|
setMessageType('warning');
|
||||||
setMessageData(
|
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 buffer = await workbook.xlsx.writeBuffer();
|
||||||
const blob = new Blob([buffer], {
|
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.href = URL.createObjectURL(blob);
|
||||||
link.download = "opening_stock_template.xlsx";
|
link.download = 'opening_stock_template.xlsx';
|
||||||
link.click();
|
link.click();
|
||||||
URL.revokeObjectURL(link.href);
|
URL.revokeObjectURL(link.href);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Template download failed:", error);
|
console.error('Template download failed:', error);
|
||||||
setMessageType("error");
|
setMessageType('error');
|
||||||
setMessageData("Failed to generate the template. Please try again.");
|
setMessageData('Failed to generate the template. Please try again.');
|
||||||
}
|
}
|
||||||
}, [products]);
|
}, [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(
|
const parseExcelFile = useCallback(
|
||||||
async (file) => {
|
async (file) => {
|
||||||
if (!file) return;
|
if (!file) return;
|
||||||
|
|
||||||
setIsParsing(true);
|
setIsParsing(true);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
const workbook = new ExcelJS.Workbook();
|
||||||
const arrayBuffer = await file.arrayBuffer();
|
const arrayBuffer = await file.arrayBuffer();
|
||||||
const workbook = read(arrayBuffer, { type: "array" });
|
|
||||||
const worksheet = workbook.Sheets[workbook.SheetNames[0]];
|
await workbook.xlsx.load(arrayBuffer);
|
||||||
const rows = utils.sheet_to_json(worksheet, { header: 1, defval: "" });
|
|
||||||
|
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) {
|
if (!rows.length) {
|
||||||
setMessageType("warning");
|
setMessageType('warning');
|
||||||
setMessageData("Uploaded file is empty.");
|
setMessageData('Uploaded file is empty.');
|
||||||
setPreviewRows([]);
|
setPreviewRows([]);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const [headerRow, ...dataRows] = rows;
|
const [headerRow = [], ...dataRows] = rows;
|
||||||
const headerMap = TEMPLATE_HEADERS.reduce(
|
|
||||||
(acc, item) => ({
|
const normalizedHeaders = headerRow.map((h) =>
|
||||||
...acc,
|
h?.toString().trim().toLowerCase()
|
||||||
[item.key]: headerRow.findIndex(
|
|
||||||
(header) =>
|
|
||||||
header?.toString().trim().toLowerCase() ===
|
|
||||||
item.header.toLowerCase()
|
|
||||||
),
|
|
||||||
}),
|
|
||||||
{}
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const headerMap = {};
|
||||||
|
TEMPLATE_HEADERS.forEach((item) => {
|
||||||
|
headerMap[item.key] = normalizedHeaders.indexOf(
|
||||||
|
item.header.toLowerCase()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
const missingHeaders = Object.entries(headerMap)
|
const missingHeaders = Object.entries(headerMap)
|
||||||
.filter(([, index]) => index === -1)
|
.filter(([, index]) => index === -1)
|
||||||
.map(([key]) => key);
|
.map(([key]) => key);
|
||||||
|
|
||||||
if (missingHeaders.length) {
|
if (missingHeaders.length) {
|
||||||
setMessageType("error");
|
setMessageType('error');
|
||||||
setMessageData(
|
setMessageData(
|
||||||
`Missing required column(s) in the Excel file: ${missingHeaders.join(
|
`Missing required column(s): ${missingHeaders.join(', ')}`
|
||||||
", "
|
|
||||||
)}`
|
|
||||||
);
|
);
|
||||||
setPreviewRows([]);
|
setPreviewRows([]);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const productIndex = products.reduce((acc, product) => {
|
// 🔥 Product index
|
||||||
|
const productIndex = {};
|
||||||
|
for (const product of products) {
|
||||||
const displayName = product?.ProdVariantName
|
const displayName = product?.ProdVariantName
|
||||||
? `${product?.ProdName ?? ""} (${product?.ProdVariantName ?? ""})`
|
? `${product?.ProdName ?? ''} (${product?.ProdVariantName ?? ''})`
|
||||||
: product?.ProdName ?? "";
|
: (product?.ProdName ?? '');
|
||||||
|
|
||||||
if (displayName) {
|
if (displayName) {
|
||||||
acc[displayName.trim().toLowerCase()] = product;
|
productIndex[displayName.trim().toLowerCase()] = product;
|
||||||
}
|
}
|
||||||
return acc;
|
}
|
||||||
}, {});
|
|
||||||
|
|
||||||
const parsedRows = [];
|
const parsedRows = [];
|
||||||
const issues = [];
|
const issues = [];
|
||||||
|
|
||||||
dataRows.forEach((row, rowIndex) => {
|
dataRows.forEach((row, rowIndex) => {
|
||||||
const prodNameCell = row[headerMap.ProdName];
|
const rowNum = rowIndex + 2;
|
||||||
const qtyCell = row[headerMap.TotalQty];
|
|
||||||
const pieceCell = row[headerMap.TotalPiece];
|
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 (!productNameValue) {
|
||||||
if (qtyCell || pieceCell) {
|
if (totalQty || totalPiece) {
|
||||||
issues.push(
|
issues.push(`Row ${rowNum}: Product Name is empty.`);
|
||||||
`Row ${rowIndex + 2}: Product Name is empty while quantities are provided.`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const productMatch =
|
const productMatch = productIndex[productNameValue.toLowerCase()];
|
||||||
productIndex[productNameValue.trim().toLowerCase()];
|
|
||||||
if (!productMatch) {
|
if (!productMatch) {
|
||||||
issues.push(
|
issues.push(
|
||||||
`Row ${rowIndex + 2}: Product "${productNameValue}" is not found in available products.`
|
`Row ${rowNum}: Product "${productNameValue}" not found.`
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const totalQty = qtyCell?.toString().trim();
|
if (!totalQty && !totalPiece) return;
|
||||||
const totalPiece = pieceCell?.toString().trim();
|
|
||||||
|
|
||||||
const hasValues =
|
const isValidNumber = (val) =>
|
||||||
(totalQty && totalQty.length > 0) ||
|
!val || /^[0-9]+(\.[0-9]+)?$/.test(val);
|
||||||
(totalPiece && totalPiece.length > 0);
|
|
||||||
|
|
||||||
if (!hasValues) {
|
if (!isValidNumber(totalQty) || !isValidNumber(totalPiece)) {
|
||||||
|
issues.push(`Row ${rowNum}: Invalid number.`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const isQtyValid =
|
let cleanedPieces = totalPiece || '';
|
||||||
!totalQty || /^[0-9]+(\.[0-9]+)?$/.test(totalQty.trim());
|
if (productMatch?.OnePcsAvailable !== 'Y' && cleanedPieces) {
|
||||||
const isPieceValid =
|
|
||||||
!totalPiece || /^[0-9]+(\.[0-9]+)?$/.test(totalPiece.trim());
|
|
||||||
|
|
||||||
if (!isQtyValid || !isPieceValid) {
|
|
||||||
issues.push(
|
issues.push(
|
||||||
`Row ${rowIndex + 2}: Total Qty or Total Pieces is not a valid number.`
|
`Row ${rowNum}: Pieces not allowed for "${productNameValue}". Ignored.`
|
||||||
);
|
);
|
||||||
return;
|
cleanedPieces = '';
|
||||||
}
|
|
||||||
|
|
||||||
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({
|
parsedRows.push({
|
||||||
|
|
@ -342,42 +500,33 @@ const OpeningStockExcel = ({ products = [], onApply, onCancel }) => {
|
||||||
ProdName: productMatch.ProdName,
|
ProdName: productMatch.ProdName,
|
||||||
ProdVariantName: productMatch.ProdVariantName,
|
ProdVariantName: productMatch.ProdVariantName,
|
||||||
productDisplayName: productNameValue,
|
productDisplayName: productNameValue,
|
||||||
TotalQty: totalQty ?? "",
|
TotalQty: totalQty || '',
|
||||||
TotalPiece: cleanedPieces,
|
TotalPiece: cleanedPieces,
|
||||||
OnePcsAvailable: productMatch?.OnePcsAvailable,
|
OnePcsAvailable: productMatch?.OnePcsAvailable,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!parsedRows.length) {
|
if (!parsedRows.length) {
|
||||||
setMessageType("warning");
|
setMessageType('warning');
|
||||||
setMessageData(
|
setMessageData(
|
||||||
issues.length
|
issues.length ? issues.join('\n') : 'No valid rows found.'
|
||||||
? issues.join(" ")
|
|
||||||
: "No valid rows found in the uploaded file."
|
|
||||||
);
|
);
|
||||||
setPreviewRows([]);
|
setPreviewRows([]);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (issues.length) {
|
setMessageType(issues.length ? 'warning' : 'success');
|
||||||
setMessageType("warning");
|
setMessageData(
|
||||||
setMessageData(
|
issues.length
|
||||||
`Some rows were skipped:\n${issues.join("\n")}`
|
? `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);
|
setPreviewRows(parsedRows);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to parse Excel file:", error);
|
console.error('Excel parse error:', error);
|
||||||
setMessageType("error");
|
setMessageType('error');
|
||||||
setMessageData(
|
setMessageData('Invalid Excel file.');
|
||||||
"Failed to read the uploaded file. Please ensure it is a valid Excel document."
|
|
||||||
);
|
|
||||||
setPreviewRows([]);
|
setPreviewRows([]);
|
||||||
} finally {
|
} finally {
|
||||||
setIsParsing(false);
|
setIsParsing(false);
|
||||||
|
|
@ -396,8 +545,8 @@ const OpeningStockExcel = ({ products = [], onApply, onCancel }) => {
|
||||||
|
|
||||||
const handleApply = useCallback(() => {
|
const handleApply = useCallback(() => {
|
||||||
if (!previewRows.length) {
|
if (!previewRows.length) {
|
||||||
setMessageType("warning");
|
setMessageType('warning');
|
||||||
setMessageData("No parsed rows available to apply.");
|
setMessageData('No parsed rows available to apply.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -405,17 +554,19 @@ const OpeningStockExcel = ({ products = [], onApply, onCancel }) => {
|
||||||
previewRows.map((row) => ({
|
previewRows.map((row) => ({
|
||||||
ProdId: row.ProdId,
|
ProdId: row.ProdId,
|
||||||
ProdVariantName: row?.ProdVariantName,
|
ProdVariantName: row?.ProdVariantName,
|
||||||
OnePcsAvailable: row.OnePcsAvailable || "N",
|
OnePcsAvailable: row.OnePcsAvailable || 'N',
|
||||||
"CreatedBy": UserId,
|
CreatedBy: UserId,
|
||||||
TotalQty:
|
TotalQty:
|
||||||
row.TotalQty !== undefined && row.TotalQty !== null
|
row.TotalQty !== undefined && row.TotalQty !== null
|
||||||
? row.TotalQty.toString()
|
? row.TotalQty.toString()
|
||||||
: "",
|
: '',
|
||||||
TotalPcs:
|
TotalPcs:
|
||||||
row.OnePcsAvailable === "Y" && row.TotalPiece !== undefined && row.TotalPiece !== null
|
row.OnePcsAvailable === 'Y' &&
|
||||||
|
row.TotalPiece !== undefined &&
|
||||||
|
row.TotalPiece !== null
|
||||||
? row.TotalPiece.toString()
|
? row.TotalPiece.toString()
|
||||||
: 0,
|
: 0,
|
||||||
InwardDetails: []
|
InwardDetails: [],
|
||||||
}))
|
}))
|
||||||
);
|
);
|
||||||
}, [onApply, previewRows]);
|
}, [onApply, previewRows]);
|
||||||
|
|
@ -425,7 +576,7 @@ const OpeningStockExcel = ({ products = [], onApply, onCancel }) => {
|
||||||
setMessageData(null);
|
setMessageData(null);
|
||||||
setMessageType(null);
|
setMessageType(null);
|
||||||
if (fileInputRef.current) {
|
if (fileInputRef.current) {
|
||||||
fileInputRef.current.value = "";
|
fileInputRef.current.value = '';
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
|
@ -440,7 +591,7 @@ const OpeningStockExcel = ({ products = [], onApply, onCancel }) => {
|
||||||
<div className="openingStock-excel-actions">
|
<div className="openingStock-excel-actions">
|
||||||
<Button
|
<Button
|
||||||
className="openingStock-excel-download-btn"
|
className="openingStock-excel-download-btn"
|
||||||
icon={<RiFileExcel2Fill />}
|
icon={<RiFileExcel2Fill />}
|
||||||
onClick={handleDownloadTemplate}
|
onClick={handleDownloadTemplate}
|
||||||
disabled={!products?.length}
|
disabled={!products?.length}
|
||||||
>
|
>
|
||||||
|
|
@ -448,7 +599,7 @@ const OpeningStockExcel = ({ products = [], onApply, onCancel }) => {
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<label className="openingStock-excel-upload-label">
|
<label className="openingStock-excel-upload-label">
|
||||||
<RiFileExcel2Fill className="openingStock-excel-upload-icon" />
|
<RiFileExcel2Fill className="openingStock-excel-upload-icon" />
|
||||||
Upload Filled Excel
|
Upload Filled Excel
|
||||||
<input
|
<input
|
||||||
ref={fileInputRef}
|
ref={fileInputRef}
|
||||||
|
|
@ -461,7 +612,7 @@ const OpeningStockExcel = ({ products = [], onApply, onCancel }) => {
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
{previewRows.length > 0 && (
|
{previewRows.length > 0 && (
|
||||||
<Button
|
<Button
|
||||||
className="openingStock-excel-reset-btn"
|
className="openingStock-excel-reset-btn"
|
||||||
icon={<MdRefresh />}
|
icon={<MdRefresh />}
|
||||||
onClick={handleReset}
|
onClick={handleReset}
|
||||||
|
|
@ -471,7 +622,10 @@ const OpeningStockExcel = ({ products = [], onApply, onCancel }) => {
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div onClick={handleInstrction} className="openingStock-excel-instruction-toggle">
|
<div
|
||||||
|
onClick={handleInstrction}
|
||||||
|
className="openingStock-excel-instruction-toggle"
|
||||||
|
>
|
||||||
<MdInfo className="openingStock-excel-info-icon" />
|
<MdInfo className="openingStock-excel-info-icon" />
|
||||||
Instructions
|
Instructions
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -479,7 +633,8 @@ const OpeningStockExcel = ({ products = [], onApply, onCancel }) => {
|
||||||
{inst && (
|
{inst && (
|
||||||
<div className="openingStock-excel-instructions">
|
<div className="openingStock-excel-instructions">
|
||||||
<div className="openingStock-excel-instruction-item">
|
<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>
|
||||||
<div className="openingStock-excel-instruction-item">
|
<div className="openingStock-excel-instruction-item">
|
||||||
2. Only update the `Total Qty` and `Total Pieces` columns.
|
2. Only update the `Total Qty` and `Total Pieces` columns.
|
||||||
|
|
@ -505,15 +660,17 @@ const OpeningStockExcel = ({ products = [], onApply, onCancel }) => {
|
||||||
<tr key={row.key}>
|
<tr key={row.key}>
|
||||||
<td>{row.productDisplayName}</td>
|
<td>{row.productDisplayName}</td>
|
||||||
<td>{row.TotalQty}</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>
|
||||||
))
|
))
|
||||||
) : (
|
) : (
|
||||||
<tr>
|
<tr>
|
||||||
<td colSpan="3" className="openingStock-excel-empty-state">
|
<td colSpan="3" className="openingStock-excel-empty-state">
|
||||||
{isParsing
|
{isParsing
|
||||||
? "Parsing uploaded file..."
|
? 'Parsing uploaded file...'
|
||||||
: "No data parsed yet. Upload a filled Excel file to preview changes."}
|
: 'No data parsed yet. Upload a filled Excel file to preview changes.'}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
)}
|
)}
|
||||||
|
|
@ -522,11 +679,10 @@ const OpeningStockExcel = ({ products = [], onApply, onCancel }) => {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="openingStock-excel-footer">
|
<div className="openingStock-excel-footer">
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
className="openingStock-excel-apply-btn"
|
className="openingStock-excel-apply-btn"
|
||||||
type="primary"
|
type="primary"
|
||||||
icon={<RiFileExcel2Fill />}
|
icon={<RiFileExcel2Fill />}
|
||||||
onClick={handleApply}
|
onClick={handleApply}
|
||||||
disabled={!previewRows.length}
|
disabled={!previewRows.length}
|
||||||
>
|
>
|
||||||
|
|
@ -538,4 +694,3 @@ const OpeningStockExcel = ({ products = [], onApply, onCancel }) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
export default OpeningStockExcel;
|
export default OpeningStockExcel;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,6 @@ const commonUrl = import.meta.env.ENV_COMMON_BASE_URL;
|
||||||
const subDirectory = import.meta.env.BASE_URL;
|
const subDirectory = import.meta.env.BASE_URL;
|
||||||
|
|
||||||
const ProtectedRoutes = ({ routesConfig }) => {
|
const ProtectedRoutes = ({ routesConfig }) => {
|
||||||
|
|
||||||
useTemplate(
|
useTemplate(
|
||||||
getSession('CompId'),
|
getSession('CompId'),
|
||||||
getSession('BranchId'),
|
getSession('BranchId'),
|
||||||
|
|
@ -91,7 +90,7 @@ const ProtectedRoutes = ({ routesConfig }) => {
|
||||||
AppId: AppId,
|
AppId: AppId,
|
||||||
CompId: CompId,
|
CompId: CompId,
|
||||||
BranchId: BranchId,
|
BranchId: BranchId,
|
||||||
UserId:UserId
|
UserId: UserId,
|
||||||
};
|
};
|
||||||
const response = await dispatch(gettableData(DineinData)).unwrap();
|
const response = await dispatch(gettableData(DineinData)).unwrap();
|
||||||
if (response?.data?.statusCode === 1) {
|
if (response?.data?.statusCode === 1) {
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,9 @@ import moment from 'moment';
|
||||||
import jsPDF from 'jspdf';
|
import jsPDF from 'jspdf';
|
||||||
import { changePrintStatus } from '../Features/BookingScreen/BookingData/BookingData';
|
import { changePrintStatus } from '../Features/BookingScreen/BookingData/BookingData';
|
||||||
import store from '../app/store.js';
|
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 cookies = new Cookies();
|
||||||
const SECRET_KEY = import.meta.env.ENV_SECRET_KEY;
|
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 jsonString = JSON.stringify(obj);
|
||||||
const encrypted = CryptoJS.AES.encrypt(jsonString, SECRET_KEY).toString();
|
const encrypted = CryptoJS.AES.encrypt(jsonString, SECRET_KEY).toString();
|
||||||
|
|
||||||
// Make Base64 string URL-safe
|
// Make Base64 string URL-safe
|
||||||
const urlSafe = encrypted
|
const urlSafe = encrypted
|
||||||
.replace(/\+/g, "-")
|
.replace(/\+/g, '-')
|
||||||
.replace(/\//g, "_")
|
.replace(/\//g, '_')
|
||||||
.replace(/=+$/, "");
|
.replace(/=+$/, '');
|
||||||
|
|
||||||
return urlSafe;
|
return urlSafe;
|
||||||
}
|
};
|
||||||
|
|
||||||
// Decrypt URL-safe AES string to original object
|
// Decrypt URL-safe AES string to original object
|
||||||
export const decryptToObject=(encryptedUrlSafeStr) =>{
|
export const decryptToObject = (encryptedUrlSafeStr) => {
|
||||||
try {
|
try {
|
||||||
if (!encryptedUrlSafeStr) return null;
|
if (!encryptedUrlSafeStr) return null;
|
||||||
|
|
||||||
// Restore base64 format
|
// Restore base64 format
|
||||||
let base64 = encryptedUrlSafeStr
|
let base64 = encryptedUrlSafeStr.replace(/-/g, '+').replace(/_/g, '/');
|
||||||
.replace(/-/g, "+")
|
|
||||||
.replace(/_/g, "/");
|
|
||||||
|
|
||||||
// Add missing padding
|
// Add missing padding
|
||||||
while (base64.length % 4 !== 0) {
|
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);
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
};
|
||||||
export const TokendecryptedValuesFun = (value) => {
|
export const TokendecryptedValuesFun = (value) => {
|
||||||
if (!value) {
|
if (!value) {
|
||||||
return null;
|
return null;
|
||||||
|
|
@ -173,7 +173,7 @@ export async function ClearCookie() {
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function printDiv(id, style) {
|
export async function printDiv(id, style) {
|
||||||
console.log("printDiv",id, style)
|
console.log('printDiv', id, style);
|
||||||
return new Promise(async (resolve, reject) => {
|
return new Promise(async (resolve, reject) => {
|
||||||
const content = document.getElementById(id);
|
const content = document.getElementById(id);
|
||||||
|
|
||||||
|
|
@ -251,21 +251,20 @@ export function dateFormatChange(dateString) {
|
||||||
export function dateFormatChange1(dateString) {
|
export function dateFormatChange1(dateString) {
|
||||||
return moment(dateString, [
|
return moment(dateString, [
|
||||||
'YYYY-MM-DDTHH:mm:ss.SSS',
|
'YYYY-MM-DDTHH:mm:ss.SSS',
|
||||||
'YYYY-MM-DDTHH:mm:ss'
|
'YYYY-MM-DDTHH:mm:ss',
|
||||||
]).format('DD-MMM-YYYY hh:mm A');
|
]).format('DD-MMM-YYYY hh:mm A');
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ExtractDateFormate(dateString) {
|
export function ExtractDateFormate(dateString) {
|
||||||
if (!dateString) return "";
|
if (!dateString) return '';
|
||||||
const date = new Date(dateString);
|
const date = new Date(dateString);
|
||||||
if (isNaN(date)) return "";
|
if (isNaN(date)) return '';
|
||||||
const day = date.getDate();
|
const day = date.getDate();
|
||||||
const month = date.toLocaleString("en-US", { month: "short" });
|
const month = date.toLocaleString('en-US', { month: 'short' });
|
||||||
const year = date.getFullYear();
|
const year = date.getFullYear();
|
||||||
return `${day}-${month}-${year}`;
|
return `${day}-${month}-${year}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// export function dateTimeFormatChange(dateString) {
|
// export function dateTimeFormatChange(dateString) {
|
||||||
// let date = moment(dateString, ['YYYY-MM-DDTHH:mm:ss']).format('DD-MM-YYYY hh:mm:ss A');
|
// let date = moment(dateString, ['YYYY-MM-DDTHH:mm:ss']).format('DD-MM-YYYY hh:mm:ss A');
|
||||||
// let finaldate = date.replace('T', ' ');
|
// let finaldate = date.replace('T', ' ');
|
||||||
|
|
@ -273,7 +272,9 @@ export function ExtractDateFormate(dateString) {
|
||||||
// }
|
// }
|
||||||
|
|
||||||
export function dateTimeFormatChange(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) {
|
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);
|
// const content = document.getElementById(id);
|
||||||
|
|
||||||
// if (!content) {
|
// if (!content) {
|
||||||
|
|
@ -311,87 +373,39 @@ export function handleGeneratePDF(id) {
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// try {
|
// try {
|
||||||
// // Create a wrapper to apply styles
|
// // Create wrapper div
|
||||||
// const wrapper = document.createElement('div');
|
// const wrapper = document.createElement('div');
|
||||||
// wrapper.innerHTML = `
|
// wrapper.innerHTML = `
|
||||||
// ${style}
|
// ${style}
|
||||||
// ${content.innerHTML}
|
// ${content.innerHTML}
|
||||||
// `;
|
// `;
|
||||||
|
|
||||||
// // Generate PDF Blob
|
// // PDF options
|
||||||
// const opt = {
|
// const opt = {
|
||||||
// margin: 0.5,
|
// margin: 0,
|
||||||
// image: { type: 'jpeg', quality: 0.98 },
|
// image: { type: 'jpeg', quality: 0.9 },
|
||||||
// html2canvas: { scale: 2, useCORS: true },
|
// html2canvas: {
|
||||||
// jsPDF: { unit: 'mm', format: 'a4', orientation: 'portrait' },
|
// scale: 2,
|
||||||
// pagebreak: {
|
// useCORS: true,
|
||||||
// mode: ['avoid-all', 'css', 'legacy'],
|
// letterRendering: true,
|
||||||
// before: '.page-break-before',
|
// },
|
||||||
// after: '.page-break-after',
|
// jsPDF: {
|
||||||
// avoid: '.page-break-avoid'
|
// unit: 'mm',
|
||||||
// }
|
// format: 'a4',
|
||||||
|
// orientation: 'portrait',
|
||||||
|
// },
|
||||||
|
// pagebreak: {
|
||||||
|
// mode: ['avoid-all', 'css', 'legacy'],
|
||||||
|
// },
|
||||||
// };
|
// };
|
||||||
|
|
||||||
// const pdfBlob = await html2pdf().set(opt).from(wrapper).outputPdf('blob');
|
// const blob = await html2pdf().set(opt).from(wrapper).outputPdf('blob');
|
||||||
// resolve(pdfBlob);
|
// resolve(blob);
|
||||||
// } catch (error) {
|
// } catch (err) {
|
||||||
// reject(error);
|
// 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) => {
|
export const blobToBase64 = (blob) => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
|
|
@ -399,7 +413,7 @@ export const blobToBase64 = (blob) => {
|
||||||
reader.onloadend = () => {
|
reader.onloadend = () => {
|
||||||
const bytes = new Uint8Array(reader.result);
|
const bytes = new Uint8Array(reader.result);
|
||||||
let binary = '';
|
let binary = '';
|
||||||
bytes.forEach((b) => binary += String.fromCharCode(b));
|
bytes.forEach((b) => (binary += String.fromCharCode(b)));
|
||||||
resolve(btoa(binary));
|
resolve(btoa(binary));
|
||||||
};
|
};
|
||||||
reader.onerror = reject;
|
reader.onerror = reject;
|
||||||
|
|
@ -424,5 +438,3 @@ export const validateSafeInput = (value) => {
|
||||||
|
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
8103
src/check.jsx
8103
src/check.jsx
File diff suppressed because it is too large
Load Diff
|
|
@ -1,34 +1,27 @@
|
||||||
import { defineConfig } from "vite";
|
import { defineConfig } from "vite";
|
||||||
import react from "@vitejs/plugin-react";
|
import react from "@vitejs/plugin-react";
|
||||||
import { terser } from "rollup-plugin-terser";
|
|
||||||
|
|
||||||
export default defineConfig(({ command, mode }) => {
|
export default defineConfig(({ mode }) => {
|
||||||
if (mode === "production") {
|
const isProd = mode === "production";
|
||||||
return {
|
|
||||||
plugins: [
|
return {
|
||||||
react(),
|
plugins: [react()],
|
||||||
// terser({ // Add terser plugin for production
|
|
||||||
// compress: {
|
base: isProd ? "/apps/retail/" : "/",
|
||||||
// drop_console: true, // This removes all console.* statements
|
|
||||||
// },
|
envDir: "src",
|
||||||
// }),
|
envPrefix: "ENV_",
|
||||||
],
|
|
||||||
base: "/apps/retail/",
|
server: {
|
||||||
envDir: "src",
|
host: true, // 🔥 auto detect your IP
|
||||||
envPrefix: "ENV_",
|
port: process.env.PORT || 3015,
|
||||||
};
|
},
|
||||||
} else {
|
|
||||||
return {
|
build: {
|
||||||
plugins: [react()],
|
minify: "esbuild",
|
||||||
envDir: "src",
|
esbuild: {
|
||||||
envPrefix: "ENV_",
|
drop: isProd ? ["console", "debugger"] : [],
|
||||||
server: {
|
|
||||||
host: "192.168.1.34",
|
|
||||||
port: process.env.PORT || 3015,
|
|
||||||
},
|
},
|
||||||
optimizeDeps: {
|
},
|
||||||
exclude: ["console.log"],
|
};
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue