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 d315b5c..5bcfdb2 100644 --- a/inventory/src/components/product-import/steps/ValidationStepNew/components/ValidationCell.tsx +++ b/inventory/src/components/product-import/steps/ValidationStepNew/components/ValidationCell.tsx @@ -11,7 +11,6 @@ import InputCell from './cells/InputCell' import SelectCell from './cells/SelectCell' import MultiSelectCell from './cells/MultiSelectCell' import { TableCell } from '@/components/ui/table' -import { Skeleton } from '@/components/ui/skeleton' // Context for copy down selection mode export const CopyDownContext = React.createContext<{ @@ -80,7 +79,8 @@ const BaseCellContent = React.memo(({ className = '', fieldKey = '', onStartEdit, - onEndEdit + onEndEdit, + isValidating }: { field: Field; value: any; @@ -91,6 +91,7 @@ const BaseCellContent = React.memo(({ fieldKey?: string; onStartEdit?: () => void; onEndEdit?: () => void; + isValidating?: boolean; }) => { // Get field type information const fieldType = fieldKey === 'line' || fieldKey === 'subline' @@ -123,6 +124,7 @@ const BaseCellContent = React.memo(({ hasErrors={hasErrors} className={className} disabled={field.disabled} + isValidating={isValidating} /> ); } @@ -139,6 +141,7 @@ const BaseCellContent = React.memo(({ hasErrors={hasErrors} className={className} disabled={field.disabled} + isValidating={isValidating} /> ); } @@ -155,6 +158,7 @@ const BaseCellContent = React.memo(({ hasErrors={hasErrors} className={className} disabled={field.disabled} + isValidating={isValidating} /> ); } @@ -170,6 +174,7 @@ const BaseCellContent = React.memo(({ isMultiline={isMultiline} isPrice={isPrice} disabled={field.disabled} + isValidating={isValidating} /> ); }, (prev, next) => { @@ -466,35 +471,33 @@ const ValidationCell = React.memo(({ )} - {isLoading ? ( -
- -
- ) : ( -
- -
- )} +
+ + {isLoading && ( + + )} +
); 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 73caad1..144715b 100644 --- a/inventory/src/components/product-import/steps/ValidationStepNew/components/ValidationTable.tsx +++ b/inventory/src/components/product-import/steps/ValidationStepNew/components/ValidationTable.tsx @@ -1,4 +1,4 @@ -import React, { useMemo, useCallback, useState } from 'react' +import React, { useMemo, useCallback, useState, useEffect, useRef } from 'react' import { useReactTable, getCoreRowModel, @@ -6,6 +6,7 @@ import { RowSelectionState, ColumnDef } from '@tanstack/react-table' +import { useVirtualizer } from '@tanstack/react-virtual' import { Fields, Field } from '../../../types' import { RowData, Template } from '../hooks/validationTypes' import ValidationCell, { CopyDownContext } from './ValidationCell' @@ -193,6 +194,14 @@ const ValidationTable = ({ upcValidationResults }: ValidationTableProps) => { const { translations } = useRsi(); + const tableRootRef = useRef(null); + const getScrollElement = useCallback(() => tableRootRef.current?.parentElement ?? null, []); + const [, forceRerender] = useState(0); + + useEffect(() => { + if (!tableRootRef.current) return; + forceRerender((value) => value + 1); + }, []); // Add state for copy down selection mode const [isInCopyDownMode, setIsInCopyDownMode] = useState(false); @@ -392,7 +401,10 @@ const ValidationTable = ({ } else if (fieldKey === 'subline' && rowId && rowSublines[rowId]) { options = rowSublines[rowId]; } - + + const validatingKey = `${row.index}-${fieldKey}`; + const isCellValidating = validatingCells.has(validatingKey); + // Get the current cell value first const currentValue = fieldKey === 'item_number' && row.original[field.key] ? row.original[field.key] @@ -471,7 +483,7 @@ const ValidationTable = ({ value={currentValue} onChange={(value) => handleFieldUpdate(row.index, field.key as T, value)} errors={cellErrors} - isValidating={isLoading} + isValidating={isLoading || isCellValidating} fieldKey={fieldKey} options={options} itemNumber={itemNumber} @@ -505,6 +517,47 @@ const ValidationTable = ({ getRowId: useCallback((_row: RowData, index: number) => String(index), []), }); + const rowModel = table.getRowModel(); + const rows = rowModel.rows; + const visibleColumnCount = table.getVisibleFlatColumns().length; + + const rowVirtualizer = useVirtualizer({ + count: rows.length, + getScrollElement, + estimateSize: () => 66, + overscan: 8, + measureElement: + typeof window !== 'undefined' + ? (el: Element | null) => el?.getBoundingClientRect().height || 0 + : undefined, + }); + + const scrollElement = getScrollElement(); + + const virtualRows = scrollElement + ? rowVirtualizer.getVirtualItems() + : rows.map((_, index) => ({ + index, + key: `row-fallback-${index}`, + start: 0, + end: 0, + size: 0, + })); + const paddingTop = scrollElement && virtualRows.length > 0 ? virtualRows[0].start : 0; + const paddingBottom = + scrollElement && virtualRows.length > 0 + ? rowVirtualizer.getTotalSize() - virtualRows[virtualRows.length - 1].end + : 0; + + const measureVirtualRow = useCallback( + (node: HTMLTableRowElement | null) => { + const scrollEl = getScrollElement(); + if (!scrollEl || !node) return; + rowVirtualizer.measureElement(node); + }, + [getScrollElement, rowVirtualizer] + ); + // Calculate total table width for stable horizontal scrolling const totalWidth = useMemo(() => { return columns.reduce((total, col) => total + (col.size || 0), 0); @@ -525,7 +578,7 @@ const ValidationTable = ({ return ( -
+
{/* Add global styles for copy down mode */} {isInCopyDownMode && (