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(({
|
||||
field,
|
||||
value,
|
||||
@@ -454,14 +270,17 @@ const ValidationCell = React.memo(({
|
||||
isValidating,
|
||||
fieldKey,
|
||||
options = [],
|
||||
itemNumber,
|
||||
width,
|
||||
copyDown,
|
||||
rowIndex,
|
||||
totalRows = 0}: ValidationCellProps) => {
|
||||
totalRows = 0
|
||||
}: ValidationCellProps) => {
|
||||
// Use the CopyDown context
|
||||
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
|
||||
const {
|
||||
@@ -469,10 +288,12 @@ const ValidationCell = React.memo(({
|
||||
isRequiredButEmpty,
|
||||
shouldShowErrorIcon,
|
||||
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
|
||||
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
|
||||
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 (
|
||||
<TableCell
|
||||
className="p-1 group relative"
|
||||
className="p-1 group relative"
|
||||
style={{
|
||||
width: `${width}px`,
|
||||
minWidth: `${width}px`,
|
||||
@@ -536,7 +357,7 @@ const ValidationCell = React.memo(({
|
||||
}} />
|
||||
</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">
|
||||
<TooltipProvider>
|
||||
<Tooltip>
|
||||
@@ -587,11 +408,10 @@ const ValidationCell = React.memo(({
|
||||
</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}
|
||||
value={displayValue}
|
||||
onChange={onChange}
|
||||
hasErrors={hasError || isRequiredButEmpty}
|
||||
options={options}
|
||||
|
||||
Reference in New Issue
Block a user