Merge pull request 'srinath' (#296) from srinath into main

Reviewed-on: Pozomind/pozo-retail-app#296
This commit is contained in:
karthikalakshmi 2026-03-23 11:04:48 +05:30
commit a50ea556f1
3 changed files with 261 additions and 80 deletions

View File

@ -12,6 +12,7 @@ import { uploadImage } from "../../../../../Features/upload/upload";
import { SelfBookingEmailSendLink } from "../../../../../Features/ConfigMasterPage/ConfigMasterPage"; import { SelfBookingEmailSendLink } from "../../../../../Features/ConfigMasterPage/ConfigMasterPage";
import { Messages } from "../../../../../Components/Notifications/Messages"; import { Messages } from "../../../../../Components/Notifications/Messages";
const { RangePicker } = DatePicker; const { RangePicker } = DatePicker;
import dayjs from "dayjs";
const uploadApiUrl = import.meta.env.ENV_IMAGE_UPLOAD_API_URL; const uploadApiUrl = import.meta.env.ENV_IMAGE_UPLOAD_API_URL;
const ReprintPDFDataShare = ({ printDatas, printerTemplateStyle, SettingDataSelector, open = false, onClose = () => { } }) => { const ReprintPDFDataShare = ({ printDatas, printerTemplateStyle, SettingDataSelector, open = false, onClose = () => { } }) => {
@ -40,7 +41,9 @@ const ReprintPDFDataShare = ({ printDatas, printerTemplateStyle, SettingDataSele
fetchData(dateStrings); fetchData(dateStrings);
} }
}; };
const disabledDate = (current) => {
return current && current > dayjs().endOf("day");
};
const fetchData = async (dates = dateStrings) => { const fetchData = async (dates = dateStrings) => {
setIsFetching(true); setIsFetching(true);
try { try {
@ -184,8 +187,9 @@ Thank you for choosing us!`;
<div> <div>
<RangePicker <RangePicker
placeholder={['Start Date', 'End Date']} placeholder={['Start Date', 'End Date']}
format="YYYY-MM-DD" format="DD-MM-YYYY"
onChange={handleDateChange} onChange={handleDateChange}
disabledDate={disabledDate}
/> />
</div> </div>
<div> <div>

View File

@ -606,6 +606,8 @@ const ProductExcel = ({
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [message, setMessage] = useState(false); const [message, setMessage] = useState(false);
const [Datas, setDatas] = useState(); const [Datas, setDatas] = useState();
console.log(Datas, 'Datas32323232');
const [currentPage, setCurrentPage] = useState(1); const [currentPage, setCurrentPage] = useState(1);
const [MappingOpen, setMappingOpen] = useState(false); const [MappingOpen, setMappingOpen] = useState(false);
const [fieldMapping, setFieldMapping] = useState({}); const [fieldMapping, setFieldMapping] = useState({});
@ -843,6 +845,7 @@ const ProductExcel = ({
}; };
const handleFileUpload = (e) => { const handleFileUpload = (e) => {
const file = e.target.files[0]; const file = e.target.files[0];
const reader = new FileReader(); const reader = new FileReader();
@ -957,15 +960,28 @@ const ProductExcel = ({
const rows = nonEmptyRows.slice(1); const rows = nonEmptyRows.slice(1);
const dateFields = [ const dateFields = [
'Available From',
'Available To',
'Manufacture Date', 'Manufacture Date',
'Expire Date', 'Expire Date',
'Stock Date', 'Stock Date',
]; ];
function convertToDisplayFormat(dateString) { function convertToDisplayFormat(dateString) {
if (!dateString) return '';
// Handle DD/MM/YY or DD/MM/YYYY (from Excel dateNF format)
const slashParts = String(dateString).split('/');
if (slashParts.length === 3) {
const [d, m, y] = slashParts;
const fullYear = y.length === 2 ? '20' + y : y;
return `${d.padStart(2,'0')}-${m.padStart(2,'0')}-${fullYear}`;
}
// Handle DD-MM-YYYY already in correct format
const dashParts = String(dateString).split('-');
if (dashParts.length === 3 && dashParts[2].length === 4) {
return dateString; // already DD-MM-YYYY
}
// Fallback: try native Date parse (YYYY-MM-DD etc)
const date = new Date(dateString); const date = new Date(dateString);
if (isNaN(date.getTime())) return '';
const day = date.getDate().toString().padStart(2, '0'); const day = date.getDate().toString().padStart(2, '0');
const month = (date.getMonth() + 1).toString().padStart(2, '0'); const month = (date.getMonth() + 1).toString().padStart(2, '0');
const year = date.getFullYear().toString(); const year = date.getFullYear().toString();
@ -999,6 +1015,12 @@ const ProductExcel = ({
item['Product Varient Name'] === undefined item['Product Varient Name'] === undefined
? 'Variant 1' ? 'Variant 1'
: item['Product Varient Name'], : item['Product Varient Name'],
'Manufacture Date':
item['Manufacture Date'] == 'NaN-NaN-NaN' ? '' : item['Manufacture Date'],
'Expire Date':
item['Expire Date'] == 'NaN-NaN-NaN' ? '' : item['Expire Date'],
'Available From':
item['Available From'] == 'NaN-NaN-NaN' ? '' : item['Available From'],
'Available To': 'Available To':
item['Available To'] == 'NaN-NaN-NaN' ? '' : item['Available To'], item['Available To'] == 'NaN-NaN-NaN' ? '' : item['Available To'],
ProdId: item['ProdId'] || null, ProdId: item['ProdId'] || null,
@ -1061,9 +1083,9 @@ const ProductExcel = ({
}; };
const updateImageUrl = (url, index, key) => { const updateImageUrl = (url, index, key) => {
const newData1 = [...Datas]; const newData1 = [...Datas];
newData1[key] = { ...newData1[key], Productimage: url }; newData1[key] = { ...newData1[key], Productimage: url };
setDatas(newData1); setDatas(newData1);
setExcelData(newData1); setExcelData(newData1);
}; };
@ -1078,7 +1100,7 @@ const ProductExcel = ({
if (selectedImageIndex != null && selectedImageIndex != undefined) { if (selectedImageIndex != null && selectedImageIndex != undefined) {
try { try {
if (selectedImage.image !== undefined) { if (selectedImage?.image !== undefined) {
const response = await fetch(selectedImage.image); const response = await fetch(selectedImage.image);
const datas = await response.blob(); const datas = await response.blob();
const metadata = { const metadata = {
@ -1296,13 +1318,7 @@ const ProductExcel = ({
validation: { validation: {
type: 'list', type: 'list',
allowBlank: true, allowBlank: true,
formulae: [ formulae: [`Sheet2!$B$2:$B$${(CategoryNames?.length || 0) + 1}`],
'"' +
(typeof CategoryNames !== 'undefined'
? CategoryNames.join(',')
: 'Sample Category') +
'"',
],
}, },
sampleValue: sampleData?.category || 'Sample Category', sampleValue: sampleData?.category || 'Sample Category',
headerColor: 'FF2929', headerColor: 'FF2929',
@ -1315,7 +1331,7 @@ const ProductExcel = ({
...headerStyle, ...headerStyle,
font: { bold: true, color: { argb: '#000000' } }, font: { bold: true, color: { argb: '#000000' } },
}, },
validation: null, validation: null, // set dynamically per row below
sampleValue: sampleData?.subCategory || 'Sample Sub Category', sampleValue: sampleData?.subCategory || 'Sample Sub Category',
headerColor: '52c41a', headerColor: '52c41a',
}, },
@ -1327,7 +1343,7 @@ const ProductExcel = ({
...headerStyle, ...headerStyle,
font: { bold: true, color: { argb: '#000000' } }, font: { bold: true, color: { argb: '#000000' } },
}, },
validation: null, validation: null, // set dynamically per row below
sampleValue: sampleData?.brand || 'Sample Brand', sampleValue: sampleData?.brand || 'Sample Brand',
headerColor: '52c41a', headerColor: '52c41a',
}, },
@ -1439,20 +1455,8 @@ const ProductExcel = ({
...headerStyle, ...headerStyle,
font: { bold: true, color: { argb: '#000000' } }, font: { bold: true, color: { argb: '#000000' } },
}, },
validation: { validation: null, // set via Sheet2 range reference below
type: 'list', sampleValue: sampleData?.tax || 'NIL - 0%',
allowBlank: true,
formulae: [
'"' +
(typeof TaxDatas !== 'undefined'
? TaxDatas.map((item) =>
item.replace(/,/g, '').replace('-', '-').trim()
).join(',')
: 'NIL - 0%') +
'"',
],
},
sampleValue: 'NIL - 0%',
headerColor: '52c41a', headerColor: '52c41a',
}, },
AutoGenerateQr: { AutoGenerateQr: {
@ -1501,7 +1505,7 @@ const ProductExcel = ({
}, },
OnePiecePrice: { OnePiecePrice: {
header: 'OnePiece Price', header: 'OnePiece Price',
key: 'StockAvailable', key: 'OnePiecePrice',
width: 20, width: 20,
style: { style: {
...headerStyle, ...headerStyle,
@ -1520,7 +1524,7 @@ const ProductExcel = ({
}, },
AutoGenOnePieceQr: { AutoGenOnePieceQr: {
header: 'Auto Generate One Piece Qrcode', header: 'Auto Generate One Piece Qrcode',
key: 'StockDate', key: 'AutoGenOnePieceQr',
width: 30, width: 30,
style: { style: {
...headerStyle, ...headerStyle,
@ -1536,7 +1540,7 @@ const ProductExcel = ({
}, },
AutoGenOnePieceQrNum: { AutoGenOnePieceQrNum: {
header: 'Auto Generate One Piece Qrcode Number', header: 'Auto Generate One Piece Qrcode Number',
key: 'StockDate', key: 'AutoGenOnePieceQrNum',
width: 40, width: 40,
style: { style: {
...headerStyle, ...headerStyle,
@ -1620,27 +1624,31 @@ const ProductExcel = ({
}, },
AvailableFrom: { AvailableFrom: {
header: 'Available From', header: 'Available From',
key: 'AvailableFrom TIME', key: 'AvailableFrom',
width: 20, width: 20,
style: { style: {
...headerStyle, ...headerStyle,
font: { bold: true, color: { argb: '#000000' } }, font: { bold: true, color: { argb: '#000000' } },
numFmt: '@',
}, },
validation: null, validation: null,
sampleValue: new Date(), sampleValue: '08:00:00',
headerColor: '52c41a', headerColor: '52c41a',
isTimeField: true,
}, },
AvailableTo: { AvailableTo: {
header: 'Available To', header: 'Available To',
key: 'AvailableTo TIME', key: 'AvailableTo',
width: 20, width: 20,
style: { style: {
...headerStyle, ...headerStyle,
font: { bold: true, color: { argb: '#000000' } }, font: { bold: true, color: { argb: '#000000' } },
numFmt: '@',
}, },
validation: null, validation: null,
sampleValue: new Date(), sampleValue: '22:00:00',
headerColor: '52c41a', headerColor: '52c41a',
isTimeField: true,
}, },
}; };
@ -1772,11 +1780,26 @@ const ProductExcel = ({
}; };
}); });
// Add sample data in row 2 // Add sample data in row 2 time fields forced to text format
selectedColumns.forEach((config, index) => { selectedColumns.forEach((config, index) => {
const columnLetter = getExcelColumnLetter(index + 1); // FIXED const columnLetter = getExcelColumnLetter(index + 1);
worksheet.getCell(`${columnLetter}2`).value = config.sampleValue; const cell = worksheet.getCell(`${columnLetter}2`);
if (config.isTimeField) {
cell.numFmt = '@';
cell.value = String(config.sampleValue);
} else {
cell.value = config.sampleValue;
}
});
// Force text format on all data rows for time fields
selectedColumns.forEach((config, index) => {
if (config.isTimeField) {
const columnLetter = getExcelColumnLetter(index + 1);
for (let r = 2; r <= 1002; r++) {
worksheet.getCell(`${columnLetter}${r}`).numFmt = '@';
}
}
}); });
// Add validation to columns // Add validation to columns
@ -1803,29 +1826,42 @@ const ProductExcel = ({
} }
}); });
// // Now set protection for all rows
// // First, unlock ALL cells in the worksheet
// for (let rowNum = 1; rowNum <= worksheet.rowCount; rowNum++) {
// const row = worksheet.getRow(rowNum);
// row.eachCell({ includeEmpty: true }, (cell, colNumber) => {
// const config = selectedColumns[colNumber - 1];
// cell.protection = { locked: config?.locked || false };
// });
// }
// // Then lock ONLY the header row (row 1) and sample row (row 2)
// worksheet.getRow(1).eachCell({ includeEmpty: true }, (cell) => {
// cell.protection = { locked: true };
// });
// worksheet.getRow(2).eachCell({ includeEmpty: true }, (cell) => {
// cell.protection = { locked: true };
// });
// Add empty rows for data entry // Add empty rows for data entry
for (let i = 1; i <= 1000; i++) { for (let i = 1; i <= 1000; i++) {
worksheet.addRow({}); worksheet.addRow({});
} }
// Now set protection for all rows // Explicitly unlock all data-entry cells (rows 3+) for every column
// First, unlock ALL cells in the worksheet for (let rowNum = 3; rowNum <= worksheet.rowCount; rowNum++) {
for (let rowNum = 1; rowNum <= worksheet.rowCount; rowNum++) { for (let colNum = 1; colNum <= selectedColumns.length; colNum++) {
const row = worksheet.getRow(rowNum); const config = selectedColumns[colNum - 1];
row.eachCell({ includeEmpty: true }, (cell, colNumber) => { worksheet.getCell(rowNum, colNum).protection = { locked: config?.locked || false };
const config = selectedColumns[colNumber - 1]; }
cell.protection = { locked: config?.locked || false };
});
} }
// Then lock ONLY the header row (row 1) and sample row (row 2) // Lock header row (row 1) and sample row (row 2)
worksheet.getRow(1).eachCell({ includeEmpty: true }, (cell) => { for (let colNum = 1; colNum <= selectedColumns.length; colNum++) {
cell.protection = { locked: true }; worksheet.getCell(1, colNum).protection = { locked: true };
}); worksheet.getCell(2, colNum).protection = { locked: true };
}
worksheet.getRow(2).eachCell({ includeEmpty: true }, (cell) => {
cell.protection = { locked: true };
});
// Protect the worksheet // Protect the worksheet
await worksheet.protect('', { await worksheet.protect('', {
@ -2149,6 +2185,139 @@ const ProductExcel = ({
}); });
} }
// Write TaxDatas into Sheet2 column N for range-based dropdown (avoids 255-char limit)
if (typeof TaxDatas !== 'undefined' && TaxDatas?.length > 0) {
TaxDatas.forEach((taxItem, idx) => {
worksheet1.getCell(`N${idx + 2}`).value = taxItem.replace(/,/g, '').trim();
});
}
// Apply Tax validation using Sheet2 column N range
const taxColIndex = selectedColumns.findIndex((c) => c.key === 'TaxId') + 1;
if (taxColIndex > 0 && TaxDatas?.length > 0) {
for (let rowNumber = 3; rowNumber <= 1002; rowNumber++) {
worksheet.getCell(rowNumber, taxColIndex).dataValidation = {
type: 'list',
allowBlank: true,
formulae: [`Sheet2!$N$2:$N$${TaxDatas.length + 1}`],
showErrorMessage: false,
};
}
}
// Dynamic SubCategory dropdown per Category
// Build a map: categoryName [subcat names]
const catSubcatMap = {};
if (CategoryNames && SubCatData && Subcatnumfid) {
CategoryNames.forEach((catName, catIdx) => {
const catId = CategoryId?.[catIdx];
const subcats = SubCatData.filter(
(_, sIdx) => Subcatnumfid[sIdx] == catId
);
catSubcatMap[catName] = subcats;
});
}
// Write each category's subcats into Sheet2 starting at column O (col 15)
// Row 1 = category name header, rows 2+ = subcat names
const catSubcatStartCol = 15; // column O
const catNameToColLetter = {};
if (CategoryNames) {
CategoryNames.forEach((catName, catIdx) => {
const colIndex = catSubcatStartCol + catIdx;
const colLetter = getExcelColumnLetter(colIndex);
catNameToColLetter[catName] = colLetter;
// Write header (category name)
worksheet1.getCell(`${colLetter}1`).value = catName;
// Write subcats for this category
const subcats = catSubcatMap[catName] || [];
subcats.forEach((subcat, sIdx) => {
worksheet1.getCell(`${colLetter}${sIdx + 2}`).value = subcat;
});
// Define a named range for this category's subcats
const safeName = catName.replace(/[^A-Za-z0-9_]/g, '_');
const rangeRef = subcats.length > 0
? `Sheet2!$${colLetter}$2:$${colLetter}$${subcats.length + 1}`
: `Sheet2!$${colLetter}$2:$${colLetter}$2`;
workbook.definedNames.add(rangeRef, safeName);
});
}
// Find which column index ProdCat and ProdSubCat are in Sheet1
const catColIndex = selectedColumns.findIndex((c) => c.key === 'ProdCat') + 1;
const subCatColIndex = selectedColumns.findIndex((c) => c.key === 'ProdSubCat') + 1;
if (catColIndex > 0 && subCatColIndex > 0) {
const catColLetter = getExcelColumnLetter(catColIndex);
for (let rowNumber = 3; rowNumber <= 1002; rowNumber++) {
const cell = worksheet.getCell(rowNumber, subCatColIndex);
cell.dataValidation = {
type: 'list',
allowBlank: true,
formulae: [`INDIRECT(SUBSTITUTE(${catColLetter}${rowNumber}," ","_"))`],
showErrorMessage: false,
};
}
}
// Dynamic Brand dropdown per SubCategory
// Build a map: subcatName [brand names] using Allbranddatanumfid (parent subcat ID)
const subcatBrandMap = {};
if (SubCatData && Allbranddata && Allbranddatanumfid) {
SubCatData.forEach((subcatName, sIdx) => {
const subcatId = SubcatId?.[sIdx];
const brands = Allbranddata.filter(
(_, bIdx) => Allbranddatanumfid[bIdx] == subcatId
);
subcatBrandMap[subcatName] = brands;
});
}
// Write each subcat's brands into Sheet2 starting after the category columns
const brandStartCol = catSubcatStartCol + (CategoryNames?.length || 0) + 1;
if (SubCatData) {
SubCatData.forEach((subcatName, sIdx) => {
const colIndex = brandStartCol + sIdx;
const colLetter = getExcelColumnLetter(colIndex);
worksheet1.getCell(`${colLetter}1`).value = `B_${subcatName}`;
const brands = subcatBrandMap[subcatName] || [];
brands.forEach((brand, bIdx) => {
worksheet1.getCell(`${colLetter}${bIdx + 2}`).value = brand;
});
const safeName = `B_${subcatName.replace(/[^A-Za-z0-9_]/g, '_')}`;
const rangeRef = brands.length > 0
? `Sheet2!$${colLetter}$2:$${colLetter}$${brands.length + 1}`
: `Sheet2!$${colLetter}$2:$${colLetter}$2`;
workbook.definedNames.add(rangeRef, safeName);
});
}
// Apply INDIRECT brand validation based on selected SubCategory
const brandColIndex = selectedColumns.findIndex((c) => c.key === 'Brand') + 1;
if (subCatColIndex > 0 && brandColIndex > 0) {
const subCatColLetter = getExcelColumnLetter(subCatColIndex);
for (let rowNumber = 3; rowNumber <= 1002; rowNumber++) {
const cell = worksheet.getCell(rowNumber, brandColIndex);
cell.dataValidation = {
type: 'list',
allowBlank: true,
formulae: [`INDIRECT("B_"&SUBSTITUTE(${subCatColLetter}${rowNumber}," ","_"))`],
showErrorMessage: false,
};
}
}
//
// Original complex logic for column C (SUBCAT DATAS) // Original complex logic for column C (SUBCAT DATAS)
worksheet1 worksheet1
.getColumn('C') .getColumn('C')
@ -2527,7 +2696,7 @@ const ProductExcel = ({
<div className="imgUpldr" style={{ fontSize: '15px' }}> <div className="imgUpldr" style={{ fontSize: '15px' }}>
<Imageupload <Imageupload
singleImage={true} singleImage={true}
updateImageUrl={(url) => updateImageUrl(url, index)} updateImageUrl={(url) => updateImageUrl(url, index, record.key)}
// ImageLink={record.Productimage || ""} // ImageLink={record.Productimage || ""}
ImageLink={ ImageLink={
onlineImage onlineImage

View File

@ -629,6 +629,10 @@ const ProductForm = ({ formType }) => {
}, [editstate?.Rack, RackData]); }, [editstate?.Rack, RackData]);
useEffect(() => { useEffect(() => {
// Auto-fill "Variant X" only when we are adding a new variant.
// When editing, another effect will populate the full data.
if (!openVariant || variantEdit) return;
let variantAddData11 = [...variantPriceData]; let variantAddData11 = [...variantPriceData];
if (variantAddData11?.length > 0) { if (variantAddData11?.length > 0) {
const lastAddProdVariantName11 = const lastAddProdVariantName11 =
@ -677,7 +681,32 @@ const ProductForm = ({ formType }) => {
ProdVariantName: `Variant ${parseInt(variantAddData11?.length) + parseInt(2)}`, ProdVariantName: `Variant ${parseInt(variantAddData11?.length) + parseInt(2)}`,
}); });
} }
}, [openVariant]); }, [openVariant, variantEdit, variantPriceData]);
useEffect(() => {
// Populate edit values only after the variant form is mounted.
if (!openVariant || !variantEdit) return;
if (
variantEditIndex === undefined ||
variantEditIndex === null ||
!variantPriceData?.length
) {
return;
}
const row = variantPriceData?.[variantEditIndex];
if (!row) return;
formVariantRef?.current?.setFieldsValue({
ProdVariantName: row?.ProdVariantName,
AmountOfPiece: row?.OnePcsPrice,
MRP: row?.MRP,
SellPrice: row?.SellPrice,
WhSalePrice: row?.WhSalePrice,
OfferPrice: row?.OfferPrice,
SpecialPrice: row?.SpecialPrice,
});
}, [openVariant, variantEdit, variantEditIndex, variantPriceData]);
const onComplete = useCallback(() => { const onComplete = useCallback(() => {
setMessageData(null); setMessageData(null);
@ -1573,28 +1602,7 @@ const ProductForm = ({ formType }) => {
}; };
const variantActionsFormatter = (row, index) => { const variantActionsFormatter = (row, index) => {
if (StockOpen == 'Y') { setOpenVariant(true);
formVariantRef?.current?.setFieldsValue({
ProdVariantName: row?.ProdVariantName,
AmountOfPiece: row?.OnePcsPrice,
MRP: row?.MRP,
SellPrice: row?.SellPrice,
WhSalePrice: row?.WhSalePrice,
OfferPrice: row?.OfferPrice,
SpecialPrice: row?.SpecialPrice,
});
} else {
formVariantRef?.current?.setFieldsValue({
ProdVariantName: row?.ProdVariantName,
AmountOfPiece: row?.OnePcsPrice,
MRP: row?.MRP,
SellPrice: row?.SellPrice,
WhSalePrice: row?.WhSalePrice,
OfferPrice: row?.OfferPrice,
SpecialPrice: row?.SpecialPrice,
});
}
setVariantEdit(true); setVariantEdit(true);
setVariantEditIndex(index); setVariantEditIndex(index);
}; };