Fix and UI imporvements

This commit is contained in:
Urtzi Alfaro
2025-12-09 10:21:41 +01:00
parent 667e6e0404
commit 508f4569b9
22 changed files with 833 additions and 953 deletions

View File

@@ -58,6 +58,7 @@ const DemoPage = () => {
const [creationError, setCreationError] = useState('');
const [estimatedProgress, setEstimatedProgress] = useState(0);
const [progressStartTime, setProgressStartTime] = useState<number | null>(null);
const [estimatedRemainingSeconds, setEstimatedRemainingSeconds] = useState<number | null>(null);
// BUG-010 FIX: State for partial status warning
const [partialWarning, setPartialWarning] = useState<{
@@ -227,19 +228,19 @@ const DemoPage = () => {
} catch (error) {
console.error('Error creating demo:', error);
setCreationError('Error al iniciar la demo. Por favor, inténtalo de nuevo.');
} finally {
setCreatingTier(null);
setProgressStartTime(null);
setEstimatedProgress(0);
// Reset progress
setCloneProgress({
parent: 0,
children: [0, 0, 0],
distribution: 0,
overall: 0
});
setCreationError('Error al iniciar la demo. Por favor, inténtalo de nuevo.');
}
// NOTE: State reset moved to navigation callback and error handlers
// to prevent modal from disappearing before redirect
};
const pollForSessionStatus = async (sessionId, tier, sessionData) => {
@@ -287,17 +288,33 @@ const DemoPage = () => {
const statusData = await statusResponse.json();
// Capture estimated remaining time from backend
if (statusData.estimated_remaining_seconds !== undefined) {
setEstimatedRemainingSeconds(statusData.estimated_remaining_seconds);
}
// Update progress based on actual backend status
updateProgressFromBackendStatus(statusData, tier);
// BUG-010 FIX: Handle ready status separately from partial
if (statusData.status === 'ready') {
// Full success - navigate immediately
// Full success - set to 100% and navigate after delay
clearInterval(progressInterval);
setCloneProgress(prev => ({ ...prev, overall: 100 }));
setTimeout(() => {
// Reset state before navigation
setCreatingTier(null);
setProgressStartTime(null);
setEstimatedProgress(0);
setCloneProgress({
parent: 0,
children: [0, 0, 0],
distribution: 0,
overall: 0
});
// Navigate to the main dashboard which will automatically route to enterprise or bakery dashboard based on subscription tier
navigate('/app/dashboard');
}, 1000);
}, 1500); // Increased from 1000ms to show 100% completion
return;
} else if (statusData.status === 'PARTIAL' || statusData.status === 'partial') {
// BUG-010 FIX: Show warning modal for partial status
@@ -313,6 +330,15 @@ const DemoPage = () => {
return;
} else if (statusData.status === 'FAILED' || statusData.status === 'failed') {
clearInterval(progressInterval);
setCreatingTier(null);
setProgressStartTime(null);
setEstimatedProgress(0);
setCloneProgress({
parent: 0,
children: [0, 0, 0],
distribution: 0,
overall: 0
});
setCreationError('Error al clonar los datos de demo. Por favor, inténtalo de nuevo.');
return;
}
@@ -343,6 +369,15 @@ const DemoPage = () => {
} catch (error) {
clearInterval(progressInterval);
console.error('Error polling for status:', error);
setCreatingTier(null);
setProgressStartTime(null);
setEstimatedProgress(0);
setCloneProgress({
parent: 0,
children: [0, 0, 0],
distribution: 0,
overall: 0
});
setCreationError('Error verificando el estado de la demo. Por favor, inténtalo de nuevo.');
} finally {
// Clean up abort controller reference
@@ -466,7 +501,9 @@ const DemoPage = () => {
if (progress.parent && progress.children && progress.distribution !== undefined) {
// This looks like an enterprise results structure from the end of cloning
// Calculate progress based on parent, children, and distribution status
if (progress.parent.overall_status === 'ready' || progress.parent.overall_status === 'partial') {
// FIX 1: Handle both "completed" and "ready" for parent status
const parentStatus = progress.parent.overall_status;
if (parentStatus === 'ready' || parentStatus === 'completed' || parentStatus === 'partial') {
parentProgress = 100;
} else if (progress.parent.overall_status === 'pending') {
parentProgress = 50; // Increased from 25 for better perceived progress
@@ -482,9 +519,11 @@ const DemoPage = () => {
if (progress.children && progress.children.length > 0) {
childrenProgressArray = progress.children.map((child: any) => {
if (child.status === 'ready' || child.status === 'completed') return 100;
if (child.status === 'partial') return 75;
if (child.status === 'pending') return 30;
// FIX 2: Handle both status types for children
const childStatus = child.status || child.overall_status;
if (childStatus === 'ready' || childStatus === 'completed') return 100;
if (childStatus === 'partial') return 75;
if (childStatus === 'pending') return 30;
return 0;
});
const avgChildrenProgress = childrenProgressArray.reduce((a, b) => a + b, 0) / childrenProgressArray.length;
@@ -499,15 +538,22 @@ const DemoPage = () => {
}
if (progress.distribution) {
if (progress.distribution.status === 'ready' || progress.distribution.status === 'completed') {
// FIX 3: Handle both status types for distribution
const distStatus = progress.distribution.status || progress.distribution.overall_status;
if (distStatus === 'ready' || distStatus === 'completed') {
distributionProgress = 100;
} else if (progress.distribution.status === 'pending') {
} else if (distStatus === 'pending') {
distributionProgress = 50;
} else {
distributionProgress = progress.distribution.status === 'failed' ? 100 : 75;
distributionProgress = distStatus === 'failed' ? 100 : 75;
}
backendProgress = Math.round(backendProgress * 0.8 + distributionProgress * 0.2);
}
// FIX 4: Allow 100% progress when all components complete
if (parentProgress === 100 && childrenProgressArray.every(p => p === 100) && distributionProgress === 100) {
backendProgress = 100;
}
} else {
// If it's not the enterprise result structure, fall back to service-based calculation
const services = progress || {};
@@ -525,8 +571,9 @@ const DemoPage = () => {
distributionProgress = backendProgress * 0.8;
}
// Use the maximum of backend progress and estimated progress to prevent backtracking
const overallProgress = Math.max(Math.min(95, backendProgress), estimatedProgress);
// FIX 5: Don't cap at 95% when backend reports 100%
const cappedBackendProgress = backendProgress === 100 ? 100 : Math.min(95, backendProgress);
const overallProgress = Math.max(cappedBackendProgress, estimatedProgress);
setCloneProgress({
parent: Math.max(parentProgress, estimatedProgress * 0.9),
@@ -681,61 +728,127 @@ const DemoPage = () => {
<Modal
isOpen={creatingTier !== null}
onClose={() => { }}
size="md"
size="lg"
>
<ModalHeader
title="Configurando Tu Demo"
title={
<div className="flex items-center gap-3">
<div className="animate-spin rounded-full h-5 w-5 border-b-2 border-primary"></div>
<span>Configurando Tu Demo</span>
</div>
}
showCloseButton={false}
/>
<ModalBody padding="lg">
<div className="space-y-4">
<div className="flex justify-between text-sm">
<span>Progreso total</span>
<span>{cloneProgress.overall}%</span>
</div>
<div className="w-full bg-gray-200 dark:bg-gray-700 rounded-full h-2">
<div
className="bg-primary h-2 rounded-full transition-all duration-300"
style={{ width: `${cloneProgress.overall}%` }}
></div>
</div>
<div className="text-center text-sm text-[var(--text-secondary)] mt-4">
{getLoadingMessage(creatingTier, cloneProgress.overall)}
</div>
{creatingTier === 'enterprise' && (
<div className="space-y-3 mt-4">
<div className="flex justify-between text-sm">
<span className="font-medium">Obrador Central</span>
<span>{cloneProgress.parent}%</span>
<div className="space-y-6">
{/* Overall Progress Section */}
<div className="text-center">
<div className="flex justify-between text-sm mb-2">
<span className="font-medium">Progreso Total</span>
<span className="font-semibold text-lg">{cloneProgress.overall}%</span>
</div>
<div className="w-full bg-gray-200 dark:bg-gray-700 rounded-full h-3 overflow-hidden">
<div
className="bg-gradient-to-r from-blue-500 to-purple-600 h-3 rounded-full transition-all duration-500 relative overflow-hidden"
style={{ width: `${cloneProgress.overall}%` }}
>
<div className="absolute inset-0 bg-gradient-to-r from-transparent via-white/40 to-transparent animate-shimmer"></div>
</div>
</div>
{estimatedRemainingSeconds !== null && estimatedRemainingSeconds > 0 && (
<div className="mt-3 text-sm text-[var(--text-secondary)]">
~{estimatedRemainingSeconds}s restantes
</div>
)}
<div className="mt-4 text-[var(--text-secondary)]">
{getLoadingMessage(creatingTier, cloneProgress.overall)}
</div>
</div>
{/* Enterprise Detailed Progress */}
{creatingTier === 'enterprise' && (
<div className="space-y-5 mt-6">
{/* Parent Tenant */}
<div className="bg-blue-50 dark:bg-blue-900/20 rounded-xl p-4 border border-blue-200 dark:border-blue-800">
<div className="flex justify-between items-center mb-2">
<div className="flex items-center gap-2">
<div className="w-3 h-3 rounded-full bg-blue-500"></div>
<span className="font-semibold text-blue-900 dark:text-blue-100">Obrador Central</span>
</div>
<span className="font-medium text-blue-700 dark:text-blue-300">{cloneProgress.parent}%</span>
</div>
<div className="w-full bg-gray-200 dark:bg-gray-700 rounded-full h-2.5">
<div
className="bg-gradient-to-r from-blue-400 to-blue-600 h-2.5 rounded-full transition-all duration-500"
style={{ width: `${cloneProgress.parent}%` }}
></div>
</div>
</div>
{/* Child Outlets */}
<div className="grid grid-cols-3 gap-3">
{cloneProgress.children.map((progress, index) => (
<div key={index} className="text-center">
<div className="text-xs text-[var(--text-tertiary)] mb-1">Outlet {index + 1}</div>
<div className="w-full bg-gray-200 dark:bg-gray-700 rounded-full h-1.5">
<div
key={index}
className="bg-green-50 dark:bg-green-900/20 rounded-lg p-3 border border-green-200 dark:border-green-800"
>
<div className="flex justify-between items-center mb-1">
<span className="text-xs font-medium text-green-700 dark:text-green-300">Outlet {index + 1}</span>
<span className="text-xs font-semibold text-green-700 dark:text-green-300">{progress}%</span>
</div>
<div className="w-full bg-gray-200 dark:bg-gray-700 rounded-full h-2">
<div
className="bg-blue-500 h-1.5 rounded-full transition-all duration-300"
className="bg-gradient-to-r from-green-400 to-green-600 h-2 rounded-full transition-all duration-500"
style={{ width: `${progress}%` }}
></div>
</div>
<div className="text-xs mt-1">{progress}%</div>
</div>
))}
</div>
<div className="flex justify-between text-sm mt-2">
<span className="font-medium">Distribución</span>
<span>{cloneProgress.distribution}%</span>
</div>
<div className="w-full bg-gray-200 dark:bg-gray-700 rounded-full h-1.5">
<div
className="bg-purple-500 h-1.5 rounded-full transition-all duration-300"
style={{ width: `${cloneProgress.distribution}%` }}
></div>
{/* Distribution System */}
<div className="bg-purple-50 dark:bg-purple-900/20 rounded-xl p-4 border border-purple-200 dark:border-purple-800">
<div className="flex justify-between items-center mb-2">
<div className="flex items-center gap-2">
<div className="w-3 h-3 rounded-full bg-purple-500"></div>
<span className="font-semibold text-purple-900 dark:text-purple-100">Distribución</span>
</div>
<span className="font-medium text-purple-700 dark:text-purple-300">{cloneProgress.distribution}%</span>
</div>
<div className="w-full bg-gray-200 dark:bg-gray-700 rounded-full h-2.5">
<div
className="bg-gradient-to-r from-purple-400 to-purple-600 h-2.5 rounded-full transition-all duration-500"
style={{ width: `${cloneProgress.distribution}%` }}
></div>
</div>
</div>
</div>
)}
{/* Professional Progress Indicator */}
{creatingTier === 'professional' && cloneProgress.overall < 100 && (
<div className="text-center py-3">
<div className="flex justify-center items-center gap-1">
<div className="w-2 h-2 bg-primary rounded-full animate-bounce" style={{ animationDelay: '0ms' }}></div>
<div className="w-2 h-2 bg-primary rounded-full animate-bounce" style={{ animationDelay: '150ms' }}></div>
<div className="w-2 h-2 bg-primary rounded-full animate-bounce" style={{ animationDelay: '300ms' }}></div>
</div>
<p className="text-sm text-[var(--text-tertiary)] mt-2">
Procesando servicios en paralelo...
</p>
</div>
)}
{/* Information Box */}
<div className="bg-gray-50 dark:bg-gray-800/50 rounded-lg p-3 border border-gray-200 dark:border-gray-700">
<p className="text-xs text-[var(--text-tertiary)] text-center">
{creatingTier === 'enterprise'
? 'Creando obrador central, outlets y sistema de distribución...'
: 'Personalizando tu panadería con datos reales...'}
</p>
</div>
</div>
</ModalBody>
</Modal>