diff --git a/inventory/src/components/product-import/steps/ValidationStepNew/components/UpcValidationTableAdapter.tsx b/inventory/src/components/product-import/steps/ValidationStepNew/components/UpcValidationTableAdapter.tsx index c6a2e54..80980c4 100644 --- a/inventory/src/components/product-import/steps/ValidationStepNew/components/UpcValidationTableAdapter.tsx +++ b/inventory/src/components/product-import/steps/ValidationStepNew/components/UpcValidationTableAdapter.tsx @@ -28,6 +28,7 @@ interface UpcValidationTableAdapterProps { validatingRows: Set getItemNumber: (rowIndex: number) => string | undefined } + itemNumbers?: Map } /** @@ -56,75 +57,79 @@ function UpcValidationTableAdapter({ rowSublines, isLoadingLines, isLoadingSublines, - upcValidation + upcValidation, + itemNumbers }: UpcValidationTableAdapterProps) { // Prepare the validation table with UPC data - const AdaptedTable = useMemo(() => React.memo((props: React.ComponentProps) => { - // 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(); + + // Create combined validatingCells set from validating rows and external cells + const combinedValidatingCells = useMemo(() => { + const combined = new Set(); // 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(); - - // 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(); + return combined; + }, [upcValidation.validatingRows, externalValidatingCells]); + + // Create a consolidated item numbers map from all sources + const consolidatedItemNumbers = useMemo(() => { + const result = new Map(); - // 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 ( - - ); - }), [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(); + + // 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 ( - ({ templates={templates} applyTemplate={applyTemplate} getTemplateDisplayText={getTemplateDisplayText} - validatingCells={new Set()} - itemNumbers={new Map()} + validatingCells={combinedValidatingCells} + itemNumbers={consolidatedItemNumbers} isLoadingTemplates={isLoadingTemplates} copyDown={copyDown} - upcValidationResults={new Map()} + upcValidationResults={upcValidationResults} rowProductLines={rowProductLines} rowSublines={rowSublines} isLoadingLines={isLoadingLines} diff --git a/inventory/src/components/product-import/steps/ValidationStepNew/components/ValidationCell.tsx b/inventory/src/components/product-import/steps/ValidationStepNew/components/ValidationCell.tsx index f4d907d..0c5fd91 100644 --- a/inventory/src/components/product-import/steps/ValidationStepNew/components/ValidationCell.tsx +++ b/inventory/src/components/product-import/steps/ValidationStepNew/components/ValidationCell.tsx @@ -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 { diff --git a/inventory/src/components/product-import/steps/ValidationStepNew/components/ValidationContainer.tsx b/inventory/src/components/product-import/steps/ValidationStepNew/components/ValidationContainer.tsx index a744a24..8749643 100644 --- a/inventory/src/components/product-import/steps/ValidationStepNew/components/ValidationContainer.tsx +++ b/inventory/src/components/product-import/steps/ValidationStepNew/components/ValidationContainer.tsx @@ -58,7 +58,10 @@ const ValidationContainer = ({ loadTemplates, setData, fields, - isLoadingTemplates } = validationState + isLoadingTemplates, + validatingCells, + setValidatingCells + } = validationState // Use product lines fetching hook const { @@ -70,9 +73,6 @@ const ValidationContainer = ({ fetchSublines } = useProductLinesFetching(data); - // Add state for tracking cells in loading state - const [validatingCells, setValidatingCells] = useState>(new Set()); - // Use UPC validation hook const upcValidation = useUpcValidation(data, setData); @@ -959,6 +959,7 @@ const ValidationContainer = ({ isLoadingLines={isLoadingLines} isLoadingSublines={isLoadingSublines} upcValidation={upcValidation} + itemNumbers={upcValidation.itemNumbers} /> ); }, [ diff --git a/inventory/src/components/product-import/steps/ValidationStepNew/components/ValidationTable.tsx b/inventory/src/components/product-import/steps/ValidationStepNew/components/ValidationTable.tsx index e2f9b3d..b82d1e0 100644 --- a/inventory/src/components/product-import/steps/ValidationStepNew/components/ValidationTable.tsx +++ b/inventory/src/components/product-import/steps/ValidationStepNew/components/ValidationTable.tsx @@ -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 = ({ 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, any> | null => { @@ -413,16 +435,32 @@ const ValidationTable = ({ } - // 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 ( } - 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} diff --git a/inventory/src/components/product-import/steps/ValidationStepNew/hooks/useTemplateManagement.tsx b/inventory/src/components/product-import/steps/ValidationStepNew/hooks/useTemplateManagement.tsx index c1feb01..2cc6773 100644 --- a/inventory/src/components/product-import/steps/ValidationStepNew/hooks/useTemplateManagement.tsx +++ b/inventory/src/components/product-import/steps/ValidationStepNew/hooks/useTemplateManagement.tsx @@ -15,7 +15,8 @@ export const useTemplateManagement = ( upcValidation: { validateUpc: (rowIndex: number, supplierId: string, upcValue: string) => Promise<{success: boolean, itemNumber?: string}>, applyItemNumbersToData: (onApplied?: (updatedRowIds: number[]) => void) => void - } + }, + setValidatingCells?: React.Dispatch>> ) => { // Template state const [templates, setTemplates] = useState([]); @@ -227,6 +228,12 @@ export const useTemplateManagement = ( 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 = ( 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 = ( setRowValidationStatus, validateRow, upcValidation, + setValidatingCells ] ); diff --git a/inventory/src/components/product-import/steps/ValidationStepNew/hooks/useUpcValidation.tsx b/inventory/src/components/product-import/steps/ValidationStepNew/hooks/useUpcValidation.tsx index f72cd7a..a25cc21 100644 --- a/inventory/src/components/product-import/steps/ValidationStepNew/hooks/useUpcValidation.tsx +++ b/inventory/src/components/product-import/steps/ValidationStepNew/hooks/useUpcValidation.tsx @@ -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 }; diff --git a/inventory/src/components/product-import/steps/ValidationStepNew/hooks/useValidationState.tsx b/inventory/src/components/product-import/steps/ValidationStepNew/hooks/useValidationState.tsx index 1a193d1..0d1e674 100644 --- a/inventory/src/components/product-import/steps/ValidationStepNew/hooks/useValidationState.tsx +++ b/inventory/src/components/product-import/steps/ValidationStepNew/hooks/useValidationState.tsx @@ -88,6 +88,9 @@ export const useValidationState = ({ Map >(new Map()); + // Add state for tracking cells in loading state + const [validatingCells, setValidatingCells] = useState>(new Set()); + const initialValidationDoneRef = useRef(false); const isValidatingRef = useRef(false); @@ -119,7 +122,8 @@ export const useValidationState = ({ setRowValidationStatus, validateRow, isApplyingTemplateRef, - upcValidation + upcValidation, + setValidatingCells ); // Use filter management hook @@ -672,6 +676,10 @@ export const useValidationState = ({ rowValidationStatus, validateRow, hasErrors, + + // CRITICAL: Export validatingCells to make it available to ValidationContainer + validatingCells, + setValidatingCells, // Row selection rowSelection,