Restore line and subline fields

This commit is contained in:
2025-03-15 18:50:33 -04:00
parent 97fa7f3495
commit cb46970808
6 changed files with 549 additions and 45 deletions

View File

@@ -99,6 +99,8 @@ const BaseCellContent = React.memo(({
(field.fieldType.type === 'input' || field.fieldType.type === 'multi-input') &&
field.fieldType.price === true;
console.log(`BaseCellContent: field.key=${field.key}, fieldType=${fieldType}, disabled=${field.disabled}, options=`, options);
if (fieldType === 'select') {
return (
<SelectCell
@@ -108,6 +110,7 @@ const BaseCellContent = React.memo(({
options={options}
hasErrors={hasErrors}
className={className}
disabled={field.disabled}
/>
);
}
@@ -121,6 +124,7 @@ const BaseCellContent = React.memo(({
options={options}
hasErrors={hasErrors}
className={className}
disabled={field.disabled}
/>
);
}
@@ -133,6 +137,7 @@ const BaseCellContent = React.memo(({
hasErrors={hasErrors}
isMultiline={isMultiline}
isPrice={isPrice}
disabled={field.disabled}
/>
);
}, (prev, next) => {
@@ -605,6 +610,8 @@ const ValidationCell = ({
</div>
) : (
<div className={`truncate overflow-hidden ${isCopyDownHovered && !copyDownContext.isInCopyDownMode ? 'bg-blue-50/50' : ''}`}>
{/* Log options for debugging */}
{(() => { console.log(`ValidationCell: fieldKey=${fieldKey}, options=`, options); return null; })()}
<BaseCellContent
field={field}
value={value}

View File

@@ -119,6 +119,8 @@ const ValidationContainer = <T extends string>({
// Only fetch if we have a valid company ID
if (!companyId) return;
console.log(`Fetching product lines for row ${rowIndex}, company ${companyId}`);
// Set loading state for this row
setIsLoadingLines(prev => ({ ...prev, [rowIndex]: true }));
@@ -129,6 +131,7 @@ const ValidationContainer = <T extends string>({
}
const productLines = await response.json();
console.log(`Received product lines for row ${rowIndex}:`, productLines);
// Store the product lines for this specific row
setRowProductLines(prev => ({ ...prev, [rowIndex]: productLines }));
@@ -148,6 +151,8 @@ const ValidationContainer = <T extends string>({
// Only fetch if we have a valid line ID
if (!lineId) return;
console.log(`Fetching sublines for row ${rowIndex}, line ${lineId}`);
// Set loading state for this row
setIsLoadingSublines(prev => ({ ...prev, [rowIndex]: true }));
@@ -158,6 +163,7 @@ const ValidationContainer = <T extends string>({
}
const sublines = await response.json();
console.log(`Received sublines for row ${rowIndex}:`, sublines);
// Store the sublines for this specific row
setRowSublines(prev => ({ ...prev, [rowIndex]: sublines }));
@@ -365,6 +371,7 @@ const ValidationContainer = <T extends string>({
// Enhanced updateRow function - memoized
const enhancedUpdateRow = useCallback(async (rowIndex: number, fieldKey: T, value: any) => {
// Process value before updating data
console.log(`enhancedUpdateRow called: rowIndex=${rowIndex}, fieldKey=${fieldKey}, value=`, value);
let processedValue = value;
// Strip dollar signs from price fields
@@ -921,9 +928,13 @@ const ValidationContainer = <T extends string>({
itemNumbers={itemNumbersMap}
isLoadingTemplates={isLoadingTemplates}
copyDown={handleCopyDown}
rowProductLines={rowProductLines}
rowSublines={rowSublines}
isLoadingLines={isLoadingLines}
isLoadingSublines={isLoadingSublines}
/>
);
}), [validatingUpcRows, itemNumbers, isLoadingTemplates, handleCopyDown, validatingCells]);
}), [validatingUpcRows, itemNumbers, isLoadingTemplates, handleCopyDown, validatingCells, rowProductLines, rowSublines, isLoadingLines, isLoadingSublines]);
// Memoize the rendered validation table
const renderValidationTable = useMemo(() => (
@@ -945,6 +956,10 @@ const ValidationContainer = <T extends string>({
isLoadingTemplates={isLoadingTemplates}
copyDown={handleCopyDown}
upcValidationResults={new Map()}
rowProductLines={rowProductLines}
rowSublines={rowSublines}
isLoadingLines={isLoadingLines}
isLoadingSublines={isLoadingSublines}
/>
), [
EnhancedValidationTable,
@@ -961,7 +976,11 @@ const ValidationContainer = <T extends string>({
applyTemplate,
getTemplateDisplayText,
isLoadingTemplates,
handleCopyDown
handleCopyDown,
rowProductLines,
rowSublines,
isLoadingLines,
isLoadingSublines
]);
// Add scroll container ref at the container level

View File

@@ -166,10 +166,18 @@ const ValidationTable = <T extends string>({
validatingCells,
itemNumbers,
isLoadingTemplates = false,
copyDown
copyDown,
rowProductLines = {},
rowSublines = {},
isLoadingLines = {},
isLoadingSublines = {}
}: ValidationTableProps<T>) => {
const { translations } = useRsi<T>();
// Debug logs
console.log('ValidationTable rowProductLines:', rowProductLines);
console.log('ValidationTable rowSublines:', rowSublines);
// Add state for copy down selection mode
const [isInCopyDownMode, setIsInCopyDownMode] = useState(false);
const [sourceRowIndex, setSourceRowIndex] = useState<number | null>(null);
@@ -285,7 +293,7 @@ const ValidationTable = <T extends string>({
const cache = new Map<string, readonly any[]>();
fields.forEach((field) => {
if (field.disabled) return;
// Don't skip disabled fields
if (field.fieldType.type === 'select' || field.fieldType.type === 'multi-select') {
const fieldKey = String(field.key);
@@ -308,7 +316,7 @@ const ValidationTable = <T extends string>({
// Memoize field columns with stable handlers
const fieldColumns = useMemo(() => fields.map((field): ColumnDef<RowData<T>, any> | null => {
if (field.disabled) return null;
// Don't filter out disabled fields, just pass the disabled state to the cell component
const fieldWidth = field.width || (
field.fieldType.type === "checkbox" ? 80 :
@@ -327,25 +335,51 @@ const ValidationTable = <T extends string>({
accessorKey: fieldKey,
header: field.label || fieldKey,
size: fieldWidth,
cell: ({ row }) => (
<MemoizedCell
field={field as Field<string>}
value={row.original[field.key as keyof typeof row.original]}
onChange={(value) => handleFieldUpdate(row.index, field.key as T, value)}
errors={validationErrors.get(row.index)?.[fieldKey] || []}
isValidating={validatingCells.has(`${row.index}-${field.key}`)}
fieldKey={fieldKey}
options={fieldOptions}
itemNumber={itemNumbers.get(row.index)}
width={fieldWidth}
rowIndex={row.index}
copyDown={(endRowIndex?: number) => handleCopyDown(row.index, field.key as string, endRowIndex)}
totalRows={data.length}
/>
)
cell: ({ row }) => {
// Get row-specific options for line and subline fields
let options = fieldOptions;
const rowId = row.original.__index;
if (fieldKey === 'line' && rowId && rowProductLines[rowId]) {
options = rowProductLines[rowId];
console.log(`Setting line options for row ${rowId}:`, options);
} else if (fieldKey === 'subline' && rowId && rowSublines[rowId]) {
options = rowSublines[rowId];
console.log(`Setting subline options for row ${rowId}:`, options);
}
// Determine if this cell is in loading state
let isLoading = validatingCells.has(`${row.index}-${field.key}`);
// Add loading state for line/subline fields
if (fieldKey === 'line' && rowId && isLoadingLines[rowId]) {
isLoading = true;
console.log(`Line field for row ${rowId} is loading`);
} else if (fieldKey === 'subline' && rowId && isLoadingSublines[rowId]) {
isLoading = true;
console.log(`Subline field for row ${rowId} is loading`);
}
return (
<MemoizedCell
field={field as Field<string>}
value={row.original[field.key as keyof typeof row.original]}
onChange={(value) => handleFieldUpdate(row.index, field.key as T, value)}
errors={validationErrors.get(row.index)?.[fieldKey] || []}
isValidating={isLoading}
fieldKey={fieldKey}
options={options}
itemNumber={itemNumbers.get(row.index)}
width={fieldWidth}
rowIndex={row.index}
copyDown={(endRowIndex?: number) => handleCopyDown(row.index, field.key as string, endRowIndex)}
totalRows={data.length}
/>
);
}
};
}).filter((col): col is ColumnDef<RowData<T>, any> => col !== null),
[fields, validationErrors, validatingCells, itemNumbers, handleFieldUpdate, handleCopyDown, optionsCache, data.length]);
[fields, validationErrors, validatingCells, itemNumbers, handleFieldUpdate, handleCopyDown, optionsCache, data.length, rowProductLines, rowSublines, isLoadingLines, isLoadingSublines]);
// Combine columns
const columns = useMemo(() => [selectionColumn, templateColumn, ...fieldColumns], [selectionColumn, templateColumn, fieldColumns]);

View File

@@ -51,6 +51,8 @@ const SelectCell = <T extends string>({
// Add state for hover
const [isHovered, setIsHovered] = useState(false);
console.log(`SelectCell: field.key=${field.key}, disabled=${disabled}, options=`, options);
// Helper function to check if a class is present in the className string
const hasClass = (cls: string): boolean => {
const classNames = className.split(' ');
@@ -66,6 +68,7 @@ const SelectCell = <T extends string>({
// Memoize options processing to avoid recalculation on every render
const selectOptions = useMemo(() => {
console.log(`Processing options for ${field.key}:`, options);
// Fast path check - if we have raw options, just use those
if (options && options.length > 0) {
// Check if options already have the correct structure to avoid mapping
@@ -146,31 +149,95 @@ const SelectCell = <T extends string>({
if (disabled) {
const displayText = displayValue;
// For debugging, let's render the Popover component even if disabled
// This will help us determine if the issue is with the disabled state
return (
<div className={cn(
"w-full px-3 py-2 h-10 rounded-md text-sm flex items-center",
"border",
hasErrors ? "border-destructive" : "border-input",
isProcessing ? "text-muted-foreground" : "",
className
)}
style={{
backgroundColor: hasClass('!bg-blue-100') ? '#dbeafe' :
hasClass('!bg-blue-200') ? '#bfdbfe' :
hasClass('hover:!bg-blue-100') && isHovered ? '#dbeafe' :
undefined,
borderColor: hasClass('!border-blue-500') ? '#3b82f6' :
hasClass('!border-blue-200') ? '#bfdbfe' :
hasClass('!border-blue-200') && isHovered ? '#bfdbfe' :
undefined,
borderRadius: hasClass('!rounded-md') ? '0.375rem' : undefined,
cursor: hasClass('hover:!bg-blue-100') ? 'pointer' : undefined
}}
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
<Popover
open={open}
onOpenChange={(isOpen) => {
setOpen(isOpen);
if (isOpen && onStartEdit) onStartEdit();
}}
>
{displayText || ""}
</div>
<PopoverTrigger asChild>
<Button
variant="outline"
role="combobox"
aria-expanded={open}
className={cn(
"w-full justify-between font-normal",
"border",
!internalValue && "text-muted-foreground",
isProcessing && "text-muted-foreground",
hasErrors ? "border-destructive" : "",
className
)}
style={{
backgroundColor: hasClass('!bg-blue-100') ? '#dbeafe' :
hasClass('!bg-blue-200') ? '#bfdbfe' :
hasClass('hover:!bg-blue-100') && isHovered ? '#dbeafe' :
undefined,
borderColor: hasClass('!border-blue-500') ? '#3b82f6' :
hasClass('!border-blue-200') ? '#bfdbfe' :
hasClass('!border-blue-200') && isHovered ? '#bfdbfe' :
undefined,
borderRadius: hasClass('!rounded-md') ? '0.375rem' : undefined,
borderWidth: hasClass('!border-blue-500') || hasClass('!border-blue-200') ? '0px' : undefined,
cursor: hasClass('hover:!bg-blue-100') ? 'pointer' : undefined
}}
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
setOpen(!open);
if (!open && onStartEdit) onStartEdit();
}}
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
>
<span className={isProcessing ? "opacity-70" : ""}>
{displayValue}
</span>
<ChevronsUpDown className="mr-1.5 h-4 w-4 shrink-0 opacity-50" />
</Button>
</PopoverTrigger>
<PopoverContent
className="p-0 w-[var(--radix-popover-trigger-width)]"
align="start"
sideOffset={4}
>
<Command shouldFilter={true}>
<CommandInput
placeholder="Search..."
className="h-9"
/>
<CommandList
ref={commandListRef}
onWheel={handleWheel}
className="max-h-[200px]"
>
<CommandEmpty>No options found.</CommandEmpty>
<CommandGroup>
{selectOptions.map((option) => (
<CommandItem
key={option.value}
value={option.value}
onSelect={(value) => handleSelect(value)}
className="flex w-full"
>
<Check
className={cn(
"mr-2 h-4 w-4 flex-shrink-0",
internalValue === option.value ? "opacity-100" : "opacity-0"
)}
/>
<span className="truncate w-full">{option.label}</span>
</CommandItem>
))}
</CommandGroup>
</CommandList>
</Command>
</PopoverContent>
</Popover>
);
}