Merge separate itemNumberCell in to ValidationCell
This commit is contained in:
@@ -262,190 +262,6 @@ function compareErrorArrays(prevErrors: ErrorObject[], nextErrors: ErrorObject[]
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const ItemNumberCell = React.memo(({
|
|
||||||
value,
|
|
||||||
itemNumber,
|
|
||||||
isValidating,
|
|
||||||
width,
|
|
||||||
errors = [],
|
|
||||||
field,
|
|
||||||
onChange,
|
|
||||||
copyDown,
|
|
||||||
rowIndex,
|
|
||||||
totalRows = 0
|
|
||||||
}: {
|
|
||||||
value: any,
|
|
||||||
itemNumber?: string,
|
|
||||||
isValidating?: boolean,
|
|
||||||
width: number,
|
|
||||||
errors?: ErrorObject[],
|
|
||||||
field: Field<string>,
|
|
||||||
onChange: (value: any) => void,
|
|
||||||
copyDown?: (endRowIndex?: number) => void,
|
|
||||||
rowIndex: number,
|
|
||||||
totalRows?: number
|
|
||||||
}) => {
|
|
||||||
// If we have a value or itemNumber, ignore "required" errors
|
|
||||||
const displayValue = itemNumber || value;
|
|
||||||
|
|
||||||
// Use the utility function to process errors once
|
|
||||||
const {
|
|
||||||
hasError,
|
|
||||||
isRequiredButEmpty,
|
|
||||||
shouldShowErrorIcon,
|
|
||||||
errorMessages
|
|
||||||
} = React.useMemo(() =>
|
|
||||||
processErrors(displayValue, errors),
|
|
||||||
[displayValue, errors]
|
|
||||||
);
|
|
||||||
|
|
||||||
// Add state for hover on copy down button
|
|
||||||
const [isCopyDownHovered, setIsCopyDownHovered] = React.useState(false);
|
|
||||||
// Add state for hover on target row
|
|
||||||
const [isTargetRowHovered, setIsTargetRowHovered] = React.useState(false);
|
|
||||||
|
|
||||||
// Get copy down context
|
|
||||||
const copyDownContext = React.useContext(CopyDownContext);
|
|
||||||
|
|
||||||
// Handle copy down button click
|
|
||||||
const handleCopyDownClick = () => {
|
|
||||||
if (copyDown && totalRows > rowIndex + 1) {
|
|
||||||
// Enter copy down mode
|
|
||||||
copyDownContext.setIsInCopyDownMode(true);
|
|
||||||
copyDownContext.setSourceRowIndex(rowIndex);
|
|
||||||
copyDownContext.setSourceFieldKey('item_number');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Check if this cell is the source of the current copy down operation
|
|
||||||
const isSourceCell = copyDownContext.isInCopyDownMode &&
|
|
||||||
copyDownContext.sourceRowIndex === rowIndex &&
|
|
||||||
copyDownContext.sourceFieldKey === 'item_number';
|
|
||||||
|
|
||||||
// Check if this cell is in a row that can be a target for copy down
|
|
||||||
const isInTargetRow = copyDownContext.isInCopyDownMode &&
|
|
||||||
copyDownContext.sourceFieldKey === 'item_number' &&
|
|
||||||
rowIndex > (copyDownContext.sourceRowIndex || 0);
|
|
||||||
|
|
||||||
// Check if this row is the currently selected target row
|
|
||||||
const isSelectedTarget = isInTargetRow && rowIndex <= (copyDownContext.targetRowIndex || 0);
|
|
||||||
|
|
||||||
// Handle click on a potential target cell
|
|
||||||
const handleTargetCellClick = () => {
|
|
||||||
if (isInTargetRow && copyDownContext.sourceRowIndex !== null) {
|
|
||||||
copyDownContext.handleCopyDownComplete(
|
|
||||||
copyDownContext.sourceRowIndex,
|
|
||||||
'item_number',
|
|
||||||
rowIndex
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
//item_number fields
|
|
||||||
return (
|
|
||||||
<TableCell
|
|
||||||
className="p-1 group relative"
|
|
||||||
style={{
|
|
||||||
width: `${width}px`,
|
|
||||||
minWidth: `${width}px`,
|
|
||||||
maxWidth: `${width}px`,
|
|
||||||
boxSizing: 'border-box',
|
|
||||||
cursor: isInTargetRow ? 'pointer' : undefined,
|
|
||||||
...(isSourceCell ? { backgroundColor: '#dbeafe', borderRadius: '0.375rem', padding: 0, boxShadow: '0 0 0 2px #3b82f6' } :
|
|
||||||
isSelectedTarget ? { backgroundColor: '#bfdbfe', borderRadius: '0.375rem', padding: 0 } :
|
|
||||||
isInTargetRow && isTargetRowHovered ? { backgroundColor: '#dbeafe', borderRadius: '0.375rem', padding: 0, cursor: 'pointer' } :
|
|
||||||
isInTargetRow ? { borderRadius: '0.375rem', padding: 0, cursor: 'pointer' } : {})
|
|
||||||
}}
|
|
||||||
onClick={isInTargetRow ? handleTargetCellClick : undefined}
|
|
||||||
onMouseEnter={isInTargetRow ? () => setIsTargetRowHovered(true) : undefined}
|
|
||||||
onMouseLeave={isInTargetRow ? () => setIsTargetRowHovered(false) : undefined}
|
|
||||||
>
|
|
||||||
<div className={`relative ${hasError || isRequiredButEmpty ? 'border-red-500' : ''}`}>
|
|
||||||
{shouldShowErrorIcon && !isInTargetRow && (
|
|
||||||
<div className="absolute right-1.5 top-1/2 -translate-y-1/2 z-20">
|
|
||||||
<ValidationIcon error={{
|
|
||||||
message: errorMessages,
|
|
||||||
level: 'error',
|
|
||||||
type: ErrorType.Custom
|
|
||||||
}} />
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{!shouldShowErrorIcon && copyDown && !isEmpty(displayValue) && !copyDownContext.isInCopyDownMode && (
|
|
||||||
<div className="absolute right-1.5 top-1/2 -translate-y-1/2 z-20 opacity-0 group-hover:opacity-100 transition-opacity">
|
|
||||||
<TooltipProvider>
|
|
||||||
<Tooltip>
|
|
||||||
<TooltipTrigger asChild>
|
|
||||||
<button
|
|
||||||
onClick={handleCopyDownClick}
|
|
||||||
onMouseEnter={() => setIsCopyDownHovered(true)}
|
|
||||||
onMouseLeave={() => setIsCopyDownHovered(false)}
|
|
||||||
className="p-1 rounded-full hover:bg-blue-100 text-blue-500/70 hover:text-blue-600 transition-colors"
|
|
||||||
aria-label="Copy value to rows below"
|
|
||||||
>
|
|
||||||
<ArrowDown className="h-3.5 w-3.5" />
|
|
||||||
</button>
|
|
||||||
</TooltipTrigger>
|
|
||||||
<TooltipContent side="right">
|
|
||||||
<div className="flex flex-col">
|
|
||||||
<p className="font-medium">Copy value to rows below</p>
|
|
||||||
</div>
|
|
||||||
</TooltipContent>
|
|
||||||
</Tooltip>
|
|
||||||
</TooltipProvider>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{isSourceCell && (
|
|
||||||
<div className="absolute right-1.5 top-1/2 -translate-y-1/2 z-20">
|
|
||||||
<TooltipProvider>
|
|
||||||
<Tooltip>
|
|
||||||
<TooltipTrigger asChild>
|
|
||||||
<button
|
|
||||||
onClick={() => copyDownContext.setIsInCopyDownMode(false)}
|
|
||||||
className="p-1 rounded-full bg-blue-100 text-blue-600 hover:bg-blue-200 transition-colors"
|
|
||||||
aria-label="Cancel copy down"
|
|
||||||
>
|
|
||||||
<X className="h-3.5 w-3.5" />
|
|
||||||
</button>
|
|
||||||
</TooltipTrigger>
|
|
||||||
<TooltipContent side="right">
|
|
||||||
<p>Cancel copy down</p>
|
|
||||||
</TooltipContent>
|
|
||||||
</Tooltip>
|
|
||||||
</TooltipProvider>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{isValidating ? (
|
|
||||||
<div className={`flex items-center justify-center gap-2 border ${hasError || isRequiredButEmpty ? 'border-red-500' : 'border-input'} rounded-sm px-2 py-1.5`}>
|
|
||||||
<Loader2 className="h-4 w-4 animate-spin text-blue-500" />
|
|
||||||
<span>Loading...</span>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div className={`truncate overflow-hidden ${isCopyDownHovered && !copyDownContext.isInCopyDownMode ? 'bg-blue-50/50' : ''}`}>
|
|
||||||
<BaseCellContent
|
|
||||||
field={field}
|
|
||||||
value={displayValue}
|
|
||||||
onChange={onChange}
|
|
||||||
hasErrors={hasError || isRequiredButEmpty}
|
|
||||||
options={(field.fieldType && typeof field.fieldType === 'object' && (field.fieldType as any).options) || []}
|
|
||||||
className={isSourceCell || isSelectedTarget || isInTargetRow ? `${
|
|
||||||
isSourceCell ? '!bg-blue-100 !border-blue-500 !rounded-md' :
|
|
||||||
isSelectedTarget ? '!bg-blue-200 !border-blue-200 !rounded-md' :
|
|
||||||
isInTargetRow ? 'hover:!bg-blue-100 !border-blue-200 !rounded-md' : ''
|
|
||||||
}` : ''}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</TableCell>
|
|
||||||
);
|
|
||||||
}, (prev, next) => (
|
|
||||||
prev.value === next.value &&
|
|
||||||
prev.itemNumber === next.itemNumber &&
|
|
||||||
prev.isValidating === next.isValidating &&
|
|
||||||
compareErrorArrays(prev.errors || [], next.errors || [])
|
|
||||||
));
|
|
||||||
|
|
||||||
ItemNumberCell.displayName = 'ItemNumberCell';
|
|
||||||
|
|
||||||
const ValidationCell = React.memo(({
|
const ValidationCell = React.memo(({
|
||||||
field,
|
field,
|
||||||
value,
|
value,
|
||||||
@@ -454,14 +270,17 @@ const ValidationCell = React.memo(({
|
|||||||
isValidating,
|
isValidating,
|
||||||
fieldKey,
|
fieldKey,
|
||||||
options = [],
|
options = [],
|
||||||
|
itemNumber,
|
||||||
width,
|
width,
|
||||||
copyDown,
|
copyDown,
|
||||||
rowIndex,
|
rowIndex,
|
||||||
totalRows = 0}: ValidationCellProps) => {
|
totalRows = 0
|
||||||
|
}: ValidationCellProps) => {
|
||||||
// Use the CopyDown context
|
// Use the CopyDown context
|
||||||
const copyDownContext = React.useContext(CopyDownContext);
|
const copyDownContext = React.useContext(CopyDownContext);
|
||||||
// Only destructure what we need to avoid unused variables warning
|
|
||||||
const { isInCopyDownMode, sourceRowIndex, sourceFieldKey } = copyDownContext;
|
// Display value prioritizes itemNumber if available (for item_number fields)
|
||||||
|
const displayValue = fieldKey === 'item_number' && itemNumber ? itemNumber : value;
|
||||||
|
|
||||||
// Use the optimized processErrors function to avoid redundant filtering
|
// Use the optimized processErrors function to avoid redundant filtering
|
||||||
const {
|
const {
|
||||||
@@ -469,10 +288,12 @@ const ValidationCell = React.memo(({
|
|||||||
isRequiredButEmpty,
|
isRequiredButEmpty,
|
||||||
shouldShowErrorIcon,
|
shouldShowErrorIcon,
|
||||||
errorMessages
|
errorMessages
|
||||||
} = React.useMemo(() => processErrors(value, errors), [value, errors]);
|
} = React.useMemo(() => processErrors(displayValue, errors), [displayValue, errors]);
|
||||||
|
|
||||||
// Track whether this cell is the source of a copy-down operation
|
// Track whether this cell is the source of a copy-down operation
|
||||||
const isSourceCell = isInCopyDownMode && rowIndex === sourceRowIndex && fieldKey === sourceFieldKey;
|
const isSourceCell = copyDownContext.isInCopyDownMode &&
|
||||||
|
rowIndex === copyDownContext.sourceRowIndex &&
|
||||||
|
fieldKey === copyDownContext.sourceFieldKey;
|
||||||
|
|
||||||
// Add state for hover on copy down button
|
// Add state for hover on copy down button
|
||||||
const [isCopyDownHovered, setIsCopyDownHovered] = React.useState(false);
|
const [isCopyDownHovered, setIsCopyDownHovered] = React.useState(false);
|
||||||
@@ -507,10 +328,10 @@ const ValidationCell = React.memo(({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
//normal selects, normal inputs, not item_number or multi-select
|
|
||||||
return (
|
return (
|
||||||
<TableCell
|
<TableCell
|
||||||
className="p-1 group relative"
|
className="p-1 group relative"
|
||||||
style={{
|
style={{
|
||||||
width: `${width}px`,
|
width: `${width}px`,
|
||||||
minWidth: `${width}px`,
|
minWidth: `${width}px`,
|
||||||
@@ -536,7 +357,7 @@ const ValidationCell = React.memo(({
|
|||||||
}} />
|
}} />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{!shouldShowErrorIcon && copyDown && !isEmpty(value) && !copyDownContext.isInCopyDownMode && (
|
{!shouldShowErrorIcon && copyDown && !isEmpty(displayValue) && !copyDownContext.isInCopyDownMode && (
|
||||||
<div className="absolute right-0.5 top-1/2 -translate-y-1/2 z-20 opacity-0 group-hover:opacity-100 transition-opacity">
|
<div className="absolute right-0.5 top-1/2 -translate-y-1/2 z-20 opacity-0 group-hover:opacity-100 transition-opacity">
|
||||||
<TooltipProvider>
|
<TooltipProvider>
|
||||||
<Tooltip>
|
<Tooltip>
|
||||||
@@ -587,11 +408,10 @@ const ValidationCell = React.memo(({
|
|||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className={`truncate overflow-hidden ${isCopyDownHovered && !copyDownContext.isInCopyDownMode ? 'bg-blue-50/50' : ''}`}>
|
<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; })()}
|
{(() => { console.log(`ValidationCell: fieldKey=${fieldKey}, options=`, options); return null; })()}
|
||||||
<BaseCellContent
|
<BaseCellContent
|
||||||
field={field}
|
field={field}
|
||||||
value={value}
|
value={displayValue}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
hasErrors={hasError || isRequiredButEmpty}
|
hasErrors={hasError || isRequiredButEmpty}
|
||||||
options={options}
|
options={options}
|
||||||
|
|||||||
Reference in New Issue
Block a user