Merge pull request 'combo sales screen price qty edit' (#125) from combo-table-qty-price-edit into main

Reviewed-on: Pozomind/pozo-retail-app#125
This commit is contained in:
karthikalakshmi 2026-02-10 19:56:53 +05:30
commit d4385831c6
3 changed files with 3570 additions and 186 deletions

View File

@ -73,9 +73,9 @@ import {
} from '../../../../../Features/Offer/Offernew/BookingOffernew.js'; } from '../../../../../Features/Offer/Offernew/BookingOffernew.js';
import { useRemoveProducts } from '../BSBillingTable3/RemoveCartWithOffer.jsx'; import { useRemoveProducts } from '../BSBillingTable3/RemoveCartWithOffer.jsx';
import { useUtilsComponent } from '../../../../../Services/utils.js'; import { useUtilsComponent } from '../../../../../Services/utils.js';
import ComboEditQty from '../ComboBillTableEdit/ComboEditQty.jsx';
import ComboEditRate from '../ComboBillTableEdit/ComboEditRate.jsx'; import ComboEditRate from '../ComboBillTableEdit/ComboEditRate.jsx';
import CustomerPriceHistory from '../../UtillComponents/CustomerPriceHistory.jsx'; import CustomerPriceHistory from '../../UtillComponents/CustomerPriceHistory.jsx';
import ComboEditQtyAndRate from '../ComboBillTableEdit/ComboEditQtyAndRate.jsx';
const ComboSalesBillTable = () => { const ComboSalesBillTable = () => {
const { removeExtraCharge } = useUtilsComponent(); const { removeExtraCharge } = useUtilsComponent();
@ -177,6 +177,10 @@ const ComboSalesBillTable = () => {
const [qtyEdit, setQtyEdit] = useState(false); const [qtyEdit, setQtyEdit] = useState(false);
const qtyTdRef = useRef(null); const qtyTdRef = useRef(null);
const qtyInputRef = useRef(null); const qtyInputRef = useRef(null);
const [shortKeyMethod, setshortKeyMethod] = useState(null);
const [activeRowIndex, setActiveRowIndex] = useState(null);
const [activeColIndex, setActiveColIndex] = useState(null);
const tableRef = useRef(null);
const handleEditQTYcombo = () => { const handleEditQTYcombo = () => {
if (!qtyEdit) { if (!qtyEdit) {
@ -268,7 +272,7 @@ const ComboSalesBillTable = () => {
const item = getSelectedItem(); const item = getSelectedItem();
if (!item) return; if (!item) return;
handleEditQuantity(item); handleEditQuantityCombo(item);
setshortKeyMethod(method); setshortKeyMethod(method);
}; };
@ -285,7 +289,7 @@ const ComboSalesBillTable = () => {
// 👉 CTRL shortcuts // 👉 CTRL shortcuts
if (!event.ctrlKey) return; if (!event.ctrlKey) return;
if (key === 'q') { if (key === 'q') {
event.preventDefault(); event.preventDefault();
handleShortcut('qty'); handleShortcut('qty');
} else if (key === 'e') { } else if (key === 'e') {
@ -298,7 +302,7 @@ const ComboSalesBillTable = () => {
event.preventDefault(); event.preventDefault();
handleShortcut('weightAmount'); handleShortcut('weightAmount');
} }
}; };
window.addEventListener('keydown', handleKeyDown); window.addEventListener('keydown', handleKeyDown);
@ -2079,6 +2083,95 @@ const ComboSalesBillTable = () => {
...(tableDataDinein || []), ...(tableDataDinein || []),
]; ];
// Set last row as active when data changes
useEffect(() => {
if (combinedTableData?.length > 0 && activeRowIndex === null) {
setActiveRowIndex(combinedTableData.length - 1);
}
}, [combinedTableData?.length]);
// Table navigation
useEffect(() => {
const handleTableNav = (e) => {
if (EditQtyCombo || EditRateCombo) return;
if (!combinedTableData?.length || activeRowIndex === null) return;
const visibleCols =
tableOptions?.filter((opt) =>
['Item', 'Quantity', 'Rate', 'Total'].includes(opt.OptionName)
) || [];
// Arrow Up/Down - Row navigation
if (e.key === 'ArrowUp') {
e.preventDefault();
setActiveRowIndex((prev) => Math.max(0, prev - 1));
setActiveColIndex(null);
} else if (e.key === 'ArrowDown') {
e.preventDefault();
setActiveRowIndex((prev) =>
Math.min(combinedTableData.length - 1, prev + 1)
);
setActiveColIndex(null);
}
// Arrow Left/Right - Column navigation
else if (e.key === 'ArrowLeft') {
e.preventDefault();
if (activeColIndex === null) {
setActiveColIndex(visibleCols.length - 1);
} else {
setActiveColIndex((prev) => Math.max(0, prev - 1));
}
} else if (e.key === 'ArrowRight') {
e.preventDefault();
if (activeColIndex === null) {
setActiveColIndex(0);
} else {
setActiveColIndex((prev) =>
Math.min(visibleCols.length - 1, prev + 1)
);
}
}
// Tab - Move to next column
else if (e.key === 'Tab') {
e.preventDefault();
if (activeColIndex === null) {
setActiveColIndex(0);
} else {
const nextCol = activeColIndex + 1;
if (nextCol >= visibleCols.length) {
setActiveColIndex(0);
if (activeRowIndex < combinedTableData.length - 1) {
setActiveRowIndex((prev) => prev + 1);
}
} else {
setActiveColIndex(nextCol);
}
}
}
// Enter - Trigger cell action
else if (e.key === 'Enter' && activeColIndex !== null) {
e.preventDefault();
const row = combinedTableData[activeRowIndex];
const col = visibleCols[activeColIndex];
if (row && col && editField) {
if (col.OptionName === 'Quantity') handleEditQuantityCombo(row);
else if (col.OptionName === 'Rate') handleEditRateCombo(row);
}
}
};
window.addEventListener('keydown', handleTableNav);
return () => window.removeEventListener('keydown', handleTableNav);
}, [
activeRowIndex,
activeColIndex,
combinedTableData,
EditQtyCombo,
EditRateCombo,
editField,
tableOptions,
]);
console.log(combinedTableData, 'combinedTableData', tableData); console.log(combinedTableData, 'combinedTableData', tableData);
const displayTableData = const displayTableData =
BillOrderPre === 'N' ? [...combinedTableData].reverse() : combinedTableData; BillOrderPre === 'N' ? [...combinedTableData].reverse() : combinedTableData;
@ -2266,7 +2359,7 @@ const ComboSalesBillTable = () => {
return ( return (
<tr <tr
key={`${row?.InwardDtlId || index}-${row?.BookingTypeName || ''}-${row?.SalesId || ''}-${index}`} key={`${row?.InwardDtlId || index}-${row?.BookingTypeName || ''}-${row?.SalesId || ''}-${index}`}
className={row.highlighted ? 'highlighted-row' : ''} className={`${row.highlighted ? 'highlighted-row' : ''} ${activeRowIndex === index ? 'active-row' : ''}`}
style={{ style={{
fontFamily: SelectedFont, fontFamily: SelectedFont,
backgroundColor: backgroundColor:
@ -2310,86 +2403,100 @@ const ComboSalesBillTable = () => {
: 'none', : 'none',
}} }}
> >
{tableOptions?.map((item) => ( {tableOptions?.map((item, colIdx) => {
<> const isActiveCell =
{/* HSN Code */} activeRowIndex === index &&
{item.OptionName === 'HSNCode' && ( activeColIndex !== null &&
<td ['Item', 'Quantity', 'Rate', 'Total'].indexOf(
key={`hsn-${index}`} item.OptionName
className="hsnCodeTd" ) === activeColIndex;
onClick={() => editField && handleEditQuantity(row)}
>
{row.HSNCode || '-'}
</td>
)}
{/* Product Name / Item */} return (
{(item.OptionName === 'Item' || <>
item.OptionName === 'Product Name') && ( {/* HSN Code */}
{item.OptionName === 'HSNCode' && (
<td <td
key={`prod-${index}`} key={`hsn-${index}`}
className="productNameTd" className="hsnCodeTd"
onClick={() => onClick={() => editField && handleEditQuantity(row)}
row.FullProductIdentifierDtls?.length > 0 ||
row.ProductIdentifierDtls?.length > 0
? handleImeiDetails(row)
: editField && handleEditQuantity(row)
}
> >
{row?.Type !== 'OS' ? row.ProdName : row.ServiceName} {row.HSNCode || '-'}
{row?.BrandName ? ` ${row.BrandName}` : ''}
{row?.Type !== 'C' && (
<p className="Tbl3-Variant">
{/* {row?.ProdVariantName} {row?.Size} {row?.UomName} */}
(
{!row?.ProdVariantName?.toLowerCase()?.includes(
'variant'
) && <span>{row?.ProdVariantName} </span>}
{row?.Size}
{row?.SinglePc == 'Y' ? 'PCS' : row?.UomName})
</p>
)}
{row?.Type === 'C' &&
row.ProdDetail?.map((prod, idx) => (
<p key={idx}>
{prod.ProdName} - {prod.Size} {prod.UomName}
</p>
))}
</td> </td>
)} )}
{/* Barcode */} {/* Product Name / Item */}
{item.OptionName === 'Barcode' && ( {(item.OptionName === 'Item' ||
<td item.OptionName === 'Product Name') && (
key={`barcode-${index}`} <td
className="barcodeTd" key={`prod-${index}`}
onClick={() => editField && handleEditQuantity(row)} className={`productNameTd ${isActiveCell ? 'active-cell' : ''}`}
> onClick={() =>
{row.QRCode || '-'} row.FullProductIdentifierDtls?.length > 0 ||
</td> row.ProductIdentifierDtls?.length > 0
)} ? handleImeiDetails(row)
: editField && handleEditQuantity(row)
}
>
{row?.Type !== 'OS' ? row.ProdName : row.ServiceName}
{row?.BrandName ? ` ${row.BrandName}` : ''}
{row?.Type !== 'C' && (
<p className="Tbl3-Variant">
{/* {row?.ProdVariantName} {row?.Size} {row?.UomName} */}
(
{!row?.ProdVariantName?.toLowerCase()?.includes(
'variant'
) && <span>{row?.ProdVariantName} </span>}
{row?.Size}
{row?.SinglePc == 'Y' ? 'PCS' : row?.UomName})
</p>
)}
{row?.Type === 'C' &&
row.ProdDetail?.map((prod, idx) => (
<p key={idx}>
{prod.ProdName} - {prod.Size} {prod.UomName}
</p>
))}
</td>
)}
{/* Quantity */} {/* Barcode */}
{item.OptionName === 'Quantity' && ( {item.OptionName === 'Barcode' && (
<td <td
ref={qtyTdRef} key={`barcode-${index}`}
key={`qty-${index}`} className="barcodeTd"
// className="qtyTd" onClick={() => editField && handleEditQuantity(row)}
className={ >
EditQtyCombo && row?.localId === Index {row.QRCode || '-'}
</td>
)}
{/* Quantity */}
{item.OptionName === 'Quantity' && (
<td
ref={(el) => {
if (
shortKeyMethod === 'qty' &&
row?.localId === Index
) {
el?.focus();
el?.click();
}
}}
key={`qty-${index}`}
tabIndex={0}
className={`${EditQtyCombo && row?.localId === Index
? 'EditQTYcomboTD' ? 'EditQTYcomboTD'
: 'qtyTd' : 'qtyTd'
} } ${isActiveCell ? 'active-cell' : ''}`}
onClick={() => onClick={() =>
editField && handleEditQuantityCombo(row) editField && handleEditQuantityCombo(row)
} }
// onClick={() => handleEditQTYcombo()} >
> {(!EditQtyCombo || row?.localId !== Index) && (
{(!EditQtyCombo || row?.localId !== Index) && ( <>{row.OrderQty}</>
<>{row.OrderQty}</> )}
)} {EditQtyCombo && row?.localId === Index && (
{EditQtyCombo && row?.localId === Index && ( <ComboEditQtyAndRate
<ComboEditQty
key={`edit-qty-${Index}`} key={`edit-qty-${Index}`}
handleEditQuantityCancel={ handleEditQuantityCancel={
handleEditQuantityComboCancel handleEditQuantityComboCancel
@ -2399,30 +2506,27 @@ const ComboSalesBillTable = () => {
Index={Index} Index={Index}
setIndex={setIndex} setIndex={setIndex}
setEditQtyCombo={setEditQtyCombo} setEditQtyCombo={setEditQtyCombo}
// id={`qty-${index}`} />
/> )}
)} </td>
</td> )}
)}
{/* Rate */} {/* Rate */}
{item.OptionName === 'Rate' && ( {item.OptionName === 'Rate' && (
<td <td
key={`rate-${index}`} key={`rate-${index}`}
// className="rateTd" className={`${EditRateCombo ? 'rateTdEditRate' : 'rateTd'} ${isActiveCell ? 'active-cell' : ''}`}
className={EditRateCombo ? 'rateTdEditRate' : 'rateTd'} onClick={() => editField && handleEditRateCombo(row)}
// onClick={() => editField && handleEditQuantity(row)} >
onClick={() => editField && handleEditRateCombo(row)}
>
{!EditRateCombo || row?.localId !== Index ? ( {!EditRateCombo || row?.localId !== Index ? (
<> <>
{allowDecimal {allowDecimal
? Number(row?.OrderRate).toFixed(2) ? Number(row?.OrderRate).toFixed(2)
: Number(row?.OrderRate)} : Number(row?.OrderRate)}
</> </>
) : null} ) : null}
{EditRateCombo && row?.localId === Index && ( {EditRateCombo && row?.localId === Index && (
<ComboEditRate <ComboEditQtyAndRate
key={`edit-rate-${Index}`} key={`edit-rate-${Index}`}
handleEditRateComboCancel={ handleEditRateComboCancel={
handleEditRateComboCancel handleEditRateComboCancel
@ -2432,101 +2536,110 @@ const ComboSalesBillTable = () => {
Index={Index} Index={Index}
setIndex={setIndex} setIndex={setIndex}
setEditRateCombo={setEditRateCombo} setEditRateCombo={setEditRateCombo}
/> />
)} )}
</td> </td>
)} )}
{/* Disc % */} {/* Disc % */}
{item.OptionName === 'Discount' && ( {item.OptionName === 'Discount' && (
<td <td
key={`discPer-${index}`} key={`discPer-${index}`}
className="discPerTd" className="discPerTd"
onClick={() => editField && handleEditQuantity(row)} onClick={() => editField && handleEditQuantity(row)}
> >
{row?.Offer > 0 {row?.Offer > 0
? `${( ? `${(
(row.Offer / (row?.OrderQty * row?.OrderRate)) * (row.Offer / (row?.OrderQty * row?.OrderRate)) *
100 100
).toFixed(2)}%` ).toFixed(2)}%`
: '-'} : '-'}
</td> </td>
)} )}
{/* Disc Amt */} {/* Disc Amt */}
{item.OptionName === 'Amount' && ( {item.OptionName === 'Amount' && (
<td <td
key={`discAmt-${index}`} key={`discAmt-${index}`}
className="discAmtTd" className="discAmtTd"
onClick={() => editField && handleEditQuantity(row)} onClick={() => editField && handleEditQuantity(row)}
> >
{row.Offer || '-'} {row.Offer || '-'}
</td> </td>
)} )}
{/* Total */} {/* Total */}
{item.OptionName === 'Total' && ( {item.OptionName === 'Total' && (
<td <td
key={`total-${index}`} key={`total-${index}`}
className="totalTd" className={`totalTd ${isActiveCell ? 'active-cell' : ''}`}
onClick={() => { onClick={() => {
// if (editField) { // if (editField) {
// handleEditQuantity(row) // handleEditQuantity(row)
// } // }
if (GetCustId) { if (GetCustId) {
handleCustomerProductPriceHistory(row); handleCustomerProductPriceHistory(row);
} }
}}
>
{allowDecimal
? Number(row.TotalAmt - (row?.Offer || 0)).toFixed(2)
: Number(row.TotalAmt - (row?.Offer || 0))}
</td>
)}
{/* Optional columns */}
{item.OptionName === 'SalesMan' && (
<td key={`salesMan-${index}`} className="salesManTd">
{row.salesMan || '-'}
</td>
)}
{item.OptionName === '%' && (
<td key={`percentage-${index}`} className="percentageTd">
{row.percentage || '-'}
</td>
)}
{item.OptionName === 'Commission' && (
<td key={`commission-${index}`} className="commissionTd">
{row.commission || '-'}
</td>
)}
{item.OptionName === 'StockId' && (
<td key={`stockId-${index}`} className="stockIdTd">
{row.stockId || '-'}
</td>
)}
{item.OptionName === 'Points' && (
<td key={`points-${index}`} className="pointsTd">
{row.points || '-'}
</td>
)}
{/* Delete */}
{item.OptionName === 'Delete' && (
<td key={`delete-${index}`} className="deleteTd">
<AiOutlineClose
onClick={() => removeFromCart(row)}
style={{
color: '#FF4D4F',
cursor: 'pointer',
textAlign: 'center',
width: '100%',
}} }}
/> >
</td> {allowDecimal
)} ? Number(row.TotalAmt - (row?.Offer || 0)).toFixed(
</> 2
))} )
: Number(row.TotalAmt - (row?.Offer || 0))}
</td>
)}
{/* Optional columns */}
{item.OptionName === 'SalesMan' && (
<td key={`salesMan-${index}`} className="salesManTd">
{row.salesMan || '-'}
</td>
)}
{item.OptionName === '%' && (
<td
key={`percentage-${index}`}
className="percentageTd"
>
{row.percentage || '-'}
</td>
)}
{item.OptionName === 'Commission' && (
<td
key={`commission-${index}`}
className="commissionTd"
>
{row.commission || '-'}
</td>
)}
{item.OptionName === 'StockId' && (
<td key={`stockId-${index}`} className="stockIdTd">
{row.stockId || '-'}
</td>
)}
{item.OptionName === 'Points' && (
<td key={`points-${index}`} className="pointsTd">
{row.points || '-'}
</td>
)}
{/* Delete */}
{item.OptionName === 'Delete' && (
<td key={`delete-${index}`} className="deleteTd">
<AiOutlineClose
onClick={() => removeFromCart(row)}
style={{
color: '#FF4D4F',
cursor: 'pointer',
textAlign: 'center',
width: '100%',
}}
/>
</td>
)}
</>
);
})}
</tr> </tr>
); );
})} })}

View File

@ -28,7 +28,7 @@
} }
tbody { tbody {
> tr:last-child { >tr:last-child {
background-color: #e5d434 !important; background-color: #e5d434 !important;
position: sticky; position: sticky;
bottom: 0; bottom: 0;
@ -40,6 +40,16 @@
background-color: #e5d434; background-color: #e5d434;
} }
&.active-row {
outline: 2px solid #1292ee;
outline-offset: -2px;
}
td.active-cell {
background-color: #1292ee !important;
color: #ffffff;
}
td { td {
padding: 4px 8px; padding: 4px 8px;
border: 1px solid #9c9c9c; border: 1px solid #9c9c9c;
@ -189,3 +199,14 @@
padding: 1px !important; padding: 1px !important;
text-align: center; text-align: center;
} }
.focused-cell {
background-color: #fff3cd !important;
border: 2px solid #ffc107 !important;
box-shadow: 0 0 5px rgba(255, 193, 7, 0.5);
}
:where(.css-dev-only-do-not-override-mncuj7).ant-select-dropdown .ant-select-item-option-active:not(.ant-select-item-option-disabled) {
background-color: rgba(116, 107, 107, 0.356);
}