Fix validation timing issues with templates

This commit is contained in:
2025-03-07 15:30:09 -05:00
parent 60cdb1cee3
commit b15387041b
4 changed files with 511 additions and 466 deletions

View File

@@ -141,13 +141,26 @@ const ItemNumberCell = React.memo(({
field: Field<string>,
onChange: (value: any) => void
}) => {
// Determine if the field has an error
const hasError = errors.some(error => error.level === 'error' || error.level === 'warning');
const isRequiredButEmpty = errors.some(error => error.level === 'required' && (!value || value.trim() === ''));
const nonRequiredErrors = errors.filter(error => error.level !== 'required');
// Determine if the field is required but empty
const isRequiredButEmpty = errors.some(error =>
error.message?.toLowerCase().includes('required') &&
(!value || (typeof value === 'string' && value.trim() === ''))
);
// Only show error icons for non-empty fields
const shouldShowErrorIcon = hasError && value && (typeof value !== 'string' || value.trim() !== '');
// Get error messages for the tooltip
const errorMessages = shouldShowErrorIcon
? errors.filter(e => e.level === 'error' || e.level === 'warning').map(e => e.message).join('\n')
: '';
return (
<TableCell className="p-1" style={{ width: `${width}px`, minWidth: `${width}px` }}>
<div className={`relative ${hasError ? 'border-red-500' : (isRequiredButEmpty ? 'border-red-500' : '')}`}>
<div className={`relative ${hasError || isRequiredButEmpty ? 'border-red-500' : ''}`}>
{isValidating ? (
<div className="flex items-center justify-center gap-2">
<Loader2 className="h-4 w-4 animate-spin text-blue-500" />
@@ -164,10 +177,10 @@ const ItemNumberCell = React.memo(({
/>
</div>
)}
{nonRequiredErrors.length > 0 && (
{shouldShowErrorIcon && (
<div className="absolute right-2 top-1/2 -translate-y-1/2 z-20">
<ValidationIcon error={{
message: nonRequiredErrors.map(e => e.message).join('\n'),
message: errorMessages,
level: 'error'
}} />
</div>
@@ -209,21 +222,27 @@ const ValidationCell = ({
);
}
// Error states
// Determine if the field has an error
const hasError = errors.some(error => error.level === 'error' || error.level === 'warning');
const isRequiredButEmpty = errors.some(error => {
if (error.level !== 'required') return false;
// Handle different value types
if (Array.isArray(value)) {
return value.length === 0;
}
if (typeof value === 'string') {
return !value || value.trim() === '';
}
return value === undefined || value === null;
});
const nonRequiredErrors = errors.filter(error => error.level !== 'required');
// Determine if the field is required but empty
const isRequiredButEmpty = errors.some(error =>
error.message?.toLowerCase().includes('required') &&
(value === undefined || value === null ||
(typeof value === 'string' && value.trim() === '') ||
(Array.isArray(value) && value.length === 0))
);
// Only show error icons for non-empty fields
const shouldShowErrorIcon = hasError &&
!(value === undefined || value === null ||
(typeof value === 'string' && value.trim() === '') ||
(Array.isArray(value) && value.length === 0));
// Get error messages for the tooltip
const errorMessages = shouldShowErrorIcon
? errors.filter(e => e.level === 'error' || e.level === 'warning').map(e => e.message).join('\n')
: '';
// Check if this is a multiline field
const isMultiline = typeof field.fieldType === 'object' &&
@@ -235,7 +254,7 @@ const ValidationCell = ({
return (
<TableCell className="p-1" style={{ width: `${width}px`, minWidth: `${width}px` }}>
<div className={`relative ${hasError ? 'border-red-500' : (isRequiredButEmpty ? 'border-red-500' : '')} ${cellHeight}`}>
<div className={`relative ${hasError || isRequiredButEmpty ? 'border-red-500' : ''} ${cellHeight}`}>
<div className={`truncate overflow-hidden ${isMultiline ? 'h-full' : ''}`}>
@@ -248,10 +267,10 @@ const ValidationCell = ({
/>
</div>
{nonRequiredErrors.length > 0 && (
{shouldShowErrorIcon && (
<div className="absolute right-2 top-1/2 -translate-y-1/2 z-20">
<ValidationIcon error={{
message: nonRequiredErrors.map(e => e.message).join('\n'),
message: errorMessages,
level: 'error'
}} />
</div>

View File

@@ -562,17 +562,51 @@ const ValidationContainer = <T extends string>({
onNext?.(data)
}, [onNext, data, applyItemNumbersToData]);
// Delete selected rows - memoized
const deleteSelectedRows = useCallback(() => {
// Get selected row indices
const selectedRowIndexes = Object.keys(rowSelection).map(Number);
const newData = data.filter((_, index) => !selectedRowIndexes.includes(index));
if (selectedRowIndexes.length === 0) {
toast.error("No rows selected");
return;
}
// Sort indices in descending order to avoid index shifting during removal
const sortedIndices = [...selectedRowIndexes].sort((a, b) => b - a);
// Create a new array without the selected rows
const newData = [...data];
// Remove rows from bottom up to avoid index issues
sortedIndices.forEach(index => {
if (index >= 0 && index < newData.length) {
newData.splice(index, 1);
}
});
// Update the data with rows removed
setData(newData);
// Clear row selection
setRowSelection({});
// Show success message
toast.success(
selectedRowIndexes.length === 1
? "Row deleted"
: `${selectedRowIndexes.length} rows deleted`
);
// Reindex the data in the next render cycle
requestAnimationFrame(() => {
// Update indices to maintain consistency
setData(current =>
current.map((row, newIndex) => ({
...row,
__index: String(newIndex)
}))
);
});
}, [data, rowSelection, setData, setRowSelection]);
// Enhanced ValidationTable component that's aware of item numbers

View File

@@ -137,7 +137,7 @@ export const validateSpecialFields = <T extends string>(row: Data<T>): Record<st
message: 'Supplier is required',
level: 'error',
source: ErrorSources.Row
} as ErrorType]
}]
}
// Validate company field
@@ -146,7 +146,7 @@ export const validateSpecialFields = <T extends string>(row: Data<T>): Record<st
message: 'Company is required',
level: 'error',
source: ErrorSources.Row
} as ErrorType]
}]
}
return errors