From 875d0b8f55b7290aea5740bc5536319823dba2cf Mon Sep 17 00:00:00 2001 From: Matt Date: Fri, 7 Mar 2025 16:16:57 -0500 Subject: [PATCH] Fix applying templates to or discarding multiple rows --- .../components/SearchableTemplateSelect.tsx | 11 -- .../components/ValidationContainer.tsx | 42 ++++-- .../components/ValidationTable.tsx | 27 +++- .../hooks/useValidationState.tsx | 122 ++++++++++++------ 4 files changed, 138 insertions(+), 64 deletions(-) diff --git a/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/components/SearchableTemplateSelect.tsx b/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/components/SearchableTemplateSelect.tsx index cc09e15..e867565 100644 --- a/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/components/SearchableTemplateSelect.tsx +++ b/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/components/SearchableTemplateSelect.tsx @@ -52,17 +52,6 @@ const SearchableTemplateSelect: React.FC = ({ const [open, setOpen] = useState(false); const [] = useState(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 useEffect(() => { if (defaultBrand) { diff --git a/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/components/ValidationContainer.tsx b/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/components/ValidationContainer.tsx index 1f578fb..b158483 100644 --- a/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/components/ValidationContainer.tsx +++ b/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/components/ValidationContainer.tsx @@ -54,7 +54,8 @@ const ValidationContainer = ({ loadTemplates, setData, fields, - isLoadingTemplates + isLoadingTemplates, + rowValidationStatus } = validationState // Add state for tracking product lines and sublines per row @@ -563,16 +564,37 @@ const ValidationContainer = ({ }, [onNext, data, applyItemNumbersToData]); const deleteSelectedRows = useCallback(() => { - // Get selected row indices - const selectedRowIndexes = Object.keys(rowSelection).map(Number); + // Get selected row keys (which may be UUIDs) + 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"); 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 - const sortedIndices = [...selectedRowIndexes].sort((a, b) => b - a); + const sortedIndices = [...selectedIndices].sort((a, b) => b - a); // Create a new array without the selected rows const newData = [...data]; @@ -592,9 +614,9 @@ const ValidationContainer = ({ // Show success message toast.success( - selectedRowIndexes.length === 1 + selectedIndices.length === 1 ? "Row deleted" - : `${selectedRowIndexes.length} rows deleted` + : `${selectedIndices.length} rows deleted` ); // Reindex the data in the next render cycle @@ -829,7 +851,11 @@ const ValidationContainer = ({ diff --git a/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/components/ValidationTable.tsx b/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/components/ValidationTable.tsx index b645979..2b06598 100644 --- a/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/components/ValidationTable.tsx +++ b/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/components/ValidationTable.tsx @@ -350,6 +350,10 @@ const ValidationTable = ({ cell: ({ row }) => { const templateValue = row.original.__template || null; 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 ( {isLoadingTemplates ? ( @@ -362,7 +366,7 @@ const ValidationTable = ({ templates={templates} value={templateValue || ''} onValueChange={(value) => { - applyTemplate(value, [row.index]); + applyTemplate(value, [rowIndex]); }} getTemplateDisplayText={getTemplateDisplayText} defaultBrand={defaultBrand} @@ -371,7 +375,7 @@ const ValidationTable = ({ ); } - }), [templates, applyTemplate, getTemplateDisplayText, isLoadingTemplates]); + }), [templates, applyTemplate, getTemplateDisplayText, isLoadingTemplates, data]); // Memoize field columns const fieldColumns = useMemo(() => fields.map((field): ColumnDef, any> | null => { @@ -421,9 +425,26 @@ const ValidationTable = ({ state: { rowSelection }, enableRowSelection: true, 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 if (data.length === 0) { return ( diff --git a/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/hooks/useValidationState.tsx b/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/hooks/useValidationState.tsx index c853bf4..381caf5 100644 --- a/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/hooks/useValidationState.tsx +++ b/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/hooks/useValidationState.tsx @@ -980,6 +980,25 @@ useEffect(() => { 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 isApplyingTemplateRef.current = true; @@ -989,51 +1008,49 @@ useEffect(() => { top: window.scrollY }; - // Track updated rows + // Track updated rows for UPC validation const updatedRows: number[] = []; - // Apply template to data - capture the updated data to use for validation - let updatedData: RowData[] = []; + // Create a copy of the data to track updates + const newData = [...data]; - setData(prevData => { - const newData = [...prevData]; + // Apply template to each valid row + 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 => { - if (index >= 0 && index < newData.length) { - // Create a new row with template values - const updatedRow = { ...newData[index] }; - - // Clear existing errors and validation status - delete updatedRow.__errors; - - // Apply template fields (excluding metadata fields) - 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); + const updatedRow = { ...originalRow }; + + // Clear existing errors + delete updatedRow.__errors; + + // Apply template fields (excluding metadata fields) + Object.entries(template).forEach(([key, value]) => { + if (!['id', '__errors', '__meta', '__template', '__original', '__corrected', '__changes'].includes(key)) { + (updatedRow as any)[key] = value; } }); - // Store the updated data for validation - updatedData = [...newData]; + // Mark the row as using this template + 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 setValidationErrors(prev => { const newErrors = new Map(prev); - rowIndexes.forEach(index => { + validRowIndexes.forEach(index => { newErrors.delete(index); }); return newErrors; @@ -1041,7 +1058,7 @@ useEffect(() => { setRowValidationStatus(prev => { const newStatus = new Map(prev); - rowIndexes.forEach(index => { + validRowIndexes.forEach(index => { newStatus.set(index, 'validated'); // Mark as validated immediately }); return newStatus; @@ -1053,10 +1070,10 @@ useEffect(() => { }); // Show success toast - if (rowIndexes.length === 1) { + if (validRowIndexes.length === 1) { toast.success('Template applied'); - } else if (rowIndexes.length > 1) { - toast.success(`Template applied to ${rowIndexes.length} rows`); + } else if (validRowIndexes.length > 1) { + toast.success(`Template applied to ${validRowIndexes.length} rows`); } // Schedule UPC validation with a delay @@ -1065,7 +1082,7 @@ useEffect(() => { const processRows = async () => { for (const rowIndex of updatedRows) { // Get the current row data after template application - const currentRow = updatedData[rowIndex]; + const currentRow = newData[rowIndex]; // Check if UPC validation is needed if (currentRow && currentRow.upc && currentRow.supplier) { @@ -1085,7 +1102,7 @@ useEffect(() => { // Start processing rows processRows(); }, 500); - }, [templates, validateUpc, setData, setValidationErrors, setRowValidationStatus]); + }, [data, templates, validateUpc, setData, setValidationErrors, setRowValidationStatus]); // Apply template to selected rows const applyTemplateToSelected = useCallback((templateId: string) => { @@ -1097,17 +1114,38 @@ useEffect(() => { selectedTemplateId: templateId })); - // Get selected row indexes - const selectedIndexes = Object.keys(rowSelection).map(i => parseInt(i)); + // Get selected row keys (which may be UUIDs) + 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) { - toast.error('No rows selected'); + toast.error('Could not find selected rows'); return; } // Apply template to selected rows applyTemplate(templateId, selectedIndexes); - }, [rowSelection, applyTemplate, setTemplateState]); + }, [rowSelection, applyTemplate, setTemplateState, data]); // Add field options query const { data: fieldOptionsData } = useQuery({