Fix item number not getting updated when applying template
This commit is contained in:
@@ -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);
|
||||
});
|
||||
|
||||
// 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>();
|
||||
|
||||
// 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);
|
||||
|
||||
return {
|
||||
...row,
|
||||
item_number: itemNumber
|
||||
};
|
||||
}
|
||||
return row;
|
||||
combined.add(cellKey);
|
||||
});
|
||||
|
||||
// Create a Map for upcValidationResults with the same structure expected by ValidationTable
|
||||
const upcValidationResultsMap = new Map<number, { itemNumber: string }>();
|
||||
return combined;
|
||||
}, [upcValidation.validatingRows, externalValidatingCells]);
|
||||
|
||||
// Create a consolidated item numbers map from all sources
|
||||
const consolidatedItemNumbers = useMemo(() => {
|
||||
const result = new Map<number, string>();
|
||||
|
||||
// Populate with any item numbers we have from validation
|
||||
// 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`);
|
||||
|
||||
itemNumbers.forEach((itemNumber, rowIndex) => {
|
||||
console.log(`[ADAPTER-DEBUG] Adding item number for row ${rowIndex} from itemNumbers: ${itemNumber}`);
|
||||
result.set(rowIndex, itemNumber);
|
||||
});
|
||||
}
|
||||
|
||||
// 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}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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}
|
||||
/>
|
||||
);
|
||||
}, [
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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`
|
||||
);
|
||||
|
||||
// Schedule UPC validation for the next tick to allow UI to update first
|
||||
setTimeout(() => {
|
||||
// Track successful validations
|
||||
const validationPromises: Promise<{success: boolean, itemNumber?: string}>[] = [];
|
||||
console.log(`Validating UPCs for ${upcValidationRows.length} rows after template application`);
|
||||
|
||||
// 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`);
|
||||
|
||||
// Apply item numbers to the data
|
||||
upcValidation.applyItemNumbersToData(updatedRowIds => {
|
||||
console.log(`Applied item numbers to ${updatedRowIds.length} rows`);
|
||||
|
||||
// After applying item numbers, trigger validation for those rows
|
||||
updatedRowIds.forEach(rowIndex => {
|
||||
validateRow(rowIndex, 'item_number');
|
||||
|
||||
// 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];
|
||||
|
||||
// Update this specific row with the item number
|
||||
if (rowIndex >= 0 && rowIndex < newData.length) {
|
||||
newData[rowIndex] = {
|
||||
...newData[rowIndex],
|
||||
item_number: result.itemNumber
|
||||
};
|
||||
}
|
||||
|
||||
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);
|
||||
console.error(`Error validating UPC for row ${rowIndex}:`, err);
|
||||
|
||||
// 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);
|
||||
}
|
||||
}, 100);
|
||||
};
|
||||
|
||||
// Start processing validations
|
||||
processNextValidation();
|
||||
}
|
||||
|
||||
// Reset the template application flag
|
||||
isApplyingTemplateRef.current = false;
|
||||
},
|
||||
[
|
||||
data,
|
||||
@@ -321,6 +449,7 @@ export const useTemplateManagement = <T extends string>(
|
||||
setRowValidationStatus,
|
||||
validateRow,
|
||||
upcValidation,
|
||||
setValidatingCells
|
||||
]
|
||||
);
|
||||
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
@@ -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
|
||||
@@ -672,6 +676,10 @@ export const useValidationState = <T extends string>({
|
||||
rowValidationStatus,
|
||||
validateRow,
|
||||
hasErrors,
|
||||
|
||||
// CRITICAL: Export validatingCells to make it available to ValidationContainer
|
||||
validatingCells,
|
||||
setValidatingCells,
|
||||
|
||||
// Row selection
|
||||
rowSelection,
|
||||
|
||||
Reference in New Issue
Block a user