expired libary change

This commit is contained in:
mohan 2026-03-23 16:08:11 +05:30
parent a50ea556f1
commit 55255069c4
20 changed files with 9355 additions and 17509 deletions

9194
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -11,8 +11,7 @@
"format": "prettier --write ."
},
"dependencies": {
"@ant-design/plots": "^1.2.6",
"@autocomplete/material-ui": "0.0.17",
"@ant-design/plots": "^2.6.8",
"@capacitor-community/speech-recognition": "^7.0.1",
"@capacitor-mlkit/barcode-scanning": "^8.0.1",
"@capacitor/app": "^8.0.1",
@ -21,53 +20,49 @@
"@dnd-kit/sortable": "^10.0.0",
"@emotion/react": "^11.11.4",
"@emotion/styled": "^11.11.5",
"@material-ui/lab": "^4.0.0-alpha.61",
"@mui/material": "^5.15.18",
"@react-google-maps/api": "^2.20.8",
"@reduxjs/toolkit": "^1.9.5",
"@tanstack/react-query": "^5.90.11",
"@tanstack/react-query-devtools": "^5.91.1",
"@tanstack/react-virtual": "^3.13.18",
"@types/node": "^25.5.0",
"@zxing/library": "^0.21.3",
"antd": "^5.17.4",
"antd-img-crop": "^4.22.0",
"aos": "^2.3.4",
"axios": "^1.7.2",
"apexcharts": "^5.10.4",
"axios": "^1.13.6",
"browser-image-compression": "^2.0.2",
"classnames": "^2.5.1",
"cropperjs": "^1.6.2",
"crypto-js": "^4.1.1",
"date-fns": "^4.1.0",
"devtools-detect": "^4.0.2",
"exceljs": "^4.3.0",
"exceljs": "^4.4.0",
"file-saver": "^2.0.5",
"google-maps-react": "^2.0.6",
"highcharts": "^11.4.3",
"highcharts-react-official": "^3.2.1",
"history": "^5.3.0",
"html2canvas": "^1.4.1",
"html2pdf": "^0.0.11",
"html2pdf.js": "^0.10.3",
"html5-qrcode": "^2.3.8",
"jquery": "^3.7.1",
"js-cookie": "^3.0.5",
"jsbarcode": "^3.11.6",
"jspdf": "^2.5.1",
"jspdf": "^4.2.1",
"lucide": "^0.577.0",
"lucide-react": "^0.536.0",
"material-ui": "^0.20.2",
"moment": "^2.30.1",
"mui-autocomplete": "^2.0.1",
"number-to-words": "^1.2.4",
"pdf-lib": "^1.17.1",
"qr-scanner": "^1.4.2",
"qrcode": "^1.5.3",
"qrcode.react": "^4.2.0",
"react": "^18.3.1",
"react-apexcharts": "^2.1.0",
"react-beautiful-dnd": "^13.1.1",
"react-color": "^2.19.3",
"react-countup": "^6.5.3",
"react-device-detect": "^2.2.3",
"react-devtools": "^5.2.0",
"react-dom": "^18.3.1",
"react-icons": "^4.12.0",
"react-image-crop": "^11.0.10",
@ -82,25 +77,23 @@
"recharts": "^3.5.0",
"string-similarity": "^4.0.4",
"tesseract.js": "^6.0.0",
"universal-cookie": "^6.1.3",
"universal-cookie": "^8.0.1",
"webfontloader": "^1.6.28",
"xlsx": "^0.18.5",
"zustand": "^5.0.8"
},
"devDependencies": {
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
"@vitejs/plugin-react": "^4.3.0",
"@vitejs/plugin-react": "^6.0.1",
"eslint": "^8.57.0",
"eslint-plugin-react": "^7.34.2",
"eslint-plugin-react-hooks": "^4.6.2",
"eslint-plugin-react-refresh": "^0.4.7",
"prettier": "^3.5.3",
"rollup-plugin-obfuscator": "^1.1.0",
"rollup-plugin-terser": "^7.0.2",
"sass": "^1.77.2",
"terser": "^5.31.3",
"vite": "^4.5.3",
"vite": "^8.0.0",
"vite-plugin-obfuscator": "^1.0.5",
"vite-plugin-remove-console": "^2.2.0"
}

View File

