Fix item number not getting updated when applying template

This commit is contained in:
2025-03-22 20:55:34 -04:00
parent aacb3a2fd0
commit a51a48ce89
7 changed files with 428 additions and 155 deletions

View File

@@ -28,6 +28,7 @@ interface UpcValidationTableAdapterProps<T extends string> {
validatingRows: Set<number>
getItemNumber: (rowIndex: number) => string | undefined
}
itemNumbers?: Map<number, string>
}
/**
@@ -56,75 +57,79 @@ function UpcValidationTableAdapter<T extends string>({
rowSublines,
isLoadingLines,
isLoadingSublines,
upcValidation
upcValidation,
itemNumbers
}: UpcValidationTableAdapterProps<T>) {
// Prepare the validation table with UPC data
const AdaptedTable = useMemo(() => React.memo((props: React.ComponentProps<typeof ValidationTable>) => {
// Create validatingCells set from validating rows, but only for item_number fields
// This ensures only the item_number column shows loading state during UPC validation
const combinedValidatingCells = new Set<string>();
// Create combined validatingCells set from validating rows and external cells
const combinedValidatingCells = useMemo(() => {
const combined = new Set<string>();
// Add UPC validation cells
upcValidation.validatingRows.forEach(rowIndex => {
// Only mark the item_number cells as validating, NOT the UPC or supplier
combinedValidatingCells.add(`${rowIndex}-item_number`);
combined.add(`${rowIndex}-item_number`);
});
// Add any other validating cells from state
externalValidatingCells.forEach(cellKey => {
combinedValidatingCells.add(cellKey);
combined.add(cellKey);
});
// Convert the Map to the expected format for the ValidationTable
// Create a new Map from the item numbers to ensure proper typing
const itemNumbersMap = new Map<number, string>();
return combined;
}, [upcValidation.validatingRows, externalValidatingCells]);
// Merge the item numbers with the data for display purposes only
const enhancedData = props.data.map((row: any, index: number) => {
const itemNumber = upcValidation.getItemNumber(index);
if (itemNumber) {
// Add to our map for proper prop passing
itemNumbersMap.set(index, itemNumber);
// Create a consolidated item numbers map from all sources
const consolidatedItemNumbers = useMemo(() => {
const result = new Map<number, string>();
return {
...row,
item_number: itemNumber
};
}
return row;
});
// First add from itemNumbers directly - this is the source of truth for template applications
if (itemNumbers) {
// Log all numbers for debugging
console.log(`[ADAPTER-DEBUG] Received itemNumbers map with ${itemNumbers.size} entries`);
// Create a Map for upcValidationResults with the same structure expected by ValidationTable
const upcValidationResultsMap = new Map<number, { itemNumber: string }>();
itemNumbers.forEach((itemNumber, rowIndex) => {
console.log(`[ADAPTER-DEBUG] Adding item number for row ${rowIndex} from itemNumbers: ${itemNumber}`);
result.set(rowIndex, itemNumber);
});
}
// Populate with any item numbers we have from validation
// For each row, ensure we have the most up-to-date item number
data.forEach((_, index) => {
// Check if upcValidation has an item number for this row
const itemNumber = upcValidation.getItemNumber(index);
if (itemNumber) {
upcValidationResultsMap.set(index, { itemNumber });
console.log(`[ADAPTER-DEBUG] Adding item number for row ${index} from upcValidation: ${itemNumber}`);
result.set(index, itemNumber);
}
// Also check if it's directly in the data
const dataItemNumber = data[index].item_number;
if (dataItemNumber && !result.has(index)) {
console.log(`[ADAPTER-DEBUG] Adding item number for row ${index} from data: ${dataItemNumber}`);
result.set(index, dataItemNumber);
}
});
return (
<ValidationTable
{...props}
data={enhancedData}
validatingCells={combinedValidatingCells}
itemNumbers={itemNumbersMap}
isLoadingTemplates={isLoadingTemplates}
copyDown={copyDown}
rowProductLines={rowProductLines}
rowSublines={rowSublines}
isLoadingLines={isLoadingLines}
isLoadingSublines={isLoadingSublines}
upcValidationResults={upcValidationResultsMap}
/>
);
}), [upcValidation.validatingRows, upcValidation.getItemNumber, isLoadingTemplates, copyDown, externalValidatingCells, rowProductLines, rowSublines, isLoadingLines, isLoadingSublines]);
return result;
}, [data, itemNumbers, upcValidation]);
// Create upcValidationResults map using the consolidated item numbers
const upcValidationResults = useMemo(() => {
const results = new Map<number, { itemNumber: string }>();
// Populate with our consolidated item numbers
consolidatedItemNumbers.forEach((itemNumber, rowIndex) => {
results.set(rowIndex, { itemNumber });
});
return results;
}, [consolidatedItemNumbers]);
// Render the validation table with the provided props and UPC data
return (
<AdaptedTable
<ValidationTable
data={data}
fields={fields}
rowSelection={rowSelection}
@@ -137,11 +142,11 @@ function UpcValidationTableAdapter<T extends string>({
templates={templates}
applyTemplate={applyTemplate}
getTemplateDisplayText={getTemplateDisplayText}
validatingCells={new Set()}
itemNumbers={new Map()}
validatingCells={combinedValidatingCells}
itemNumbers={consolidatedItemNumbers}
isLoadingTemplates={isLoadingTemplates}
copyDown={copyDown}
upcValidationResults={new Map<number, { itemNumber: string }>()}
upcValidationResults={upcValidationResults}
rowProductLines={rowProductLines}
rowSublines={rowSublines}
isLoadingLines={isLoadingLines}

View File

@@ -293,8 +293,18 @@ const ValidationCell = React.memo(({
// Use the CopyDown context
const copyDownContext = React.useContext(CopyDownContext);
// Display value prioritizes itemNumber if available (for item_number fields)
const displayValue = fieldKey === 'item_number' && itemNumber ? itemNumber : value;
// CRITICAL FIX: For item_number fields, always prioritize the itemNumber prop over the value
// This ensures that when the itemNumber changes, the display value changes
let displayValue;
if (fieldKey === 'item_number' && itemNumber) {
// Always log when an item_number field is rendered to help debug
console.log(`[VC-DEBUG] ValidationCell rendering item_number for row=${rowIndex} with itemNumber=${itemNumber}, value=${value}`);
// Prioritize itemNumber prop for item_number fields
displayValue = itemNumber;
} else {
displayValue = value;
}
// Use the optimized processErrors function to avoid redundant filtering
const {

View File

@@ -58,7 +58,10 @@ const ValidationContainer = <T extends string>({
loadTemplates,
setData,
fields,
isLoadingTemplates } = validationState
isLoadingTemplates,
validatingCells,
setValidatingCells
} = validationState
// Use product lines fetching hook
const {
@@ -70,9 +73,6 @@ const ValidationContainer = <T extends string>({
fetchSublines
} = useProductLinesFetching(data);
// Add state for tracking cells in loading state
const [validatingCells, setValidatingCells] = useState<Set<string>>(new Set());
// Use UPC validation hook
const upcValidation = useUpcValidation(data, setData);
@@ -959,6 +959,7 @@ const ValidationContainer = <T extends string>({
isLoadingLines={isLoadingLines}
isLoadingSublines={isLoadingSublines}
upcValidation={upcValidation}
itemNumbers={upcValidation.itemNumbers}
/>
);
}, [

View File

@@ -138,11 +138,15 @@ const MemoizedCell = React.memo(({
/>
);
}, (prev, next) => {
// CRITICAL FIX: Never memoize item_number cells - always re-render them
if (prev.fieldKey === 'item_number') {
return false; // Never skip re-renders for item_number cells
}
// Optimize the memo comparison function for better performance
// Only re-render if these essential props change
const valueEqual = prev.value === next.value;
const isValidatingEqual = prev.isValidating === next.isValidating;
const itemNumberEqual = prev.itemNumber === next.itemNumber;
// Shallow equality check for errors array
const errorsEqual = prev.errors === next.errors || (
@@ -161,7 +165,7 @@ const MemoizedCell = React.memo(({
);
// Skip checking for props that rarely change
return valueEqual && isValidatingEqual && errorsEqual && optionsEqual && itemNumberEqual;
return valueEqual && isValidatingEqual && errorsEqual && optionsEqual;
});
MemoizedCell.displayName = 'MemoizedCell';
@@ -335,10 +339,28 @@ const ValidationTable = <T extends string>({
return isValidatingUpc?.(rowIndex) || validatingUpcRows.includes(rowIndex);
}, [isValidatingUpc, validatingUpcRows]);
// Use upcValidationResults for display
// Use upcValidationResults for display, prioritizing the most recent values
const getRowUpcResult = useCallback((rowIndex: number) => {
return upcValidationResults?.get(rowIndex)?.itemNumber;
}, [upcValidationResults]);
// ALWAYS get from the data array directly - most authoritative source
const rowData = data[rowIndex];
if (rowData && rowData.item_number) {
return rowData.item_number;
}
// Maps are only backup sources when data doesn't have a value
const itemNumberFromMap = itemNumbers.get(rowIndex);
if (itemNumberFromMap) {
return itemNumberFromMap;
}
// Last resort - upcValidationResults
const upcResult = upcValidationResults?.get(rowIndex)?.itemNumber;
if (upcResult) {
return upcResult;
}
return undefined;
}, [data, itemNumbers, upcValidationResults]);
// Memoize field columns with stable handlers
const fieldColumns = useMemo(() => fields.map((field): ColumnDef<RowData<T>, any> | null => {
@@ -413,16 +435,32 @@ const ValidationTable = <T extends string>({
}
// Get item number from UPC validation results if available
let itemNumber = itemNumbers.get(row.index);
if (!itemNumber && fieldKey === 'item_number') {
itemNumber = getRowUpcResult(row.index);
// CRITICAL CHANGE: Get item number directly from row data first for item_number fields
let itemNumber;
if (fieldKey === 'item_number') {
// Check directly in row data first - this is the most accurate source
const directValue = row.original[fieldKey];
if (directValue) {
itemNumber = directValue;
} else {
// Fall back to centralized getter that checks all sources
itemNumber = getRowUpcResult(row.index);
}
}
// CRITICAL: For item_number fields, create a unique key that includes the itemNumber value
// This forces a complete re-render when the itemNumber changes
const cellKey = fieldKey === 'item_number'
? `cell-${row.index}-${fieldKey}-${itemNumber || 'empty'}-${Date.now()}` // Force re-render on every render cycle for item_number
: `cell-${row.index}-${fieldKey}`;
return (
<MemoizedCell
key={cellKey} // CRITICAL: Add key to force re-render when itemNumber changes
field={fieldWithType as Field<string>}
value={row.original[field.key as keyof typeof row.original]}
value={fieldKey === 'item_number' && row.original[field.key]
? row.original[field.key] // Use direct value from row data
: row.original[field.key as keyof typeof row.original]}
onChange={(value) => handleFieldUpdate(row.index, field.key as T, value)}
errors={cellErrors}
isValidating={isLoading}

View File

@@ -15,7 +15,8 @@ export const useTemplateManagement = <T extends string>(
upcValidation: {
validateUpc: (rowIndex: number, supplierId: string, upcValue: string) => Promise<{success: boolean, itemNumber?: string}>,
applyItemNumbersToData: (onApplied?: (updatedRowIds: number[]) => void) => void
}
},
setValidatingCells?: React.Dispatch<React.SetStateAction<Set<string>>>
) => {
// Template state
const [templates, setTemplates] = useState<Template[]>([]);
@@ -227,6 +228,12 @@ export const useTemplateManagement = <T extends string>(
batchStatuses.set(index, "validated");
});
// Check which rows need UPC validation
const upcValidationRows = validRowIndexes.filter((rowIndex) => {
const row = newData[rowIndex];
return row && row.upc && row.supplier;
});
// Perform a single update for all rows
setData(newData);
@@ -259,59 +266,180 @@ export const useTemplateManagement = <T extends string>(
toast.success(`Template applied to ${validRowIndexes.length} rows`);
}
// Check which rows need UPC validation
const upcValidationRows = validRowIndexes.filter((rowIndex) => {
const row = newData[rowIndex];
return row && row.upc && row.supplier;
});
// Reset template application flag to allow validation
isApplyingTemplateRef.current = false;
// Validate UPCs for rows that have both UPC and supplier
// If there are rows with both UPC and supplier, validate them
if (upcValidationRows.length > 0) {
console.log(
`Validating UPCs for ${upcValidationRows.length} rows after template application`
);
console.log(`Validating UPCs for ${upcValidationRows.length} rows after template application`);
// Schedule UPC validation for the next tick to allow UI to update first
setTimeout(() => {
// Track successful validations
const validationPromises: Promise<{success: boolean, itemNumber?: string}>[] = [];
// Process each row sequentially - this mimics the exact manual edit behavior
const processNextValidation = (index = 0) => {
if (index >= upcValidationRows.length) {
return; // All rows processed
}
upcValidationRows.forEach((rowIndex) => {
const row = newData[rowIndex];
if (row && row.upc && row.supplier) {
// FIXED: Directly call validateUpc instead of validateRow
console.log(`Directly calling validateUpc for row ${rowIndex} with UPC ${row.upc} and supplier ${row.supplier}`);
const validationPromise = upcValidation.validateUpc(rowIndex, row.supplier.toString(), row.upc.toString());
validationPromises.push(validationPromise);
const rowIndex = upcValidationRows[index];
const row = newData[rowIndex];
if (row && row.supplier && row.upc) {
// The EXACT implementation from handleUpdateRow when supplier is edited manually:
// 1. Mark the item_number cell as being validated - THIS IS CRITICAL FOR LOADING STATE
const cellKey = `${rowIndex}-item_number`;
// Clear validation errors for this field
setValidationErrors(prev => {
const newErrors = new Map(prev);
if (newErrors.has(rowIndex)) {
const rowErrors = { ...newErrors.get(rowIndex) };
if (rowErrors.item_number) {
delete rowErrors.item_number;
}
newErrors.set(rowIndex, rowErrors);
}
return newErrors;
});
// Set loading state - using setValidatingCells from props
if (setValidatingCells) {
setValidatingCells(prev => {
const newSet = new Set(prev);
newSet.add(cellKey);
return newSet;
});
}
});
// After all validations complete, apply item numbers
if (validationPromises.length > 0) {
Promise.all(validationPromises)
.then(results => {
const successCount = results.filter(r => r.success).length;
console.log(`${successCount}/${validationPromises.length} UPC validations succeeded`);
// Validate UPC for this row
upcValidation.validateUpc(rowIndex, row.supplier.toString(), row.upc.toString())
.then(result => {
if (result.success && result.itemNumber) {
// CRITICAL FIX: Directly update data with the item number to ensure immediate UI update
setData(prevData => {
const newData = [...prevData];
// Apply item numbers to the data
upcValidation.applyItemNumbersToData(updatedRowIds => {
console.log(`Applied item numbers to ${updatedRowIds.length} rows`);
// Update this specific row with the item number
if (rowIndex >= 0 && rowIndex < newData.length) {
newData[rowIndex] = {
...newData[rowIndex],
item_number: result.itemNumber
};
}
// After applying item numbers, trigger validation for those rows
updatedRowIds.forEach(rowIndex => {
validateRow(rowIndex, 'item_number');
return newData;
});
});
// Also trigger other relevant updates
upcValidation.applyItemNumbersToData();
// Mark for revalidation after item numbers are updated
setTimeout(() => {
// Validate the row EXACTLY like in manual edit
validateRow(rowIndex, 'item_number');
// CRITICAL FIX: Make one final check to ensure data is correct
setTimeout(() => {
// Get the current item number from the data
const currentItemNumber = (() => {
try {
const dataAtThisPointInTime = data[rowIndex];
return dataAtThisPointInTime?.item_number;
} catch (e) {
return undefined;
}
})();
// If the data is wrong at this point, fix it directly
if (currentItemNumber !== result.itemNumber) {
// Directly update the data to fix the issue
setData(dataRightNow => {
const fixedData = [...dataRightNow];
if (rowIndex >= 0 && rowIndex < fixedData.length) {
fixedData[rowIndex] = {
...fixedData[rowIndex],
item_number: result.itemNumber
};
}
return fixedData;
});
// Then do a force update after a brief delay
setTimeout(() => {
setData(currentData => {
// Critical fix: ensure the item number is correct
if (currentData[rowIndex] && currentData[rowIndex].item_number !== result.itemNumber) {
// Create a completely new array with the correct item number
const fixedData = [...currentData];
fixedData[rowIndex] = {
...fixedData[rowIndex],
item_number: result.itemNumber
};
return fixedData;
}
// Create a completely new array
return [...currentData];
});
}, 20);
} else {
// Item number is already correct, just do the force update
setData(currentData => {
// Create a completely new array
return [...currentData];
});
}
}, 50);
// Clear loading state
if (setValidatingCells) {
setValidatingCells(prev => {
const newSet = new Set(prev);
newSet.delete(cellKey);
return newSet;
});
}
// Continue to next row after validation is complete
setTimeout(() => processNextValidation(index + 1), 100);
}, 100);
} else {
// Clear loading state on failure
if (setValidatingCells) {
setValidatingCells(prev => {
const newSet = new Set(prev);
newSet.delete(cellKey);
return newSet;
});
}
// Continue to next row if validation fails
setTimeout(() => processNextValidation(index + 1), 100);
}
})
.catch(err => {
console.error("Error in UPC validation batch:", err);
});
}
}, 100);
}
console.error(`Error validating UPC for row ${rowIndex}:`, err);
// Reset the template application flag
isApplyingTemplateRef.current = false;
// Clear loading state on error
if (setValidatingCells) {
setValidatingCells(prev => {
const newSet = new Set(prev);
newSet.delete(cellKey);
return newSet;
});
}
// Continue to next row despite error
setTimeout(() => processNextValidation(index + 1), 100);
});
} else {
// Skip this row and continue to the next
processNextValidation(index + 1);
}
};
// Start processing validations
processNextValidation();
}
},
[
data,
@@ -321,6 +449,7 @@ export const useTemplateManagement = <T extends string>(
setRowValidationStatus,
validateRow,
upcValidation,
setValidatingCells
]
);

View File

@@ -49,10 +49,40 @@ export const useUpcValidation = (
// Update item number
const updateItemNumber = useCallback((rowIndex: number, itemNumber: string) => {
console.log(`Setting item number for row ${rowIndex} to ${itemNumber}`);
validationStateRef.current.itemNumbers.set(rowIndex, itemNumber);
setItemNumberUpdates(new Map(validationStateRef.current.itemNumbers));
}, []);
// CRITICAL: Update BOTH the data state and the ref
// First, update the data directly to ensure UI consistency
setData(prevData => {
// Create a new copy of the data
const newData = [...prevData];
// Only update if the row exists
if (rowIndex >= 0 && rowIndex < newData.length) {
// First, we need a new object reference for the row to force a re-render
newData[rowIndex] = {
...newData[rowIndex],
item_number: itemNumber
};
}
return newData;
});
// Also update the itemNumbers map AFTER the data is updated
// This ensures the map reflects the current state of the data
setTimeout(() => {
// Update the ref with the same value
validationStateRef.current.itemNumbers.set(rowIndex, itemNumber);
// CRITICAL: Force a React state update to ensure all components re-render
// Created a brand new Map object to ensure React detects the change
const newItemNumbersMap = new Map(validationStateRef.current.itemNumbers);
setItemNumberUpdates(newItemNumbersMap);
// Force an immediate React render cycle by triggering state updates
setValidatingCellKeys(new Set(validationStateRef.current.validatingCells));
setValidatingRows(new Set(validationStateRef.current.validatingRows));
}, 0);
}, [setData]);
// Mark a row as being validated
const startValidatingRow = useCallback((rowIndex: number) => {
@@ -132,11 +162,22 @@ export const useUpcValidation = (
);
previousKeys.forEach(key => validationStateRef.current.activeValidations.delete(key));
// Start validation - track this with the ref to avoid race conditions
startValidatingRow(rowIndex);
startValidatingCell(rowIndex, 'item_number');
// Log validation start to help debug template issues
console.log(`[UPC-DEBUG] Starting UPC validation for row ${rowIndex} with supplier ${supplierId}, upc ${upcValue}`);
console.log(`Validating UPC: rowIndex=${rowIndex}, supplierId=${supplierId}, upc=${upcValue}`);
// IMPORTANT: Set validation state using setState to FORCE UI updates
validationStateRef.current.validatingRows.add(rowIndex);
setValidatingRows(new Set(validationStateRef.current.validatingRows));
setIsValidatingUpc(true);
// Start cell validation and explicitly update UI via setState
const cellKey = getCellKey(rowIndex, 'item_number');
validationStateRef.current.validatingCells.add(cellKey);
setValidatingCellKeys(new Set(validationStateRef.current.validatingCells));
console.log(`[UPC-DEBUG] Set loading state for row ${rowIndex}, cell key ${cellKey}`);
console.log(`[UPC-DEBUG] Current validating rows: ${Array.from(validationStateRef.current.validatingRows).join(', ')}`);
console.log(`[UPC-DEBUG] Current validating cells: ${Array.from(validationStateRef.current.validatingCells).join(', ')}`);
try {
// Create a unique key for this validation to track it
@@ -157,18 +198,43 @@ export const useUpcValidation = (
});
// Fetch the product by UPC
console.log(`[UPC-DEBUG] Fetching product data for UPC ${upcValue} with supplier ${supplierId}`);
const product = await fetchProductByUpc(supplierId, upcValue);
console.log(`[UPC-DEBUG] Fetch complete for row ${rowIndex}, success: ${!product.error}`);
// Check if this validation is still relevant (hasn't been superseded by another)
if (!validationStateRef.current.activeValidations.has(validationKey)) {
console.log(`Validation ${validationKey} was cancelled`);
console.log(`[UPC-DEBUG] Validation ${validationKey} was cancelled`);
return { success: false };
}
// Extract the item number from the API response - check for !error since API returns { error: boolean, data: any }
if (product && !product.error && product.data?.itemNumber) {
// Store this validation result
updateItemNumber(rowIndex, product.data.itemNumber);
console.log(`[UPC-DEBUG] Got item number for row ${rowIndex}: ${product.data.itemNumber}`);
// CRITICAL FIX: Directly update the data with the new item number first
setData(prevData => {
const newData = [...prevData];
if (rowIndex >= 0 && rowIndex < newData.length) {
// This should happen before updating the map
newData[rowIndex] = {
...newData[rowIndex],
item_number: product.data.itemNumber
};
}
return newData;
});
// Then, update the map to match what's now in the data
validationStateRef.current.itemNumbers.set(rowIndex, product.data.itemNumber);
// CRITICAL: Force a React state update to ensure all components re-render
// Created a brand new Map object to ensure React detects the change
const newItemNumbersMap = new Map(validationStateRef.current.itemNumbers);
setItemNumberUpdates(newItemNumbersMap);
// Force a shallow copy of the itemNumbers map to trigger useEffect dependencies
validationStateRef.current.itemNumbers = new Map(validationStateRef.current.itemNumbers);
return {
success: true,
@@ -176,7 +242,7 @@ export const useUpcValidation = (
};
} else {
// No item number found but validation was still attempted
console.log(`No item number found for UPC ${upcValue}`);
console.log(`[UPC-DEBUG] No item number found for UPC ${upcValue}`);
// Clear any existing item number to show validation was attempted and failed
if (validationStateRef.current.itemNumbers.has(rowIndex)) {
@@ -187,58 +253,71 @@ export const useUpcValidation = (
return { success: false };
}
} catch (error) {
console.error('Error validating UPC:', error);
console.error('[UPC-DEBUG] Error validating UPC:', error);
return { success: false };
} finally {
// End validation
stopValidatingRow(rowIndex);
stopValidatingCell(rowIndex, 'item_number');
// End validation - FORCE UI update by using setState directly
console.log(`[UPC-DEBUG] Ending validation for row ${rowIndex}`);
validationStateRef.current.validatingRows.delete(rowIndex);
setValidatingRows(new Set(validationStateRef.current.validatingRows));
if (validationStateRef.current.validatingRows.size === 0) {
setIsValidatingUpc(false);
}
validationStateRef.current.validatingCells.delete(cellKey);
setValidatingCellKeys(new Set(validationStateRef.current.validatingCells));
console.log(`[UPC-DEBUG] Cleared loading state for row ${rowIndex}`);
console.log(`[UPC-DEBUG] Updated validating rows: ${Array.from(validationStateRef.current.validatingRows).join(', ')}`);
console.log(`[UPC-DEBUG] Updated validating cells: ${Array.from(validationStateRef.current.validatingCells).join(', ')}`);
}
}, [fetchProductByUpc, updateItemNumber, startValidatingCell, stopValidatingCell, startValidatingRow, stopValidatingRow, setData]);
}, [fetchProductByUpc, updateItemNumber, setData]);
// Apply item numbers to data
const applyItemNumbersToData = useCallback((onApplied?: (updatedRowIds: number[]) => void) => {
// Create a copy of the current item numbers map to avoid race conditions
const currentItemNumbers = new Map(validationStateRef.current.itemNumbers);
// Apply all pending item numbers to the data state
const applyItemNumbersToData = useCallback((callback?: (updatedRows: number[]) => void) => {
// Skip if we have nothing to apply
if (validationStateRef.current.itemNumbers.size === 0) {
if (callback) callback([]);
return;
}
// Only apply if we have any item numbers
if (currentItemNumbers.size === 0) return;
// Track updated row indices to pass to callback
const updatedRowIndices: number[] = [];
// Log for debugging
console.log(`Applying ${currentItemNumbers.size} item numbers to data`);
// Gather all row IDs that will be updated
const rowIds: number[] = [];
// Update the data state with all item numbers
setData(prevData => {
// Create a new copy of the data
const newData = [...prevData];
// Update each row with its item number without affecting other fields
currentItemNumbers.forEach((itemNumber, rowIndex) => {
if (rowIndex < newData.length) {
console.log(`Setting item_number for row ${rowIndex} to ${itemNumber}`);
// Apply each item number to the data
validationStateRef.current.itemNumbers.forEach((itemNumber, rowIndex) => {
// Ensure row exists and value has actually changed
if (rowIndex >= 0 && rowIndex < newData.length &&
newData[rowIndex]?.item_number !== itemNumber) {
// Only update the item_number field, leaving other fields unchanged
// Create a new row object to force re-rendering
newData[rowIndex] = {
...newData[rowIndex],
item_number: itemNumber
};
// Track which rows were updated
updatedRowIndices.push(rowIndex);
// Track which row was updated for the callback
rowIds.push(rowIndex);
}
});
return newData;
});
// Call the callback if provided, after state updates are processed
if (onApplied && updatedRowIndices.length > 0) {
// Use setTimeout to ensure this happens after the state update
setTimeout(() => {
onApplied(updatedRowIndices);
}, 100); // Use 100ms to ensure the data update is fully processed
// Force a re-render by updating React state
setTimeout(() => {
setItemNumberUpdates(new Map(validationStateRef.current.itemNumbers));
}, 0);
// Call the callback with the updated row IDs
if (callback) {
callback(rowIds);
}
}, [setData]);
@@ -405,6 +484,9 @@ export const useUpcValidation = (
getItemNumber,
applyItemNumbersToData,
// CRITICAL: Expose the itemNumbers map directly
itemNumbers: validationStateRef.current.itemNumbers,
// Initialization state
initialValidationDone: initialUpcValidationDoneRef.current
};

View File

@@ -88,6 +88,9 @@ export const useValidationState = <T extends string>({
Map<number, "pending" | "validating" | "validated" | "error">
>(new Map());
// Add state for tracking cells in loading state
const [validatingCells, setValidatingCells] = useState<Set<string>>(new Set());
const initialValidationDoneRef = useRef(false);
const isValidatingRef = useRef(false);
@@ -119,7 +122,8 @@ export const useValidationState = <T extends string>({
setRowValidationStatus,
validateRow,
isApplyingTemplateRef,
upcValidation
upcValidation,
setValidatingCells
);
// Use filter management hook
@@ -673,6 +677,10 @@ export const useValidationState = <T extends string>({
validateRow,
hasErrors,
// CRITICAL: Export validatingCells to make it available to ValidationContainer
validatingCells,
setValidatingCells,
// Row selection
rowSelection,
setRowSelection,