Improve the sales import
This commit is contained in:
@@ -20,6 +20,7 @@ interface TrainingProgress {
|
||||
message: string;
|
||||
currentStep?: string;
|
||||
estimatedTimeRemaining?: number;
|
||||
estimatedCompletionTime?: string;
|
||||
}
|
||||
|
||||
export const MLTrainingStep: React.FC<MLTrainingStepProps> = ({
|
||||
@@ -59,7 +60,8 @@ export const MLTrainingStep: React.FC<MLTrainingStepProps> = ({
|
||||
progress: data.data?.progress || 0,
|
||||
message: data.data?.message || 'Entrenando modelo...',
|
||||
currentStep: data.data?.current_step,
|
||||
estimatedTimeRemaining: data.data?.estimated_time_remaining_seconds || data.data?.estimated_time_remaining
|
||||
estimatedTimeRemaining: data.data?.estimated_time_remaining_seconds || data.data?.estimated_time_remaining,
|
||||
estimatedCompletionTime: data.data?.estimated_completion_time
|
||||
});
|
||||
}, []);
|
||||
|
||||
@@ -221,7 +223,7 @@ export const MLTrainingStep: React.FC<MLTrainingStepProps> = ({
|
||||
|
||||
const formatTime = (seconds?: number) => {
|
||||
if (!seconds) return '';
|
||||
|
||||
|
||||
if (seconds < 60) {
|
||||
return `${Math.round(seconds)}s`;
|
||||
} else if (seconds < 3600) {
|
||||
@@ -231,6 +233,33 @@ export const MLTrainingStep: React.FC<MLTrainingStepProps> = ({
|
||||
}
|
||||
};
|
||||
|
||||
const formatEstimatedCompletionTime = (isoString?: string) => {
|
||||
if (!isoString) return '';
|
||||
|
||||
try {
|
||||
const completionDate = new Date(isoString);
|
||||
const now = new Date();
|
||||
|
||||
// If completion is today, show time only
|
||||
if (completionDate.toDateString() === now.toDateString()) {
|
||||
return completionDate.toLocaleTimeString('es-ES', {
|
||||
hour: '2-digit',
|
||||
minute: '2-digit'
|
||||
});
|
||||
}
|
||||
|
||||
// If completion is another day, show date and time
|
||||
return completionDate.toLocaleString('es-ES', {
|
||||
month: 'short',
|
||||
day: 'numeric',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit'
|
||||
});
|
||||
} catch (error) {
|
||||
return '';
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div className="text-center">
|
||||
@@ -293,24 +322,61 @@ export const MLTrainingStep: React.FC<MLTrainingStepProps> = ({
|
||||
</p>
|
||||
|
||||
{trainingProgress.stage !== 'completed' && (
|
||||
<div className="space-y-2">
|
||||
<div className="w-full bg-[var(--bg-tertiary)] rounded-full h-2">
|
||||
<div
|
||||
className="bg-[var(--color-primary)] h-2 rounded-full transition-all duration-300"
|
||||
style={{ width: `${trainingProgress.progress}%` }}
|
||||
/>
|
||||
<div className="space-y-3">
|
||||
{/* Enhanced Progress Bar */}
|
||||
<div className="relative">
|
||||
<div className="w-full bg-[var(--bg-tertiary)] rounded-full h-3 overflow-hidden">
|
||||
<div
|
||||
className="bg-gradient-to-r from-[var(--color-primary)] to-[var(--color-primary)]/80 h-3 rounded-full transition-all duration-500 ease-out relative"
|
||||
style={{ width: `${trainingProgress.progress}%` }}
|
||||
>
|
||||
{/* Animated shimmer effect */}
|
||||
<div className="absolute inset-0 bg-gradient-to-r from-transparent via-white/20 to-transparent animate-shimmer"></div>
|
||||
</div>
|
||||
</div>
|
||||
{/* Progress percentage badge */}
|
||||
<div className="absolute -top-1 left-1/2 transform -translate-x-1/2 -translate-y-full mb-1">
|
||||
<span className="text-xs font-semibold text-[var(--color-primary)] bg-[var(--bg-primary)] px-2 py-1 rounded-full shadow-sm border border-[var(--color-primary)]/20">
|
||||
{trainingProgress.progress}%
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex justify-between text-xs text-[var(--text-tertiary)]">
|
||||
<span>{trainingProgress.currentStep || t('onboarding:steps.ml_training.progress.data_preparation', 'Procesando...')}</span>
|
||||
<div className="flex items-center gap-2">
|
||||
{/* Training Information */}
|
||||
<div className="flex flex-col gap-2 text-xs text-[var(--text-tertiary)]">
|
||||
{/* Current Step */}
|
||||
<div className="flex justify-between items-center">
|
||||
<span className="font-medium">{trainingProgress.currentStep || t('onboarding:steps.ml_training.progress.data_preparation', 'Procesando...')}</span>
|
||||
{jobId && (
|
||||
<span className={`text-xs ${isConnected ? 'text-green-500' : 'text-red-500'}`}>
|
||||
{isConnected ? '🟢 Conectado' : '🔴 Desconectado'}
|
||||
<span className={`text-xs px-2 py-0.5 rounded-full ${isConnected ? 'bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400' : 'bg-red-100 text-red-700 dark:bg-red-900/30 dark:text-red-400'}`}>
|
||||
{isConnected ? '● En vivo' : '● Reconectando...'}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Time Information */}
|
||||
<div className="flex flex-wrap gap-x-4 gap-y-1 text-xs">
|
||||
{trainingProgress.estimatedTimeRemaining && (
|
||||
<span>{t('onboarding:steps.ml_training.estimated_time_remaining', 'Tiempo restante estimado: {{time}}', { time: formatTime(trainingProgress.estimatedTimeRemaining) })}</span>
|
||||
<div className="flex items-center gap-1">
|
||||
<svg className="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||
</svg>
|
||||
<span>
|
||||
{t('onboarding:steps.ml_training.estimated_time_remaining', 'Tiempo restante: {{time}}', {
|
||||
time: formatTime(trainingProgress.estimatedTimeRemaining)
|
||||
})}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
{trainingProgress.estimatedCompletionTime && (
|
||||
<div className="flex items-center gap-1">
|
||||
<svg className="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" />
|
||||
</svg>
|
||||
<span>
|
||||
Finalizará: {formatEstimatedCompletionTime(trainingProgress.estimatedCompletionTime)}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user