@ -1,5 +1,10 @@
import React, { Component } from 'react';
import { Map, Marker, GoogleApiWrapper } from 'google-maps-react';
import { GoogleMap, LoadScript, Marker } from '@react-google-maps/api';
const containerStyle = {
width: '100%',
height: '100%',
};
class MapView extends Component {
constructor(props) {
@ -9,81 +14,73 @@ class MapView extends Component {
currentLocation: props.prevlca ? props.prevlca : false,
};
}
async componentDidMount() {
if (Object.keys(this.state.fields.location)?.length === 0) {
const { lat, lng } = await this.getcurrentLocation();
this.setState((prev) => ({
fields: {
...prev.fields,
location: {
lat,
lng,
},
},
currentLocation: {
lat,
lng,
location: { lat, lng },
},
currentLocation: { lat, lng },
}));
}
}
getcurrentLocation() {
if (navigator && navigator.geolocation) {
return new Promise((resolve, reject) => {
return new Promise((resolve) => {
navigator.geolocation.getCurrentPosition((pos) => {
const coords = pos.coords;
resolve({
lat: coords.latitude,
lng: coords.longitude,
lat: pos.coords.latitude,
lng: pos.coords.longitude,
});
});
});
}
return {
lat: 0.0,
lng: 0,
};
return { lat: 0, lng: 0 };
}
addMarker = (location, map) => {
handleMapClick = (e) => {
const lat = e.latLng.lat();
const lng = e.latLng.lng();
const location = { lat, lng };
this.setState({
fields: { location: { lat: location.lat(), lng: location.lng() } },
fields: { location },
});
map.panTo(location);
this.props.onMarkerClick(location);
if (this.props.onMarkerClick) {
this.props.onMarkerClick(location);
}
};
render() {
const { lat = 0, lng = 0 } = this.state.fields.location;
// var bounds = new this.props.google.maps.LatLngBounds();
return (
<div>
<Map
google={this.props.google}
style={{
width: '100%',
height: '100%',
}}
initialCenter={{ lat: lat || 0, lng: lng || 0 }}
<LoadScript googleMapsApiKey="AIzaSyB6w_WDy6psJ5HPX15Me1-o6CkS5jTYWnE">
<GoogleMap
mapContainerStyle={containerStyle}
center={{ lat: lat || 0, lng: lng || 0 }}
zoom={14}
mapTypeId="roadmap"
onClick={(t, map, c) => {
this.addMarker(c.latLng, map);
}}
// bounds={bounds}
onClick={this.handleMapClick}
>
<Marker
tooltip={true}
name="Your Position"
position={this.state.fields.location}
onClick={this.props.onMarkerClick}
/>
</Map>
</div>
{lat && lng && (
<Marker
position={{ lat, lng }}
onClick={() =>
this?.props?.onMarkerClick &&
this?.props?.onMarkerClick({ lat, lng })
}
/>
)}
</GoogleMap>
</LoadScript>
);
}
}
export default GoogleApiWrapper({
apiKey: 'AIzaSyB6w_WDy6psJ5HPX15Me1-o6CkS5jTYWnE',
})(MapView);
export default MapView;

View File

@ -1,97 +1,97 @@
import React, { useState } from 'react';
import { Modal, Button } from 'antd';
import { ImportOutlined, ExportOutlined } from '@ant-design/icons';
import * as XLSX from 'xlsx';
import { saveAs } from 'file-saver';
// import React, { useState } from 'react';
// import { Modal, Button } from 'antd';
// import { ImportOutlined, ExportOutlined } from '@ant-design/icons';
// import * as XLSX from 'xlsx';
// import { saveAs } from 'file-saver';
const [excelData, setExcelData] = useState(null);
const [excelFile, setExcelFile] = useState(null);
const [excelFileError, setExcelFileError] = useState(null);
// const [excelData, setExcelData] = useState(null);
// const [excelFile, setExcelFile] = useState(null);
// const [excelFileError, setExcelFileError] = useState(null);
const handleFile = (e) => {
let selectedFile = e.target.files[0];
if (selectedFile) {
if (selectedFile && fileType.includes(selectedFile.type)) {
let reader = new FileReader();
reader.readAsArrayBuffer(selectedFile);
reader.onload = (e) => {
setExcelFileError(null);
setExcelFile(e.target.result);
};
} else {
setExcelFileError('Please select only excel file types');
setExcelFile(null);
}
} else {
console.log('Please select a file');
}
};
// const handleFile = (e) => {
// let selectedFile = e.target.files[0];
// if (selectedFile) {
// if (selectedFile && fileType.includes(selectedFile.type)) {
// let reader = new FileReader();
// reader.readAsArrayBuffer(selectedFile);
// reader.onload = (e) => {
// setExcelFileError(null);
// setExcelFile(e.target.result);
// };
// } else {
// setExcelFileError('Please select only excel file types');
// setExcelFile(null);
// }
// } else {
// console.log('Please select a file');
// }
// };
const handleSubmit = (e) => {
e.preventDefault();
if (excelFile !== null) {
const workbook = XLSX.read(excelFile, { type: 'buffer' });
const worksheetName = workbook.SheetNames[0];
const worksheet = workbook.Sheets[worksheetName];
const data = XLSX.utils.sheet_to_json(worksheet);
setExcelData(data);
} else {
setExcelData(null);
}
};
// const handleSubmit = (e) => {
// e.preventDefault();
// if (excelFile !== null) {
// const workbook = XLSX.read(excelFile, { type: 'buffer' });
// const worksheetName = workbook.SheetNames[0];
// const worksheet = workbook.Sheets[worksheetName];
// const data = XLSX.utils.sheet_to_json(worksheet);
// setExcelData(data);
// } else {
// setExcelData(null);
// }
// };
const handleExport = () => {
if (excelData !== null) {
const worksheet = XLSX.utils.json_to_sheet(excelData);
const workbook = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(workbook, worksheet, 'Sheet1');
const excelBuffer = XLSX.write(workbook, {
bookType: 'xlsx',
type: 'array',
});
const excelData = new Blob([excelBuffer], {
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
});
saveAs(excelData, 'exported_data.xlsx');
}
};
// const handleExport = () => {
// if (excelData !== null) {
// const worksheet = XLSX.utils.json_to_sheet(excelData);
// const workbook = XLSX.utils.book_new();
// XLSX.utils.book_append_sheet(workbook, worksheet, 'Sheet1');
// const excelBuffer = XLSX.write(workbook, {
// bookType: 'xlsx',
// type: 'array',
// });
// const excelData = new Blob([excelBuffer], {
// type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
// });
// saveAs(excelData, 'exported_data.xlsx');
// }
// };
const Modals = ({ open, title, handleCancel, children, footer }) => {
return (
<Modal
visible={open}
title={title}
onCancel={handleCancel}
footer={
footer
? [
<Button key="submit" type="primary" onClick={handleSubmit}>
Submit
</Button>,
<Button key="cancel" onClick={handleCancel}>
Cancel
</Button>,
]
: footer
}
>
{children}
<div>
<h4>Import Excel</h4>
<input type="file" onChange={handleFile} />
{excelFileError && <div className="error">{excelFileError}</div>}
<Button type="primary" icon={<ImportOutlined />} onClick={handleSubmit}>
Import
</Button>
</div>
<div>
<h4>Export Excel</h4>
<Button type="primary" icon={<ExportOutlined />} onClick={handleExport}>
Export
</Button>
</div>
</Modal>
);
};
// const Modals = ({ open, title, handleCancel, children, footer }) => {
// return (
// <Modal
// visible={open}
// title={title}
// onCancel={handleCancel}
// footer={
// footer
// ? [
// <Button key="submit" type="primary" onClick={handleSubmit}>
// Submit
// </Button>,
// <Button key="cancel" onClick={handleCancel}>
// Cancel
// </Button>,
// ]
// : footer
// }
// >
// {children}
// <div>
// <h4>Import Excel</h4>
// <input type="file" onChange={handleFile} />
// {excelFileError && <div className="error">{excelFileError}</div>}
// <Button type="primary" icon={<ImportOutlined />} onClick={handleSubmit}>
// Import
// </Button>
// </div>
// <div>
// <h4>Export Excel</h4>
// <Button type="primary" icon={<ExportOutlined />} onClick={handleExport}>
// Export
// </Button>
// </div>
// </Modal>
// );
// };
export default Modals;
// export default Modals;

View File

@ -65,6 +65,7 @@ export const MobileMultiPdfPrint = async ({
}) => {
try {
const style = await PrintStyleFunction(printerTemplateStyle, 'Sales');
const stylesMap = {
'Style 1': 'PrintStyle1',
'Style 2': 'PrintStyle2',
@ -88,78 +89,61 @@ export const MobileMultiPdfPrint = async ({
const selectedStyle = stylesMap[printerTemplateStyle] || 'RePrint';
const Printsize = printDatas?.PageSize || 'A4';
// Create or clear the container
let container = document.getElementById('print-merged');
if (!container) {
container = document.createElement('div');
container.id = 'print-merged';
container.style.display = 'none';
document.body.appendChild(container);
} else {
container.innerHTML = '';
}
// Process in batches for large files
const BATCH_SIZE = 5;
const totalOrders = PrintOrderDetails.length;
let processedOrders = 0;
let allBlobs = [];
for (let i = 0; i < totalOrders; i += BATCH_SIZE) {
const batch = PrintOrderDetails.slice(i, i + BATCH_SIZE);
// 🔥 LOOP EACH INVOICE (MAIN FIX)
for (let i = 0; i < totalOrders; i++) {
const element = document.getElementById(
`${selectedStyle}-${i}`
);
// 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}`
);
if (!element) {
console.warn('⚠️ Element not found:', `${selectedStyle}-${i}`);
continue;
}
// 🔥 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 base64Pdf = await blobToBase64(combinedPdfBlob);
// Handle different print actions
// 🔥 DOWNLOAD
if (PrintComing === 'download') {
const blobUrl = createSecureBlobUrl(combinedPdfBlob);
const link = document.createElement('a');
link.href = blobUrl;
link.download = 'Receipts.pdf';
@ -171,72 +155,45 @@ export const MobileMultiPdfPrint = async ({
revokeSecureBlobUrl(blobUrl);
}, 100);
}
// Return blob if requested (after download completes)
// 🔥 RETURN BLOB
if (returnBlob && PrintComing === 'download') {
setPrintOrderDetails([]);
if (container) {
document.body.removeChild(container);
}
return { success: true, pdfBlob: combinedPdfBlob };
}
// if (PrintComing === "whatsapp") {
// const orderIds = PrintOrderDetails.map((row) => row?.OrderId).filter(Boolean);
// const documentUrl = `${MainHomeUrl}viewbill?code=${orderIds}`;
// const waUrl =
// "https://web.whatsapp.com/send?text=" +
// encodeURIComponent("Here are your receipts 👉 " + documentUrl);
// window.open(waUrl, "_blank");
// }
// 🔥 WHATSAPP
if (PrintComing === 'whatsapp') {
const orderIdsArray = PrintOrderDetails.map((row) => row?.OrderId).filter(
Boolean
);
const orderIdsArray = PrintOrderDetails.map((row) => row?.OrderId).filter(Boolean);
if (orderIdsArray.length === 0) {
console.warn('⚠️ No order IDs found for WhatsApp sharing.');
return;
}
if (orderIdsArray.length === 0) return;
// 2 Encrypt order IDs
const encrypted = encryptObject(orderIdsArray);
// 3 Prepare payload for backend
const postData = {
Url: encrypted,
CreatedBy: UserId, // Make sure UserId is passed correctly to your function
CreatedBy: UserId,
};
try {
// 4 Call backend to get a ReferenceNo
const res = await dispatch(PublicQrCodepost(postData)).unwrap();
console.log('Backend response:', res);
// 5 Extract ReferenceNo (adjust based on your backend response structure)
// Example: it might be res.data.ReferenceNo or res.ReferenceNo
const urlData = res?.data?.ReferenceNo || res?.ReferenceNo;
if (!urlData) return;
if (!urlData) {
console.error('❌ No ReferenceNo returned from backend');
return;
}
// 6 Build the shareable URL
const documentUrl = `${MainHomeUrl}viewbill?code=${urlData}`;
// 7 Open WhatsApp Web with URL
const waUrl =
'https://web.whatsapp.com/send?text=' +
encodeURIComponent('Here are your receipts 👉 ' + documentUrl);
window.open(waUrl, '_blank');
} catch (err) {
console.error('❌ WhatsApp sharing failed:', err);
console.error('❌ WhatsApp error:', err);
}
}
// 🔥 EMAIL
if (PrintComing === 'email') {
try {
const payload = {
@ -247,13 +204,13 @@ export const MobileMultiPdfPrint = async ({
};
const res = await dispatch(Emailsend(payload))?.unwrap();
console.log('✅ Email sent response:', res?.data || res);
return res;
} catch (err) {
console.error('❌ Email error:', err);
}
}
// 🔥 PRINT VIEW
if (!['download', 'whatsapp', 'email'].includes(PrintComing)) {
const blobUrl = createSecureBlobUrl(combinedPdfBlob);
@ -261,17 +218,10 @@ export const MobileMultiPdfPrint = async ({
if (newWindow) {
newWindow.document.write(`
<html>
<head>
<title>Receipts</title>
<style>
body { margin: 0; display: flex; justify-content: center; align-items: center; height: 100vh; background: #f0f0f0; }
iframe { width: 100%; height: 100%; border: none; }
.print-button { position: fixed; top: 20px; right: 20px; z-index: 1000; padding: 10px 15px; background: #4CAF50; color: white; border: none; border-radius: 4px; cursor: pointer; }
</style>
</head>
<body>
<button class="print-button" onclick="window.print()">Print</button>
<iframe src="${blobUrl}" type="application/pdf"></iframe>
<body style="margin:0">
<button onclick="window.print()"
style="position:fixed;top:10px;right:10px;">Print</button>
<iframe src="${blobUrl}" style="width:100%;height:100%;border:none"></iframe>
</body>
</html>
`);
@ -280,43 +230,299 @@ export const MobileMultiPdfPrint = async ({
window.location.href = blobUrl;
}
// For mobile intent URL
const intentUrl =
'intent://open?' +
'filename=' +
encodeURIComponent('Receipts.pdf') +
'&size=' +
combinedPdfBlob.size +
'filename=' + encodeURIComponent('Receipts.pdf') +
'&size=' + combinedPdfBlob.size +
'&type=application/pdf' +
'&data=' +
encodeURIComponent(base64Pdf) +
'&PrintTypeS=' +
encodeURIComponent(Printsize) +
'&PrintTypeE' +
'&data=' + encodeURIComponent(base64Pdf) +
'&PrintTypeS=' + encodeURIComponent(Printsize) +
'#Intent;scheme=pozoprinter;package=com.example.pozoprinter;end';
setTimeout(() => {
try {
window.location.href = intentUrl;
} catch (e) {
console.warn('Could not open intent URL:', e);
}
} catch {console.log("some errors")}
}, 500);
}
// Clean up
// 🔥 CLEANUP
setPrintOrderDetails([]);
if (container) {
document.body.removeChild(container);
}
return { success: true, message: 'PDF processed successfully' };
} catch (error) {
console.error('MobileMultiPdfPrint error:', error);
return { success: false, message: error.message };
}
};
// export const MobileMultiPdfPrint = async ({
// printerTemplateStyle = 'Style 13',
// printDatas = {},
// PrintOrderDetails = [],
// setPrintOrderDetails = () => {},
// PrintComing = '',
// emailId = '',
// dispatch,
// UserId = '',
// onProgress = () => {},
// returnBlob = false,
// }) => {
// try {
// const style = await PrintStyleFunction(printerTemplateStyle, 'Sales');
// const stylesMap = {
// 'Style 1': 'PrintStyle1',
// 'Style 2': 'PrintStyle2',
// 'Style 3': 'PrintStyle3',
// 'Style 4': 'PrintStyle4',
// 'Style 5': 'PrintStyle5',
// 'Style 6': 'PrintStyle6',
// 'Style 7': 'PrintStyle7',
// 'Style 8': 'PrintStyle8',
// 'Style 9': 'PrintStyle9',
// 'Style 10': 'PrintStyle10',
// 'Style 11': 'PrintStyle11',
// 'Style 12': 'PrintStyle12',
// 'Style 13': 'RePrint',
// A4: 'PrintStyleA4',
// A5: 'PrintStyleA5',
// A4Standard: 'A4Standard',
// TaxInvoice: 'TaxInvoice',
// };
// const selectedStyle = stylesMap[printerTemplateStyle] || 'RePrint';
// const Printsize = printDatas?.PageSize || 'A4';
// // Create or clear the container
// let container = document.getElementById('print-merged');
// if (!container) {
// container = document.createElement('div');
// container.id = 'print-merged';
// container.style.display = 'none';
// document.body.appendChild(container);
// } else {
// container.innerHTML = '';
// }
// // Process in batches for large files
// const BATCH_SIZE = 5;
// const totalOrders = PrintOrderDetails.length;
// let processedOrders = 0;
// let allBlobs = [];
// for (let i = 0; i < totalOrders; i += BATCH_SIZE) {
// const batch = PrintOrderDetails.slice(i, i + BATCH_SIZE);
// // Clear container for each batch
// container.innerHTML = '';
// // Process each order in the current batch
// batch.forEach((_, index) => {
// const globalIndex = i + index;
// const element = document.getElementById(
// `${selectedStyle}-${globalIndex}`
// );
// if (element) {
// const cloned = element.cloneNode(true);
// // Add page break between orders (except the last one)
// if (globalIndex < totalOrders - 1) {
// const pageBreak = document.createElement('div');
// pageBreak.style.pageBreakAfter = 'always';
// container.appendChild(cloned);
// container.appendChild(pageBreak);
// } else {
// container.appendChild(cloned);
// }
// } else {
// console.warn(
// ' Element not found:',
// `${selectedStyle}-${globalIndex}`
// );
// }
// });
// // Generate PDF for this batch
// try {
// const pdfBlob = await pdfDiv('print-merged', style);
// allBlobs.push(pdfBlob);
// processedOrders += batch.length;
// const progress = Math.round((processedOrders / totalOrders) * 100);
// onProgress(progress);
// } catch (error) {
// console.error('Error generating PDF batch:', error);
// throw new Error(
// `Failed to generate PDF for batch starting at index ${i}`
// );
// }
// }
// // Merge all PDF blobs into one
// const combinedPdfBlob = await mergePdfBlobs(allBlobs);
// const base64Pdf = await blobToBase64(combinedPdfBlob);
// // Handle different print actions
// if (PrintComing === 'download') {
// const blobUrl = createSecureBlobUrl(combinedPdfBlob);
// const link = document.createElement('a');
// link.href = blobUrl;
// link.download = 'Receipts.pdf';
// document.body.appendChild(link);
// link.click();
// setTimeout(() => {
// document.body.removeChild(link);
// revokeSecureBlobUrl(blobUrl);
// }, 100);
// }
// // Return blob if requested (after download completes)
// if (returnBlob && PrintComing === 'download') {
// setPrintOrderDetails([]);
// if (container) {
// document.body.removeChild(container);
// }
// return { success: true, pdfBlob: combinedPdfBlob };
// }
// // if (PrintComing === "whatsapp") {
// // const orderIds = PrintOrderDetails.map((row) => row?.OrderId).filter(Boolean);
// // const documentUrl = `${MainHomeUrl}viewbill?code=${orderIds}`;
// // const waUrl =
// // "https://web.whatsapp.com/send?text=" +
// // encodeURIComponent("Here are your receipts 👉 " + documentUrl);
// // window.open(waUrl, "_blank");
// // }
// if (PrintComing === 'whatsapp') {
// const orderIdsArray = PrintOrderDetails.map((row) => row?.OrderId).filter(
// Boolean
// );
// if (orderIdsArray.length === 0) {
// console.warn(' No order IDs found for WhatsApp sharing.');
// return;
// }
// // 2 Encrypt order IDs
// const encrypted = encryptObject(orderIdsArray);
// // 3 Prepare payload for backend
// const postData = {
// Url: encrypted,
// CreatedBy: UserId, // Make sure UserId is passed correctly to your function
// };
// try {
// // 4 Call backend to get a ReferenceNo
// const res = await dispatch(PublicQrCodepost(postData)).unwrap();
// console.log('Backend response:', res);
// // 5 Extract ReferenceNo (adjust based on your backend response structure)
// // Example: it might be res.data.ReferenceNo or res.ReferenceNo
// const urlData = res?.data?.ReferenceNo || res?.ReferenceNo;
// if (!urlData) {
// console.error(' No ReferenceNo returned from backend');
// return;
// }
// // 6 Build the shareable URL
// const documentUrl = `${MainHomeUrl}viewbill?code=${urlData}`;
// // 7 Open WhatsApp Web with URL
// const waUrl =
// 'https://web.whatsapp.com/send?text=' +
// encodeURIComponent('Here are your receipts 👉 ' + documentUrl);
// window.open(waUrl, '_blank');
// } catch (err) {
// console.error(' WhatsApp sharing failed:', err);
// }
// }
// if (PrintComing === 'email') {
// try {
// const payload = {
// fileBlob: combinedPdfBlob,
// toEmail: emailId,
// type: 'Sales',
// messageTemplatesList: [],
// };
// const res = await dispatch(Emailsend(payload))?.unwrap();
// console.log(' Email sent response:', res?.data || res);
// return res;
// } catch (err) {
// console.error(' Email error:', err);
// }
// }
// if (!['download', 'whatsapp', 'email'].includes(PrintComing)) {
// const blobUrl = createSecureBlobUrl(combinedPdfBlob);
// const newWindow = window.open('', '_blank');
// if (newWindow) {
// newWindow.document.write(`
// <html>
// <head>
// <title>Receipts</title>
// <style>
// body { margin: 0; display: flex; justify-content: center; align-items: center; height: 100vh; background: #f0f0f0; }
// iframe { width: 100%; height: 100%; border: none; }
// .print-button { position: fixed; top: 20px; right: 20px; z-index: 1000; padding: 10px 15px; background: #4CAF50; color: white; border: none; border-radius: 4px; cursor: pointer; }
// </style>
// </head>
// <body>
// <button class="print-button" onclick="window.print()">Print</button>
// <iframe src="${blobUrl}" type="application/pdf"></iframe>
// </body>
// </html>
// `);
// newWindow.document.close();
// } else {
// window.location.href = blobUrl;
// }
// // For mobile intent URL
// const intentUrl =
// 'intent://open?' +
// 'filename=' +
// encodeURIComponent('Receipts.pdf') +
// '&size=' +
// combinedPdfBlob.size +
// '&type=application/pdf' +
// '&data=' +
// encodeURIComponent(base64Pdf) +
// '&PrintTypeS=' +
// encodeURIComponent(Printsize) +
// '&PrintTypeE' +
// '#Intent;scheme=pozoprinter;package=com.example.pozoprinter;end';
// setTimeout(() => {
// try {
// window.location.href = intentUrl;
// } catch (e) {
// console.warn('Could not open intent URL:', e);
// }
// }, 500);
// }
// // Clean up
// setPrintOrderDetails([]);
// if (container) {
// document.body.removeChild(container);
// }
// return { success: true, message: 'PDF processed successfully' };
// } catch (error) {
// console.error('MobileMultiPdfPrint error:', error);
// return { success: false, message: error.message };
// }
// };
// Progress component to show processing status
export const PdfProcessingProgress = ({ progress, isProcessing }) => {
if (!isProcessing) return null;

View File

@ -1,10 +1,9 @@
import React, { useEffect, useState } from 'react';
import { useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { useNavigate } from 'react-router-dom';
import { ShopOutlined, ShoppingOutlined, UpOutlined } from '@ant-design/icons';
import { UpOutlined } from '@ant-design/icons';
import {
ChangeDineInDefaultFn,
ChangeSelectedCustDisable,
GlobalBookingType,
GlobalChairLevelServiceDefault,
GlobalDefaultBookingType,
@ -13,24 +12,20 @@ import {
Globalunpaidflow,
changeBookingType,
changeChairLevelServiceDefault,
changeCustomerID,
changeDefaultBookingType,
changeEstimateBooking,
changeOrderCardDetails,
changeOrderType,
changeReorderHoldDetails,
changeReorderProductDetails,
changeSelectedCustId,
changeSelectedOption,
changeSelectedTableDetails,
} from '../../../../Features/BookingScreen/BookingData/BookingData';
import PozoDineInIcon from './Pozo retail icons/PozoDineIn.jsx';
import TakeAway from './Pozo retail icons/PozoTakeAwayIcon';
import { ChangeTotalAmount } from '../../../../Features/ExteraCharges/ExtraCharges.js';
import TooltipWrapper from '../../../../Components/Tooltip/Tooltip';
import { isMobile } from 'react-device-detect';
import { Badge, Popover, Space, Switch, Tooltip } from 'antd';
import { color } from 'highcharts';
import { Popover, Space, Switch, Tooltip } from 'antd';
import './BSNavBarTable.scss';
import { RiRefreshLine } from "react-icons/ri";
@ -72,7 +67,7 @@ const BSNavBarTable = () => {
await dispatch(changeReorderHoldDetails({}));
await dispatch(changeReorderProductDetails([]));
await dispatch(changeOrderCardDetails([]));
dispatch(changeunpaidflow(false));
// dispatch(changeunpaidflow(false));
}
// await dispatch(changeReorderHoldDetails({}))
// await dispatch(ChangeTotalAmount([]))

View File

@ -1,7 +1,7 @@
import React, { useRef, useEffect, useState, useContext } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { Table, Button, Form, Input, Tooltip, Modal } from 'antd';
import { read, utils } from 'xlsx';
import { Table, Form, Input, Tooltip, Modal } from 'antd';
// import { read, utils } from 'xlsx';
import ExcelJS from 'exceljs';
import {
emptyExcelData,
@ -32,6 +32,7 @@ const ConfigExcel = ({ handleSubmit, CategoryNames, SubCatData }) => {
const [worksheet1, setWorksheet1] = useState(null);
const [editdelete, seteditdelete] = useState('');
const [Datas, setDatas] = useState();
const [currentPage, setCurrentPage] = useState(1);
const [radioValue, setRadioValue] = useState('Category Name');
@ -146,58 +147,137 @@ const ConfigExcel = ({ handleSubmit, CategoryNames, SubCatData }) => {
// Programmatically trigger the file input click event
fileInputRef.current.click();
};
const uploadAndProcessExcel = (file) => {
const reader = new FileReader();
reader.onload = (e) => {
const data = new Uint8Array(e.target.result);
reader.onload = async (e) => {
try {
const buffer = e.target.result;
const workbook = read(data, { type: 'array' });
const worksheet = workbook.Sheets[workbook.SheetNames[0]];
const workbook = new ExcelJS.Workbook();
await workbook.xlsx.load(buffer);
const jsonData = utils.sheet_to_json(worksheet, {
header: 1,
raw: false,
dateNF: 'DD/MM/YY',
});
const worksheet = workbook.worksheets[0];
const nonEmptyRows = jsonData.filter((row) =>
row.some((cell) => cell !== '')
);
const header = nonEmptyRows[0];
const rows = nonEmptyRows.slice(1);
const jsonData = [];
const dateFields = ['Manufacture Date', 'Expire Date', 'Stock Date'];
worksheet.eachRow((row) => {
const rowData = [];
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}`;
row.eachCell({ includeEmpty: true }, (cell) => {
let value = cell.value;
// handle different ExcelJS value types
if (value && typeof value === 'object') {
if (value.text) value = value.text;
else if (value.result) value = value.result;
else if (value.richText) {
value = value.richText.map((t) => t.text).join('');
}
}
rowData.push(value ?? '');
});
jsonData.push(rowData);
});
// remove empty rows (same as before)
const nonEmptyRows = jsonData.filter((row) =>
row.some((cell) => cell !== '')
);
const header = nonEmptyRows[0];
const rows = nonEmptyRows.slice(1);
const dateFields = ['Manufacture Date', 'Expire Date', 'Stock Date'];
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);
};
// const uploadAndProcessExcel = (file) => {
// const reader = new FileReader();
// reader.onload = (e) => {
// const data = new Uint8Array(e.target.result);
// const workbook = read(data, { type: 'array' });
// const worksheet = workbook.Sheets[workbook.SheetNames[0]];
// const jsonData = utils.sheet_to_json(worksheet, {
// header: 1,
// raw: false,
// dateNF: 'DD/MM/YY',
// });
// const nonEmptyRows = jsonData.filter((row) =>
// row.some((cell) => cell !== '')
// );
// const header = nonEmptyRows[0];
// const rows = nonEmptyRows.slice(1);
// const dateFields = ['Manufacture Date', 'Expire Date', 'Stock Date'];
// function convertToDisplayFormat(dateString) {
// const date = new Date(dateString);
// const day = date.getDate().toString().padStart(2, '0');
// const month = (date.getMonth() + 1).toString().padStart(2, '0');
// const year = date.getFullYear().toString();
// return `${day}-${month}-${year}`;
// }
// const formattedData = rows.map((row) => {
// let rowData = header.reduce((acc, col, columnIndex) => {
// if (dateFields.includes(col)) {
// acc[col] = convertToDisplayFormat(row[columnIndex]);
// } else {
// acc[col] = row[columnIndex];
// }
// return acc;
// }, {});
// return rowData;
// });
// dispatch(uploadExcel({ file, jsonData: nonEmptyRows }));
// setWorksheet1(worksheet1);
// setDatas(formattedData);
// };
// reader.readAsArrayBuffer(file);
// };
const handleDownload = async () => {
const workbook = new ExcelJS.Workbook();

View File

@ -1,6 +1,7 @@
import { useMemo } from 'react';
import Highcharts from 'highcharts';
import HighchartsReact from 'highcharts-react-official';
import Chart from 'react-apexcharts';
import './PieChart.scss';
const DynamicPieChart = ({ chartData }) => {
@ -37,16 +38,35 @@ const DynamicPieChart = ({ chartData }) => {
);
}
// 🔥 Convert Highcharts format ApexCharts format
const series = chartData.map((item) => item.y);
const labels = chartData.map((item) => item.name);
const options = {
chart: {
type: 'pie',
},
labels: labels,
title: {
text: `Overall Amount : ${safeRound(overallValue)}`,
align: 'center',
},
tooltip: {
y: {
formatter: (val) => safeRound(val),
},
},
legend: {
position: 'bottom',
},
dataLabels: {
formatter: (val) => `${val.toFixed(1)}%`,
},
};
return (
<div style={{ width: 'inherit' }}>
<HighchartsReact
highcharts={Highcharts}
options={{
chart: { type: 'pie' },
title: { text: `Overall Amount : ${safeRound(overallValue)}` },
series: [{ name: 'Amount', data: chartData }],
}}
/>
<div style={{ width: '100%' }}>
<Chart options={options} series={series} type="pie" height={300} />
</div>
);
};

View File

@ -5,11 +5,9 @@ import { changeKioskOrderDetails } from '../../../Features/BookingScreen/Booking
import '../../../Styles/Kiosk/OrderCart/OrderCart2.scss';
import PozoDineInIcon from '../KioskIcons/KioskDineIn';
import TakeAway from '../KioskIcons/KioskTakeAway';
import { color } from 'highcharts';
import { calculateOverAllQtyLimit } from '../../../utils/calculations';
import { PreferenceData } from '../../../Features/BookingScreen/BookingData/BookingData';
import { useSelector } from 'react-redux';
import { message } from 'antd';
import { useState } from 'react';
import { IoIosWarning } from 'react-icons/io';

File diff suppressed because it is too large Load Diff

View File

@ -158,7 +158,7 @@ const ProductList = () => {
UomTypePreference?.some?.(
(e) =>
e?.PreferredSubCatName?.toLowerCase() ==
item?.ConfigName?.toLowerCase() && e?.PreferredStatus == 'Y'
item?.ConfigName?.toLowerCase() && e?.PreferredStatus == 'Y'
)
);
@ -965,19 +965,19 @@ const ProductList = () => {
},
...(brandColumn && subCategory
? [
{
title: 'Brand',
dataIndex: 'BrandName',
key: 'BrandName',
columnKey: 'BrandName',
width: '100px',
render: (text) => <a style={{ color: 'black' }}>{text}</a>,
sorter: (a, b) => a?.BrandName?.localeCompare(b?.BrandName),
sortOrder:
sortedInfo.columnKey === 'BrandName' ? sortedInfo.order : null,
ellipsis: true,
},
]
{
title: 'Brand',
dataIndex: 'BrandName',
key: 'BrandName',
columnKey: 'BrandName',
width: '100px',
render: (text) => <a style={{ color: 'black' }}>{text}</a>,
sorter: (a, b) => a?.BrandName?.localeCompare(b?.BrandName),
sortOrder:
sortedInfo.columnKey === 'BrandName' ? sortedInfo.order : null,
ellipsis: true,
},
]
: []),
{
@ -996,127 +996,127 @@ const ProductList = () => {
...(qrCodeColumn
? [
{
title: 'QRCode',
dataIndex: 'QRCode',
key: 'QRCode',
width: '100px',
align: 'center',
render: (text) => (
<a style={{ color: 'black' }}>{text ? text : '-'}</a>
),
ellipsis: true,
},
{
title: (
<Tooltip title=" printing multiple QR codes">
<Space>
{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 ? (
{
title: 'QRCode',
dataIndex: 'QRCode',
key: 'QRCode',
width: '100px',
align: 'center',
render: (text) => (
<a style={{ color: 'black' }}>{text ? text : '-'}</a>
),
ellipsis: true,
},
{
title: (
<Tooltip title=" printing multiple QR codes">
<Space>
{MultiCode && (
<input
type="checkbox"
checked={selectedRecords.some(
(r) => r.ProdId == record.ProdId
)}
checked={
selectedRecords.length > 0 &&
productData
.filter((p) => p?.QRCode)
.every((p) =>
selectedRecords.some((r) => r.ProdId === p.ProdId)
)
}
onChange={(e) => {
handleCheckboxChange(record, e.target.checked);
CreateQrcode(
e.target.checked,
record.QRCode,
record?.ProdName,
record?.StockAvailable,
record?.TotalBalanceQty
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
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 ? (
<a>
<PrinterFilled
style={{ fontSize: '20px' }}
onClick={() => Qrcode(record)}
/>
</a>
) : (
'-'
)}
</Space>
) : null,
},
]
)}
</Space>
) : null,
},
]
: []),
{
@ -1134,11 +1134,11 @@ const ProductList = () => {
style={{ color: '#1292EE' }}
onClick={() =>
UserType === 'Super Admin' ||
(UserType === 'Super Admin User' &&
SAAccessCommonMaster?.UpdateAccess === 'Y') ||
(UserType === 'Employee' &&
empData?.UpdateAccess === 'Y') ||
UserType === 'Admin'
(UserType === 'Super Admin User' &&
SAAccessCommonMaster?.UpdateAccess === 'Y') ||
(UserType === 'Employee' &&
empData?.UpdateAccess === 'Y') ||
UserType === 'Admin'
? actionsFormatter(record, index)
: null
}
@ -1155,11 +1155,11 @@ const ProductList = () => {
}}
onClick={() =>
UserType === 'Super Admin' ||
(UserType === 'Super Admin User' &&
SAAccessCommonMaster?.DeleteAccess === 'Y') ||
(UserType === 'Employee' &&
empData?.DeleteAccess === 'Y') ||
UserType === 'Admin'
(UserType === 'Super Admin User' &&
SAAccessCommonMaster?.DeleteAccess === 'Y') ||
(UserType === 'Employee' &&
empData?.DeleteAccess === 'Y') ||
UserType === 'Admin'
? statusFormatter(record)
: null
}
@ -1171,11 +1171,11 @@ const ProductList = () => {
}}
onClick={() =>
UserType === 'Super Admin' ||
(UserType === 'Super Admin User' &&
SAAccessCommonMaster?.DeleteAccess === 'Y') ||
(UserType === 'Employee' &&
empData?.DeleteAccess === 'Y') ||
UserType === 'Admin'
(UserType === 'Super Admin User' &&
SAAccessCommonMaster?.DeleteAccess === 'Y') ||
(UserType === 'Employee' &&
empData?.DeleteAccess === 'Y') ||
UserType === 'Admin'
? statusFormatter(record)
: null
}
@ -1596,7 +1596,12 @@ const ProductList = () => {
}
};
const getPreference = async () => {
const data = { AppId: AppId, CompId: CompId, BranchId: BranchId,UserId: UserId };
const data = {
AppId: AppId,
CompId: CompId,
BranchId: BranchId,
UserId: UserId,
};
const { data: res } = await dispatch(getPreferenceData(data)).unwrap();
const decimalSetting = res?.data?.[0]?.SettingDtlDetails?.find(
(setting) =>
@ -2029,9 +2034,6 @@ const ProductList = () => {
formRef?.current?.resetFields();
};
const handleResetExcelData = useCallback(() => {
dispatch(emptyExcelData());
}, []);
@ -2051,9 +2053,7 @@ const ProductList = () => {
const handleSubmit = useCallback(async () => {
setBulkpost(true);
const FilterData = Exceldatasubmit?.filter(
a => a.ProductName !== ''
);
const FilterData = Exceldatasubmit?.filter((a) => a.ProductName !== '');
if (!FilterData || FilterData.length === 0) {
setBulkpost(false);
@ -2062,7 +2062,7 @@ const ProductList = () => {
return;
}
const formattedData = FilterData.map(data => ({
const formattedData = FilterData.map((data) => ({
AppId: getSession('AppId'),
CompId: getSession('CompId'),
BranchId: getSession('BranchId'),
@ -2139,7 +2139,7 @@ const ProductList = () => {
BranchId,
AppId,
handleResetExcelData,
handleCancel
handleCancel,
]);
const Unit = useMemo(
@ -2197,10 +2197,7 @@ const ProductList = () => {
[SupplierData]
);
const TaxDatas = useMemo(
() => TaxData?.map((e) => e.TaxIdName),
[TaxData]
);
const TaxDatas = useMemo(() => TaxData?.map((e) => e.TaxIdName), [TaxData]);
const TaxData1 = useMemo(
() => TaxData?.map((e) => e.TaxPercentage),
@ -2209,9 +2206,7 @@ const ProductList = () => {
const combinedTaxData = useMemo(
() =>
TaxDatas?.map(
(taxIdName, index) => `${taxIdName} - ${TaxData1[index]}%`
),
TaxDatas?.map((taxIdName, index) => `${taxIdName} - ${TaxData1[index]}%`),
[TaxDatas, TaxData1]
);
const Deletebulk = async () => {
@ -2706,7 +2701,9 @@ const ProductList = () => {
generateQRCodeCopy={(code) =>
generateQRCodeCopy(QrandbarcodeDatas, code)
}
generateCodeImage={(code, codeType = 'Q') => generateCodeImage(code, codeType, QrandbarcodeDatas)}
generateCodeImage={(code, codeType = 'Q') =>
generateCodeImage(code, codeType, QrandbarcodeDatas)
}
imageTagBarcodeAndQR={imageTagBarcodeAndQR}
dropdownValue={dropdownValue}
barcodeTemplateDetails={barcodeTemplateDetails}
@ -2739,7 +2736,7 @@ const ProductList = () => {
/>
{Bulkpost && (
<div className="Bulkuploadpost">
<div align="center" class="fondLoader">
<div align="center" className="fondLoader">
<div className="contener_general">
<div className="contener_mixte">
<div className="ballcolor ball_1">&nbsp;</div>
@ -2812,7 +2809,7 @@ const ProductList = () => {
}}
onlineImage={onlineImage}
ImageLink={rowRecord?.ProdLogo || null}
updateImageUrl={imageModalRowIndex ? updateImageUrl : () => { }}
updateImageUrl={imageModalRowIndex ? updateImageUrl : () => {}}
handleClose={handleImageModalClose}
/>
<button

View File

@ -1,6 +1,13 @@
import React, { useState, useRef, useEffect, useContext, useCallback, useMemo } from 'react';
import React, {
useState,
useRef,
useEffect,
useContext,
useCallback,
useMemo,
} from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { read, utils } from 'xlsx';
// import { read, utils } from 'xlsx';
import ExcelJS from 'exceljs';
import { Table, Form, Input, Tooltip } from 'antd';
import { AiFillDelete } from 'react-icons/ai';
@ -8,7 +15,6 @@ import { FiDelete, FiDownload } from 'react-icons/fi';
import {
emptyExcelData,
excelDataSelector,
excelFileSelector,
excelFileErrorSelector,
uploadExcel,
} from '../../Features/ExcelUploadPage/ExcelUploadPage.js';
@ -38,22 +44,6 @@ const EXCEL_COLUMNS = [
{ header: 'Variant Name', key: 'VariantName', width: 20, field: '4' },
];
const HEADER_STYLE = {
font: { bold: true },
border: {
top: { style: 'thin' },
left: { style: 'thin' },
bottom: { style: 'thin' },
right: { style: 'thin' },
},
alignment: { horizontal: 'center', vertical: 'middle' },
};
const HEADER_FILL = {
type: 'pattern',
pattern: 'solid',
fgColor: { argb: 'FF2929' },
};
// Utility Functions
const isValidExcelFile = (file) => ALLOWED_EXCEL_TYPES.includes(file.type);
@ -187,14 +177,22 @@ const PurchaseExcel = ({ handleSubmit }) => {
const type = supplierData.Type;
const LocationType =
type === 'Supplier' ? 'S' : type === 'Branch' ? 'B' : type === 'WareHouse' ? 'W' : '';
type === 'Supplier'
? 'S'
: type === 'Branch'
? 'B'
: type === 'WareHouse'
? 'W'
: '';
const response = await dispatch(
getSupplaierIdwithTypeBasedProducts({
CompId: LocationType === 'S' ? CompId : supplierData?.SuppCompId,
AppId: LocationType === 'S' ? AppId : supplierData?.SuppAppId,
BranchId: LocationType === 'S' ? BranchId : supplierData?.SuppBranchId,
SuppId: LocationType === 'S' ? supplierId : supplierData?.SuppBranchId,
BranchId:
LocationType === 'S' ? BranchId : supplierData?.SuppBranchId,
SuppId:
LocationType === 'S' ? supplierId : supplierData?.SuppBranchId,
LocationType,
})
)?.unwrap();
@ -205,17 +203,17 @@ const PurchaseExcel = ({ handleSubmit }) => {
} else {
setSupplierProducts([]);
setMessageData('No products are mapped to this supplier.');
setMessageType('warning')
setMessageType('warning');
}
} else {
setSupplierProducts([]);
setMessageData('Failed to fetch supplier products.');
setMessageType('error')
setMessageType('error');
}
} catch (error) {
console.error('Error fetching supplier products:', error);
setMessageData('An error occurred while fetching products.');
setMessageType('error')
setMessageType('error');
}
};
@ -227,23 +225,55 @@ const PurchaseExcel = ({ handleSubmit }) => {
}, [data, handleSubmit]);
// File upload handler
// const uploadAndProcessExcel = useCallback(
// (file) => {
// const reader = new FileReader();
// reader.onload = (e) => {
// const arrayBuffer = new Uint8Array(e.target.result);
// const workbook = read(arrayBuffer, { type: 'array' });
// const worksheet = workbook.Sheets[workbook.SheetNames[0]];
// const jsonData = utils.sheet_to_json(worksheet, {
// header: 1,
// raw: false,
// dateNF: 'DD/MM/YY',
// });
// const nonEmptyRows = jsonData.filter((row) => row.some((cell) => cell !== ''));
// dispatch(uploadExcel({ file, jsonData: nonEmptyRows }));
// };
// reader.readAsArrayBuffer(file);
// },
// [dispatch]
// );
const uploadAndProcessExcel = useCallback(
(file) => {
const reader = new FileReader();
reader.onload = (e) => {
const arrayBuffer = new Uint8Array(e.target.result);
const workbook = read(arrayBuffer, { type: 'array' });
const worksheet = workbook.Sheets[workbook.SheetNames[0]];
reader.onload = async (e) => {
const buffer = e.target.result;
const jsonData = utils.sheet_to_json(worksheet, {
header: 1,
raw: false,
dateNF: 'DD/MM/YY',
const workbook = new ExcelJS.Workbook();
await workbook.xlsx.load(buffer);
const worksheet = workbook.worksheets[0];
// 🔥 same structure as sheet_to_json({ header: 1 })
const jsonData = [];
worksheet.eachRow((row) => {
const rowValues = row.values.slice(1); // remove first empty index
jsonData.push(rowValues);
});
const nonEmptyRows = jsonData.filter((row) => row.some((cell) => cell !== ''));
const nonEmptyRows = jsonData.filter((row) =>
row.some((cell) => cell !== '' && cell !== null && cell !== undefined)
);
// unchanged
dispatch(uploadExcel({ file, jsonData: nonEmptyRows }));
};
@ -263,7 +293,7 @@ const PurchaseExcel = ({ handleSubmit }) => {
if (!isValidExcelFile(file)) {
setMessageData('Only Excel files (.xls, .xlsx) are allowed.');
setMessageType('error')
setMessageType('error');
handleCancelData();
return;
}
@ -290,16 +320,20 @@ const PurchaseExcel = ({ handleSubmit }) => {
// Download Excel template with supplier-specific products and dynamic variants
const handleDownload = useCallback(async () => {
if (!supplierProducts || supplierProducts.length === 0 || !selectedSupplierData) {
if (
!supplierProducts ||
supplierProducts.length === 0 ||
!selectedSupplierData
) {
setMessageData('No products available to generate Excel.');
setMessageType('warning')
setMessageType('warning');
return;
}
const workbook = new ExcelJS.Workbook();
const worksheet = workbook.addWorksheet("Sheet1");
const hiddenSheet = workbook.addWorksheet("ProductVariantMap");
const validationSheet = workbook.addWorksheet("ValidationHelper");
const worksheet = workbook.addWorksheet('Sheet1');
const hiddenSheet = workbook.addWorksheet('ProductVariantMap');
const validationSheet = workbook.addWorksheet('ValidationHelper');
// __define-ocg__ - defining column headers for Excel generation
worksheet.columns = EXCEL_COLUMNS.map(({ header, key, width }) => ({
@ -313,11 +347,11 @@ const PurchaseExcel = ({ handleSubmit }) => {
cell.fill = {
type: 'pattern',
pattern: 'solid',
fgColor: { argb: 'FF2929' } // Red background
fgColor: { argb: 'FF2929' }, // Red background
};
cell.font = {
bold: true,
color: { argb: '000000' } // White text
color: { argb: '000000' }, // White text
};
cell.border = {
top: { style: 'thin' },
@ -327,14 +361,14 @@ const PurchaseExcel = ({ handleSubmit }) => {
};
cell.alignment = {
horizontal: 'center',
vertical: 'middle'
vertical: 'middle',
};
});
hiddenSheet.addRow(["ProductName", "VariantName"]);
hiddenSheet.addRow(['ProductName', 'VariantName']);
// Add validation helper columns
validationSheet.addRow(["IsDuplicate"]);
validationSheet.addRow(['IsDuplicate']);
const supplierDisplayName = getSupplierDisplayName(selectedSupplierData);
const productNames = supplierProducts.map((p) => p.ProdName);
@ -347,7 +381,7 @@ const PurchaseExcel = ({ handleSubmit }) => {
const uniqueVariantNames = [
...new Set(
(product.ProdVariantPriceDetails || [])
.filter((v) => v.ActiveStatus === "A")
.filter((v) => v.ActiveStatus === 'A')
.map((v) => v.ProdVariantName)
),
];
@ -362,10 +396,9 @@ const PurchaseExcel = ({ handleSubmit }) => {
const endRow = currentRow - 1;
const safeName = product.ProdName
.trim()
.replace(/[^A-Za-z0-9_]/g, "_")
.replace(/^(\d)/, "_$1");
const safeName = product.ProdName.trim()
.replace(/[^A-Za-z0-9_]/g, '_')
.replace(/^(\d)/, '_$1');
const rangeRef = `ProductVariantMap!$B$${startRow}:$B$${endRow}`;
productRanges[safeName] = rangeRef;
@ -378,21 +411,23 @@ const PurchaseExcel = ({ handleSubmit }) => {
for (let i = 0; i < 50; i++) worksheet.addRow({});
// Find ProductName column index
const productNameColIndex = EXCEL_COLUMNS.findIndex(col => col.key === "ProductName");
const productNameColIndex = EXCEL_COLUMNS.findIndex(
(col) => col.key === 'ProductName'
);
const productNameColLetter = String.fromCharCode(65 + productNameColIndex);
// Step 3: Add a helper column for duplicate detection (will be hidden)
const helperColIndex = EXCEL_COLUMNS.length;
const helperColLetter = String.fromCharCode(65 + helperColIndex);
worksheet.getColumn(helperColLetter).header = "DuplicateCheck";
worksheet.getColumn(helperColLetter).header = 'DuplicateCheck';
worksheet.getColumn(helperColLetter).width = 5;
// Add formula to detect duplicates in helper column
for (let rowNumber = 2; rowNumber <= 51; rowNumber++) {
const helperCell = worksheet.getCell(`${helperColLetter}${rowNumber}`);
helperCell.value = {
formula: `IF(${productNameColLetter}${rowNumber}="","",COUNTIF($${productNameColLetter}$2:$${productNameColLetter}$51,${productNameColLetter}${rowNumber})>1)`
formula: `IF(${productNameColLetter}${rowNumber}="","",COUNTIF($${productNameColLetter}$2:$${productNameColLetter}$51,${productNameColLetter}${rowNumber})>1)`,
};
}
@ -400,96 +435,99 @@ const PurchaseExcel = ({ handleSubmit }) => {
EXCEL_COLUMNS.forEach(({ header, key }, index) => {
const columnLetter = String.fromCharCode(65 + index);
worksheet.getColumn(columnLetter).eachCell({ includeEmpty: true }, (cell, rowNumber) => {
if (rowNumber <= 1) return;
worksheet
.getColumn(columnLetter)
.eachCell({ includeEmpty: true }, (cell, rowNumber) => {
if (rowNumber <= 1) return;
if (key === "Supplier") {
if (rowNumber === 2) cell.value = supplierDisplayName;
cell.dataValidation = {
type: "list",
allowBlank: false,
formulae: [`"${supplierDisplayName}"`],
};
} else if (key === "ProductName") {
// Keep the dropdown list
cell.dataValidation = {
type: "list",
allowBlank: true,
formulae: [`"${productNames.join(",")}"`],
};
if (key === 'Supplier') {
if (rowNumber === 2) cell.value = supplierDisplayName;
cell.dataValidation = {
type: 'list',
allowBlank: false,
formulae: [`"${supplierDisplayName}"`],
};
} else if (key === 'ProductName') {
// Keep the dropdown list
cell.dataValidation = {
type: 'list',
allowBlank: true,
formulae: [`"${productNames.join(',')}"`],
};
// Add conditional formatting to highlight duplicates
worksheet.addConditionalFormatting({
ref: `${productNameColLetter}${rowNumber}`,
rules: [
{
type: 'expression',
formulae: [`${helperColLetter}${rowNumber}=TRUE`],
style: {
fill: {
type: 'pattern',
pattern: 'solid',
bgColor: { argb: 'FFFF6B6B' }
// Add conditional formatting to highlight duplicates
worksheet.addConditionalFormatting({
ref: `${productNameColLetter}${rowNumber}`,
rules: [
{
type: 'expression',
formulae: [`${helperColLetter}${rowNumber}=TRUE`],
style: {
fill: {
type: 'pattern',
pattern: 'solid',
bgColor: { argb: 'FFFF6B6B' },
},
font: {
color: { argb: 'FFFFFFFF' },
bold: true,
},
},
font: {
color: { argb: 'FFFFFFFF' },
bold: true
}
}
}
]
});
},
],
});
// Add a warning note
cell.note = "⚠️ WARNING: Each product should only be selected once. Duplicates will be highlighted in red.";
// Add a warning note
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") {
const productCell = `${productNameColLetter}${rowNumber}`;
const formula = `INDIRECT(SUBSTITUTE(SUBSTITUTE(SUBSTITUTE(SUBSTITUTE(SUBSTITUTE(${productCell}," ","_"),"-","_"),"&","_"),"(","_"),")","_"))`;
cell.dataValidation = {
type: "list",
allowBlank: true,
formulae: [formula],
};
cell.note = "Select product first; variants depend on chosen product.";
} else if (key === "Quantity") {
cell.dataValidation = {
type: "custom",
allowBlank: true,
formulae: [`ISNUMBER(${columnLetter}${rowNumber})`],
showErrorMessage: true,
errorTitle: "Validation Error",
error: "Enter a valid number.",
};
} else if (key === "QrCode") {
cell.dataValidation = {
type: "custom",
allowBlank: false,
formulae: [`ISNUMBER(${columnLetter}${rowNumber})`],
showErrorMessage: true,
errorTitle: "Validation Error",
error: "Enter a valid number.",
};
}
});
cell.dataValidation = {
type: 'list',
allowBlank: true,
formulae: [formula],
};
cell.note =
'Select product first; variants depend on chosen product.';
} else if (key === 'Quantity') {
cell.dataValidation = {
type: 'custom',
allowBlank: true,
formulae: [`ISNUMBER(${columnLetter}${rowNumber})`],
showErrorMessage: true,
errorTitle: 'Validation Error',
error: 'Enter a valid number.',
};
} else if (key === 'QrCode') {
cell.dataValidation = {
type: 'custom',
allowBlank: false,
formulae: [`ISNUMBER(${columnLetter}${rowNumber})`],
showErrorMessage: true,
errorTitle: 'Validation Error',
error: 'Enter a valid number.',
};
}
});
});
// Step 5: Hide the helper column
worksheet.getColumn(helperColLetter).hidden = true;
// Step 6: Hide the mapping sheets
hiddenSheet.state = "veryHidden";
validationSheet.state = "veryHidden";
hiddenSheet.state = 'veryHidden';
validationSheet.state = 'veryHidden';
// Step 7: Export file
const buffer = await workbook.xlsx.writeBuffer();
const blob = new Blob([buffer], {
type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
});
const filename = `PurchaseOrder_${supplierDisplayName}.xlsx`;
const link = document.createElement("a");
const link = document.createElement('a');
link.href = URL.createObjectURL(blob);
link.download = filename;
link.click();
@ -499,7 +537,6 @@ const PurchaseExcel = ({ handleSubmit }) => {
handleModalCancel();
}, [supplierProducts, selectedSupplierData, getSupplierDisplayName]);
const handleCancelData = useCallback(() => {
dispatch(emptyExcelData());
if (fileInputRef.current) {
@ -601,7 +638,7 @@ const PurchaseExcel = ({ handleSubmit }) => {
} catch (error) {
console.error('Error fetching suppliers:', error);
setMessageData('Failed to fetch suppliers.');
setMessageType('error')
setMessageType('error');
}
}, [CompId, AppId, BranchId, dispatch]);
@ -620,7 +657,7 @@ const PurchaseExcel = ({ handleSubmit }) => {
const handleSupplierBasedProducts = useCallback(async () => {
if (!selectedSupplier) {
setMessageData('Please select a supplier.');
setMessageType('warning')
setMessageType('warning');
return;
}
@ -629,7 +666,14 @@ const PurchaseExcel = ({ handleSubmit }) => {
return (
<div className="container">
<Messages messageType={messageType} messageData={messageData} onComplete={() => { setMessageData(null); setMessageType(null); }} />
<Messages
messageType={messageType}
messageData={messageData}
onComplete={() => {
setMessageData(null);
setMessageType(null);
}}
/>
<div className="form">
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
<label>
@ -698,7 +742,10 @@ const PurchaseExcel = ({ handleSubmit }) => {
)}
</div>
<div className="viewerExcelupload" style={{ maxHeight: '500px', overflow: 'auto' }}>
<div
className="viewerExcelupload"
style={{ maxHeight: '500px', overflow: 'auto' }}
>
{hasData && (
<Table
columns={columns}
@ -808,4 +855,4 @@ const PurchaseExcel = ({ handleSubmit }) => {
);
};
export default PurchaseExcel;
export default PurchaseExcel;

View File

@ -15,16 +15,12 @@ import {
PurchaseOrderMailPost,
} from '../../Features/PurchaseOrder/PurchaseOrder';
import { Breadcrumb } from 'antd';
import { styled } from '@mui/system';
import {
breadCrumbSelector,
changeBreadCrumb,
} from '../../Features/AppPage/CenterPage';
const Input = styled('input')({
display: 'none',
});
const subDirectory = import.meta.env.BASE_URL;
const PurchaseOrderMail = () => {
@ -599,7 +595,7 @@ const PurchaseOrderMail = () => {
]}
>
<InputField
label={<label class="required">Receiver Name</label>}
label={<label className="required">Receiver Name</label>}
autoComplete="off"
onChange={(e) => {
setdeliveryName(e?.target?.value);
@ -631,7 +627,7 @@ const PurchaseOrderMail = () => {
]}
>
<InputField
label={<label class="required">Mobile Number</label>}
label={<label className="required">Mobile Number</label>}
autoComplete="off"
maxLength={10}
onChange={(e) => {
@ -661,7 +657,7 @@ const PurchaseOrderMail = () => {
]}
>
<InputField
label={<label class="required">Shipping Address</label>}
label={<label className="required">Shipping Address</label>}
autoComplete="off"
onChange={(e) => {
setdeliveryAddress(e?.target?.value);
@ -688,7 +684,7 @@ const PurchaseOrderMail = () => {
]}
>
<InputField
label={<label class="required">City / Town</label>}
label={<label className="required">City / Town</label>}
autoComplete="off"
onChange={(e) => {
setdeliverycity(e?.target?.value);
@ -715,7 +711,7 @@ const PurchaseOrderMail = () => {
]}
>
<InputField
label={<label class="required">State </label>}
label={<label className="required">State </label>}
autoComplete="off"
onChange={(e) => {
setdeliveryState(e?.target?.value);
@ -745,7 +741,7 @@ const PurchaseOrderMail = () => {
]}
>
<InputField
label={<label class="required">PIN Code</label>}
label={<label className="required">PIN Code</label>}
autoComplete="off"
onChange={(e) => {
setdeliveryZip(e?.target?.value);

View File

@ -6,9 +6,9 @@ import React, {
useCallback,
} from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { read, utils } from 'xlsx';
// import { read, utils } from 'xlsx';
import ExcelJS from 'exceljs';
import { Table, Form, Input, Button, Tooltip, Modal } from 'antd';
import { Table, Form, Input, Tooltip, Modal } from 'antd';
import {
emptyExcelData,
excelDataSelector,
@ -272,28 +272,36 @@ const StockExcel = ({
const uploadAndProcessExcel = (file) => {
const reader = new FileReader();
reader.onload = (e) => {
const data = new Uint8Array(e.target.result);
reader.onload = async (e) => {
const buffer = e.target.result;
const workbook = read(data, { type: 'array' });
const worksheet = workbook.Sheets[workbook.SheetNames[0]];
const workbook = new ExcelJS.Workbook();
await workbook.xlsx.load(buffer);
const jsonData = utils.sheet_to_json(worksheet, {
header: 1,
raw: false,
dateNF: 'DD/MM/YY',
const worksheet = workbook.worksheets[0];
// 🔥 same structure as xlsx (array of arrays)
const jsonData = [];
worksheet.eachRow((row) => {
const rowValues = row.values.slice(1); // remove empty index
jsonData.push(rowValues);
});
const nonEmptyRows = jsonData.filter((row) =>
row.some((cell) => cell !== '')
row.some((cell) => cell !== '' && cell !== null && cell !== undefined)
);
const header = nonEmptyRows[0];
const rows = nonEmptyRows.slice(1);
const dateFields = ['Manufacture Date', 'Expire Date', 'Stock Date'];
function convertToDisplayFormat(dateString) {
if (!dateString) return '';
const date = new Date(dateString);
if (isNaN(date)) return dateString;
const day = date.getDate().toString().padStart(2, '0');
const month = (date.getMonth() + 1).toString().padStart(2, '0');
const year = date.getFullYear().toString();
@ -307,13 +315,12 @@ const StockExcel = ({
} else {
acc[col] = row[columnIndex];
}
return acc;
}, {});
return rowData;
});
// unchanged logic
dispatch(uploadExcel({ file, jsonData: nonEmptyRows }));
setWorksheet1(worksheet1);
setDatas(formattedData);
@ -322,6 +329,59 @@ const StockExcel = ({
reader.readAsArrayBuffer(file);
};
// const uploadAndProcessExcel = (file) => {
// const reader = new FileReader();
// reader.onload = (e) => {
// const data = new Uint8Array(e.target.result);
// const workbook = read(data, { type: 'array' });
// const worksheet = workbook.Sheets[workbook.SheetNames[0]];
// const jsonData = utils.sheet_to_json(worksheet, {
// header: 1,
// raw: false,
// dateNF: 'DD/MM/YY',
// });
// const nonEmptyRows = jsonData.filter((row) =>
// row.some((cell) => cell !== '')
// );
// const header = nonEmptyRows[0];
// const rows = nonEmptyRows.slice(1);
// const dateFields = ['Manufacture Date', 'Expire Date', 'Stock Date'];
// function convertToDisplayFormat(dateString) {
// const date = new Date(dateString);
// const day = date.getDate().toString().padStart(2, '0');
// const month = (date.getMonth() + 1).toString().padStart(2, '0');
// const year = date.getFullYear().toString();
// return `${day}-${month}-${year}`;
// }
// const formattedData = rows.map((row) => {
// let rowData = header.reduce((acc, col, columnIndex) => {
// if (dateFields.includes(col)) {
// acc[col] = convertToDisplayFormat(row[columnIndex]);
// } else {
// acc[col] = row[columnIndex];
// }
// return acc;
// }, {});
// return rowData;
// });
// dispatch(uploadExcel({ file, jsonData: nonEmptyRows }));
// setWorksheet1(worksheet1);
// setDatas(formattedData);
// };
// reader.readAsArrayBuffer(file);
// };
// ...existing code...
const handleDownload = async () => {
try {
@ -441,8 +501,8 @@ const StockExcel = ({
: c.key === 'Quantity'
? 10
: c.key === 'Supplier' ||
c.key === 'InvoiceDeliveryChallan' ||
c.key === 'SupplierInvoiceNumber'
c.key === 'InvoiceDeliveryChallan' ||
c.key === 'SupplierInvoiceNumber'
? 30
: 20,
}));
@ -651,7 +711,7 @@ const StockExcel = ({
cell.dataValidation = {
type: 'list',
formulae: ['"Own,With Paid"'],
allowBlank: true
allowBlank: true,
};
if (!cell.value) {
@ -1239,7 +1299,7 @@ const StockExcel = ({
...record,
...values,
});
} catch (errInfo) { }
} catch (errInfo) {}
};
let childNode = children;

View File

@ -48,9 +48,8 @@ import {
} from '../../Features/ExcelUploadPage/ExcelUploadPage.js';
const subDirectory = import.meta.env.BASE_URL;
import ExcelJS from 'exceljs';
import { read, utils } from 'xlsx';
// import { read, utils } from 'xlsx';
import { v4 as uuidv4 } from 'uuid';
import { InputField } from '../../Components/Forms/InputField.jsx';
import { MdOutlineAppRegistration } from 'react-icons/md';
import {
getFieldSetupData,
@ -737,7 +736,12 @@ const StockPriceUpdate = () => {
fetchData();
}, []);
const getPreference = async () => {
const data = { AppId: AppId, CompId: CompId, BranchId: BranchId,UserId: UserId };
const data = {
AppId: AppId,
CompId: CompId,
BranchId: BranchId,
UserId: UserId,
};
const { data: res } = await dispatch(getPreferenceData(data)).unwrap();
const decimalSetting = res?.data?.[0]?.SettingDtlDetails?.find(
(setting) =>
@ -1820,15 +1824,17 @@ const StockPriceUpdate = () => {
excelDataRef.current?.setFieldsValue(formValues);
};
const extractPriceChangeDataFromExcel = async (arrayBuffer) => {
const workbook = new ExcelJS.Workbook();
await workbook.xlsx.load(arrayBuffer);
const extractPriceChangeDataFromExcel = (arrayBuffer) => {
const workbook = read(arrayBuffer, { type: 'array' });
const worksheet = workbook.Sheets[workbook.SheetNames[0]];
const worksheet = workbook.worksheets[0];
const rows = utils.sheet_to_json(worksheet, {
header: 1,
raw: false,
defval: '',
// 🔥 same structure as xlsx (array of arrays)
const rows = [];
worksheet.eachRow((row) => {
const rowValues = row.values.slice(1); // remove empty index
rows.push(rowValues.map((cell) => cell ?? '')); // match defval: ''
});
if (!rows.length) return [];
@ -1901,7 +1907,7 @@ const StockPriceUpdate = () => {
? Number(getVal(row, 'Old Wholesale Price'))
: null,
// 🔹 New prices (normalized)
// 🔹 New prices
NewMRP: newMRP,
NewSellPrice: newSP,
NewWholeSalePrice: newWholesale,
@ -1911,6 +1917,96 @@ const StockPriceUpdate = () => {
return result;
};
// const extractPriceChangeDataFromExcel = (arrayBuffer) => {
// const workbook = read(arrayBuffer, { type: 'array' });
// const worksheet = workbook.Sheets[workbook.SheetNames[0]];
// const rows = utils.sheet_to_json(worksheet, {
// header: 1,
// raw: false,
// defval: '',
// });
// if (!rows.length) return [];
// const header = rows[0];
// setHeaders(header);
// const dataRows = rows.slice(1);
// // Helper to safely get column value
// const getVal = (row, colName) => {
// const idx = header.indexOf(colName);
// return idx !== -1 ? row[idx] : '';
// };
// const result = dataRows
// .map((row) => {
// const prodId = getVal(row, 'ProdId')?.toString().trim();
// if (!prodId) return null;
// const newMRP =
// getVal(row, 'New MRP') !== '' ? Number(getVal(row, 'New MRP')) : null;
// const newSP =
// getVal(row, 'New SP') !== '' ? Number(getVal(row, 'New SP')) : null;
// const newWholesale =
// getVal(row, 'New Wholesale Price') !== ''
// ? Number(getVal(row, 'New Wholesale Price'))
// : null;
// // Must have at least one change
// if (newMRP === null && newSP === null && newWholesale === null) {
// return null;
// }
// return {
// // 🔹 Product identity
// localId: uuidv4(),
// ProdId: prodId,
// InwardDtlId: getVal(row, 'InwardDtlId')
// ? getVal(row, 'InwardDtlId').toString()
// : null,
// // 🔹 Product info
// VariantName: getVal(row, 'Variant Name') || null,
// BatchRef: getVal(row, 'Batch No') || null,
// ProductName: getVal(row, 'Product Name') || null,
// Size: getVal(row, 'Size') || null,
// UOMName: getVal(row, 'UOMName') || null,
// StockMaintainence: getVal(row, 'Stock Maintainence') || null,
// // 🔹 Stock / dates
// StockDate: getVal(row, 'Stock Date') || null,
// ExpDate: getVal(row, 'Exp Date') || null,
// downloadType: getVal(row, 'downloadType') || null,
// Stock:
// getVal(row, 'Stock') !== '' ? Number(getVal(row, 'Stock')) : null,
// // 🔹 Old prices
// OldMRP:
// getVal(row, 'Old MRP') !== ''
// ? Number(getVal(row, 'Old MRP'))
// : null,
// OldSP:
// getVal(row, 'Old SP') !== '' ? Number(getVal(row, 'Old SP')) : null,
// OldWholeSalePrice:
// getVal(row, 'Old Wholesale Price') !== ''
// ? Number(getVal(row, 'Old Wholesale Price'))
// : null,
// // 🔹 New prices (normalized)
// NewMRP: newMRP,
// NewSellPrice: newSP,
// NewWholeSalePrice: newWholesale,
// };
// })
// .filter(Boolean);
// return result;
// };
const handleCancelData = () => {
dispatch(emptyExcelData());

View File

@ -1,28 +1,28 @@
import React, { useCallback, useMemo, useRef, useState } from "react";
import { read, utils } from "xlsx";
import ExcelJS from "exceljs";
import { Button } from "antd";
import { MdRefresh, MdInfo } from "react-icons/md";
import { RiFileExcel2Fill } from "react-icons/ri";
import { Messages } from "../../Components/Notifications/Messages.jsx";
import { getSession } from "../../Services/Others.js";
import "./OpeningStockExcel.scss"
import { useCallback, useMemo, useRef, useState } from 'react';
// import { read, utils } from "xlsx";
import ExcelJS from 'exceljs';
import { Button } from 'antd';
import { MdRefresh, MdInfo } from 'react-icons/md';
import { RiFileExcel2Fill } from 'react-icons/ri';
import { Messages } from '../../Components/Notifications/Messages.jsx';
import { getSession } from '../../Services/Others.js';
import './OpeningStockExcel.scss';
const TEMPLATE_HEADERS = [
{ key: "ProdName", header: "Product Name" },
{ key: "TotalQty", header: "Total Qty" },
{ key: "TotalPiece", header: "Total Pieces" },
{ key: 'ProdName', header: 'Product Name' },
{ key: 'TotalQty', header: 'Total Qty' },
{ key: 'TotalPiece', header: 'Total Pieces' },
];
const OpeningStockExcel = ({ products = [], onApply, onCancel }) => {
const UserId = getSession("UserId");
const UserId = getSession('UserId');
const fileInputRef = useRef(null);
const [previewRows, setPreviewRows] = useState([]);
const [messageType, setMessageType] = useState(null);
const [messageData, setMessageData] = useState(null);
const [isParsing, setIsParsing] = useState(false);
const [inst, setInst] = useState(false)
console.log(previewRows, "previewRows")
const [inst, setInst] = useState(false);
console.log(previewRows, 'previewRows');
const onComplete = useCallback(() => {
setMessageData(null);
setMessageType(null);
@ -31,9 +31,9 @@ const OpeningStockExcel = ({ products = [], onApply, onCancel }) => {
const templateColumns = useMemo(
() => [
{
title: "Product Name",
dataIndex: "productDisplayName",
key: "productDisplayName",
title: 'Product Name',
dataIndex: 'productDisplayName',
key: 'productDisplayName',
width: 200,
},
// {
@ -43,36 +43,35 @@ const OpeningStockExcel = ({ products = [], onApply, onCancel }) => {
// width: 180,
// },
{
title: "Total Qty",
dataIndex: "TotalQty",
key: "TotalQty",
title: 'Total Qty',
dataIndex: 'TotalQty',
key: 'TotalQty',
width: 50,
},
{
title: "Total Pcs",
dataIndex: "TotalPiece",
key: "TotalPiece",
title: 'Total Pcs',
dataIndex: 'TotalPiece',
key: 'TotalPiece',
width: 50,
render: (value, row) =>
row?.OnePcsAvailable === "Y" ? value : "N/A",
render: (value, row) => (row?.OnePcsAvailable === 'Y' ? value : 'N/A'),
},
],
[]
);
const handleInstrction = () => {
setInst(!inst)
}
setInst(!inst);
};
const handleDownloadTemplate = useCallback(async () => {
if (!products?.length) {
setMessageType("warning");
setMessageData("No products available to include in the template.");
setMessageType('warning');
setMessageData('No products available to include in the template.');
return;
}
try {
const workbook = new ExcelJS.Workbook();
const worksheet = workbook.addWorksheet("Opening Stock");
const worksheet = workbook.addWorksheet('Opening Stock');
const headerRow = worksheet.addRow(
TEMPLATE_HEADERS.map((item) => item.header)
@ -80,33 +79,29 @@ const OpeningStockExcel = ({ products = [], onApply, onCancel }) => {
headerRow.eachCell((cell) => {
cell.protection = { locked: true };
cell.fill = {
type: "pattern",
pattern: "solid",
fgColor: { argb: "FF0F6CB6" },
type: 'pattern',
pattern: 'solid',
fgColor: { argb: 'FF0F6CB6' },
};
cell.font = {
color: { argb: "FFFFFFFF" },
color: { argb: 'FFFFFFFF' },
bold: true,
};
cell.alignment = { vertical: "middle", horizontal: "center" };
cell.alignment = { vertical: 'middle', horizontal: 'center' };
cell.border = {
top: { style: "thin", color: { argb: "FFFFFFFF" } },
left: { style: "thin", color: { argb: "FFFFFFFF" } },
bottom: { style: "thin", color: { argb: "FFFFFFFF" } },
right: { style: "thin", color: { argb: "FFFFFFFF" } },
top: { style: 'thin', color: { argb: 'FFFFFFFF' } },
left: { style: 'thin', color: { argb: 'FFFFFFFF' } },
bottom: { style: 'thin', color: { argb: 'FFFFFFFF' } },
right: { style: 'thin', color: { argb: 'FFFFFFFF' } },
};
});
products.forEach((product) => {
const displayName = product?.ProdVariantName
? `${product?.ProdName ?? ""} (${product?.ProdVariantName ?? ""})`
: product?.ProdName ?? "";
? `${product?.ProdName ?? ''} (${product?.ProdVariantName ?? ''})`
: (product?.ProdName ?? '');
const newRow = worksheet.addRow([
displayName,
"",
"",
]);
const newRow = worksheet.addRow([displayName, '', '']);
const rowNumber = newRow.number;
const productCell = worksheet.getCell(rowNumber, 1);
@ -114,61 +109,61 @@ const OpeningStockExcel = ({ products = [], onApply, onCancel }) => {
const qtyCell = worksheet.getCell(rowNumber, 2);
qtyCell.dataValidation = {
type: "decimal",
operator: "greaterThanOrEqual",
type: 'decimal',
operator: 'greaterThanOrEqual',
allowBlank: true,
showErrorMessage: true,
showInputMessage: true,
formulae: [0],
errorTitle: "Invalid Quantity",
error: "Please enter a numeric value greater than or equal to 0.",
inputTitle: "Total Qty",
inputMessage: "Enter the total quantity (numeric).",
errorTitle: 'Invalid Quantity',
error: 'Please enter a numeric value greater than or equal to 0.',
inputTitle: 'Total Qty',
inputMessage: 'Enter the total quantity (numeric).',
};
qtyCell.protection = { locked: false };
const pieceCell = worksheet.getCell(rowNumber, 3);
const allowPieces = product?.OnePcsAvailable === "Y";
const allowPieces = product?.OnePcsAvailable === 'Y';
if (allowPieces) {
pieceCell.dataValidation = {
type: "decimal",
operator: "greaterThanOrEqual",
type: 'decimal',
operator: 'greaterThanOrEqual',
allowBlank: true,
showErrorMessage: true,
showInputMessage: true,
formulae: [0],
errorTitle: "Invalid Total Pieces",
error: "Please enter a numeric value greater than or equal to 0.",
inputTitle: "Total Pieces",
inputMessage: "Enter the total pieces (numeric).",
errorTitle: 'Invalid Total Pieces',
error: 'Please enter a numeric value greater than or equal to 0.',
inputTitle: 'Total Pieces',
inputMessage: 'Enter the total pieces (numeric).',
};
pieceCell.protection = { locked: false };
} else {
pieceCell.dataValidation = {
type: "custom",
type: 'custom',
allowBlank: true,
showErrorMessage: true,
showInputMessage: true,
formulae: [`=LEN(TRIM(C${rowNumber}))=0`],
errorTitle: "Not Allowed",
errorTitle: 'Not Allowed',
error:
"Total Pieces entry is not allowed for this product. Leave this cell empty.",
inputTitle: "Total Pieces",
'Total Pieces entry is not allowed for this product. Leave this cell empty.',
inputTitle: 'Total Pieces',
inputMessage:
"Total Pieces is not allowed for this product and must remain empty.",
'Total Pieces is not allowed for this product and must remain empty.',
};
pieceCell.fill = {
type: "pattern",
pattern: "solid",
fgColor: { argb: "FFEFEFEF" },
type: 'pattern',
pattern: 'solid',
fgColor: { argb: 'FFEFEFEF' },
};
pieceCell.font = {
color: { argb: "FF6E6E6E" },
color: { argb: 'FF6E6E6E' },
italic: true,
};
pieceCell.note =
"Total Pieces entry is disabled because single-piece handling is not available for this product.";
'Total Pieces entry is disabled because single-piece handling is not available for this product.';
pieceCell.protection = { locked: true };
}
});
@ -185,7 +180,7 @@ const OpeningStockExcel = ({ products = [], onApply, onCancel }) => {
});
try {
await worksheet.protect("OpeningStockTemplate", {
await worksheet.protect('OpeningStockTemplate', {
selectLockedCells: true,
selectUnlockedCells: true,
formatCells: false,
@ -197,143 +192,306 @@ const OpeningStockExcel = ({ products = [], onApply, onCancel }) => {
deleteRows: false,
});
} catch (protectError) {
console.warn("Worksheet protection not supported in this environment:", protectError);
setMessageType("warning");
console.warn(
'Worksheet protection not supported in this environment:',
protectError
);
setMessageType('warning');
setMessageData(
"Template protection is not supported in this browser. The sheet will still download without protection."
'Template protection is not supported in this browser. The sheet will still download without protection.'
);
}
const buffer = await workbook.xlsx.writeBuffer();
const blob = new Blob([buffer], {
type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
});
const link = document.createElement("a");
const link = document.createElement('a');
link.href = URL.createObjectURL(blob);
link.download = "opening_stock_template.xlsx";
link.download = 'opening_stock_template.xlsx';
link.click();
URL.revokeObjectURL(link.href);
} catch (error) {
console.error("Template download failed:", error);
setMessageType("error");
setMessageData("Failed to generate the template. Please try again.");
console.error('Template download failed:', error);
setMessageType('error');
setMessageData('Failed to generate the template. Please try again.');
}
}, [products]);
// const parseExcelFile = useCallback(
// async (file) => {
// if (!file) return;
// setIsParsing(true);
// try {
// const arrayBuffer = await file.arrayBuffer();
// const workbook = read(arrayBuffer, { type: "array" });
// const worksheet = workbook.Sheets[workbook.SheetNames[0]];
// const rows = utils.sheet_to_json(worksheet, { header: 1, defval: "" });
// if (!rows.length) {
// setMessageType("warning");
// setMessageData("Uploaded file is empty.");
// setPreviewRows([]);
// return;
// }
// const [headerRow, ...dataRows] = rows;
// const headerMap = TEMPLATE_HEADERS.reduce(
// (acc, item) => ({
// ...acc,
// [item.key]: headerRow.findIndex(
// (header) =>
// header?.toString().trim().toLowerCase() ===
// item.header.toLowerCase()
// ),
// }),
// {}
// );
// const missingHeaders = Object.entries(headerMap)
// .filter(([, index]) => index === -1)
// .map(([key]) => key);
// if (missingHeaders.length) {
// setMessageType("error");
// setMessageData(
// `Missing required column(s) in the Excel file: ${missingHeaders.join(
// ", "
// )}`
// );
// setPreviewRows([]);
// return;
// }
// const productIndex = products.reduce((acc, product) => {
// const displayName = product?.ProdVariantName
// ? `${product?.ProdName ?? ""} (${product?.ProdVariantName ?? ""})`
// : product?.ProdName ?? "";
// if (displayName) {
// acc[displayName.trim().toLowerCase()] = product;
// }
// return acc;
// }, {});
// const parsedRows = [];
// const issues = [];
// dataRows.forEach((row, rowIndex) => {
// const prodNameCell = row[headerMap.ProdName];
// const qtyCell = row[headerMap.TotalQty];
// const pieceCell = row[headerMap.TotalPiece];
// const productNameValue = prodNameCell?.toString().trim();
// if (!productNameValue) {
// if (qtyCell || pieceCell) {
// issues.push(
// `Row ${rowIndex + 2}: Product Name is empty while quantities are provided.`
// );
// }
// return;
// }
// const productMatch =
// productIndex[productNameValue.trim().toLowerCase()];
// if (!productMatch) {
// issues.push(
// `Row ${rowIndex + 2}: Product "${productNameValue}" is not found in available products.`
// );
// return;
// }
// const totalQty = qtyCell?.toString().trim();
// const totalPiece = pieceCell?.toString().trim();
// const hasValues =
// (totalQty && totalQty.length > 0) ||
// (totalPiece && totalPiece.length > 0);
// if (!hasValues) {
// return;
// }
// const isQtyValid =
// !totalQty || /^[0-9]+(\.[0-9]+)?$/.test(totalQty.trim());
// const isPieceValid =
// !totalPiece || /^[0-9]+(\.[0-9]+)?$/.test(totalPiece.trim());
// if (!isQtyValid || !isPieceValid) {
// issues.push(
// `Row ${rowIndex + 2}: Total Qty or Total Pieces is not a valid number.`
// );
// return;
// }
// const allowPieces = productMatch?.OnePcsAvailable === "Y";
// let cleanedPieces = totalPiece ?? "";
// if (!allowPieces && cleanedPieces) {
// issues.push(
// `Row ${rowIndex + 2}: Total Pieces is not allowed for "${productNameValue}" and the value has been ignored.`
// );
// cleanedPieces = "";
// }
// parsedRows.push({
// key: productMatch.ProdId,
// ProdId: productMatch.ProdId,
// ProdName: productMatch.ProdName,
// ProdVariantName: productMatch.ProdVariantName,
// productDisplayName: productNameValue,
// TotalQty: totalQty ?? "",
// TotalPiece: cleanedPieces,
// OnePcsAvailable: productMatch?.OnePcsAvailable,
// });
// });
// if (!parsedRows.length) {
// setMessageType("warning");
// setMessageData(
// issues.length
// ? issues.join(" ")
// : "No valid rows found in the uploaded file."
// );
// setPreviewRows([]);
// return;
// }
// if (issues.length) {
// setMessageType("warning");
// setMessageData(
// `Some rows were skipped:\n${issues.join("\n")}`
// );
// } else {
// setMessageType("success");
// setMessageData(
// `Successfully parsed ${parsedRows.length} row(s) from the uploaded file.`
// );
// }
// setPreviewRows(parsedRows);
// } catch (error) {
// console.error("Failed to parse Excel file:", error);
// setMessageType("error");
// setMessageData(
// "Failed to read the uploaded file. Please ensure it is a valid Excel document."
// );
// setPreviewRows([]);
// } finally {
// setIsParsing(false);
// }
// },
// [products]
// );
const parseExcelFile = useCallback(
async (file) => {
if (!file) return;
setIsParsing(true);
try {
const workbook = new ExcelJS.Workbook();
const arrayBuffer = await file.arrayBuffer();
const workbook = read(arrayBuffer, { type: "array" });
const worksheet = workbook.Sheets[workbook.SheetNames[0]];
const rows = utils.sheet_to_json(worksheet, { header: 1, defval: "" });
await workbook.xlsx.load(arrayBuffer);
const worksheet = workbook.worksheets[0];
if (!worksheet) throw new Error('No sheet found');
const rows = [];
worksheet.eachRow((row, rowNumber) => {
rows.push(row.values.slice(1)); // remove first empty index
});
if (!rows.length) {
setMessageType("warning");
setMessageData("Uploaded file is empty.");
setMessageType('warning');
setMessageData('Uploaded file is empty.');
setPreviewRows([]);
return;
}
const [headerRow, ...dataRows] = rows;
const headerMap = TEMPLATE_HEADERS.reduce(
(acc, item) => ({
...acc,
[item.key]: headerRow.findIndex(
(header) =>
header?.toString().trim().toLowerCase() ===
item.header.toLowerCase()
),
}),
{}
const [headerRow = [], ...dataRows] = rows;
const normalizedHeaders = headerRow.map((h) =>
h?.toString().trim().toLowerCase()
);
const headerMap = {};
TEMPLATE_HEADERS.forEach((item) => {
headerMap[item.key] = normalizedHeaders.indexOf(
item.header.toLowerCase()
);
});
const missingHeaders = Object.entries(headerMap)
.filter(([, index]) => index === -1)
.map(([key]) => key);
if (missingHeaders.length) {
setMessageType("error");
setMessageType('error');
setMessageData(
`Missing required column(s) in the Excel file: ${missingHeaders.join(
", "
)}`
`Missing required column(s): ${missingHeaders.join(', ')}`
);
setPreviewRows([]);
return;
}
const productIndex = products.reduce((acc, product) => {
// 🔥 Product index
const productIndex = {};
for (const product of products) {
const displayName = product?.ProdVariantName
? `${product?.ProdName ?? ""} (${product?.ProdVariantName ?? ""})`
: product?.ProdName ?? "";
? `${product?.ProdName ?? ''} (${product?.ProdVariantName ?? ''})`
: (product?.ProdName ?? '');
if (displayName) {
acc[displayName.trim().toLowerCase()] = product;
productIndex[displayName.trim().toLowerCase()] = product;
}
return acc;
}, {});
}
const parsedRows = [];
const issues = [];
dataRows.forEach((row, rowIndex) => {
const prodNameCell = row[headerMap.ProdName];
const qtyCell = row[headerMap.TotalQty];
const pieceCell = row[headerMap.TotalPiece];
const rowNum = rowIndex + 2;
const productNameValue = row[headerMap.ProdName]?.toString().trim();
const totalQty = row[headerMap.TotalQty]?.toString().trim();
const totalPiece = row[headerMap.TotalPiece]?.toString().trim();
const productNameValue = prodNameCell?.toString().trim();
if (!productNameValue) {
if (qtyCell || pieceCell) {
issues.push(
`Row ${rowIndex + 2}: Product Name is empty while quantities are provided.`
);
if (totalQty || totalPiece) {
issues.push(`Row ${rowNum}: Product Name is empty.`);
}
return;
}
const productMatch =
productIndex[productNameValue.trim().toLowerCase()];
const productMatch = productIndex[productNameValue.toLowerCase()];
if (!productMatch) {
issues.push(
`Row ${rowIndex + 2}: Product "${productNameValue}" is not found in available products.`
`Row ${rowNum}: Product "${productNameValue}" not found.`
);
return;
}
const totalQty = qtyCell?.toString().trim();
const totalPiece = pieceCell?.toString().trim();
if (!totalQty && !totalPiece) return;
const hasValues =
(totalQty && totalQty.length > 0) ||
(totalPiece && totalPiece.length > 0);
const isValidNumber = (val) =>
!val || /^[0-9]+(\.[0-9]+)?$/.test(val);
if (!hasValues) {
if (!isValidNumber(totalQty) || !isValidNumber(totalPiece)) {
issues.push(`Row ${rowNum}: Invalid number.`);
return;
}
const isQtyValid =
!totalQty || /^[0-9]+(\.[0-9]+)?$/.test(totalQty.trim());
const isPieceValid =
!totalPiece || /^[0-9]+(\.[0-9]+)?$/.test(totalPiece.trim());
if (!isQtyValid || !isPieceValid) {
let cleanedPieces = totalPiece || '';
if (productMatch?.OnePcsAvailable !== 'Y' && cleanedPieces) {
issues.push(
`Row ${rowIndex + 2}: Total Qty or Total Pieces is not a valid number.`
`Row ${rowNum}: Pieces not allowed for "${productNameValue}". Ignored.`
);
return;
}
const allowPieces = productMatch?.OnePcsAvailable === "Y";
let cleanedPieces = totalPiece ?? "";
if (!allowPieces && cleanedPieces) {
issues.push(
`Row ${rowIndex + 2}: Total Pieces is not allowed for "${productNameValue}" and the value has been ignored.`
);
cleanedPieces = "";
cleanedPieces = '';
}
parsedRows.push({
@ -342,42 +500,33 @@ const OpeningStockExcel = ({ products = [], onApply, onCancel }) => {
ProdName: productMatch.ProdName,
ProdVariantName: productMatch.ProdVariantName,
productDisplayName: productNameValue,
TotalQty: totalQty ?? "",
TotalQty: totalQty || '',
TotalPiece: cleanedPieces,
OnePcsAvailable: productMatch?.OnePcsAvailable,
});
});
if (!parsedRows.length) {
setMessageType("warning");
setMessageType('warning');
setMessageData(
issues.length
? issues.join(" ")
: "No valid rows found in the uploaded file."
issues.length ? issues.join('\n') : 'No valid rows found.'
);
setPreviewRows([]);
return;
}
if (issues.length) {
setMessageType("warning");
setMessageData(
`Some rows were skipped:\n${issues.join("\n")}`
);
} else {
setMessageType("success");
setMessageData(
`Successfully parsed ${parsedRows.length} row(s) from the uploaded file.`
);
}
setMessageType(issues.length ? 'warning' : 'success');
setMessageData(
issues.length
? `Some rows skipped:\n${issues.join('\n')}`
: `Parsed ${parsedRows.length} row(s).`
);
setPreviewRows(parsedRows);
} catch (error) {
console.error("Failed to parse Excel file:", error);
setMessageType("error");
setMessageData(
"Failed to read the uploaded file. Please ensure it is a valid Excel document."
);
console.error('Excel parse error:', error);
setMessageType('error');
setMessageData('Invalid Excel file.');
setPreviewRows([]);
} finally {
setIsParsing(false);
@ -396,8 +545,8 @@ const OpeningStockExcel = ({ products = [], onApply, onCancel }) => {
const handleApply = useCallback(() => {
if (!previewRows.length) {
setMessageType("warning");
setMessageData("No parsed rows available to apply.");
setMessageType('warning');
setMessageData('No parsed rows available to apply.');
return;
}
@ -405,17 +554,19 @@ const OpeningStockExcel = ({ products = [], onApply, onCancel }) => {
previewRows.map((row) => ({
ProdId: row.ProdId,
ProdVariantName: row?.ProdVariantName,
OnePcsAvailable: row.OnePcsAvailable || "N",
"CreatedBy": UserId,
OnePcsAvailable: row.OnePcsAvailable || 'N',
CreatedBy: UserId,
TotalQty:
row.TotalQty !== undefined && row.TotalQty !== null
? row.TotalQty.toString()
: "",
: '',
TotalPcs:
row.OnePcsAvailable === "Y" && row.TotalPiece !== undefined && row.TotalPiece !== null
row.OnePcsAvailable === 'Y' &&
row.TotalPiece !== undefined &&
row.TotalPiece !== null
? row.TotalPiece.toString()
: 0,
InwardDetails: []
InwardDetails: [],
}))
);
}, [onApply, previewRows]);
@ -425,7 +576,7 @@ const OpeningStockExcel = ({ products = [], onApply, onCancel }) => {
setMessageData(null);
setMessageType(null);
if (fileInputRef.current) {
fileInputRef.current.value = "";
fileInputRef.current.value = '';
}
}, []);
@ -440,7 +591,7 @@ const OpeningStockExcel = ({ products = [], onApply, onCancel }) => {
<div className="openingStock-excel-actions">
<Button
className="openingStock-excel-download-btn"
icon={<RiFileExcel2Fill />}
icon={<RiFileExcel2Fill />}
onClick={handleDownloadTemplate}
disabled={!products?.length}
>
@ -448,7 +599,7 @@ const OpeningStockExcel = ({ products = [], onApply, onCancel }) => {
</Button>
<label className="openingStock-excel-upload-label">
<RiFileExcel2Fill className="openingStock-excel-upload-icon" />
<RiFileExcel2Fill className="openingStock-excel-upload-icon" />
Upload Filled Excel
<input
ref={fileInputRef}
@ -461,7 +612,7 @@ const OpeningStockExcel = ({ products = [], onApply, onCancel }) => {
</label>
{previewRows.length > 0 && (
<Button
<Button
className="openingStock-excel-reset-btn"
icon={<MdRefresh />}
onClick={handleReset}
@ -471,7 +622,10 @@ const OpeningStockExcel = ({ products = [], onApply, onCancel }) => {
)}
</div>
<div onClick={handleInstrction} className="openingStock-excel-instruction-toggle">
<div
onClick={handleInstrction}
className="openingStock-excel-instruction-toggle"
>
<MdInfo className="openingStock-excel-info-icon" />
Instructions
</div>
@ -479,7 +633,8 @@ const OpeningStockExcel = ({ products = [], onApply, onCancel }) => {
{inst && (
<div className="openingStock-excel-instructions">
<div className="openingStock-excel-instruction-item">
1. Download the template and provide the quantities for each product.
1. Download the template and provide the quantities for each
product.
</div>
<div className="openingStock-excel-instruction-item">
2. Only update the `Total Qty` and `Total Pieces` columns.
@ -505,15 +660,17 @@ const OpeningStockExcel = ({ products = [], onApply, onCancel }) => {
<tr key={row.key}>
<td>{row.productDisplayName}</td>
<td>{row.TotalQty}</td>
<td>{row?.OnePcsAvailable === "Y" ? row.TotalPiece : "N/A"}</td>
<td>
{row?.OnePcsAvailable === 'Y' ? row.TotalPiece : 'N/A'}
</td>
</tr>
))
) : (
<tr>
<td colSpan="3" className="openingStock-excel-empty-state">
{isParsing
? "Parsing uploaded file..."
: "No data parsed yet. Upload a filled Excel file to preview changes."}
? 'Parsing uploaded file...'
: 'No data parsed yet. Upload a filled Excel file to preview changes.'}
</td>
</tr>
)}
@ -522,11 +679,10 @@ const OpeningStockExcel = ({ products = [], onApply, onCancel }) => {
</div>
<div className="openingStock-excel-footer">
<Button
className="openingStock-excel-apply-btn"
type="primary"
icon={<RiFileExcel2Fill />}
icon={<RiFileExcel2Fill />}
onClick={handleApply}
disabled={!previewRows.length}
>
@ -538,4 +694,3 @@ const OpeningStockExcel = ({ products = [], onApply, onCancel }) => {
};
export default OpeningStockExcel;

View File

@ -25,7 +25,6 @@ const commonUrl = import.meta.env.ENV_COMMON_BASE_URL;
const subDirectory = import.meta.env.BASE_URL;
const ProtectedRoutes = ({ routesConfig }) => {
useTemplate(
getSession('CompId'),
getSession('BranchId'),
@ -91,7 +90,7 @@ const ProtectedRoutes = ({ routesConfig }) => {
AppId: AppId,
CompId: CompId,
BranchId: BranchId,
UserId:UserId
UserId: UserId,
};
const response = await dispatch(gettableData(DineinData)).unwrap();
if (response?.data?.statusCode === 1) {

View File

@ -4,7 +4,9 @@ import moment from 'moment';
import jsPDF from 'jspdf';
import { changePrintStatus } from '../Features/BookingScreen/BookingData/BookingData';
import store from '../app/store.js';
import html2pdf from 'html2pdf.js';
// import html2pdf from 'html2pdf.js';
import html2canvas from 'html2canvas';
const cookies = new Cookies();
const SECRET_KEY = import.meta.env.ENV_SECRET_KEY;
@ -82,42 +84,40 @@ export const decryptedValuesFun = (value) => {
}
};
export const encryptObject=(obj)=> {
const jsonString = JSON.stringify(obj);
const encrypted = CryptoJS.AES.encrypt(jsonString, SECRET_KEY).toString();
export const encryptObject = (obj) => {
const jsonString = JSON.stringify(obj);
const encrypted = CryptoJS.AES.encrypt(jsonString, SECRET_KEY).toString();
// Make Base64 string URL-safe
const urlSafe = encrypted
.replace(/\+/g, "-")
.replace(/\//g, "_")
.replace(/=+$/, "");
// Make Base64 string URL-safe
const urlSafe = encrypted
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=+$/, '');
return urlSafe;
}
return urlSafe;
};
// Decrypt URL-safe AES string to original object
export const decryptToObject=(encryptedUrlSafeStr) =>{
try {
if (!encryptedUrlSafeStr) return null;
// Decrypt URL-safe AES string to original object
export const decryptToObject = (encryptedUrlSafeStr) => {
try {
if (!encryptedUrlSafeStr) return null;
// Restore base64 format
let base64 = encryptedUrlSafeStr
.replace(/-/g, "+")
.replace(/_/g, "/");
// Restore base64 format
let base64 = encryptedUrlSafeStr.replace(/-/g, '+').replace(/_/g, '/');
// Add missing padding
while (base64.length % 4 !== 0) {
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;
// Add missing padding
while (base64.length % 4 !== 0) {
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;
}
};
export const TokendecryptedValuesFun = (value) => {
if (!value) {
return null;
@ -173,7 +173,7 @@ export async function ClearCookie() {
}
export async function printDiv(id, style) {
console.log("printDiv",id, style)
console.log('printDiv', id, style);
return new Promise(async (resolve, reject) => {
const content = document.getElementById(id);
@ -251,21 +251,20 @@ export function dateFormatChange(dateString) {
export function dateFormatChange1(dateString) {
return moment(dateString, [
'YYYY-MM-DDTHH:mm:ss.SSS',
'YYYY-MM-DDTHH:mm:ss'
'YYYY-MM-DDTHH:mm:ss',
]).format('DD-MMM-YYYY hh:mm A');
}
export function ExtractDateFormate(dateString) {
if (!dateString) return "";
if (!dateString) return '';
const date = new Date(dateString);
if (isNaN(date)) return "";
if (isNaN(date)) return '';
const day = date.getDate();
const month = date.toLocaleString("en-US", { month: "short" });
const month = date.toLocaleString('en-US', { month: 'short' });
const year = date.getFullYear();
return `${day}-${month}-${year}`;
}
// export function dateTimeFormatChange(dateString) {
// let date = moment(dateString, ['YYYY-MM-DDTHH:mm:ss']).format('DD-MM-YYYY hh:mm:ss A');
// let finaldate = date.replace('T', ' ');
@ -273,7 +272,9 @@ export function ExtractDateFormate(dateString) {
// }
export function dateTimeFormatChange(dateString) {
return moment(dateString, ['YYYY-MM-DDTHH:mm:ss']).format('DD-MMM-YYYY hh:mm A');
return moment(dateString, ['YYYY-MM-DDTHH:mm:ss']).format(
'DD-MMM-YYYY hh:mm A'
);
}
export function convertTo12HourFormat(inputDate) {
@ -301,8 +302,69 @@ export function handleGeneratePDF(id) {
});
}
}
// export async function pdfDiv(id, style) {
// return new Promise(async (resolve, reject) => {
export const pdfDiv = async (id, style = '') => {
const content = document.getElementById(id);
if (!content) {
throw new Error(`Element with ID "${id}" not found`);
}
// 🔥 wrapper
const wrapper = document.createElement('div');
wrapper.style.width = '794px';
wrapper.style.background = '#fff';
wrapper.innerHTML = `
${style}
${content.innerHTML}
`;
document.body.appendChild(wrapper);
try {
const canvas = await html2canvas(wrapper, {
scale: 2,
useCORS: true,
});
const pdf = new jsPDF('p', 'mm', 'a4');
const imgWidth = 210;
const pageHeight = 297;
const imgHeight = (canvas.height * imgWidth) / canvas.width;
const imgData = canvas.toDataURL('image/jpeg', 1.0);
let heightLeft = imgHeight;
let position = 0;
// ✅ First page
pdf.addImage(imgData, 'JPEG', 0, position, imgWidth, imgHeight);
heightLeft -= pageHeight;
// ✅ Correct page splitting
while (heightLeft > 0) {
position = -(imgHeight - heightLeft); // 🔥 FIX HERE
pdf.addPage();
pdf.addImage(imgData, 'JPEG', 0, position, imgWidth, imgHeight);
heightLeft -= pageHeight;
}
document.body.removeChild(wrapper);
return pdf.output('blob');
} catch (err) {
document.body.removeChild(wrapper);
throw err;
}
};
// export const pdfDiv = async (id, style = '') => {
// return new Promise(async(resolve, reject) => {
// const content = document.getElementById(id);
// if (!content) {
@ -311,87 +373,39 @@ export function handleGeneratePDF(id) {
// }
// try {
// // Create a wrapper to apply styles
// // Create wrapper div
// const wrapper = document.createElement('div');
// wrapper.innerHTML = `
// ${style}
// ${style}
// ${content.innerHTML}
// `;
// // Generate PDF Blob
// // PDF options
// const opt = {
// margin: 0.5,
// image: { type: 'jpeg', quality: 0.98 },
// html2canvas: { scale: 2, useCORS: true },
// jsPDF: { unit: 'mm', format: 'a4', orientation: 'portrait' },
// pagebreak: {
// mode: ['avoid-all', 'css', 'legacy'],
// before: '.page-break-before',
// after: '.page-break-after',
// avoid: '.page-break-avoid'
// }
// 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 pdfBlob = await html2pdf().set(opt).from(wrapper).outputPdf('blob');
// resolve(pdfBlob);
// } catch (error) {
// reject(error);
// const blob = await html2pdf().set(opt).from(wrapper).outputPdf('blob');
// resolve(blob);
// } catch (err) {
// reject(err);
// }
// });
// }
// export async function blobToBase64(blob) {
// return new Promise((resolve, reject) => {
// const reader = new FileReader();
// reader.onloadend = () => resolve(reader.result.split(',')[1]);
// reader.onerror = reject;
// reader.readAsDataURL(blob);
// });
// }
// New pdfDiv and blobToBase64 functions
export const pdfDiv = async (id, style = "") => {
return new Promise(async (resolve, reject) => {
const content = document.getElementById(id);
if (!content) {
reject(new Error(`Element with ID "${id}" not found`));
return;
}
try {
// Create wrapper div
const wrapper = document.createElement("div");
wrapper.innerHTML = `
${style}
${content.innerHTML}
`;
// PDF options
const opt = {
margin: 0,
image: { type: "jpeg", quality: 0.9 },
html2canvas: {
scale: 2,
useCORS: true,
letterRendering: true,
},
jsPDF: {
unit: "mm",
format: "a4",
orientation: "portrait",
},
pagebreak: {
mode: ["avoid-all", "css", "legacy"],
},
};
const blob = await html2pdf().set(opt).from(wrapper).outputPdf("blob");
resolve(blob);
} catch (err) {
reject(err);
}
});
};
// };
export const blobToBase64 = (blob) => {
return new Promise((resolve, reject) => {
@ -399,7 +413,7 @@ export const blobToBase64 = (blob) => {
reader.onloadend = () => {
const bytes = new Uint8Array(reader.result);
let binary = '';
bytes.forEach((b) => binary += String.fromCharCode(b));
bytes.forEach((b) => (binary += String.fromCharCode(b)));
resolve(btoa(binary));
};
reader.onerror = reject;
@ -424,5 +438,3 @@ export const validateSafeInput = (value) => {
return Promise.resolve();
};

File diff suppressed because it is too large Load Diff

View File

@ -1,34 +1,27 @@
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import { terser } from "rollup-plugin-terser";
export default defineConfig(({ command, mode }) => {
if (mode === "production") {
return {
plugins: [
react(),
// terser({ // Add terser plugin for production
// compress: {
// drop_console: true, // This removes all console.* statements
// },
// }),
],
base: "/apps/retail/",
envDir: "src",
envPrefix: "ENV_",
};
} else {
return {
plugins: [react()],
envDir: "src",
envPrefix: "ENV_",
server: {
host: "192.168.1.34",
port: process.env.PORT || 3015,
export default defineConfig(({ mode }) => {
const isProd = mode === "production";
return {
plugins: [react()],
base: isProd ? "/apps/retail/" : "/",
envDir: "src",
envPrefix: "ENV_",
server: {
host: true, // 🔥 auto detect your IP
port: process.env.PORT || 3015,
},
build: {
minify: "esbuild",
esbuild: {
drop: isProd ? ["console", "debugger"] : [],
},
optimizeDeps: {
exclude: ["console.log"],
},
};
}
},
};
});