Simplify the onboardinf flow components 4
This commit is contained in:
@@ -192,6 +192,27 @@ export const OnboardingWizard: React.FC = () => {
|
|||||||
|
|
||||||
console.log(`✅ Successfully completed step: "${currentStep.id}"`);
|
console.log(`✅ Successfully completed step: "${currentStep.id}"`);
|
||||||
|
|
||||||
|
// Special handling for smart-inventory-setup: auto-complete suppliers step
|
||||||
|
if (currentStep.id === 'smart-inventory-setup' && data?.shouldAutoCompleteSuppliers) {
|
||||||
|
try {
|
||||||
|
console.log('🔄 Auto-completing suppliers step to enable ML training...');
|
||||||
|
await markStepCompleted.mutateAsync({
|
||||||
|
userId: user.id,
|
||||||
|
stepName: 'suppliers',
|
||||||
|
data: {
|
||||||
|
auto_completed: true,
|
||||||
|
completed_at: new Date().toISOString(),
|
||||||
|
source: 'inventory_creation_auto_completion',
|
||||||
|
message: 'Suppliers step auto-completed to proceed with ML training'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
console.log('✅ Suppliers step auto-completed successfully');
|
||||||
|
} catch (supplierError) {
|
||||||
|
console.warn('⚠️ Could not auto-complete suppliers step:', supplierError);
|
||||||
|
// Don't fail the entire flow if suppliers step completion fails
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (currentStep.id === 'completion') {
|
if (currentStep.id === 'completion') {
|
||||||
navigate('/app');
|
navigate('/app');
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { useState, useCallback } from 'react';
|
import React, { useState, useCallback, useEffect } from 'react';
|
||||||
import { Button } from '../../../ui/Button';
|
import { Button } from '../../../ui/Button';
|
||||||
import { useCurrentTenant } from '../../../../stores/tenant.store';
|
import { useCurrentTenant } from '../../../../stores/tenant.store';
|
||||||
import { useCreateTrainingJob, useTrainingWebSocket } from '../../../../api/hooks/training';
|
import { useCreateTrainingJob, useTrainingWebSocket } from '../../../../api/hooks/training';
|
||||||
@@ -20,9 +20,7 @@ interface TrainingProgress {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const MLTrainingStep: React.FC<MLTrainingStepProps> = ({
|
export const MLTrainingStep: React.FC<MLTrainingStepProps> = ({
|
||||||
onPrevious,
|
onComplete
|
||||||
onComplete,
|
|
||||||
isFirstStep
|
|
||||||
}) => {
|
}) => {
|
||||||
const [trainingProgress, setTrainingProgress] = useState<TrainingProgress | null>(null);
|
const [trainingProgress, setTrainingProgress] = useState<TrainingProgress | null>(null);
|
||||||
const [isTraining, setIsTraining] = useState(false);
|
const [isTraining, setIsTraining] = useState(false);
|
||||||
@@ -87,6 +85,14 @@ export const MLTrainingStep: React.FC<MLTrainingStepProps> = ({
|
|||||||
} : undefined
|
} : undefined
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Auto-trigger training when component mounts
|
||||||
|
useEffect(() => {
|
||||||
|
if (currentTenant?.id && !isTraining && !trainingProgress && !error) {
|
||||||
|
console.log('🚀 Auto-starting ML training for tenant:', currentTenant.id);
|
||||||
|
handleStartTraining();
|
||||||
|
}
|
||||||
|
}, [currentTenant?.id]); // Only run when tenant is available
|
||||||
|
|
||||||
const handleStartTraining = async () => {
|
const handleStartTraining = async () => {
|
||||||
if (!currentTenant?.id) {
|
if (!currentTenant?.id) {
|
||||||
setError('No se encontró información del tenant');
|
setError('No se encontró información del tenant');
|
||||||
@@ -140,7 +146,7 @@ export const MLTrainingStep: React.FC<MLTrainingStepProps> = ({
|
|||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
<div className="text-center">
|
<div className="text-center">
|
||||||
<p className="text-[var(--text-secondary)] mb-6">
|
<p className="text-[var(--text-secondary)] mb-6">
|
||||||
Ahora entrenaremos tu modelo de inteligencia artificial utilizando los datos de ventas
|
Perfecto! Ahora entrenaremos automáticamente tu modelo de inteligencia artificial utilizando los datos de ventas
|
||||||
e inventario que has proporcionado. Este proceso puede tomar varios minutos.
|
e inventario que has proporcionado. Este proceso puede tomar varios minutos.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -151,14 +157,12 @@ export const MLTrainingStep: React.FC<MLTrainingStepProps> = ({
|
|||||||
{!isTraining && !trainingProgress && (
|
{!isTraining && !trainingProgress && (
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<div className="mx-auto w-16 h-16 bg-[var(--color-primary)]/10 rounded-full flex items-center justify-center">
|
<div className="mx-auto w-16 h-16 bg-[var(--color-primary)]/10 rounded-full flex items-center justify-center">
|
||||||
<svg className="w-8 h-8 text-[var(--color-primary)]" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-[var(--color-primary)]"></div>
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9.663 17h4.673M12 3v1m6.364 1.636l-.707.707M21 12h-1M4 12H3m3.343-5.657l-.707-.707m2.828 9.9a5 5 0 117.072 0l-.548.547A3.374 3.374 0 0014 18.469V19a2 2 0 11-4 0v-.531c0-.895-.356-1.754-.988-2.386l-.548-.547z" />
|
|
||||||
</svg>
|
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h3 className="text-lg font-semibold mb-2">Listo para Entrenar</h3>
|
<h3 className="text-lg font-semibold mb-2">Iniciando Entrenamiento Automático</h3>
|
||||||
<p className="text-[var(--text-secondary)] text-sm">
|
<p className="text-[var(--text-secondary)] text-sm">
|
||||||
Tu modelo está listo para ser entrenado con los datos proporcionados.
|
Preparando el entrenamiento de tu modelo con los datos proporcionados...
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -246,36 +250,7 @@ export const MLTrainingStep: React.FC<MLTrainingStepProps> = ({
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Actions */}
|
{/* Auto-completion when training finishes - no manual buttons needed */}
|
||||||
<div className="flex justify-between">
|
|
||||||
<Button
|
|
||||||
variant="outline"
|
|
||||||
onClick={onPrevious}
|
|
||||||
disabled={isFirstStep || isTraining}
|
|
||||||
>
|
|
||||||
Anterior
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
{!isTraining && !trainingProgress && (
|
|
||||||
<Button
|
|
||||||
onClick={handleStartTraining}
|
|
||||||
size="lg"
|
|
||||||
disabled={!currentTenant?.id}
|
|
||||||
>
|
|
||||||
Iniciar Entrenamiento
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{trainingProgress?.stage === 'completed' && (
|
|
||||||
<Button
|
|
||||||
onClick={() => onComplete()}
|
|
||||||
size="lg"
|
|
||||||
variant="success"
|
|
||||||
>
|
|
||||||
Continuar
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -7,6 +7,7 @@ import { useCurrentTenant } from '../../../../stores/tenant.store';
|
|||||||
import { useCreateIngredient } from '../../../../api/hooks/inventory';
|
import { useCreateIngredient } from '../../../../api/hooks/inventory';
|
||||||
import { useImportFileOnly } from '../../../../api/hooks/dataImport';
|
import { useImportFileOnly } from '../../../../api/hooks/dataImport';
|
||||||
import { useClassifyProductsBatch } from '../../../../api/hooks/classification';
|
import { useClassifyProductsBatch } from '../../../../api/hooks/classification';
|
||||||
|
import { useAuth } from '../../../../contexts/AuthContext';
|
||||||
|
|
||||||
interface UploadSalesDataStepProps {
|
interface UploadSalesDataStepProps {
|
||||||
onNext: () => void;
|
onNext: () => void;
|
||||||
@@ -64,27 +65,34 @@ export const UploadSalesDataStep: React.FC<UploadSalesDataStepProps> = ({
|
|||||||
const fileInputRef = useRef<HTMLInputElement>(null);
|
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||||
|
|
||||||
const currentTenant = useCurrentTenant();
|
const currentTenant = useCurrentTenant();
|
||||||
|
const { user } = useAuth();
|
||||||
const { validateFile } = useValidateFileOnly();
|
const { validateFile } = useValidateFileOnly();
|
||||||
const createIngredient = useCreateIngredient();
|
const createIngredient = useCreateIngredient();
|
||||||
const { importFile } = useImportFileOnly();
|
const { importFile } = useImportFileOnly();
|
||||||
const classifyProducts = useClassifyProductsBatch();
|
const classifyProducts = useClassifyProductsBatch();
|
||||||
|
|
||||||
const handleFileSelect = (event: React.ChangeEvent<HTMLInputElement>) => {
|
const handleFileSelect = async (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
const file = event.target.files?.[0];
|
const file = event.target.files?.[0];
|
||||||
if (file) {
|
if (file) {
|
||||||
setSelectedFile(file);
|
setSelectedFile(file);
|
||||||
setValidationResult(null);
|
setValidationResult(null);
|
||||||
setError('');
|
setError('');
|
||||||
|
|
||||||
|
// Automatically trigger validation and classification
|
||||||
|
await handleAutoValidateAndClassify(file);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleDrop = (event: React.DragEvent<HTMLDivElement>) => {
|
const handleDrop = async (event: React.DragEvent<HTMLDivElement>) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
const file = event.dataTransfer.files[0];
|
const file = event.dataTransfer.files[0];
|
||||||
if (file) {
|
if (file) {
|
||||||
setSelectedFile(file);
|
setSelectedFile(file);
|
||||||
setValidationResult(null);
|
setValidationResult(null);
|
||||||
setError('');
|
setError('');
|
||||||
|
|
||||||
|
// Automatically trigger validation and classification
|
||||||
|
await handleAutoValidateAndClassify(file);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -92,67 +100,69 @@ export const UploadSalesDataStep: React.FC<UploadSalesDataStepProps> = ({
|
|||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleValidateFile = async () => {
|
const handleAutoValidateAndClassify = async (file: File) => {
|
||||||
if (!selectedFile || !currentTenant?.id) return;
|
if (!currentTenant?.id) return;
|
||||||
|
|
||||||
setIsValidating(true);
|
setIsValidating(true);
|
||||||
setError('');
|
setError('');
|
||||||
setProgressState({ stage: 'preparing', progress: 0, message: 'Preparando validación del archivo...' });
|
setProgressState({ stage: 'preparing', progress: 0, message: 'Preparando validación automática del archivo...' });
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
// Step 1: Validate the file
|
||||||
const result = await validateFile(
|
const result = await validateFile(
|
||||||
currentTenant.id,
|
currentTenant.id,
|
||||||
selectedFile,
|
file,
|
||||||
{
|
{
|
||||||
onProgress: (stage: string, progress: number, message: string) => {
|
onProgress: (stage: string, progress: number, message: string) => {
|
||||||
setProgressState({ stage, progress, message });
|
// Map validation progress to 0-50%
|
||||||
|
setProgressState({ stage, progress: Math.min(progress * 0.5, 50), message });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
if (result.success && result.validationResult) {
|
if (result.success && result.validationResult) {
|
||||||
setValidationResult(result.validationResult);
|
setValidationResult(result.validationResult);
|
||||||
setProgressState(null);
|
setProgressState({ stage: 'analyzing', progress: 60, message: 'Validación exitosa. Generando sugerencias automáticamente...' });
|
||||||
|
|
||||||
|
// Step 2: Automatically trigger classification
|
||||||
|
await generateInventorySuggestionsAuto(result.validationResult);
|
||||||
} else {
|
} else {
|
||||||
setError(result.error || 'Error al validar el archivo');
|
setError(result.error || 'Error al validar el archivo');
|
||||||
setProgressState(null);
|
setProgressState(null);
|
||||||
|
setIsValidating(false);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
setError('Error validando archivo: ' + (error instanceof Error ? error.message : 'Error desconocido'));
|
setError('Error validando archivo: ' + (error instanceof Error ? error.message : 'Error desconocido'));
|
||||||
setProgressState(null);
|
setProgressState(null);
|
||||||
}
|
setIsValidating(false);
|
||||||
|
|
||||||
setIsValidating(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleContinue = () => {
|
|
||||||
if (validationResult) {
|
|
||||||
// Generate inventory suggestions based on validation
|
|
||||||
generateInventorySuggestions();
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const generateInventorySuggestions = async () => {
|
|
||||||
if (!currentTenant?.id || !validationResult) {
|
const generateInventorySuggestionsAuto = async (validationData: ImportValidationResponse) => {
|
||||||
|
if (!currentTenant?.id) {
|
||||||
setError('No hay datos de validación disponibles para generar sugerencias');
|
setError('No hay datos de validación disponibles para generar sugerencias');
|
||||||
|
setIsValidating(false);
|
||||||
|
setProgressState(null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
setProgressState({ stage: 'analyzing', progress: 25, message: 'Analizando productos de ventas...' });
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
setProgressState({ stage: 'analyzing', progress: 65, message: 'Analizando productos de ventas...' });
|
||||||
|
|
||||||
// Extract product data from validation result - use the exact backend structure
|
// Extract product data from validation result - use the exact backend structure
|
||||||
const products = validationResult.product_list?.map((productName: string) => ({
|
const products = validationData.product_list?.map((productName: string) => ({
|
||||||
product_name: productName
|
product_name: productName
|
||||||
})) || [];
|
})) || [];
|
||||||
|
|
||||||
if (products.length === 0) {
|
if (products.length === 0) {
|
||||||
setError('No se encontraron productos en los datos de ventas');
|
setError('No se encontraron productos en los datos de ventas');
|
||||||
setProgressState(null);
|
setProgressState(null);
|
||||||
|
setIsValidating(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
setProgressState({ stage: 'classifying', progress: 50, message: 'Clasificando productos con IA...' });
|
setProgressState({ stage: 'classifying', progress: 75, message: 'Clasificando productos con IA...' });
|
||||||
|
|
||||||
// Call the classification API
|
// Call the classification API
|
||||||
const suggestions = await classifyProducts.mutateAsync({
|
const suggestions = await classifyProducts.mutateAsync({
|
||||||
@@ -160,7 +170,7 @@ export const UploadSalesDataStep: React.FC<UploadSalesDataStepProps> = ({
|
|||||||
batchData: { products }
|
batchData: { products }
|
||||||
});
|
});
|
||||||
|
|
||||||
setProgressState({ stage: 'preparing', progress: 75, message: 'Preparando sugerencias de inventario...' });
|
setProgressState({ stage: 'preparing', progress: 90, message: 'Preparando sugerencias de inventario...' });
|
||||||
|
|
||||||
// Convert API response to InventoryItem format - use exact backend structure plus UI fields
|
// Convert API response to InventoryItem format - use exact backend structure plus UI fields
|
||||||
const items: InventoryItem[] = suggestions.map(suggestion => {
|
const items: InventoryItem[] = suggestions.map(suggestion => {
|
||||||
@@ -201,13 +211,16 @@ export const UploadSalesDataStep: React.FC<UploadSalesDataStepProps> = ({
|
|||||||
setInventoryItems(items);
|
setInventoryItems(items);
|
||||||
setShowInventoryStep(true);
|
setShowInventoryStep(true);
|
||||||
setProgressState(null);
|
setProgressState(null);
|
||||||
|
setIsValidating(false);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Error generating inventory suggestions:', err);
|
console.error('Error generating inventory suggestions:', err);
|
||||||
setError('Error al generar sugerencias de inventario. Por favor, inténtalo de nuevo.');
|
setError('Error al generar sugerencias de inventario. Por favor, inténtalo de nuevo.');
|
||||||
setProgressState(null);
|
setProgressState(null);
|
||||||
|
setIsValidating(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const handleToggleSelection = (id: string) => {
|
const handleToggleSelection = (id: string) => {
|
||||||
setInventoryItems(items =>
|
setInventoryItems(items =>
|
||||||
items.map(item =>
|
items.map(item =>
|
||||||
@@ -328,7 +341,10 @@ export const UploadSalesDataStep: React.FC<UploadSalesDataStepProps> = ({
|
|||||||
totalItems: selectedItems.length,
|
totalItems: selectedItems.length,
|
||||||
validationResult,
|
validationResult,
|
||||||
file: selectedFile,
|
file: selectedFile,
|
||||||
salesImportResult
|
salesImportResult,
|
||||||
|
inventoryConfigured: true, // Flag for ML training dependency
|
||||||
|
shouldAutoCompleteSuppliers: true, // Flag to trigger suppliers auto-completion after step completion
|
||||||
|
userId: user?.id // Pass user ID for suppliers completion
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Error creating inventory items:', err);
|
console.error('Error creating inventory items:', err);
|
||||||
@@ -354,7 +370,7 @@ export const UploadSalesDataStep: React.FC<UploadSalesDataStepProps> = ({
|
|||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
<div className="text-center">
|
<div className="text-center">
|
||||||
<p className="text-[var(--text-secondary)] mb-6">
|
<p className="text-[var(--text-secondary)] mb-6">
|
||||||
Basado en tus datos de ventas, hemos generado estas sugerencias de inventario.
|
¡Perfecto! Hemos analizado automáticamente tus datos de ventas y generado estas sugerencias de inventario inteligentes.
|
||||||
Revisa y selecciona los artículos que te gustaría agregar a tu inventario.
|
Revisa y selecciona los artículos que te gustaría agregar a tu inventario.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -487,14 +503,7 @@ export const UploadSalesDataStep: React.FC<UploadSalesDataStepProps> = ({
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Actions */}
|
{/* Actions */}
|
||||||
<div className="flex flex-col sm:flex-row justify-between gap-3 sm:gap-0">
|
<div className="flex flex-col sm:flex-row justify-end gap-3 sm:gap-0">{/* Removed back button */}
|
||||||
<Button
|
|
||||||
variant="outline"
|
|
||||||
onClick={() => setShowInventoryStep(false)}
|
|
||||||
className="order-2 sm:order-1 w-full sm:w-auto"
|
|
||||||
>
|
|
||||||
Volver
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
onClick={handleCreateInventory}
|
onClick={handleCreateInventory}
|
||||||
@@ -502,7 +511,7 @@ export const UploadSalesDataStep: React.FC<UploadSalesDataStepProps> = ({
|
|||||||
loadingText="Creando Inventario..."
|
loadingText="Creando Inventario..."
|
||||||
size="lg"
|
size="lg"
|
||||||
disabled={selectedCount === 0}
|
disabled={selectedCount === 0}
|
||||||
className="order-1 sm:order-2 w-full sm:w-auto"
|
className="w-full sm:w-auto"
|
||||||
>
|
>
|
||||||
<span className="hidden sm:inline">
|
<span className="hidden sm:inline">
|
||||||
Crear {selectedCount} Artículo{selectedCount !== 1 ? 's' : ''} de Inventario
|
Crear {selectedCount} Artículo{selectedCount !== 1 ? 's' : ''} de Inventario
|
||||||
@@ -520,7 +529,7 @@ export const UploadSalesDataStep: React.FC<UploadSalesDataStepProps> = ({
|
|||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
<div className="text-center">
|
<div className="text-center">
|
||||||
<p className="text-[var(--text-secondary)] mb-6">
|
<p className="text-[var(--text-secondary)] mb-6">
|
||||||
Sube tus datos de ventas (formato CSV o JSON) para generar sugerencias de inventario inteligentes.
|
Sube tus datos de ventas (formato CSV o JSON) y automáticamente validaremos y generaremos sugerencias de inventario inteligentes.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -570,7 +579,8 @@ export const UploadSalesDataStep: React.FC<UploadSalesDataStepProps> = ({
|
|||||||
<p className="text-lg font-medium">Drop your sales data here</p>
|
<p className="text-lg font-medium">Drop your sales data here</p>
|
||||||
<p className="text-[var(--text-secondary)]">or click to browse files</p>
|
<p className="text-[var(--text-secondary)]">or click to browse files</p>
|
||||||
<p className="text-sm text-[var(--text-tertiary)] mt-2">
|
<p className="text-sm text-[var(--text-tertiary)] mt-2">
|
||||||
Supported formats: CSV, JSON (max 100MB)
|
Supported formats: CSV, JSON (max 100MB)<br/>
|
||||||
|
<span className="text-[var(--color-primary)]">Auto-validates and generates suggestions</span>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<Button
|
<Button
|
||||||
@@ -635,39 +645,25 @@ export const UploadSalesDataStep: React.FC<UploadSalesDataStepProps> = ({
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Actions */}
|
{/* Actions */}
|
||||||
<div className="flex flex-col sm:flex-row justify-between gap-3 sm:gap-0">
|
<div className="flex flex-col sm:flex-row justify-end gap-3 sm:gap-0">{/* Removed back button */}
|
||||||
<Button
|
|
||||||
variant="outline"
|
|
||||||
onClick={onPrevious}
|
|
||||||
disabled={isFirstStep}
|
|
||||||
className="order-2 sm:order-1 w-full sm:w-auto"
|
|
||||||
>
|
|
||||||
Anterior
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
<div className="flex flex-col sm:flex-row gap-3 order-1 sm:order-2">
|
{selectedFile && !showInventoryStep && (
|
||||||
{selectedFile && !validationResult && (
|
<div className="flex items-center justify-center px-4 py-2 bg-[var(--bg-secondary)] rounded-lg">
|
||||||
<Button
|
{isValidating ? (
|
||||||
onClick={handleValidateFile}
|
<>
|
||||||
isLoading={isValidating}
|
<div className="animate-spin rounded-full h-4 w-4 border-b-2 border-[var(--color-primary)] mr-2"></div>
|
||||||
loadingText="Validando..."
|
<span className="text-sm text-[var(--text-secondary)]">Procesando automáticamente...</span>
|
||||||
size="lg"
|
</>
|
||||||
className="w-full sm:w-auto"
|
) : validationResult ? (
|
||||||
>
|
<>
|
||||||
Validar Archivo
|
<svg className="w-4 h-4 text-[var(--color-success)] mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
</Button>
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
|
||||||
)}
|
</svg>
|
||||||
|
<span className="text-sm text-[var(--color-success)]">Archivo procesado exitosamente</span>
|
||||||
{validationResult && (
|
</>
|
||||||
<Button
|
) : null}
|
||||||
onClick={handleContinue}
|
</div>
|
||||||
size="lg"
|
)}
|
||||||
className="w-full sm:w-auto"
|
|
||||||
>
|
|
||||||
Continuar con estos Datos
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user