Fix a few validation loading state issues
This commit is contained in:
@@ -1,58 +0,0 @@
|
||||
# ValidationStepNew Component
|
||||
|
||||
This is a refactored version of the original ValidationStep component with improved architecture, performance, and maintainability.
|
||||
|
||||
## Overview
|
||||
|
||||
The ValidationStepNew component is designed to replace the original ValidationStep component in the React Spreadsheet Import library. It provides the same functionality but with a more modular and maintainable codebase.
|
||||
|
||||
## Features
|
||||
|
||||
- Field-level validation (required, regex, unique)
|
||||
- Row-level validation (supplier, company fields)
|
||||
- UPC validation with API integration
|
||||
- Template management (saving, loading, applying)
|
||||
- Filtering and sorting capabilities
|
||||
- Error display and management
|
||||
- Special field handling (input, multi-input, select, checkbox)
|
||||
|
||||
## Usage
|
||||
|
||||
To use the new ValidationStepNew component, select the "Use new validation component" checkbox in the MatchColumnsStep. This will route you to the new implementation instead of the original one.
|
||||
|
||||
## Architecture
|
||||
|
||||
The component is structured as follows:
|
||||
|
||||
- **ValidationContainer**: Main container component that coordinates all subcomponents
|
||||
- **ValidationTable**: Handles data display, filtering, and column configuration
|
||||
- **ValidationCell**: Cell-level component with specialized rendering based on field type
|
||||
- **ValidationToolbar**: Top toolbar with actions and statistics
|
||||
- **ValidationSidebar**: Contains filters, actions, and other UI controls
|
||||
|
||||
## State Management
|
||||
|
||||
State is managed through custom hooks:
|
||||
|
||||
- **useValidationState**: Main state management hook
|
||||
- **useValidation**: Validation logic
|
||||
- **useTemplates**: Template management
|
||||
- **useFilters**: Filtering logic
|
||||
- **useUpcValidation**: UPC validation
|
||||
|
||||
## Development
|
||||
|
||||
This component is still under development. The goal is to eventually replace the original ValidationStep component completely once all functionality is implemented and tested.
|
||||
|
||||
## Known Issues
|
||||
|
||||
- Some TypeScript errors in the UploadFlow component when integrating with the new component
|
||||
- Not all features from the original component are fully implemented yet
|
||||
|
||||
## Roadmap
|
||||
|
||||
1. Complete implementation of all features from the original component
|
||||
2. Add comprehensive tests
|
||||
3. Improve performance with virtualization for large datasets
|
||||
4. Add more customization options
|
||||
5. Replace the original component completely
|
||||
@@ -0,0 +1,73 @@
|
||||
import React from 'react'
|
||||
import { Loader2, Check, X } from 'lucide-react'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
interface InitializationTask {
|
||||
label: string
|
||||
status: 'pending' | 'in_progress' | 'completed' | 'failed'
|
||||
}
|
||||
|
||||
interface InitializingValidationProps {
|
||||
totalRows: number
|
||||
tasks: InitializationTask[]
|
||||
}
|
||||
|
||||
export const InitializingValidation: React.FC<InitializingValidationProps> = ({
|
||||
totalRows,
|
||||
tasks
|
||||
}) => {
|
||||
return (
|
||||
<div className="flex flex-col items-center justify-center h-[calc(100vh-10rem)]">
|
||||
<Loader2 className="h-12 w-12 animate-spin text-primary mb-4" />
|
||||
<h3 className="text-lg font-medium text-foreground mb-2">Initializing Validation</h3>
|
||||
<p className="text-sm text-muted-foreground mb-6">Processing {totalRows} rows...</p>
|
||||
|
||||
{/* Task checklist */}
|
||||
<div className="w-80 space-y-2">
|
||||
{tasks.map((task, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className={cn(
|
||||
"flex items-center gap-3 px-4 py-2 rounded-md transition-colors",
|
||||
task.status === 'completed' && "bg-green-50 border border-green-200",
|
||||
task.status === 'failed' && "bg-red-50 border border-red-200",
|
||||
task.status === 'in_progress' && "bg-blue-50 border border-blue-200",
|
||||
task.status === 'pending' && "bg-blue-50 border border-blue-200"
|
||||
)}
|
||||
>
|
||||
{/* Status icon */}
|
||||
<div className="flex-shrink-0">
|
||||
{task.status === 'completed' && (
|
||||
<Check className="h-4 w-4 text-green-600" />
|
||||
)}
|
||||
{task.status === 'failed' && (
|
||||
<X className="h-4 w-4 text-red-600" />
|
||||
)}
|
||||
{task.status === 'in_progress' && (
|
||||
<Loader2 className="h-4 w-4 text-blue-600 animate-spin" />
|
||||
)}
|
||||
{task.status === 'pending' && (
|
||||
<Loader2 className="h-4 w-4 text-blue-600 animate-spin" />
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Task label */}
|
||||
<span
|
||||
className={cn(
|
||||
"text-sm font-medium",
|
||||
task.status === 'completed' && "text-green-700",
|
||||
task.status === 'failed' && "text-red-700",
|
||||
task.status === 'in_progress' && "text-blue-700",
|
||||
task.status === 'pending' && "text-blue-700"
|
||||
)}
|
||||
>
|
||||
{task.label}
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default InitializingValidation
|
||||
@@ -20,6 +20,7 @@ import { Skeleton } from '@/components/ui/skeleton'
|
||||
import { Protected } from '@/components/auth/Protected'
|
||||
import { normalizeCountryCode } from '../utils/countryUtils'
|
||||
import { cleanPriceField } from '../utils/priceUtils'
|
||||
import InitializingValidation from './InitializingValidation'
|
||||
/**
|
||||
* ValidationContainer component - the main wrapper for the validation step
|
||||
*
|
||||
@@ -62,8 +63,8 @@ const ValidationContainer = <T extends string>({
|
||||
fields,
|
||||
upcValidation,
|
||||
isLoadingTemplates,
|
||||
isValidating,
|
||||
isInitializing,
|
||||
initializationTasks,
|
||||
validatingCells,
|
||||
setValidatingCells,
|
||||
editingCells,
|
||||
@@ -130,10 +131,6 @@ const ValidationContainer = <T extends string>({
|
||||
const [isTemplateFormOpen, setIsTemplateFormOpen] = useState(false)
|
||||
const [templateFormInitialData, setTemplateFormInitialData] = useState<any>(null)
|
||||
|
||||
const pendingInitializationTasks: string[] = []
|
||||
if (isValidating) pendingInitializationTasks.push('validating rows')
|
||||
if (upcValidation.validatingRows.size > 0) pendingInitializationTasks.push('checking UPCs')
|
||||
if (isLoadingTemplates) pendingInitializationTasks.push('loading templates')
|
||||
const [fieldOptions, setFieldOptions] = useState<any>(null)
|
||||
|
||||
// Track fields that need revalidation due to value changes
|
||||
@@ -699,15 +696,17 @@ const ValidationContainer = <T extends string>({
|
||||
|
||||
// Show loading state during initialization
|
||||
if (isInitializing) {
|
||||
// Convert initializationTasks object to array format
|
||||
const tasksArray = Object.values(initializationTasks).map(task => ({
|
||||
label: task.label,
|
||||
status: task.status as 'pending' | 'in_progress' | 'completed' | 'failed'
|
||||
}));
|
||||
|
||||
return (
|
||||
<div className="flex flex-col items-center justify-center h-[calc(100vh-10rem)]">
|
||||
<Loader2 className="h-12 w-12 animate-spin text-primary mb-4" />
|
||||
<h3 className="text-lg font-medium text-foreground mb-2">Initializing Validation</h3>
|
||||
<p className="text-sm text-muted-foreground">Processing {data.length} rows...</p>
|
||||
{pendingInitializationTasks.length > 0 && (
|
||||
<p className="text-xs text-muted-foreground">Still {pendingInitializationTasks.join(' | ')}</p>
|
||||
)}
|
||||
</div>
|
||||
<InitializingValidation
|
||||
totalRows={data.length}
|
||||
tasks={tasksArray}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -355,21 +355,22 @@ const ValidationTable = <T extends string>({
|
||||
? row.original[field.key]
|
||||
: row.original[field.key as keyof typeof row.original];
|
||||
|
||||
// Determine if this cell is in loading state - only show loading for empty fields
|
||||
// Determine if this cell is in loading state
|
||||
let isLoading = false;
|
||||
|
||||
// Only show loading if the field is currently empty
|
||||
const cellLoadingKey = `${row.index}-${fieldKey}`;
|
||||
const isEmpty = currentValue === undefined || currentValue === null || currentValue === '' ||
|
||||
(Array.isArray(currentValue) && currentValue.length === 0);
|
||||
|
||||
if (isEmpty) {
|
||||
// Check the validatingCells Set first (for item_number and other fields)
|
||||
const cellLoadingKey = `${row.index}-${fieldKey}`;
|
||||
// CRITICAL: Check validatingCells FIRST - this shows loading for item_number during UPC validation
|
||||
// even if the field already has a value (because we're fetching a new one)
|
||||
if (validatingCells.has(cellLoadingKey)) {
|
||||
isLoading = true;
|
||||
}
|
||||
// Only show loading for empty fields for these other cases
|
||||
else if (isEmpty) {
|
||||
// Check if UPC is validating for this row and field is item_number
|
||||
else if (fieldKey === 'item_number' && isRowValidatingUpc(row.index)) {
|
||||
if (fieldKey === 'item_number' && isRowValidatingUpc(row.index)) {
|
||||
isLoading = true;
|
||||
}
|
||||
// Add loading state for line/subline fields
|
||||
|
||||
@@ -280,14 +280,36 @@ export const useValidationState = <T extends string>({
|
||||
}, [data, initialValidationComplete, upcValidation.initialValidationDone]);
|
||||
|
||||
const hasPendingUpcValidation = upcValidation.validatingRows.size > 0;
|
||||
|
||||
// Separate initial validation from subsequent validations
|
||||
// isInitializing should ONLY be true during the first load, never again
|
||||
const isInitializing =
|
||||
!initialValidationComplete ||
|
||||
isInitialValidationRunning ||
|
||||
templateManagement.isLoadingTemplates ||
|
||||
hasPendingUpcValidation;
|
||||
(hasPendingUpcValidation && !upcValidation.initialValidationDone);
|
||||
|
||||
const isValidating = isInitialValidationRunning;
|
||||
|
||||
// Track initialization task statuses for the progress UI
|
||||
const initializationTasks = {
|
||||
upcValidation: {
|
||||
label: 'Validating UPCs and generating item numbers',
|
||||
status: upcValidation.initialValidationDone ? 'completed' :
|
||||
hasPendingUpcValidation ? 'in_progress' : 'pending'
|
||||
},
|
||||
fieldValidation: {
|
||||
label: 'Validating field requirements and formats',
|
||||
status: initialValidationComplete ? 'completed' :
|
||||
isInitialValidationRunning ? 'in_progress' : 'pending'
|
||||
},
|
||||
templateLoading: {
|
||||
label: 'Loading product templates',
|
||||
status: !templateManagement.isLoadingTemplates ? 'completed' :
|
||||
'in_progress'
|
||||
}
|
||||
};
|
||||
|
||||
// Targeted uniqueness revalidation: run only when item_number values change
|
||||
useEffect(() => {
|
||||
if (!data || data.length === 0) return;
|
||||
@@ -433,6 +455,7 @@ export const useValidationState = <T extends string>({
|
||||
// Validation
|
||||
isValidating,
|
||||
isInitializing,
|
||||
initializationTasks,
|
||||
validationErrors,
|
||||
rowValidationStatus,
|
||||
validateRow,
|
||||
|
||||
Reference in New Issue
Block a user