Fix applying templates to or discarding multiple rows

This commit is contained in:
2025-03-07 16:16:57 -05:00
parent b15387041b
commit 875d0b8f55
4 changed files with 138 additions and 64 deletions

View File

@@ -52,17 +52,6 @@ const SearchableTemplateSelect: React.FC<SearchableTemplateSelectProps> = ({
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const [] = useState<string | null>(null); const [] = useState<string | null>(null);
// Debug logging
useEffect(() => {
// Only log when selectedBrand changes or on mount
console.debug('SearchableTemplateSelect brand update:', {
selectedBrand,
defaultBrand,
templatesCount: templates?.length || 0,
templatesForBrand: templates?.filter(t => t.company === selectedBrand)?.length || 0
});
}, [selectedBrand, defaultBrand, templates]);
// Set default brand when component mounts or defaultBrand changes // Set default brand when component mounts or defaultBrand changes
useEffect(() => { useEffect(() => {
if (defaultBrand) { if (defaultBrand) {

View File

@@ -54,7 +54,8 @@ const ValidationContainer = <T extends string>({
loadTemplates, loadTemplates,
setData, setData,
fields, fields,
isLoadingTemplates isLoadingTemplates,
rowValidationStatus
} = validationState } = validationState
// Add state for tracking product lines and sublines per row // Add state for tracking product lines and sublines per row
@@ -563,16 +564,37 @@ const ValidationContainer = <T extends string>({
}, [onNext, data, applyItemNumbersToData]); }, [onNext, data, applyItemNumbersToData]);
const deleteSelectedRows = useCallback(() => { const deleteSelectedRows = useCallback(() => {
// Get selected row indices // Get selected row keys (which may be UUIDs)
const selectedRowIndexes = Object.keys(rowSelection).map(Number); const selectedKeys = Object.entries(rowSelection)
.filter(([_, selected]) => selected === true)
.map(([key, _]) => key);
if (selectedRowIndexes.length === 0) { console.log('Selected row keys for deletion:', selectedKeys);
if (selectedKeys.length === 0) {
toast.error("No rows selected"); toast.error("No rows selected");
return; return;
} }
// Map UUID keys to array indices
const selectedIndices = selectedKeys.map(key => {
// Find the matching row index in the data array
const index = data.findIndex(row =>
(row.__index && row.__index === key) || // Match by __index
(String(data.indexOf(row)) === key) // Or by numeric index
);
return index;
}).filter(index => index !== -1); // Filter out any not found
console.log('Mapped row indices for deletion:', selectedIndices);
if (selectedIndices.length === 0) {
toast.error('Could not find selected rows');
return;
}
// Sort indices in descending order to avoid index shifting during removal // Sort indices in descending order to avoid index shifting during removal
const sortedIndices = [...selectedRowIndexes].sort((a, b) => b - a); const sortedIndices = [...selectedIndices].sort((a, b) => b - a);
// Create a new array without the selected rows // Create a new array without the selected rows
const newData = [...data]; const newData = [...data];
@@ -592,9 +614,9 @@ const ValidationContainer = <T extends string>({
// Show success message // Show success message
toast.success( toast.success(
selectedRowIndexes.length === 1 selectedIndices.length === 1
? "Row deleted" ? "Row deleted"
: `${selectedRowIndexes.length} rows deleted` : `${selectedIndices.length} rows deleted`
); );
// Reindex the data in the next render cycle // Reindex the data in the next render cycle
@@ -829,7 +851,11 @@ const ValidationContainer = <T extends string>({
<Button <Button
variant={isFromScratch ? "destructive" : "outline"} variant={isFromScratch ? "destructive" : "outline"}
size="sm" size="sm"
onClick={deleteSelectedRows} onClick={() => {
console.log('Delete/Discard button clicked');
console.log('Row selection state:', rowSelection);
deleteSelectedRows();
}}
> >
{isFromScratch ? "Delete Row" : translations.validationStep.discardButtonTitle || "Discard Row"} {isFromScratch ? "Delete Row" : translations.validationStep.discardButtonTitle || "Discard Row"}
</Button> </Button>

View File

@@ -350,6 +350,10 @@ const ValidationTable = <T extends string>({
cell: ({ row }) => { cell: ({ row }) => {
const templateValue = row.original.__template || null; const templateValue = row.original.__template || null;
const defaultBrand = row.original.company || undefined; const defaultBrand = row.original.company || undefined;
const rowIndex = data.findIndex(r => r === row.original);
console.log(`Template cell for row ${row.id}, index ${rowIndex}`);
return ( return (
<TableCell className="p-1" style={{ width: '200px', minWidth: '200px' }}> <TableCell className="p-1" style={{ width: '200px', minWidth: '200px' }}>
{isLoadingTemplates ? ( {isLoadingTemplates ? (
@@ -362,7 +366,7 @@ const ValidationTable = <T extends string>({
templates={templates} templates={templates}
value={templateValue || ''} value={templateValue || ''}
onValueChange={(value) => { onValueChange={(value) => {
applyTemplate(value, [row.index]); applyTemplate(value, [rowIndex]);
}} }}
getTemplateDisplayText={getTemplateDisplayText} getTemplateDisplayText={getTemplateDisplayText}
defaultBrand={defaultBrand} defaultBrand={defaultBrand}
@@ -371,7 +375,7 @@ const ValidationTable = <T extends string>({
</TableCell> </TableCell>
); );
} }
}), [templates, applyTemplate, getTemplateDisplayText, isLoadingTemplates]); }), [templates, applyTemplate, getTemplateDisplayText, isLoadingTemplates, data]);
// Memoize field columns // Memoize field columns
const fieldColumns = useMemo(() => fields.map((field): ColumnDef<RowData<T>, any> | null => { const fieldColumns = useMemo(() => fields.map((field): ColumnDef<RowData<T>, any> | null => {
@@ -421,9 +425,26 @@ const ValidationTable = <T extends string>({
state: { rowSelection }, state: { rowSelection },
enableRowSelection: true, enableRowSelection: true,
onRowSelectionChange: setRowSelection, onRowSelectionChange: setRowSelection,
getRowId: (row) => row.__index || String(row.index) getRowId: (row) => {
// Prefer __index if available (likely a UUID)
if (row.__index) return row.__index;
// Fall back to position in array
const index = data.indexOf(row);
return String(index);
}
}); });
// Log selection changes for debugging
useEffect(() => {
const selectedCount = Object.values(rowSelection).filter(v => v === true).length;
const selectedIds = Object.entries(rowSelection)
.filter(([_, selected]) => selected === true)
.map(([id, _]) => id);
console.log(`Row selection updated: ${selectedCount} rows selected, IDs:`, selectedIds);
}, [rowSelection]);
// Don't render if no data // Don't render if no data
if (data.length === 0) { if (data.length === 0) {
return ( return (

View File

@@ -980,6 +980,25 @@ useEffect(() => {
return; return;
} }
console.log(`Applying template ${templateId} to rows:`, rowIndexes);
console.log(`Template data:`, template);
// Validate row indexes
const validRowIndexes = rowIndexes.filter(index =>
index >= 0 && index < data.length && Number.isInteger(index)
);
if (validRowIndexes.length === 0) {
toast.error('No valid rows to update');
console.error('Invalid row indexes:', rowIndexes);
return;
}
if (validRowIndexes.length !== rowIndexes.length) {
console.warn('Some row indexes were invalid and will be skipped:',
rowIndexes.filter(idx => !validRowIndexes.includes(idx)));
}
// Set the template application flag // Set the template application flag
isApplyingTemplateRef.current = true; isApplyingTemplateRef.current = true;
@@ -989,51 +1008,49 @@ useEffect(() => {
top: window.scrollY top: window.scrollY
}; };
// Track updated rows // Track updated rows for UPC validation
const updatedRows: number[] = []; const updatedRows: number[] = [];
// Apply template to data - capture the updated data to use for validation // Create a copy of the data to track updates
let updatedData: RowData<T>[] = []; const newData = [...data];
setData(prevData => { // Apply template to each valid row
const newData = [...prevData]; validRowIndexes.forEach(index => {
// Create a new row with template values
const originalRow = newData[index];
console.log(`Applying to row at index ${index}:`, originalRow);
rowIndexes.forEach(index => { const updatedRow = { ...originalRow };
if (index >= 0 && index < newData.length) {
// Create a new row with template values // Clear existing errors
const updatedRow = { ...newData[index] }; delete updatedRow.__errors;
// Clear existing errors and validation status // Apply template fields (excluding metadata fields)
delete updatedRow.__errors; Object.entries(template).forEach(([key, value]) => {
if (!['id', '__errors', '__meta', '__template', '__original', '__corrected', '__changes'].includes(key)) {
// Apply template fields (excluding metadata fields) (updatedRow as any)[key] = value;
Object.entries(template).forEach(([key, value]) => {
if (!['id', '__errors', '__meta', '__template', '__original', '__corrected', '__changes'].includes(key)) {
(updatedRow as any)[key] = value;
}
});
// Mark the row as using this template
updatedRow.__template = templateId;
// Update the row in the data array
newData[index] = updatedRow;
// Track which rows were updated
updatedRows.push(index);
} }
}); });
// Store the updated data for validation // Mark the row as using this template
updatedData = [...newData]; updatedRow.__template = templateId;
return newData; // Update the row in the data array
newData[index] = updatedRow;
// Track which rows were updated
updatedRows.push(index);
console.log(`Row ${index} updated:`, updatedRow);
}); });
// Update all data at once
setData(newData);
// Clear validation errors and status for affected rows // Clear validation errors and status for affected rows
setValidationErrors(prev => { setValidationErrors(prev => {
const newErrors = new Map(prev); const newErrors = new Map(prev);
rowIndexes.forEach(index => { validRowIndexes.forEach(index => {
newErrors.delete(index); newErrors.delete(index);
}); });
return newErrors; return newErrors;
@@ -1041,7 +1058,7 @@ useEffect(() => {
setRowValidationStatus(prev => { setRowValidationStatus(prev => {
const newStatus = new Map(prev); const newStatus = new Map(prev);
rowIndexes.forEach(index => { validRowIndexes.forEach(index => {
newStatus.set(index, 'validated'); // Mark as validated immediately newStatus.set(index, 'validated'); // Mark as validated immediately
}); });
return newStatus; return newStatus;
@@ -1053,10 +1070,10 @@ useEffect(() => {
}); });
// Show success toast // Show success toast
if (rowIndexes.length === 1) { if (validRowIndexes.length === 1) {
toast.success('Template applied'); toast.success('Template applied');
} else if (rowIndexes.length > 1) { } else if (validRowIndexes.length > 1) {
toast.success(`Template applied to ${rowIndexes.length} rows`); toast.success(`Template applied to ${validRowIndexes.length} rows`);
} }
// Schedule UPC validation with a delay // Schedule UPC validation with a delay
@@ -1065,7 +1082,7 @@ useEffect(() => {
const processRows = async () => { const processRows = async () => {
for (const rowIndex of updatedRows) { for (const rowIndex of updatedRows) {
// Get the current row data after template application // Get the current row data after template application
const currentRow = updatedData[rowIndex]; const currentRow = newData[rowIndex];
// Check if UPC validation is needed // Check if UPC validation is needed
if (currentRow && currentRow.upc && currentRow.supplier) { if (currentRow && currentRow.upc && currentRow.supplier) {
@@ -1085,7 +1102,7 @@ useEffect(() => {
// Start processing rows // Start processing rows
processRows(); processRows();
}, 500); }, 500);
}, [templates, validateUpc, setData, setValidationErrors, setRowValidationStatus]); }, [data, templates, validateUpc, setData, setValidationErrors, setRowValidationStatus]);
// Apply template to selected rows // Apply template to selected rows
const applyTemplateToSelected = useCallback((templateId: string) => { const applyTemplateToSelected = useCallback((templateId: string) => {
@@ -1097,17 +1114,38 @@ useEffect(() => {
selectedTemplateId: templateId selectedTemplateId: templateId
})); }));
// Get selected row indexes // Get selected row keys (which may be UUIDs)
const selectedIndexes = Object.keys(rowSelection).map(i => parseInt(i)); const selectedKeys = Object.entries(rowSelection)
.filter(([_, selected]) => selected === true)
.map(([key, _]) => key);
console.log('Selected row keys:', selectedKeys);
if (selectedKeys.length === 0) {
toast.error('No rows selected');
return;
}
// Map UUID keys to array indices
const selectedIndexes = selectedKeys.map(key => {
// Find the matching row index in the data array
const index = data.findIndex(row =>
(row.__index && row.__index === key) || // Match by __index
(String(data.indexOf(row)) === key) // Or by numeric index
);
return index;
}).filter(index => index !== -1); // Filter out any not found
console.log('Mapped row indices:', selectedIndexes);
if (selectedIndexes.length === 0) { if (selectedIndexes.length === 0) {
toast.error('No rows selected'); toast.error('Could not find selected rows');
return; return;
} }
// Apply template to selected rows // Apply template to selected rows
applyTemplate(templateId, selectedIndexes); applyTemplate(templateId, selectedIndexes);
}, [rowSelection, applyTemplate, setTemplateState]); }, [rowSelection, applyTemplate, setTemplateState, data]);
// Add field options query // Add field options query
const { data: fieldOptionsData } = useQuery({ const { data: fieldOptionsData } = useQuery({