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>
|
validatingRows: Set<number>
|
||||||
getItemNumber: (rowIndex: number) => string | undefined
|
getItemNumber: (rowIndex: number) => string | undefined
|
||||||
}
|
}
|
||||||
|
itemNumbers?: Map<number, string>
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -56,75 +57,79 @@ function UpcValidationTableAdapter<T extends string>({
|
|||||||
rowSublines,
|
rowSublines,
|
||||||
isLoadingLines,
|
isLoadingLines,
|
||||||
isLoadingSublines,
|
isLoadingSublines,
|
||||||
upcValidation
|
upcValidation,
|
||||||
|
itemNumbers
|
||||||
}: UpcValidationTableAdapterProps<T>) {
|
}: UpcValidationTableAdapterProps<T>) {
|
||||||
// Prepare the validation table with UPC data
|
// 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
|
// Create combined validatingCells set from validating rows and external cells
|
||||||
// This ensures only the item_number column shows loading state during UPC validation
|
const combinedValidatingCells = useMemo(() => {
|
||||||
const combinedValidatingCells = new Set<string>();
|
const combined = new Set<string>();
|
||||||
|
|
||||||
// Add UPC validation cells
|
// Add UPC validation cells
|
||||||
upcValidation.validatingRows.forEach(rowIndex => {
|
upcValidation.validatingRows.forEach(rowIndex => {
|
||||||
// Only mark the item_number cells as validating, NOT the UPC or supplier
|
// 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
|
// Add any other validating cells from state
|
||||||
externalValidatingCells.forEach(cellKey => {
|
externalValidatingCells.forEach(cellKey => {
|
||||||
combinedValidatingCells.add(cellKey);
|
combined.add(cellKey);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Convert the Map to the expected format for the ValidationTable
|
return combined;
|
||||||
// Create a new Map from the item numbers to ensure proper typing
|
}, [upcValidation.validatingRows, externalValidatingCells]);
|
||||||
const itemNumbersMap = new Map<number, string>();
|
|
||||||
|
|
||||||
// Merge the item numbers with the data for display purposes only
|
// Create a consolidated item numbers map from all sources
|
||||||
const enhancedData = props.data.map((row: any, index: number) => {
|
const consolidatedItemNumbers = useMemo(() => {
|
||||||
const itemNumber = upcValidation.getItemNumber(index);
|
const result = new Map<number, string>();
|
||||||
if (itemNumber) {
|
|
||||||
// Add to our map for proper prop passing
|
|
||||||
itemNumbersMap.set(index, itemNumber);
|
|
||||||
|
|
||||||
return {
|
// First add from itemNumbers directly - this is the source of truth for template applications
|
||||||
...row,
|
if (itemNumbers) {
|
||||||
item_number: itemNumber
|
// 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);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
return row;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Create a Map for upcValidationResults with the same structure expected by ValidationTable
|
// For each row, ensure we have the most up-to-date item number
|
||||||
const upcValidationResultsMap = new Map<number, { itemNumber: string }>();
|
|
||||||
|
|
||||||
// Populate with any item numbers we have from validation
|
|
||||||
data.forEach((_, index) => {
|
data.forEach((_, index) => {
|
||||||
|
// Check if upcValidation has an item number for this row
|
||||||
const itemNumber = upcValidation.getItemNumber(index);
|
const itemNumber = upcValidation.getItemNumber(index);
|
||||||
if (itemNumber) {
|
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 (
|
return result;
|
||||||
<ValidationTable
|
}, [data, itemNumbers, upcValidation]);
|
||||||
{...props}
|
|
||||||
data={enhancedData}
|
// Create upcValidationResults map using the consolidated item numbers
|
||||||
validatingCells={combinedValidatingCells}
|
const upcValidationResults = useMemo(() => {
|
||||||
itemNumbers={itemNumbersMap}
|
const results = new Map<number, { itemNumber: string }>();
|
||||||
isLoadingTemplates={isLoadingTemplates}
|
|
||||||
copyDown={copyDown}
|
// Populate with our consolidated item numbers
|
||||||
rowProductLines={rowProductLines}
|
consolidatedItemNumbers.forEach((itemNumber, rowIndex) => {
|
||||||
rowSublines={rowSublines}
|
results.set(rowIndex, { itemNumber });
|
||||||
isLoadingLines={isLoadingLines}
|
});
|
||||||
isLoadingSublines={isLoadingSublines}
|
|
||||||
upcValidationResults={upcValidationResultsMap}
|
return results;
|
||||||
/>
|
}, [consolidatedItemNumbers]);
|
||||||
);
|
|
||||||
}), [upcValidation.validatingRows, upcValidation.getItemNumber, isLoadingTemplates, copyDown, externalValidatingCells, rowProductLines, rowSublines, isLoadingLines, isLoadingSublines]);
|
|
||||||
|
|
||||||
// Render the validation table with the provided props and UPC data
|
// Render the validation table with the provided props and UPC data
|
||||||
return (
|
return (
|
||||||
<AdaptedTable
|
<ValidationTable
|
||||||
data={data}
|
data={data}
|
||||||
fields={fields}
|
fields={fields}
|
||||||
rowSelection={rowSelection}
|
rowSelection={rowSelection}
|
||||||
@@ -137,11 +142,11 @@ function UpcValidationTableAdapter<T extends string>({
|
|||||||
templates={templates}
|
templates={templates}
|
||||||
applyTemplate={applyTemplate}
|
applyTemplate={applyTemplate}
|
||||||
getTemplateDisplayText={getTemplateDisplayText}
|
getTemplateDisplayText={getTemplateDisplayText}
|
||||||
validatingCells={new Set()}
|
validatingCells={combinedValidatingCells}
|
||||||
itemNumbers={new Map()}
|
itemNumbers={consolidatedItemNumbers}
|
||||||
isLoadingTemplates={isLoadingTemplates}
|
isLoadingTemplates={isLoadingTemplates}
|
||||||
copyDown={copyDown}
|
copyDown={copyDown}
|
||||||
upcValidationResults={new Map<number, { itemNumber: string }>()}
|
upcValidationResults={upcValidationResults}
|
||||||
rowProductLines={rowProductLines}
|
rowProductLines={rowProductLines}
|
||||||
rowSublines={rowSublines}
|
rowSublines={rowSublines}
|
||||||
isLoadingLines={isLoadingLines}
|
isLoadingLines={isLoadingLines}
|
||||||
|
|||||||
@@ -293,8 +293,18 @@ const ValidationCell = React.memo(({
|
|||||||
// Use the CopyDown context
|
// Use the CopyDown context
|
||||||
const copyDownContext = React.useContext(CopyDownContext);
|
const copyDownContext = React.useContext(CopyDownContext);
|
||||||
|
|
||||||
// Display value prioritizes itemNumber if available (for item_number fields)
|
// CRITICAL FIX: For item_number fields, always prioritize the itemNumber prop over the value
|
||||||
const displayValue = fieldKey === 'item_number' && itemNumber ? itemNumber : 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
|
// Use the optimized processErrors function to avoid redundant filtering
|
||||||
const {
|
const {
|
||||||
|
|||||||
@@ -58,7 +58,10 @@ const ValidationContainer = <T extends string>({
|
|||||||
loadTemplates,
|
loadTemplates,
|
||||||
setData,
|
setData,
|
||||||
fields,
|
fields,
|
||||||
isLoadingTemplates } = validationState
|
isLoadingTemplates,
|
||||||
|
validatingCells,
|
||||||
|
setValidatingCells
|
||||||
|
} = validationState
|
||||||
|
|
||||||
// Use product lines fetching hook
|
// Use product lines fetching hook
|
||||||
const {
|
const {
|
||||||
@@ -70,9 +73,6 @@ const ValidationContainer = <T extends string>({
|
|||||||
fetchSublines
|
fetchSublines
|
||||||
} = useProductLinesFetching(data);
|
} = useProductLinesFetching(data);
|
||||||
|
|
||||||
// Add state for tracking cells in loading state
|
|
||||||
const [validatingCells, setValidatingCells] = useState<Set<string>>(new Set());
|
|
||||||
|
|
||||||
// Use UPC validation hook
|
// Use UPC validation hook
|
||||||
const upcValidation = useUpcValidation(data, setData);
|
const upcValidation = useUpcValidation(data, setData);
|
||||||
|
|
||||||
@@ -959,6 +959,7 @@ const ValidationContainer = <T extends string>({
|
|||||||
isLoadingLines={isLoadingLines}
|
isLoadingLines={isLoadingLines}
|
||||||
isLoadingSublines={isLoadingSublines}
|
isLoadingSublines={isLoadingSublines}
|
||||||
upcValidation={upcValidation}
|
upcValidation={upcValidation}
|
||||||
|
itemNumbers={upcValidation.itemNumbers}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}, [
|
}, [
|
||||||
|
|||||||
@@ -138,11 +138,15 @@ const MemoizedCell = React.memo(({
|
|||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}, (prev, next) => {
|
}, (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
|
// Optimize the memo comparison function for better performance
|
||||||
// Only re-render if these essential props change
|
// Only re-render if these essential props change
|
||||||
const valueEqual = prev.value === next.value;
|
const valueEqual = prev.value === next.value;
|
||||||
const isValidatingEqual = prev.isValidating === next.isValidating;
|
const isValidatingEqual = prev.isValidating === next.isValidating;
|
||||||
const itemNumberEqual = prev.itemNumber === next.itemNumber;
|
|
||||||
|
|
||||||
// Shallow equality check for errors array
|
// Shallow equality check for errors array
|
||||||
const errorsEqual = prev.errors === next.errors || (
|
const errorsEqual = prev.errors === next.errors || (
|
||||||
@@ -161,7 +165,7 @@ const MemoizedCell = React.memo(({
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Skip checking for props that rarely change
|
// Skip checking for props that rarely change
|
||||||
return valueEqual && isValidatingEqual && errorsEqual && optionsEqual && itemNumberEqual;
|
return valueEqual && isValidatingEqual && errorsEqual && optionsEqual;
|
||||||
});
|
});
|
||||||
|
|
||||||
MemoizedCell.displayName = 'MemoizedCell';
|
MemoizedCell.displayName = 'MemoizedCell';
|
||||||
@@ -335,10 +339,28 @@ const ValidationTable = <T extends string>({
|
|||||||
return isValidatingUpc?.(rowIndex) || validatingUpcRows.includes(rowIndex);
|
return isValidatingUpc?.(rowIndex) || validatingUpcRows.includes(rowIndex);
|
||||||
}, [isValidatingUpc, validatingUpcRows]);
|
}, [isValidatingUpc, validatingUpcRows]);
|
||||||
|
|
||||||
// Use upcValidationResults for display
|
// Use upcValidationResults for display, prioritizing the most recent values
|
||||||
const getRowUpcResult = useCallback((rowIndex: number) => {
|
const getRowUpcResult = useCallback((rowIndex: number) => {
|
||||||
return upcValidationResults?.get(rowIndex)?.itemNumber;
|
// ALWAYS get from the data array directly - most authoritative source
|
||||||
}, [upcValidationResults]);
|
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
|
// Memoize field columns with stable handlers
|
||||||
const fieldColumns = useMemo(() => fields.map((field): ColumnDef<RowData<T>, any> | null => {
|
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
|
// CRITICAL CHANGE: Get item number directly from row data first for item_number fields
|
||||||
let itemNumber = itemNumbers.get(row.index);
|
let itemNumber;
|
||||||
if (!itemNumber && fieldKey === 'item_number') {
|
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);
|
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 (
|
return (
|
||||||
<MemoizedCell
|
<MemoizedCell
|
||||||
|
key={cellKey} // CRITICAL: Add key to force re-render when itemNumber changes
|
||||||
field={fieldWithType as Field<string>}
|
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)}
|
onChange={(value) => handleFieldUpdate(row.index, field.key as T, value)}
|
||||||
errors={cellErrors}
|
errors={cellErrors}
|
||||||
isValidating={isLoading}
|
isValidating={isLoading}
|
||||||
|
|||||||
@@ -15,7 +15,8 @@ export const useTemplateManagement = <T extends string>(
|
|||||||
upcValidation: {
|
upcValidation: {
|
||||||
validateUpc: (rowIndex: number, supplierId: string, upcValue: string) => Promise<{success: boolean, itemNumber?: string}>,
|
validateUpc: (rowIndex: number, supplierId: string, upcValue: string) => Promise<{success: boolean, itemNumber?: string}>,
|
||||||
applyItemNumbersToData: (onApplied?: (updatedRowIds: number[]) => void) => void
|
applyItemNumbersToData: (onApplied?: (updatedRowIds: number[]) => void) => void
|
||||||
}
|
},
|
||||||
|
setValidatingCells?: React.Dispatch<React.SetStateAction<Set<string>>>
|
||||||
) => {
|
) => {
|
||||||
// Template state
|
// Template state
|
||||||
const [templates, setTemplates] = useState<Template[]>([]);
|
const [templates, setTemplates] = useState<Template[]>([]);
|
||||||
@@ -227,6 +228,12 @@ export const useTemplateManagement = <T extends string>(
|
|||||||
batchStatuses.set(index, "validated");
|
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
|
// Perform a single update for all rows
|
||||||
setData(newData);
|
setData(newData);
|
||||||
|
|
||||||
@@ -259,59 +266,180 @@ export const useTemplateManagement = <T extends string>(
|
|||||||
toast.success(`Template applied to ${validRowIndexes.length} rows`);
|
toast.success(`Template applied to ${validRowIndexes.length} rows`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check which rows need UPC validation
|
// Reset template application flag to allow validation
|
||||||
const upcValidationRows = validRowIndexes.filter((rowIndex) => {
|
isApplyingTemplateRef.current = false;
|
||||||
const row = newData[rowIndex];
|
|
||||||
return row && row.upc && row.supplier;
|
|
||||||
});
|
|
||||||
|
|
||||||
// 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) {
|
if (upcValidationRows.length > 0) {
|
||||||
console.log(
|
console.log(`Validating UPCs for ${upcValidationRows.length} rows after template application`);
|
||||||
`Validating UPCs for ${upcValidationRows.length} rows after template application`
|
|
||||||
);
|
|
||||||
|
|
||||||
// Schedule UPC validation for the next tick to allow UI to update first
|
// Process each row sequentially - this mimics the exact manual edit behavior
|
||||||
setTimeout(() => {
|
const processNextValidation = (index = 0) => {
|
||||||
// Track successful validations
|
if (index >= upcValidationRows.length) {
|
||||||
const validationPromises: Promise<{success: boolean, itemNumber?: string}>[] = [];
|
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;
|
||||||
});
|
});
|
||||||
|
|
||||||
// After all validations complete, apply item numbers
|
// Set loading state - using setValidatingCells from props
|
||||||
if (validationPromises.length > 0) {
|
if (setValidatingCells) {
|
||||||
Promise.all(validationPromises)
|
setValidatingCells(prev => {
|
||||||
.then(results => {
|
const newSet = new Set(prev);
|
||||||
const successCount = results.filter(r => r.success).length;
|
newSet.add(cellKey);
|
||||||
console.log(`${successCount}/${validationPromises.length} UPC validations succeeded`);
|
return newSet;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Apply item numbers to the data
|
// Validate UPC for this row
|
||||||
upcValidation.applyItemNumbersToData(updatedRowIds => {
|
upcValidation.validateUpc(rowIndex, row.supplier.toString(), row.upc.toString())
|
||||||
console.log(`Applied item numbers to ${updatedRowIds.length} rows`);
|
.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];
|
||||||
|
|
||||||
// After applying item numbers, trigger validation for those rows
|
// Update this specific row with the item number
|
||||||
updatedRowIds.forEach(rowIndex => {
|
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');
|
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 => {
|
.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;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, 100);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reset the template application flag
|
// Continue to next row despite error
|
||||||
isApplyingTemplateRef.current = false;
|
setTimeout(() => processNextValidation(index + 1), 100);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Skip this row and continue to the next
|
||||||
|
processNextValidation(index + 1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Start processing validations
|
||||||
|
processNextValidation();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
[
|
[
|
||||||
data,
|
data,
|
||||||
@@ -321,6 +449,7 @@ export const useTemplateManagement = <T extends string>(
|
|||||||
setRowValidationStatus,
|
setRowValidationStatus,
|
||||||
validateRow,
|
validateRow,
|
||||||
upcValidation,
|
upcValidation,
|
||||||
|
setValidatingCells
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -49,10 +49,40 @@ export const useUpcValidation = (
|
|||||||
|
|
||||||
// Update item number
|
// Update item number
|
||||||
const updateItemNumber = useCallback((rowIndex: number, itemNumber: string) => {
|
const updateItemNumber = useCallback((rowIndex: number, itemNumber: string) => {
|
||||||
console.log(`Setting item number for row ${rowIndex} to ${itemNumber}`);
|
// 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);
|
validationStateRef.current.itemNumbers.set(rowIndex, itemNumber);
|
||||||
setItemNumberUpdates(new Map(validationStateRef.current.itemNumbers));
|
|
||||||
}, []);
|
// 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
|
// Mark a row as being validated
|
||||||
const startValidatingRow = useCallback((rowIndex: number) => {
|
const startValidatingRow = useCallback((rowIndex: number) => {
|
||||||
@@ -132,11 +162,22 @@ export const useUpcValidation = (
|
|||||||
);
|
);
|
||||||
previousKeys.forEach(key => validationStateRef.current.activeValidations.delete(key));
|
previousKeys.forEach(key => validationStateRef.current.activeValidations.delete(key));
|
||||||
|
|
||||||
// Start validation - track this with the ref to avoid race conditions
|
// Log validation start to help debug template issues
|
||||||
startValidatingRow(rowIndex);
|
console.log(`[UPC-DEBUG] Starting UPC validation for row ${rowIndex} with supplier ${supplierId}, upc ${upcValue}`);
|
||||||
startValidatingCell(rowIndex, 'item_number');
|
|
||||||
|
|
||||||
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 {
|
try {
|
||||||
// Create a unique key for this validation to track it
|
// Create a unique key for this validation to track it
|
||||||
@@ -157,18 +198,43 @@ export const useUpcValidation = (
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Fetch the product by UPC
|
// Fetch the product by UPC
|
||||||
|
console.log(`[UPC-DEBUG] Fetching product data for UPC ${upcValue} with supplier ${supplierId}`);
|
||||||
const product = await fetchProductByUpc(supplierId, upcValue);
|
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)
|
// Check if this validation is still relevant (hasn't been superseded by another)
|
||||||
if (!validationStateRef.current.activeValidations.has(validationKey)) {
|
if (!validationStateRef.current.activeValidations.has(validationKey)) {
|
||||||
console.log(`Validation ${validationKey} was cancelled`);
|
console.log(`[UPC-DEBUG] Validation ${validationKey} was cancelled`);
|
||||||
return { success: false };
|
return { success: false };
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract the item number from the API response - check for !error since API returns { error: boolean, data: any }
|
// 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) {
|
if (product && !product.error && product.data?.itemNumber) {
|
||||||
// Store this validation result
|
console.log(`[UPC-DEBUG] Got item number for row ${rowIndex}: ${product.data.itemNumber}`);
|
||||||
updateItemNumber(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 {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
@@ -176,7 +242,7 @@ export const useUpcValidation = (
|
|||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
// No item number found but validation was still attempted
|
// 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
|
// Clear any existing item number to show validation was attempted and failed
|
||||||
if (validationStateRef.current.itemNumbers.has(rowIndex)) {
|
if (validationStateRef.current.itemNumbers.has(rowIndex)) {
|
||||||
@@ -187,58 +253,71 @@ export const useUpcValidation = (
|
|||||||
return { success: false };
|
return { success: false };
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error validating UPC:', error);
|
console.error('[UPC-DEBUG] Error validating UPC:', error);
|
||||||
return { success: false };
|
return { success: false };
|
||||||
} finally {
|
} finally {
|
||||||
// End validation
|
// End validation - FORCE UI update by using setState directly
|
||||||
stopValidatingRow(rowIndex);
|
console.log(`[UPC-DEBUG] Ending validation for row ${rowIndex}`);
|
||||||
stopValidatingCell(rowIndex, 'item_number');
|
|
||||||
|
validationStateRef.current.validatingRows.delete(rowIndex);
|
||||||
|
setValidatingRows(new Set(validationStateRef.current.validatingRows));
|
||||||
|
|
||||||
|
if (validationStateRef.current.validatingRows.size === 0) {
|
||||||
|
setIsValidatingUpc(false);
|
||||||
}
|
}
|
||||||
}, [fetchProductByUpc, updateItemNumber, startValidatingCell, stopValidatingCell, startValidatingRow, stopValidatingRow, setData]);
|
|
||||||
|
|
||||||
// Apply item numbers to data
|
validationStateRef.current.validatingCells.delete(cellKey);
|
||||||
const applyItemNumbersToData = useCallback((onApplied?: (updatedRowIds: number[]) => void) => {
|
setValidatingCellKeys(new Set(validationStateRef.current.validatingCells));
|
||||||
// Create a copy of the current item numbers map to avoid race conditions
|
|
||||||
const currentItemNumbers = new Map(validationStateRef.current.itemNumbers);
|
|
||||||
|
|
||||||
// Only apply if we have any item numbers
|
console.log(`[UPC-DEBUG] Cleared loading state for row ${rowIndex}`);
|
||||||
if (currentItemNumbers.size === 0) return;
|
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, setData]);
|
||||||
|
|
||||||
// Track updated row indices to pass to callback
|
// Apply all pending item numbers to the data state
|
||||||
const updatedRowIndices: number[] = [];
|
const applyItemNumbersToData = useCallback((callback?: (updatedRows: number[]) => void) => {
|
||||||
|
// Skip if we have nothing to apply
|
||||||
|
if (validationStateRef.current.itemNumbers.size === 0) {
|
||||||
|
if (callback) callback([]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Log for debugging
|
// Gather all row IDs that will be updated
|
||||||
console.log(`Applying ${currentItemNumbers.size} item numbers to data`);
|
const rowIds: number[] = [];
|
||||||
|
|
||||||
|
// Update the data state with all item numbers
|
||||||
setData(prevData => {
|
setData(prevData => {
|
||||||
// Create a new copy of the data
|
|
||||||
const newData = [...prevData];
|
const newData = [...prevData];
|
||||||
|
|
||||||
// Update each row with its item number without affecting other fields
|
// Apply each item number to the data
|
||||||
currentItemNumbers.forEach((itemNumber, rowIndex) => {
|
validationStateRef.current.itemNumbers.forEach((itemNumber, rowIndex) => {
|
||||||
if (rowIndex < newData.length) {
|
// Ensure row exists and value has actually changed
|
||||||
console.log(`Setting item_number for row ${rowIndex} to ${itemNumber}`);
|
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] = {
|
||||||
...newData[rowIndex],
|
...newData[rowIndex],
|
||||||
item_number: itemNumber
|
item_number: itemNumber
|
||||||
};
|
};
|
||||||
|
|
||||||
// Track which rows were updated
|
// Track which row was updated for the callback
|
||||||
updatedRowIndices.push(rowIndex);
|
rowIds.push(rowIndex);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return newData;
|
return newData;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Call the callback if provided, after state updates are processed
|
// Force a re-render by updating React state
|
||||||
if (onApplied && updatedRowIndices.length > 0) {
|
|
||||||
// Use setTimeout to ensure this happens after the state update
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
onApplied(updatedRowIndices);
|
setItemNumberUpdates(new Map(validationStateRef.current.itemNumbers));
|
||||||
}, 100); // Use 100ms to ensure the data update is fully processed
|
}, 0);
|
||||||
|
|
||||||
|
// Call the callback with the updated row IDs
|
||||||
|
if (callback) {
|
||||||
|
callback(rowIds);
|
||||||
}
|
}
|
||||||
}, [setData]);
|
}, [setData]);
|
||||||
|
|
||||||
@@ -405,6 +484,9 @@ export const useUpcValidation = (
|
|||||||
getItemNumber,
|
getItemNumber,
|
||||||
applyItemNumbersToData,
|
applyItemNumbersToData,
|
||||||
|
|
||||||
|
// CRITICAL: Expose the itemNumbers map directly
|
||||||
|
itemNumbers: validationStateRef.current.itemNumbers,
|
||||||
|
|
||||||
// Initialization state
|
// Initialization state
|
||||||
initialValidationDone: initialUpcValidationDoneRef.current
|
initialValidationDone: initialUpcValidationDoneRef.current
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -88,6 +88,9 @@ export const useValidationState = <T extends string>({
|
|||||||
Map<number, "pending" | "validating" | "validated" | "error">
|
Map<number, "pending" | "validating" | "validated" | "error">
|
||||||
>(new Map());
|
>(new Map());
|
||||||
|
|
||||||
|
// Add state for tracking cells in loading state
|
||||||
|
const [validatingCells, setValidatingCells] = useState<Set<string>>(new Set());
|
||||||
|
|
||||||
const initialValidationDoneRef = useRef(false);
|
const initialValidationDoneRef = useRef(false);
|
||||||
const isValidatingRef = useRef(false);
|
const isValidatingRef = useRef(false);
|
||||||
|
|
||||||
@@ -119,7 +122,8 @@ export const useValidationState = <T extends string>({
|
|||||||
setRowValidationStatus,
|
setRowValidationStatus,
|
||||||
validateRow,
|
validateRow,
|
||||||
isApplyingTemplateRef,
|
isApplyingTemplateRef,
|
||||||
upcValidation
|
upcValidation,
|
||||||
|
setValidatingCells
|
||||||
);
|
);
|
||||||
|
|
||||||
// Use filter management hook
|
// Use filter management hook
|
||||||
@@ -673,6 +677,10 @@ export const useValidationState = <T extends string>({
|
|||||||
validateRow,
|
validateRow,
|
||||||
hasErrors,
|
hasErrors,
|
||||||
|
|
||||||
|
// CRITICAL: Export validatingCells to make it available to ValidationContainer
|
||||||
|
validatingCells,
|
||||||
|
setValidatingCells,
|
||||||
|
|
||||||
// Row selection
|
// Row selection
|
||||||
rowSelection,
|
rowSelection,
|
||||||
setRowSelection,
|
setRowSelection,
|
||||||
|
|||||||
Reference in New Issue
Block a user