Add onboarding flow improvements

This commit is contained in:
Urtzi Alfaro
2025-09-03 14:06:38 +02:00
parent 0fb9f9d0f0
commit a55d48e635
31 changed files with 3813 additions and 6251 deletions

View File

@@ -0,0 +1,161 @@
import React, { useState, useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import { OnboardingWizard, OnboardingStep } from '../../../components/domain/onboarding/OnboardingWizard';
import { onboardingApiService } from '../../../services/api/onboarding.service';
import { useAuth } from '../../../hooks/useAuth';
import { LoadingSpinner } from '../../../components/shared/LoadingSpinner';
// Step Components
import { BakerySetupStep } from '../../../components/domain/onboarding/steps/BakerySetupStep';
import { DataProcessingStep } from '../../../components/domain/onboarding/steps/DataProcessingStep';
import { ReviewStep } from '../../../components/domain/onboarding/steps/ReviewStep';
import { InventorySetupStep } from '../../../components/domain/onboarding/steps/InventorySetupStep';
import { SuppliersStep } from '../../../components/domain/onboarding/steps/SuppliersStep';
import { MLTrainingStep } from '../../../components/domain/onboarding/steps/MLTrainingStep';
import { CompletionStep } from '../../../components/domain/onboarding/steps/CompletionStep';
const OnboardingPage: React.FC = () => {
const navigate = useNavigate();
const { user } = useAuth();
const [isLoading, setIsLoading] = useState(false);
const [globalData, setGlobalData] = useState<any>({});
// Define the 8 onboarding steps (simplified by merging data upload + analysis)
const steps: OnboardingStep[] = [
{
id: 'setup',
title: '🏢 Setup',
description: 'Configuración básica de tu panadería y creación del tenant',
component: BakerySetupStep,
isRequired: true,
validation: (data) => {
if (!data.bakery?.name) return 'El nombre de la panadería es requerido';
if (!data.bakery?.type) return 'El tipo de panadería es requerido';
if (!data.bakery?.location) return 'La ubicación es requerida';
// Tenant creation will happen automatically when validation passes
return null;
}
},
{
id: 'data-processing',
title: '📊 Historial de Ventas',
description: 'Sube tus datos de ventas para obtener insights personalizados',
component: DataProcessingStep,
isRequired: true,
validation: (data) => {
if (!data.files?.salesData) return 'Debes cargar el archivo de datos de ventas';
if (data.processingStage !== 'completed') return 'El procesamiento debe completarse antes de continuar';
if (!data.processingResults?.is_valid) return 'Los datos deben ser válidos para continuar';
return null;
}
},
{
id: 'review',
title: '📋 Revisión',
description: 'Revisión de productos detectados por IA y resultados',
component: ReviewStep,
isRequired: true,
validation: (data) => {
if (!data.reviewCompleted) return 'Debes revisar y aprobar los productos detectados';
return null;
}
},
{
id: 'inventory',
title: '⚙️ Inventario',
description: 'Configuración de inventario (stock, fechas de vencimiento)',
component: InventorySetupStep,
isRequired: true,
validation: (data) => {
if (!data.inventoryConfigured) return 'Debes configurar el inventario básico';
return null;
}
},
{
id: 'suppliers',
title: '🏪 Proveedores',
description: 'Configuración de proveedores y asociaciones',
component: SuppliersStep,
isRequired: false,
validation: () => null // Optional step
},
{
id: 'ml-training',
title: '🎯 Inteligencia',
description: 'Creación de tu asistente inteligente personalizado',
component: MLTrainingStep,
isRequired: true,
validation: (data) => {
if (data.trainingStatus !== 'completed') return 'El entrenamiento del modelo debe completarse';
return null;
}
},
{
id: 'completion',
title: '🎉 Listo',
description: 'Finalización y preparación para usar la plataforma',
component: CompletionStep,
isRequired: true,
validation: () => null
}
];
const handleComplete = async (allData: any) => {
setIsLoading(true);
try {
// Mark onboarding as complete in the backend
if (user?.tenant_id) {
await onboardingApiService.completeOnboarding(user.tenant_id, {
completedAt: new Date().toISOString(),
data: allData
});
}
// Navigate to dashboard
navigate('/app/dashboard', {
state: {
message: '¡Felicidades! Tu panadería ha sido configurada exitosamente.',
type: 'success'
}
});
} catch (error) {
console.error('Error completing onboarding:', error);
// Still navigate to dashboard but show warning
navigate('/app/dashboard', {
state: {
message: 'Configuración completada. Algunos ajustes finales pueden estar pendientes.',
type: 'warning'
}
});
} finally {
setIsLoading(false);
}
};
const handleExit = () => {
const confirmExit = window.confirm(
'¿Estás seguro de que quieres salir del proceso de configuración? Tu progreso se guardará automáticamente.'
);
if (confirmExit) {
navigate('/app/dashboard');
}
};
if (isLoading) {
return <LoadingSpinner overlay text="Completando configuración..." />;
}
return (
<div className="min-h-screen bg-[var(--bg-primary)]">
<OnboardingWizard
steps={steps}
onComplete={handleComplete}
onExit={handleExit}
className="py-8"
/>
</div>
);
};
export default OnboardingPage;

View File

@@ -1,451 +0,0 @@
import React, { useState } from 'react';
import { BarChart3, TrendingUp, Target, AlertCircle, CheckCircle, Eye, Download } from 'lucide-react';
import { Button, Card, Badge } from '../../../../components/ui';
import { PageHeader } from '../../../../components/layout';
const OnboardingAnalysisPage: React.FC = () => {
const [selectedPeriod, setSelectedPeriod] = useState('30days');
const analysisData = {
onboardingScore: 87,
completionRate: 92,
averageTime: '4.2 días',
stepsCompleted: 15,
totalSteps: 16,
dataQuality: 94
};
const stepProgress = [
{ step: 'Información Básica', completed: true, quality: 95, timeSpent: '25 min' },
{ step: 'Configuración de Menú', completed: true, quality: 88, timeSpent: '1.2 horas' },
{ step: 'Datos de Inventario', completed: true, quality: 92, timeSpent: '45 min' },
{ step: 'Configuración de Horarios', completed: true, quality: 100, timeSpent: '15 min' },
{ step: 'Integración de Pagos', completed: true, quality: 85, timeSpent: '30 min' },
{ step: 'Carga de Productos', completed: true, quality: 90, timeSpent: '2.1 horas' },
{ step: 'Configuración de Personal', completed: true, quality: 87, timeSpent: '40 min' },
{ step: 'Pruebas del Sistema', completed: false, quality: 0, timeSpent: '-' }
];
const insights = [
{
type: 'success',
title: 'Excelente Progreso',
description: 'Has completado el 94% del proceso de configuración inicial',
recommendation: 'Solo faltan las pruebas finales del sistema para completar tu configuración',
impact: 'high'
},
{
type: 'info',
title: 'Calidad de Datos Alta',
description: 'Tus datos tienen una calidad promedio del 94%',
recommendation: 'Considera revisar la configuración de pagos para mejorar la puntuación',
impact: 'medium'
},
{
type: 'warning',
title: 'Paso Pendiente',
description: 'Las pruebas del sistema están pendientes',
recommendation: 'Programa las pruebas para validar la configuración completa',
impact: 'high'
}
];
const dataAnalysis = [
{
category: 'Información del Negocio',
completeness: 100,
accuracy: 95,
items: 12,
issues: 0,
details: 'Toda la información básica está completa y verificada'
},
{
category: 'Menú y Productos',
completeness: 85,
accuracy: 88,
items: 45,
issues: 3,
details: '3 productos sin precios definidos'
},
{
category: 'Inventario Inicial',
completeness: 92,
accuracy: 90,
items: 28,
issues: 2,
details: '2 ingredientes sin stock mínimo definido'
},
{
category: 'Configuración Operativa',
completeness: 100,
accuracy: 100,
items: 8,
issues: 0,
details: 'Horarios y políticas completamente configuradas'
}
];
const benchmarkComparison = {
industry: {
onboardingScore: 74,
completionRate: 78,
averageTime: '6.8 días'
},
yourData: {
onboardingScore: 87,
completionRate: 92,
averageTime: '4.2 días'
}
};
const recommendations = [
{
priority: 'high',
title: 'Completar Pruebas del Sistema',
description: 'Realizar pruebas integrales para validar toda la configuración',
estimatedTime: '30 minutos',
impact: 'Garantiza funcionamiento óptimo del sistema'
},
{
priority: 'medium',
title: 'Revisar Precios de Productos',
description: 'Definir precios para los 3 productos pendientes',
estimatedTime: '15 minutos',
impact: 'Permitirá generar ventas de todos los productos'
},
{
priority: 'medium',
title: 'Configurar Stocks Mínimos',
description: 'Establecer niveles mínimos para 2 ingredientes',
estimatedTime: '10 minutos',
impact: 'Mejorará el control de inventario automático'
},
{
priority: 'low',
title: 'Optimizar Configuración de Pagos',
description: 'Revisar métodos de pago y comisiones',
estimatedTime: '20 minutos',
impact: 'Puede reducir costos de transacción'
}
];
const getInsightIcon = (type: string) => {
const iconProps = { className: "w-5 h-5" };
switch (type) {
case 'success': return <CheckCircle {...iconProps} className="w-5 h-5 text-[var(--color-success)]" />;
case 'warning': return <AlertCircle {...iconProps} className="w-5 h-5 text-yellow-600" />;
case 'info': return <Target {...iconProps} className="w-5 h-5 text-[var(--color-info)]" />;
default: return <AlertCircle {...iconProps} />;
}
};
const getInsightColor = (type: string) => {
switch (type) {
case 'success': return 'bg-green-50 border-green-200';
case 'warning': return 'bg-yellow-50 border-yellow-200';
case 'info': return 'bg-[var(--color-info)]/5 border-[var(--color-info)]/20';
default: return 'bg-[var(--bg-secondary)] border-[var(--border-primary)]';
}
};
const getPriorityColor = (priority: string) => {
switch (priority) {
case 'high': return 'red';
case 'medium': return 'yellow';
case 'low': return 'green';
default: return 'gray';
}
};
const getCompletionColor = (percentage: number) => {
if (percentage >= 95) return 'text-[var(--color-success)]';
if (percentage >= 80) return 'text-yellow-600';
return 'text-[var(--color-error)]';
};
return (
<div className="p-6 space-y-6">
<PageHeader
title="Análisis de Configuración"
description="Análisis detallado de tu proceso de configuración y recomendaciones"
action={
<div className="flex space-x-2">
<Button variant="outline">
<Eye className="w-4 h-4 mr-2" />
Ver Detalles
</Button>
<Button variant="outline">
<Download className="w-4 h-4 mr-2" />
Exportar Reporte
</Button>
</div>
}
/>
{/* Overall Score */}
<Card className="p-6">
<div className="grid grid-cols-1 md:grid-cols-3 lg:grid-cols-6 gap-6">
<div className="text-center">
<div className="relative w-24 h-24 mx-auto mb-3">
<div className="absolute inset-0 flex items-center justify-center">
<span className="text-2xl font-bold text-[var(--color-success)]">{analysisData.onboardingScore}</span>
</div>
<svg className="w-24 h-24 transform -rotate-90">
<circle
cx="48"
cy="48"
r="40"
stroke="currentColor"
strokeWidth="8"
fill="none"
className="text-gray-200"
/>
<circle
cx="48"
cy="48"
r="40"
stroke="currentColor"
strokeWidth="8"
fill="none"
strokeDasharray={`${analysisData.onboardingScore * 2.51} 251`}
className="text-[var(--color-success)]"
/>
</svg>
</div>
<p className="text-sm font-medium text-[var(--text-secondary)]">Puntuación General</p>
</div>
<div className="text-center">
<p className="text-3xl font-bold text-[var(--color-info)]">{analysisData.completionRate}%</p>
<p className="text-sm font-medium text-[var(--text-secondary)]">Completado</p>
</div>
<div className="text-center">
<p className="text-3xl font-bold text-purple-600">{analysisData.averageTime}</p>
<p className="text-sm font-medium text-[var(--text-secondary)]">Tiempo Promedio</p>
</div>
<div className="text-center">
<p className="text-3xl font-bold text-[var(--color-primary)]">{analysisData.stepsCompleted}/{analysisData.totalSteps}</p>
<p className="text-sm font-medium text-[var(--text-secondary)]">Pasos Completados</p>
</div>
<div className="text-center">
<p className="text-3xl font-bold text-teal-600">{analysisData.dataQuality}%</p>
<p className="text-sm font-medium text-[var(--text-secondary)]">Calidad de Datos</p>
</div>
<div className="text-center">
<div className="flex items-center justify-center mb-2">
<TrendingUp className="w-8 h-8 text-[var(--color-success)]" />
</div>
<p className="text-sm font-medium text-[var(--text-secondary)]">Por encima del promedio</p>
</div>
</div>
</Card>
{/* Progress Analysis */}
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
<Card className="p-6">
<h3 className="text-lg font-semibold text-[var(--text-primary)] mb-4">Progreso por Pasos</h3>
<div className="space-y-4">
{stepProgress.map((step, index) => (
<div key={index} className="flex items-center justify-between p-3 border rounded-lg">
<div className="flex items-center space-x-3">
<div className={`w-8 h-8 rounded-full flex items-center justify-center ${
step.completed ? 'bg-[var(--color-success)]/10' : 'bg-[var(--bg-tertiary)]'
}`}>
{step.completed ? (
<CheckCircle className="w-5 h-5 text-[var(--color-success)]" />
) : (
<span className="text-sm font-medium text-[var(--text-tertiary)]">{index + 1}</span>
)}
</div>
<div>
<p className="text-sm font-medium text-[var(--text-primary)]">{step.step}</p>
<p className="text-xs text-[var(--text-tertiary)]">Tiempo: {step.timeSpent}</p>
</div>
</div>
<div className="text-right">
<p className={`text-sm font-medium ${getCompletionColor(step.quality)}`}>
{step.quality}%
</p>
<p className="text-xs text-[var(--text-tertiary)]">Calidad</p>
</div>
</div>
))}
</div>
</Card>
<Card className="p-6">
<h3 className="text-lg font-semibold text-[var(--text-primary)] mb-4">Comparación con la Industria</h3>
<div className="space-y-6">
<div>
<div className="flex justify-between items-center mb-2">
<span className="text-sm font-medium text-[var(--text-secondary)]">Puntuación de Configuración</span>
<div className="text-right">
<span className="text-lg font-bold text-[var(--color-success)]">{benchmarkComparison.yourData.onboardingScore}</span>
<span className="text-sm text-[var(--text-tertiary)] ml-2">vs {benchmarkComparison.industry.onboardingScore}</span>
</div>
</div>
<div className="w-full bg-[var(--bg-quaternary)] rounded-full h-2">
<div className="bg-green-600 h-2 rounded-full" style={{ width: `${(benchmarkComparison.yourData.onboardingScore / 100) * 100}%` }}></div>
</div>
</div>
<div>
<div className="flex justify-between items-center mb-2">
<span className="text-sm font-medium text-[var(--text-secondary)]">Tasa de Completado</span>
<div className="text-right">
<span className="text-lg font-bold text-[var(--color-info)]">{benchmarkComparison.yourData.completionRate}%</span>
<span className="text-sm text-[var(--text-tertiary)] ml-2">vs {benchmarkComparison.industry.completionRate}%</span>
</div>
</div>
<div className="w-full bg-[var(--bg-quaternary)] rounded-full h-2">
<div className="bg-blue-600 h-2 rounded-full" style={{ width: `${benchmarkComparison.yourData.completionRate}%` }}></div>
</div>
</div>
<div>
<div className="flex justify-between items-center mb-2">
<span className="text-sm font-medium text-[var(--text-secondary)]">Tiempo de Configuración</span>
<div className="text-right">
<span className="text-lg font-bold text-purple-600">{benchmarkComparison.yourData.averageTime}</span>
<span className="text-sm text-[var(--text-tertiary)] ml-2">vs {benchmarkComparison.industry.averageTime}</span>
</div>
</div>
<div className="bg-green-50 p-3 rounded-lg">
<p className="text-sm text-[var(--color-success)]">38% más rápido que el promedio de la industria</p>
</div>
</div>
</div>
</Card>
</div>
{/* Insights */}
<Card className="p-6">
<h3 className="text-lg font-semibold text-[var(--text-primary)] mb-4">Insights y Recomendaciones</h3>
<div className="space-y-4">
{insights.map((insight, index) => (
<div key={index} className={`p-4 rounded-lg border ${getInsightColor(insight.type)}`}>
<div className="flex items-start space-x-3">
{getInsightIcon(insight.type)}
<div className="flex-1">
<h4 className="text-sm font-medium text-[var(--text-primary)] mb-1">{insight.title}</h4>
<p className="text-sm text-[var(--text-secondary)] mb-2">{insight.description}</p>
<p className="text-sm font-medium text-[var(--text-primary)]">
Recomendación: {insight.recommendation}
</p>
</div>
<Badge variant={insight.impact === 'high' ? 'red' : insight.impact === 'medium' ? 'yellow' : 'green'}>
{insight.impact === 'high' ? 'Alto' : insight.impact === 'medium' ? 'Medio' : 'Bajo'} Impacto
</Badge>
</div>
</div>
))}
</div>
</Card>
{/* Data Analysis */}
<Card className="p-6">
<h3 className="text-lg font-semibold text-[var(--text-primary)] mb-4">Análisis de Calidad de Datos</h3>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
{dataAnalysis.map((category, index) => (
<div key={index} className="border rounded-lg p-4">
<div className="flex items-center justify-between mb-3">
<h4 className="font-medium text-[var(--text-primary)]">{category.category}</h4>
<div className="flex items-center space-x-2">
<BarChart3 className="w-4 h-4 text-[var(--text-tertiary)]" />
<span className="text-sm text-[var(--text-tertiary)]">{category.items} elementos</span>
</div>
</div>
<div className="space-y-3">
<div>
<div className="flex justify-between text-sm mb-1">
<span>Completitud</span>
<span className={getCompletionColor(category.completeness)}>{category.completeness}%</span>
</div>
<div className="w-full bg-[var(--bg-quaternary)] rounded-full h-2">
<div
className="bg-blue-600 h-2 rounded-full transition-all duration-300"
style={{ width: `${category.completeness}%` }}
></div>
</div>
</div>
<div>
<div className="flex justify-between text-sm mb-1">
<span>Precisión</span>
<span className={getCompletionColor(category.accuracy)}>{category.accuracy}%</span>
</div>
<div className="w-full bg-[var(--bg-quaternary)] rounded-full h-2">
<div
className="bg-green-600 h-2 rounded-full transition-all duration-300"
style={{ width: `${category.accuracy}%` }}
></div>
</div>
</div>
{category.issues > 0 && (
<div className="flex items-center text-sm text-[var(--color-error)]">
<AlertCircle className="w-4 h-4 mr-1" />
<span>{category.issues} problema{category.issues > 1 ? 's' : ''} detectado{category.issues > 1 ? 's' : ''}</span>
</div>
)}
<p className="text-xs text-[var(--text-secondary)]">{category.details}</p>
</div>
</div>
))}
</div>
</Card>
{/* Action Items */}
<Card className="p-6">
<h3 className="text-lg font-semibold text-[var(--text-primary)] mb-4">Elementos de Acción</h3>
<div className="space-y-3">
{recommendations.map((rec, index) => (
<div key={index} className="flex items-center justify-between p-4 border rounded-lg">
<div className="flex items-start space-x-3 flex-1">
<Badge variant={getPriorityColor(rec.priority)}>
{rec.priority === 'high' ? 'Alta' : rec.priority === 'medium' ? 'Media' : 'Baja'}
</Badge>
<div>
<h4 className="text-sm font-medium text-[var(--text-primary)] mb-1">{rec.title}</h4>
<p className="text-sm text-[var(--text-secondary)] mb-1">{rec.description}</p>
<div className="flex items-center space-x-4 text-xs text-[var(--text-tertiary)]">
<span>Tiempo estimado: {rec.estimatedTime}</span>
<span></span>
<span>Impacto: {rec.impact}</span>
</div>
</div>
</div>
<Button size="sm">
Completar
</Button>
</div>
))}
</div>
</Card>
{/* Navigation */}
<div className="flex justify-between items-center">
<Button
variant="outline"
onClick={() => window.location.href = '/app/onboarding/upload'}
>
Volver a Carga de Datos
</Button>
<Button
onClick={() => window.location.href = '/app/onboarding/review'}
>
Continuar a Revisión Final
</Button>
</div>
</div>
);
};
export default OnboardingAnalysisPage;

View File

@@ -1,435 +0,0 @@
import React, { useState } from 'react';
import { BarChart3, TrendingUp, Target, AlertCircle, CheckCircle, Eye, Download } from 'lucide-react';
import { Button, Card, Badge } from '../../../../components/ui';
import { PageHeader } from '../../../../components/layout';
const OnboardingAnalysisPage: React.FC = () => {
const [selectedPeriod, setSelectedPeriod] = useState('30days');
const analysisData = {
onboardingScore: 87,
completionRate: 92,
averageTime: '4.2 días',
stepsCompleted: 15,
totalSteps: 16,
dataQuality: 94
};
const stepProgress = [
{ step: 'Información Básica', completed: true, quality: 95, timeSpent: '25 min' },
{ step: 'Configuración de Menú', completed: true, quality: 88, timeSpent: '1.2 horas' },
{ step: 'Datos de Inventario', completed: true, quality: 92, timeSpent: '45 min' },
{ step: 'Configuración de Horarios', completed: true, quality: 100, timeSpent: '15 min' },
{ step: 'Integración de Pagos', completed: true, quality: 85, timeSpent: '30 min' },
{ step: 'Carga de Productos', completed: true, quality: 90, timeSpent: '2.1 horas' },
{ step: 'Configuración de Personal', completed: true, quality: 87, timeSpent: '40 min' },
{ step: 'Pruebas del Sistema', completed: false, quality: 0, timeSpent: '-' }
];
const insights = [
{
type: 'success',
title: 'Excelente Progreso',
description: 'Has completado el 94% del proceso de configuración inicial',
recommendation: 'Solo faltan las pruebas finales del sistema para completar tu configuración',
impact: 'high'
},
{
type: 'info',
title: 'Calidad de Datos Alta',
description: 'Tus datos tienen una calidad promedio del 94%',
recommendation: 'Considera revisar la configuración de pagos para mejorar la puntuación',
impact: 'medium'
},
{
type: 'warning',
title: 'Paso Pendiente',
description: 'Las pruebas del sistema están pendientes',
recommendation: 'Programa las pruebas para validar la configuración completa',
impact: 'high'
}
];
const dataAnalysis = [
{
category: 'Información del Negocio',
completeness: 100,
accuracy: 95,
items: 12,
issues: 0,
details: 'Toda la información básica está completa y verificada'
},
{
category: 'Menú y Productos',
completeness: 85,
accuracy: 88,
items: 45,
issues: 3,
details: '3 productos sin precios definidos'
},
{
category: 'Inventario Inicial',
completeness: 92,
accuracy: 90,
items: 28,
issues: 2,
details: '2 ingredientes sin stock mínimo definido'
},
{
category: 'Configuración Operativa',
completeness: 100,
accuracy: 100,
items: 8,
issues: 0,
details: 'Horarios y políticas completamente configuradas'
}
];
const benchmarkComparison = {
industry: {
onboardingScore: 74,
completionRate: 78,
averageTime: '6.8 días'
},
yourData: {
onboardingScore: 87,
completionRate: 92,
averageTime: '4.2 días'
}
};
const recommendations = [
{
priority: 'high',
title: 'Completar Pruebas del Sistema',
description: 'Realizar pruebas integrales para validar toda la configuración',
estimatedTime: '30 minutos',
impact: 'Garantiza funcionamiento óptimo del sistema'
},
{
priority: 'medium',
title: 'Revisar Precios de Productos',
description: 'Definir precios para los 3 productos pendientes',
estimatedTime: '15 minutos',
impact: 'Permitirá generar ventas de todos los productos'
},
{
priority: 'medium',
title: 'Configurar Stocks Mínimos',
description: 'Establecer niveles mínimos para 2 ingredientes',
estimatedTime: '10 minutos',
impact: 'Mejorará el control de inventario automático'
},
{
priority: 'low',
title: 'Optimizar Configuración de Pagos',
description: 'Revisar métodos de pago y comisiones',
estimatedTime: '20 minutos',
impact: 'Puede reducir costos de transacción'
}
];
const getInsightIcon = (type: string) => {
const iconProps = { className: "w-5 h-5" };
switch (type) {
case 'success': return <CheckCircle {...iconProps} className="w-5 h-5 text-green-600" />;
case 'warning': return <AlertCircle {...iconProps} className="w-5 h-5 text-yellow-600" />;
case 'info': return <Target {...iconProps} className="w-5 h-5 text-blue-600" />;
default: return <AlertCircle {...iconProps} />;
}
};
const getInsightColor = (type: string) => {
switch (type) {
case 'success': return 'bg-green-50 border-green-200';
case 'warning': return 'bg-yellow-50 border-yellow-200';
case 'info': return 'bg-blue-50 border-blue-200';
default: return 'bg-gray-50 border-gray-200';
}
};
const getPriorityColor = (priority: string) => {
switch (priority) {
case 'high': return 'red';
case 'medium': return 'yellow';
case 'low': return 'green';
default: return 'gray';
}
};
const getCompletionColor = (percentage: number) => {
if (percentage >= 95) return 'text-green-600';
if (percentage >= 80) return 'text-yellow-600';
return 'text-red-600';
};
return (
<div className="p-6 space-y-6">
<PageHeader
title="Análisis de Configuración"
description="Análisis detallado de tu proceso de configuración y recomendaciones"
action={
<div className="flex space-x-2">
<Button variant="outline">
<Eye className="w-4 h-4 mr-2" />
Ver Detalles
</Button>
<Button variant="outline">
<Download className="w-4 h-4 mr-2" />
Exportar Reporte
</Button>
</div>
}
/>
{/* Overall Score */}
<Card className="p-6">
<div className="grid grid-cols-1 md:grid-cols-3 lg:grid-cols-6 gap-6">
<div className="text-center">
<div className="relative w-24 h-24 mx-auto mb-3">
<div className="absolute inset-0 flex items-center justify-center">
<span className="text-2xl font-bold text-green-600">{analysisData.onboardingScore}</span>
</div>
<svg className="w-24 h-24 transform -rotate-90">
<circle
cx="48"
cy="48"
r="40"
stroke="currentColor"
strokeWidth="8"
fill="none"
className="text-gray-200"
/>
<circle
cx="48"
cy="48"
r="40"
stroke="currentColor"
strokeWidth="8"
fill="none"
strokeDasharray={`${analysisData.onboardingScore * 2.51} 251`}
className="text-green-600"
/>
</svg>
</div>
<p className="text-sm font-medium text-gray-700">Puntuación General</p>
</div>
<div className="text-center">
<p className="text-3xl font-bold text-blue-600">{analysisData.completionRate}%</p>
<p className="text-sm font-medium text-gray-700">Completado</p>
</div>
<div className="text-center">
<p className="text-3xl font-bold text-purple-600">{analysisData.averageTime}</p>
<p className="text-sm font-medium text-gray-700">Tiempo Promedio</p>
</div>
<div className="text-center">
<p className="text-3xl font-bold text-orange-600">{analysisData.stepsCompleted}/{analysisData.totalSteps}</p>
<p className="text-sm font-medium text-gray-700">Pasos Completados</p>
</div>
<div className="text-center">
<p className="text-3xl font-bold text-teal-600">{analysisData.dataQuality}%</p>
<p className="text-sm font-medium text-gray-700">Calidad de Datos</p>
</div>
<div className="text-center">
<div className="flex items-center justify-center mb-2">
<TrendingUp className="w-8 h-8 text-green-600" />
</div>
<p className="text-sm font-medium text-gray-700">Por encima del promedio</p>
</div>
</div>
</Card>
{/* Progress Analysis */}
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
<Card className="p-6">
<h3 className="text-lg font-semibold text-gray-900 mb-4">Progreso por Pasos</h3>
<div className="space-y-4">
{stepProgress.map((step, index) => (
<div key={index} className="flex items-center justify-between p-3 border rounded-lg">
<div className="flex items-center space-x-3">
<div className={`w-8 h-8 rounded-full flex items-center justify-center ${
step.completed ? 'bg-green-100' : 'bg-gray-100'
}`}>
{step.completed ? (
<CheckCircle className="w-5 h-5 text-green-600" />
) : (
<span className="text-sm font-medium text-gray-500">{index + 1}</span>
)}
</div>
<div>
<p className="text-sm font-medium text-gray-900">{step.step}</p>
<p className="text-xs text-gray-500">Tiempo: {step.timeSpent}</p>
</div>
</div>
<div className="text-right">
<p className={`text-sm font-medium ${getCompletionColor(step.quality)}`}>
{step.quality}%
</p>
<p className="text-xs text-gray-500">Calidad</p>
</div>
</div>
))}
</div>
</Card>
<Card className="p-6">
<h3 className="text-lg font-semibold text-gray-900 mb-4">Comparación con la Industria</h3>
<div className="space-y-6">
<div>
<div className="flex justify-between items-center mb-2">
<span className="text-sm font-medium text-gray-700">Puntuación de Configuración</span>
<div className="text-right">
<span className="text-lg font-bold text-green-600">{benchmarkComparison.yourData.onboardingScore}</span>
<span className="text-sm text-gray-500 ml-2">vs {benchmarkComparison.industry.onboardingScore}</span>
</div>
</div>
<div className="w-full bg-gray-200 rounded-full h-2">
<div className="bg-green-600 h-2 rounded-full" style={{ width: `${(benchmarkComparison.yourData.onboardingScore / 100) * 100}%` }}></div>
</div>
</div>
<div>
<div className="flex justify-between items-center mb-2">
<span className="text-sm font-medium text-gray-700">Tasa de Completado</span>
<div className="text-right">
<span className="text-lg font-bold text-blue-600">{benchmarkComparison.yourData.completionRate}%</span>
<span className="text-sm text-gray-500 ml-2">vs {benchmarkComparison.industry.completionRate}%</span>
</div>
</div>
<div className="w-full bg-gray-200 rounded-full h-2">
<div className="bg-blue-600 h-2 rounded-full" style={{ width: `${benchmarkComparison.yourData.completionRate}%` }}></div>
</div>
</div>
<div>
<div className="flex justify-between items-center mb-2">
<span className="text-sm font-medium text-gray-700">Tiempo de Configuración</span>
<div className="text-right">
<span className="text-lg font-bold text-purple-600">{benchmarkComparison.yourData.averageTime}</span>
<span className="text-sm text-gray-500 ml-2">vs {benchmarkComparison.industry.averageTime}</span>
</div>
</div>
<div className="bg-green-50 p-3 rounded-lg">
<p className="text-sm text-green-700">38% más rápido que el promedio de la industria</p>
</div>
</div>
</div>
</Card>
</div>
{/* Insights */}
<Card className="p-6">
<h3 className="text-lg font-semibold text-gray-900 mb-4">Insights y Recomendaciones</h3>
<div className="space-y-4">
{insights.map((insight, index) => (
<div key={index} className={`p-4 rounded-lg border ${getInsightColor(insight.type)}`}>
<div className="flex items-start space-x-3">
{getInsightIcon(insight.type)}
<div className="flex-1">
<h4 className="text-sm font-medium text-gray-900 mb-1">{insight.title}</h4>
<p className="text-sm text-gray-700 mb-2">{insight.description}</p>
<p className="text-sm font-medium text-gray-900">
Recomendación: {insight.recommendation}
</p>
</div>
<Badge variant={insight.impact === 'high' ? 'red' : insight.impact === 'medium' ? 'yellow' : 'green'}>
{insight.impact === 'high' ? 'Alto' : insight.impact === 'medium' ? 'Medio' : 'Bajo'} Impacto
</Badge>
</div>
</div>
))}
</div>
</Card>
{/* Data Analysis */}
<Card className="p-6">
<h3 className="text-lg font-semibold text-gray-900 mb-4">Análisis de Calidad de Datos</h3>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
{dataAnalysis.map((category, index) => (
<div key={index} className="border rounded-lg p-4">
<div className="flex items-center justify-between mb-3">
<h4 className="font-medium text-gray-900">{category.category}</h4>
<div className="flex items-center space-x-2">
<BarChart3 className="w-4 h-4 text-gray-500" />
<span className="text-sm text-gray-500">{category.items} elementos</span>
</div>
</div>
<div className="space-y-3">
<div>
<div className="flex justify-between text-sm mb-1">
<span>Completitud</span>
<span className={getCompletionColor(category.completeness)}>{category.completeness}%</span>
</div>
<div className="w-full bg-gray-200 rounded-full h-2">
<div
className="bg-blue-600 h-2 rounded-full transition-all duration-300"
style={{ width: `${category.completeness}%` }}
></div>
</div>
</div>
<div>
<div className="flex justify-between text-sm mb-1">
<span>Precisión</span>
<span className={getCompletionColor(category.accuracy)}>{category.accuracy}%</span>
</div>
<div className="w-full bg-gray-200 rounded-full h-2">
<div
className="bg-green-600 h-2 rounded-full transition-all duration-300"
style={{ width: `${category.accuracy}%` }}
></div>
</div>
</div>
{category.issues > 0 && (
<div className="flex items-center text-sm text-red-600">
<AlertCircle className="w-4 h-4 mr-1" />
<span>{category.issues} problema{category.issues > 1 ? 's' : ''} detectado{category.issues > 1 ? 's' : ''}</span>
</div>
)}
<p className="text-xs text-gray-600">{category.details}</p>
</div>
</div>
))}
</div>
</Card>
{/* Action Items */}
<Card className="p-6">
<h3 className="text-lg font-semibold text-gray-900 mb-4">Elementos de Acción</h3>
<div className="space-y-3">
{recommendations.map((rec, index) => (
<div key={index} className="flex items-center justify-between p-4 border rounded-lg">
<div className="flex items-start space-x-3 flex-1">
<Badge variant={getPriorityColor(rec.priority)}>
{rec.priority === 'high' ? 'Alta' : rec.priority === 'medium' ? 'Media' : 'Baja'}
</Badge>
<div>
<h4 className="text-sm font-medium text-gray-900 mb-1">{rec.title}</h4>
<p className="text-sm text-gray-600 mb-1">{rec.description}</p>
<div className="flex items-center space-x-4 text-xs text-gray-500">
<span>Tiempo estimado: {rec.estimatedTime}</span>
<span>•</span>
<span>Impacto: {rec.impact}</span>
</div>
</div>
</div>
<Button size="sm">
Completar
</Button>
</div>
))}
</div>
</Card>
</div>
);
};
export default OnboardingAnalysisPage;

View File

@@ -1 +0,0 @@
export { default as OnboardingAnalysisPage } from './OnboardingAnalysisPage';

View File

@@ -1,610 +0,0 @@
import React, { useState } from 'react';
import { CheckCircle, AlertCircle, Edit2, Eye, Star, ArrowRight, Clock, Users, Zap } from 'lucide-react';
import { Button, Card, Badge } from '../../../../components/ui';
import { PageHeader } from '../../../../components/layout';
const OnboardingReviewPage: React.FC = () => {
const [activeSection, setActiveSection] = useState<string>('overview');
const completionData = {
overallProgress: 95,
totalSteps: 8,
completedSteps: 7,
remainingSteps: 1,
estimatedTimeRemaining: '15 minutos',
overallScore: 87
};
const sectionReview = [
{
id: 'business-info',
title: 'Información del Negocio',
status: 'completed',
score: 98,
items: [
{ field: 'Nombre de la panadería', value: 'Panadería Artesanal El Buen Pan', status: 'complete' },
{ field: 'Dirección', value: 'Av. Principal 123, Centro', status: 'complete' },
{ field: 'Teléfono', value: '+1 234 567 8900', status: 'complete' },
{ field: 'Email', value: 'info@elbuenpan.com', status: 'complete' },
{ field: 'Tipo de negocio', value: 'Panadería y Pastelería Artesanal', status: 'complete' },
{ field: 'Horario de operación', value: 'L-V: 6:00-20:00, S-D: 7:00-18:00', status: 'complete' }
],
recommendations: []
},
{
id: 'menu-products',
title: 'Menú y Productos',
status: 'completed',
score: 85,
items: [
{ field: 'Productos de panadería', value: '24 productos configurados', status: 'complete' },
{ field: 'Productos de pastelería', value: '18 productos configurados', status: 'complete' },
{ field: 'Bebidas', value: '12 opciones disponibles', status: 'complete' },
{ field: 'Precios configurados', value: '51 de 54 productos (94%)', status: 'warning' },
{ field: 'Categorías organizadas', value: '6 categorías principales', status: 'complete' },
{ field: 'Descripciones', value: '48 de 54 productos (89%)', status: 'warning' }
],
recommendations: [
'Completar precios para 3 productos pendientes',
'Añadir descripciones para 6 productos restantes'
]
},
{
id: 'inventory',
title: 'Inventario Inicial',
status: 'completed',
score: 92,
items: [
{ field: 'Materias primas', value: '45 ingredientes registrados', status: 'complete' },
{ field: 'Proveedores', value: '8 proveedores configurados', status: 'complete' },
{ field: 'Stocks iniciales', value: '43 de 45 ingredientes (96%)', status: 'warning' },
{ field: 'Puntos de reorden', value: '40 de 45 ingredientes (89%)', status: 'warning' },
{ field: 'Costos unitarios', value: 'Completado al 100%', status: 'complete' }
],
recommendations: [
'Definir stocks iniciales para 2 ingredientes',
'Establecer puntos de reorden para 5 ingredientes'
]
},
{
id: 'staff-config',
title: 'Configuración de Personal',
status: 'completed',
score: 90,
items: [
{ field: 'Empleados registrados', value: '12 miembros del equipo', status: 'complete' },
{ field: 'Roles asignados', value: '5 roles diferentes configurados', status: 'complete' },
{ field: 'Horarios de trabajo', value: '11 de 12 empleados (92%)', status: 'warning' },
{ field: 'Permisos del sistema', value: 'Configurado completamente', status: 'complete' },
{ field: 'Datos de contacto', value: 'Completado al 100%', status: 'complete' }
],
recommendations: [
'Completar horario para 1 empleado pendiente'
]
},
{
id: 'operations',
title: 'Configuración Operativa',
status: 'completed',
score: 95,
items: [
{ field: 'Horarios de operación', value: 'Configurado completamente', status: 'complete' },
{ field: 'Métodos de pago', value: '4 métodos activos', status: 'complete' },
{ field: 'Políticas de devoluciones', value: 'Definidas y configuradas', status: 'complete' },
{ field: 'Configuración de impuestos', value: '18% IVA configurado', status: 'complete' },
{ field: 'Zonas de entrega', value: '3 zonas configuradas', status: 'complete' }
],
recommendations: []
},
{
id: 'integrations',
title: 'Integraciones',
status: 'completed',
score: 88,
items: [
{ field: 'Sistema de pagos', value: 'Stripe configurado', status: 'complete' },
{ field: 'Notificaciones por email', value: 'SMTP configurado', status: 'complete' },
{ field: 'Notificaciones SMS', value: 'Twilio configurado', status: 'complete' },
{ field: 'Backup automático', value: 'Configurado diariamente', status: 'complete' },
{ field: 'APIs externas', value: '2 de 3 configuradas', status: 'warning' }
],
recommendations: [
'Configurar API de delivery restante'
]
},
{
id: 'testing',
title: 'Pruebas del Sistema',
status: 'pending',
score: 0,
items: [
{ field: 'Prueba de ventas', value: 'Pendiente', status: 'pending' },
{ field: 'Prueba de inventario', value: 'Pendiente', status: 'pending' },
{ field: 'Prueba de reportes', value: 'Pendiente', status: 'pending' },
{ field: 'Prueba de notificaciones', value: 'Pendiente', status: 'pending' },
{ field: 'Validación de integraciones', value: 'Pendiente', status: 'pending' }
],
recommendations: [
'Ejecutar todas las pruebas del sistema antes del lanzamiento'
]
},
{
id: 'training',
title: 'Capacitación del Equipo',
status: 'completed',
score: 82,
items: [
{ field: 'Capacitación básica', value: '10 de 12 empleados (83%)', status: 'warning' },
{ field: 'Manual del usuario', value: 'Entregado y revisado', status: 'complete' },
{ field: 'Sesiones prácticas', value: '2 de 3 sesiones completadas', status: 'warning' },
{ field: 'Evaluaciones', value: '8 de 10 empleados aprobados', status: 'warning' },
{ field: 'Material de referencia', value: 'Disponible en el sistema', status: 'complete' }
],
recommendations: [
'Completar capacitación para 2 empleados pendientes',
'Programar tercera sesión práctica',
'Realizar evaluaciones pendientes'
]
}
];
const overallRecommendations = [
{
priority: 'high',
category: 'Crítico',
title: 'Completar Pruebas del Sistema',
description: 'Ejecutar todas las pruebas funcionales antes del lanzamiento',
estimatedTime: '30 minutos',
impact: 'Garantiza funcionamiento correcto del sistema'
},
{
priority: 'medium',
category: 'Importante',
title: 'Finalizar Configuración de Productos',
description: 'Completar precios y descripciones pendientes',
estimatedTime: '20 minutos',
impact: 'Permite ventas completas de todos los productos'
},
{
priority: 'medium',
category: 'Importante',
title: 'Completar Capacitación del Personal',
description: 'Finalizar entrenamiento para empleados pendientes',
estimatedTime: '45 minutos',
impact: 'Asegura operación eficiente desde el primer día'
},
{
priority: 'low',
category: 'Opcional',
title: 'Optimizar Configuración de Inventario',
description: 'Definir stocks y puntos de reorden pendientes',
estimatedTime: '15 minutos',
impact: 'Mejora control automático de inventario'
}
];
const launchReadiness = {
essential: {
completed: 6,
total: 7,
percentage: 86
},
recommended: {
completed: 8,
total: 12,
percentage: 67
},
optional: {
completed: 3,
total: 6,
percentage: 50
}
};
const getStatusIcon = (status: string) => {
const iconProps = { className: "w-5 h-5" };
switch (status) {
case 'completed': return <CheckCircle {...iconProps} className="w-5 h-5 text-[var(--color-success)]" />;
case 'warning': return <AlertCircle {...iconProps} className="w-5 h-5 text-yellow-600" />;
case 'pending': return <Clock {...iconProps} className="w-5 h-5 text-[var(--text-secondary)]" />;
default: return <AlertCircle {...iconProps} />;
}
};
const getStatusColor = (status: string) => {
switch (status) {
case 'completed': return 'green';
case 'warning': return 'yellow';
case 'pending': return 'gray';
default: return 'red';
}
};
const getItemStatusIcon = (status: string) => {
const iconProps = { className: "w-4 h-4" };
switch (status) {
case 'complete': return <CheckCircle {...iconProps} className="w-4 h-4 text-[var(--color-success)]" />;
case 'warning': return <AlertCircle {...iconProps} className="w-4 h-4 text-yellow-600" />;
case 'pending': return <Clock {...iconProps} className="w-4 h-4 text-[var(--text-secondary)]" />;
default: return <AlertCircle {...iconProps} />;
}
};
const getPriorityColor = (priority: string) => {
switch (priority) {
case 'high': return 'red';
case 'medium': return 'yellow';
case 'low': return 'green';
default: return 'gray';
}
};
const getScoreColor = (score: number) => {
if (score >= 90) return 'text-[var(--color-success)]';
if (score >= 80) return 'text-yellow-600';
if (score >= 70) return 'text-[var(--color-primary)]';
return 'text-[var(--color-error)]';
};
return (
<div className="p-6 space-y-6">
<PageHeader
title="Revisión Final de Configuración"
description="Verifica todos los aspectos de tu configuración antes del lanzamiento"
action={
<div className="flex space-x-2">
<Button variant="outline">
<Edit2 className="w-4 h-4 mr-2" />
Editar Configuración
</Button>
<Button>
<Zap className="w-4 h-4 mr-2" />
Lanzar Sistema
</Button>
</div>
}
/>
{/* Overall Progress */}
<Card className="p-6">
<div className="grid grid-cols-1 md:grid-cols-4 gap-6">
<div className="text-center">
<div className="relative w-20 h-20 mx-auto mb-3">
<div className="absolute inset-0 flex items-center justify-center">
<span className={`text-2xl font-bold ${getScoreColor(completionData.overallScore)}`}>
{completionData.overallScore}
</span>
</div>
<svg className="w-20 h-20 transform -rotate-90">
<circle
cx="40"
cy="40"
r="32"
stroke="currentColor"
strokeWidth="6"
fill="none"
className="text-gray-200"
/>
<circle
cx="40"
cy="40"
r="32"
stroke="currentColor"
strokeWidth="6"
fill="none"
strokeDasharray={`${completionData.overallScore * 2.01} 201`}
className="text-[var(--color-success)]"
/>
</svg>
</div>
<p className="text-sm font-medium text-[var(--text-secondary)]">Puntuación General</p>
</div>
<div className="text-center">
<p className="text-3xl font-bold text-[var(--color-info)]">{completionData.overallProgress}%</p>
<p className="text-sm font-medium text-[var(--text-secondary)]">Progreso Total</p>
<div className="w-full bg-[var(--bg-quaternary)] rounded-full h-2 mt-2">
<div
className="bg-blue-600 h-2 rounded-full"
style={{ width: `${completionData.overallProgress}%` }}
></div>
</div>
</div>
<div className="text-center">
<p className="text-3xl font-bold text-purple-600">
{completionData.completedSteps}/{completionData.totalSteps}
</p>
<p className="text-sm font-medium text-[var(--text-secondary)]">Secciones Completadas</p>
</div>
<div className="text-center">
<p className="text-3xl font-bold text-[var(--color-primary)]">{completionData.estimatedTimeRemaining}</p>
<p className="text-sm font-medium text-[var(--text-secondary)]">Tiempo Restante</p>
</div>
</div>
</Card>
{/* Navigation Tabs */}
<div className="border-b border-[var(--border-primary)]">
<nav className="-mb-px flex space-x-8">
{['overview', 'sections', 'recommendations', 'readiness'].map((tab) => (
<button
key={tab}
onClick={() => setActiveSection(tab)}
className={`py-2 px-1 border-b-2 font-medium text-sm ${
activeSection === tab
? 'border-blue-500 text-[var(--color-info)]'
: 'border-transparent text-[var(--text-tertiary)] hover:text-[var(--text-secondary)] hover:border-[var(--border-secondary)]'
}`}
>
{tab === 'overview' && 'Resumen General'}
{tab === 'sections' && 'Revisión por Secciones'}
{tab === 'recommendations' && 'Recomendaciones'}
{tab === 'readiness' && 'Preparación para Lanzamiento'}
</button>
))}
</nav>
</div>
{/* Content based on active section */}
{activeSection === 'overview' && (
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
<Card className="p-6">
<h3 className="text-lg font-semibold text-[var(--text-primary)] mb-4">Estado por Secciones</h3>
<div className="space-y-3">
{sectionReview.map((section) => (
<div key={section.id} className="flex items-center justify-between p-3 border rounded-lg">
<div className="flex items-center space-x-3">
{getStatusIcon(section.status)}
<div>
<p className="text-sm font-medium text-[var(--text-primary)]">{section.title}</p>
<p className="text-xs text-[var(--text-tertiary)]">
{section.recommendations.length > 0
? `${section.recommendations.length} recomendación${section.recommendations.length > 1 ? 'es' : ''}`
: 'Completado correctamente'
}
</p>
</div>
</div>
<div className="text-right">
<p className={`text-sm font-medium ${getScoreColor(section.score)}`}>
{section.score}%
</p>
<Badge variant={getStatusColor(section.status)}>
{section.status === 'completed' ? 'Completado' :
section.status === 'warning' ? 'Con observaciones' : 'Pendiente'}
</Badge>
</div>
</div>
))}
</div>
</Card>
<Card className="p-6">
<h3 className="text-lg font-semibold text-[var(--text-primary)] mb-4">Próximos Pasos</h3>
<div className="space-y-4">
{overallRecommendations.slice(0, 3).map((rec, index) => (
<div key={index} className="flex items-start space-x-3 p-3 border rounded-lg">
<Badge variant={getPriorityColor(rec.priority)} className="mt-1">
{rec.category}
</Badge>
<div className="flex-1">
<h4 className="text-sm font-medium text-[var(--text-primary)]">{rec.title}</h4>
<p className="text-sm text-[var(--text-secondary)] mt-1">{rec.description}</p>
<div className="flex items-center space-x-4 mt-2 text-xs text-[var(--text-tertiary)]">
<span> {rec.estimatedTime}</span>
<span>💡 {rec.impact}</span>
</div>
</div>
<ArrowRight className="w-4 h-4 text-[var(--text-tertiary)] mt-1" />
</div>
))}
</div>
<div className="mt-6 p-4 bg-[var(--color-info)]/5 rounded-lg">
<div className="flex items-center space-x-2 mb-2">
<Star className="w-5 h-5 text-[var(--color-info)]" />
<h4 className="font-medium text-blue-900">¡Excelente progreso!</h4>
</div>
<p className="text-sm text-[var(--color-info)]">
Has completado el 95% de la configuración. Solo quedan algunos detalles finales antes del lanzamiento.
</p>
</div>
</Card>
</div>
)}
{activeSection === 'sections' && (
<div className="space-y-6">
{sectionReview.map((section) => (
<Card key={section.id} className="p-6">
<div className="flex items-center justify-between mb-4">
<div className="flex items-center space-x-3">
{getStatusIcon(section.status)}
<h3 className="text-lg font-semibold text-[var(--text-primary)]">{section.title}</h3>
<Badge variant={getStatusColor(section.status)}>
Puntuación: {section.score}%
</Badge>
</div>
<Button variant="outline" size="sm">
<Eye className="w-4 h-4 mr-2" />
Ver Detalles
</Button>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4">
{section.items.map((item, index) => (
<div key={index} className="flex items-center justify-between p-3 bg-[var(--bg-secondary)] rounded-lg">
<div className="flex items-center space-x-2">
{getItemStatusIcon(item.status)}
<span className="text-sm font-medium text-[var(--text-secondary)]">{item.field}</span>
</div>
<span className="text-sm text-[var(--text-secondary)]">{item.value}</span>
</div>
))}
</div>
{section.recommendations.length > 0 && (
<div className="bg-yellow-50 border border-yellow-200 rounded-lg p-4">
<h4 className="text-sm font-medium text-yellow-800 mb-2">Recomendaciones:</h4>
<ul className="text-sm text-yellow-700 space-y-1">
{section.recommendations.map((rec, index) => (
<li key={index} className="flex items-start">
<span className="mr-2"></span>
<span>{rec}</span>
</li>
))}
</ul>
</div>
)}
</Card>
))}
</div>
)}
{activeSection === 'recommendations' && (
<Card className="p-6">
<h3 className="text-lg font-semibold text-[var(--text-primary)] mb-4">Todas las Recomendaciones</h3>
<div className="space-y-4">
{overallRecommendations.map((rec, index) => (
<div key={index} className="flex items-start justify-between p-4 border rounded-lg">
<div className="flex items-start space-x-3 flex-1">
<Badge variant={getPriorityColor(rec.priority)}>
{rec.category}
</Badge>
<div>
<h4 className="text-sm font-medium text-[var(--text-primary)] mb-1">{rec.title}</h4>
<p className="text-sm text-[var(--text-secondary)] mb-2">{rec.description}</p>
<div className="flex items-center space-x-4 text-xs text-[var(--text-tertiary)]">
<span> Tiempo estimado: {rec.estimatedTime}</span>
<span>💡 Impacto: {rec.impact}</span>
</div>
</div>
</div>
<div className="flex space-x-2">
<Button size="sm" variant="outline">Más Información</Button>
<Button size="sm">Completar</Button>
</div>
</div>
))}
</div>
</Card>
)}
{activeSection === 'readiness' && (
<div className="space-y-6">
<Card className="p-6">
<h3 className="text-lg font-semibold text-[var(--text-primary)] mb-4">Preparación para el Lanzamiento</h3>
<div className="grid grid-cols-1 md:grid-cols-3 gap-6 mb-6">
<div className="text-center p-4 border rounded-lg">
<div className="w-16 h-16 mx-auto mb-3 bg-[var(--color-success)]/10 rounded-full flex items-center justify-center">
<CheckCircle className="w-8 h-8 text-[var(--color-success)]" />
</div>
<h4 className="font-medium text-[var(--text-primary)] mb-2">Elementos Esenciales</h4>
<p className="text-2xl font-bold text-[var(--color-success)] mb-1">
{launchReadiness.essential.completed}/{launchReadiness.essential.total}
</p>
<p className="text-sm text-[var(--text-secondary)]">{launchReadiness.essential.percentage}% completado</p>
<div className="w-full bg-[var(--bg-quaternary)] rounded-full h-2 mt-2">
<div
className="bg-green-600 h-2 rounded-full"
style={{ width: `${launchReadiness.essential.percentage}%` }}
></div>
</div>
</div>
<div className="text-center p-4 border rounded-lg">
<div className="w-16 h-16 mx-auto mb-3 bg-yellow-100 rounded-full flex items-center justify-center">
<Star className="w-8 h-8 text-yellow-600" />
</div>
<h4 className="font-medium text-[var(--text-primary)] mb-2">Recomendados</h4>
<p className="text-2xl font-bold text-yellow-600 mb-1">
{launchReadiness.recommended.completed}/{launchReadiness.recommended.total}
</p>
<p className="text-sm text-[var(--text-secondary)]">{launchReadiness.recommended.percentage}% completado</p>
<div className="w-full bg-[var(--bg-quaternary)] rounded-full h-2 mt-2">
<div
className="bg-yellow-600 h-2 rounded-full"
style={{ width: `${launchReadiness.recommended.percentage}%` }}
></div>
</div>
</div>
<div className="text-center p-4 border rounded-lg">
<div className="w-16 h-16 mx-auto mb-3 bg-[var(--color-info)]/10 rounded-full flex items-center justify-center">
<Users className="w-8 h-8 text-[var(--color-info)]" />
</div>
<h4 className="font-medium text-[var(--text-primary)] mb-2">Opcionales</h4>
<p className="text-2xl font-bold text-[var(--color-info)] mb-1">
{launchReadiness.optional.completed}/{launchReadiness.optional.total}
</p>
<p className="text-sm text-[var(--text-secondary)]">{launchReadiness.optional.percentage}% completado</p>
<div className="w-full bg-[var(--bg-quaternary)] rounded-full h-2 mt-2">
<div
className="bg-blue-600 h-2 rounded-full"
style={{ width: `${launchReadiness.optional.percentage}%` }}
></div>
</div>
</div>
</div>
<div className="bg-green-50 border border-green-200 rounded-lg p-6">
<div className="flex items-center space-x-3 mb-4">
<CheckCircle className="w-6 h-6 text-[var(--color-success)]" />
<h4 className="text-lg font-semibold text-green-900">¡Listo para Lanzar!</h4>
</div>
<p className="text-[var(--color-success)] mb-4">
Tu configuración está lista para el lanzamiento. Todos los elementos esenciales están completados
y el sistema está preparado para comenzar a operar.
</p>
<div className="flex space-x-3">
<Button
className="bg-green-600 hover:bg-green-700"
onClick={() => {
alert('¡Felicitaciones! El onboarding ha sido completado exitosamente. Ahora puedes comenzar a usar la plataforma.');
window.location.href = '/app/dashboard';
}}
>
<Zap className="w-4 h-4 mr-2" />
Lanzar Ahora
</Button>
<Button variant="outline">
Ejecutar Pruebas Finales
</Button>
</div>
</div>
</Card>
</div>
)}
{/* Overall Navigation - Always visible */}
<div className="flex justify-between items-center mt-8 p-4 bg-[var(--bg-secondary)] rounded-lg">
<Button
variant="outline"
onClick={() => window.location.href = '/app/onboarding/analysis'}
>
Volver al Análisis
</Button>
<div className="text-center">
<p className="text-sm text-[var(--text-secondary)]">Último paso del onboarding</p>
</div>
<Button
className="bg-green-600 hover:bg-green-700"
onClick={() => {
alert('¡Felicitaciones! El onboarding ha sido completado exitosamente. Ahora puedes comenzar a usar la plataforma.');
window.location.href = '/app/dashboard';
}}
>
<Zap className="w-4 h-4 mr-2" />
Finalizar Onboarding
</Button>
</div>
</div>
);
};
export default OnboardingReviewPage;

View File

@@ -1,579 +0,0 @@
import React, { useState } from 'react';
import { CheckCircle, AlertCircle, Edit2, Eye, Star, ArrowRight, Clock, Users, Zap } from 'lucide-react';
import { Button, Card, Badge } from '../../../../components/ui';
import { PageHeader } from '../../../../components/layout';
const OnboardingReviewPage: React.FC = () => {
const [activeSection, setActiveSection] = useState<string>('overview');
const completionData = {
overallProgress: 95,
totalSteps: 8,
completedSteps: 7,
remainingSteps: 1,
estimatedTimeRemaining: '15 minutos',
overallScore: 87
};
const sectionReview = [
{
id: 'business-info',
title: 'Información del Negocio',
status: 'completed',
score: 98,
items: [
{ field: 'Nombre de la panadería', value: 'Panadería Artesanal El Buen Pan', status: 'complete' },
{ field: 'Dirección', value: 'Av. Principal 123, Centro', status: 'complete' },
{ field: 'Teléfono', value: '+1 234 567 8900', status: 'complete' },
{ field: 'Email', value: 'info@elbuenpan.com', status: 'complete' },
{ field: 'Tipo de negocio', value: 'Panadería y Pastelería Artesanal', status: 'complete' },
{ field: 'Horario de operación', value: 'L-V: 6:00-20:00, S-D: 7:00-18:00', status: 'complete' }
],
recommendations: []
},
{
id: 'menu-products',
title: 'Menú y Productos',
status: 'completed',
score: 85,
items: [
{ field: 'Productos de panadería', value: '24 productos configurados', status: 'complete' },
{ field: 'Productos de pastelería', value: '18 productos configurados', status: 'complete' },
{ field: 'Bebidas', value: '12 opciones disponibles', status: 'complete' },
{ field: 'Precios configurados', value: '51 de 54 productos (94%)', status: 'warning' },
{ field: 'Categorías organizadas', value: '6 categorías principales', status: 'complete' },
{ field: 'Descripciones', value: '48 de 54 productos (89%)', status: 'warning' }
],
recommendations: [
'Completar precios para 3 productos pendientes',
'Añadir descripciones para 6 productos restantes'
]
},
{
id: 'inventory',
title: 'Inventario Inicial',
status: 'completed',
score: 92,
items: [
{ field: 'Materias primas', value: '45 ingredientes registrados', status: 'complete' },
{ field: 'Proveedores', value: '8 proveedores configurados', status: 'complete' },
{ field: 'Stocks iniciales', value: '43 de 45 ingredientes (96%)', status: 'warning' },
{ field: 'Puntos de reorden', value: '40 de 45 ingredientes (89%)', status: 'warning' },
{ field: 'Costos unitarios', value: 'Completado al 100%', status: 'complete' }
],
recommendations: [
'Definir stocks iniciales para 2 ingredientes',
'Establecer puntos de reorden para 5 ingredientes'
]
},
{
id: 'staff-config',
title: 'Configuración de Personal',
status: 'completed',
score: 90,
items: [
{ field: 'Empleados registrados', value: '12 miembros del equipo', status: 'complete' },
{ field: 'Roles asignados', value: '5 roles diferentes configurados', status: 'complete' },
{ field: 'Horarios de trabajo', value: '11 de 12 empleados (92%)', status: 'warning' },
{ field: 'Permisos del sistema', value: 'Configurado completamente', status: 'complete' },
{ field: 'Datos de contacto', value: 'Completado al 100%', status: 'complete' }
],
recommendations: [
'Completar horario para 1 empleado pendiente'
]
},
{
id: 'operations',
title: 'Configuración Operativa',
status: 'completed',
score: 95,
items: [
{ field: 'Horarios de operación', value: 'Configurado completamente', status: 'complete' },
{ field: 'Métodos de pago', value: '4 métodos activos', status: 'complete' },
{ field: 'Políticas de devoluciones', value: 'Definidas y configuradas', status: 'complete' },
{ field: 'Configuración de impuestos', value: '18% IVA configurado', status: 'complete' },
{ field: 'Zonas de entrega', value: '3 zonas configuradas', status: 'complete' }
],
recommendations: []
},
{
id: 'integrations',
title: 'Integraciones',
status: 'completed',
score: 88,
items: [
{ field: 'Sistema de pagos', value: 'Stripe configurado', status: 'complete' },
{ field: 'Notificaciones por email', value: 'SMTP configurado', status: 'complete' },
{ field: 'Notificaciones SMS', value: 'Twilio configurado', status: 'complete' },
{ field: 'Backup automático', value: 'Configurado diariamente', status: 'complete' },
{ field: 'APIs externas', value: '2 de 3 configuradas', status: 'warning' }
],
recommendations: [
'Configurar API de delivery restante'
]
},
{
id: 'testing',
title: 'Pruebas del Sistema',
status: 'pending',
score: 0,
items: [
{ field: 'Prueba de ventas', value: 'Pendiente', status: 'pending' },
{ field: 'Prueba de inventario', value: 'Pendiente', status: 'pending' },
{ field: 'Prueba de reportes', value: 'Pendiente', status: 'pending' },
{ field: 'Prueba de notificaciones', value: 'Pendiente', status: 'pending' },
{ field: 'Validación de integraciones', value: 'Pendiente', status: 'pending' }
],
recommendations: [
'Ejecutar todas las pruebas del sistema antes del lanzamiento'
]
},
{
id: 'training',
title: 'Capacitación del Equipo',
status: 'completed',
score: 82,
items: [
{ field: 'Capacitación básica', value: '10 de 12 empleados (83%)', status: 'warning' },
{ field: 'Manual del usuario', value: 'Entregado y revisado', status: 'complete' },
{ field: 'Sesiones prácticas', value: '2 de 3 sesiones completadas', status: 'warning' },
{ field: 'Evaluaciones', value: '8 de 10 empleados aprobados', status: 'warning' },
{ field: 'Material de referencia', value: 'Disponible en el sistema', status: 'complete' }
],
recommendations: [
'Completar capacitación para 2 empleados pendientes',
'Programar tercera sesión práctica',
'Realizar evaluaciones pendientes'
]
}
];
const overallRecommendations = [
{
priority: 'high',
category: 'Crítico',
title: 'Completar Pruebas del Sistema',
description: 'Ejecutar todas las pruebas funcionales antes del lanzamiento',
estimatedTime: '30 minutos',
impact: 'Garantiza funcionamiento correcto del sistema'
},
{
priority: 'medium',
category: 'Importante',
title: 'Finalizar Configuración de Productos',
description: 'Completar precios y descripciones pendientes',
estimatedTime: '20 minutos',
impact: 'Permite ventas completas de todos los productos'
},
{
priority: 'medium',
category: 'Importante',
title: 'Completar Capacitación del Personal',
description: 'Finalizar entrenamiento para empleados pendientes',
estimatedTime: '45 minutos',
impact: 'Asegura operación eficiente desde el primer día'
},
{
priority: 'low',
category: 'Opcional',
title: 'Optimizar Configuración de Inventario',
description: 'Definir stocks y puntos de reorden pendientes',
estimatedTime: '15 minutos',
impact: 'Mejora control automático de inventario'
}
];
const launchReadiness = {
essential: {
completed: 6,
total: 7,
percentage: 86
},
recommended: {
completed: 8,
total: 12,
percentage: 67
},
optional: {
completed: 3,
total: 6,
percentage: 50
}
};
const getStatusIcon = (status: string) => {
const iconProps = { className: "w-5 h-5" };
switch (status) {
case 'completed': return <CheckCircle {...iconProps} className="w-5 h-5 text-green-600" />;
case 'warning': return <AlertCircle {...iconProps} className="w-5 h-5 text-yellow-600" />;
case 'pending': return <Clock {...iconProps} className="w-5 h-5 text-gray-600" />;
default: return <AlertCircle {...iconProps} />;
}
};
const getStatusColor = (status: string) => {
switch (status) {
case 'completed': return 'green';
case 'warning': return 'yellow';
case 'pending': return 'gray';
default: return 'red';
}
};
const getItemStatusIcon = (status: string) => {
const iconProps = { className: "w-4 h-4" };
switch (status) {
case 'complete': return <CheckCircle {...iconProps} className="w-4 h-4 text-green-600" />;
case 'warning': return <AlertCircle {...iconProps} className="w-4 h-4 text-yellow-600" />;
case 'pending': return <Clock {...iconProps} className="w-4 h-4 text-gray-600" />;
default: return <AlertCircle {...iconProps} />;
}
};
const getPriorityColor = (priority: string) => {
switch (priority) {
case 'high': return 'red';
case 'medium': return 'yellow';
case 'low': return 'green';
default: return 'gray';
}
};
const getScoreColor = (score: number) => {
if (score >= 90) return 'text-green-600';
if (score >= 80) return 'text-yellow-600';
if (score >= 70) return 'text-orange-600';
return 'text-red-600';
};
return (
<div className="p-6 space-y-6">
<PageHeader
title="Revisión Final de Configuración"
description="Verifica todos los aspectos de tu configuración antes del lanzamiento"
action={
<div className="flex space-x-2">
<Button variant="outline">
<Edit2 className="w-4 h-4 mr-2" />
Editar Configuración
</Button>
<Button>
<Zap className="w-4 h-4 mr-2" />
Lanzar Sistema
</Button>
</div>
}
/>
{/* Overall Progress */}
<Card className="p-6">
<div className="grid grid-cols-1 md:grid-cols-4 gap-6">
<div className="text-center">
<div className="relative w-20 h-20 mx-auto mb-3">
<div className="absolute inset-0 flex items-center justify-center">
<span className={`text-2xl font-bold ${getScoreColor(completionData.overallScore)}`}>
{completionData.overallScore}
</span>
</div>
<svg className="w-20 h-20 transform -rotate-90">
<circle
cx="40"
cy="40"
r="32"
stroke="currentColor"
strokeWidth="6"
fill="none"
className="text-gray-200"
/>
<circle
cx="40"
cy="40"
r="32"
stroke="currentColor"
strokeWidth="6"
fill="none"
strokeDasharray={`${completionData.overallScore * 2.01} 201`}
className="text-green-600"
/>
</svg>
</div>
<p className="text-sm font-medium text-gray-700">Puntuación General</p>
</div>
<div className="text-center">
<p className="text-3xl font-bold text-blue-600">{completionData.overallProgress}%</p>
<p className="text-sm font-medium text-gray-700">Progreso Total</p>
<div className="w-full bg-gray-200 rounded-full h-2 mt-2">
<div
className="bg-blue-600 h-2 rounded-full"
style={{ width: `${completionData.overallProgress}%` }}
></div>
</div>
</div>
<div className="text-center">
<p className="text-3xl font-bold text-purple-600">
{completionData.completedSteps}/{completionData.totalSteps}
</p>
<p className="text-sm font-medium text-gray-700">Secciones Completadas</p>
</div>
<div className="text-center">
<p className="text-3xl font-bold text-orange-600">{completionData.estimatedTimeRemaining}</p>
<p className="text-sm font-medium text-gray-700">Tiempo Restante</p>
</div>
</div>
</Card>
{/* Navigation Tabs */}
<div className="border-b border-gray-200">
<nav className="-mb-px flex space-x-8">
{['overview', 'sections', 'recommendations', 'readiness'].map((tab) => (
<button
key={tab}
onClick={() => setActiveSection(tab)}
className={`py-2 px-1 border-b-2 font-medium text-sm ${
activeSection === tab
? 'border-blue-500 text-blue-600'
: 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300'
}`}
>
{tab === 'overview' && 'Resumen General'}
{tab === 'sections' && 'Revisión por Secciones'}
{tab === 'recommendations' && 'Recomendaciones'}
{tab === 'readiness' && 'Preparación para Lanzamiento'}
</button>
))}
</nav>
</div>
{/* Content based on active section */}
{activeSection === 'overview' && (
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
<Card className="p-6">
<h3 className="text-lg font-semibold text-gray-900 mb-4">Estado por Secciones</h3>
<div className="space-y-3">
{sectionReview.map((section) => (
<div key={section.id} className="flex items-center justify-between p-3 border rounded-lg">
<div className="flex items-center space-x-3">
{getStatusIcon(section.status)}
<div>
<p className="text-sm font-medium text-gray-900">{section.title}</p>
<p className="text-xs text-gray-500">
{section.recommendations.length > 0
? `${section.recommendations.length} recomendación${section.recommendations.length > 1 ? 'es' : ''}`
: 'Completado correctamente'
}
</p>
</div>
</div>
<div className="text-right">
<p className={`text-sm font-medium ${getScoreColor(section.score)}`}>
{section.score}%
</p>
<Badge variant={getStatusColor(section.status)}>
{section.status === 'completed' ? 'Completado' :
section.status === 'warning' ? 'Con observaciones' : 'Pendiente'}
</Badge>
</div>
</div>
))}
</div>
</Card>
<Card className="p-6">
<h3 className="text-lg font-semibold text-gray-900 mb-4">Próximos Pasos</h3>
<div className="space-y-4">
{overallRecommendations.slice(0, 3).map((rec, index) => (
<div key={index} className="flex items-start space-x-3 p-3 border rounded-lg">
<Badge variant={getPriorityColor(rec.priority)} className="mt-1">
{rec.category}
</Badge>
<div className="flex-1">
<h4 className="text-sm font-medium text-gray-900">{rec.title}</h4>
<p className="text-sm text-gray-600 mt-1">{rec.description}</p>
<div className="flex items-center space-x-4 mt-2 text-xs text-gray-500">
<span>⏱️ {rec.estimatedTime}</span>
<span>💡 {rec.impact}</span>
</div>
</div>
<ArrowRight className="w-4 h-4 text-gray-400 mt-1" />
</div>
))}
</div>
<div className="mt-6 p-4 bg-blue-50 rounded-lg">
<div className="flex items-center space-x-2 mb-2">
<Star className="w-5 h-5 text-blue-600" />
<h4 className="font-medium text-blue-900">¡Excelente progreso!</h4>
</div>
<p className="text-sm text-blue-800">
Has completado el 95% de la configuración. Solo quedan algunos detalles finales antes del lanzamiento.
</p>
</div>
</Card>
</div>
)}
{activeSection === 'sections' && (
<div className="space-y-6">
{sectionReview.map((section) => (
<Card key={section.id} className="p-6">
<div className="flex items-center justify-between mb-4">
<div className="flex items-center space-x-3">
{getStatusIcon(section.status)}
<h3 className="text-lg font-semibold text-gray-900">{section.title}</h3>
<Badge variant={getStatusColor(section.status)}>
Puntuación: {section.score}%
</Badge>
</div>
<Button variant="outline" size="sm">
<Eye className="w-4 h-4 mr-2" />
Ver Detalles
</Button>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4">
{section.items.map((item, index) => (
<div key={index} className="flex items-center justify-between p-3 bg-gray-50 rounded-lg">
<div className="flex items-center space-x-2">
{getItemStatusIcon(item.status)}
<span className="text-sm font-medium text-gray-700">{item.field}</span>
</div>
<span className="text-sm text-gray-600">{item.value}</span>
</div>
))}
</div>
{section.recommendations.length > 0 && (
<div className="bg-yellow-50 border border-yellow-200 rounded-lg p-4">
<h4 className="text-sm font-medium text-yellow-800 mb-2">Recomendaciones:</h4>
<ul className="text-sm text-yellow-700 space-y-1">
{section.recommendations.map((rec, index) => (
<li key={index} className="flex items-start">
<span className="mr-2">•</span>
<span>{rec}</span>
</li>
))}
</ul>
</div>
)}
</Card>
))}
</div>
)}
{activeSection === 'recommendations' && (
<Card className="p-6">
<h3 className="text-lg font-semibold text-gray-900 mb-4">Todas las Recomendaciones</h3>
<div className="space-y-4">
{overallRecommendations.map((rec, index) => (
<div key={index} className="flex items-start justify-between p-4 border rounded-lg">
<div className="flex items-start space-x-3 flex-1">
<Badge variant={getPriorityColor(rec.priority)}>
{rec.category}
</Badge>
<div>
<h4 className="text-sm font-medium text-gray-900 mb-1">{rec.title}</h4>
<p className="text-sm text-gray-600 mb-2">{rec.description}</p>
<div className="flex items-center space-x-4 text-xs text-gray-500">
<span>⏱️ Tiempo estimado: {rec.estimatedTime}</span>
<span>💡 Impacto: {rec.impact}</span>
</div>
</div>
</div>
<div className="flex space-x-2">
<Button size="sm" variant="outline">Más Información</Button>
<Button size="sm">Completar</Button>
</div>
</div>
))}
</div>
</Card>
)}
{activeSection === 'readiness' && (
<div className="space-y-6">
<Card className="p-6">
<h3 className="text-lg font-semibold text-gray-900 mb-4">Preparación para el Lanzamiento</h3>
<div className="grid grid-cols-1 md:grid-cols-3 gap-6 mb-6">
<div className="text-center p-4 border rounded-lg">
<div className="w-16 h-16 mx-auto mb-3 bg-green-100 rounded-full flex items-center justify-center">
<CheckCircle className="w-8 h-8 text-green-600" />
</div>
<h4 className="font-medium text-gray-900 mb-2">Elementos Esenciales</h4>
<p className="text-2xl font-bold text-green-600 mb-1">
{launchReadiness.essential.completed}/{launchReadiness.essential.total}
</p>
<p className="text-sm text-gray-600">{launchReadiness.essential.percentage}% completado</p>
<div className="w-full bg-gray-200 rounded-full h-2 mt-2">
<div
className="bg-green-600 h-2 rounded-full"
style={{ width: `${launchReadiness.essential.percentage}%` }}
></div>
</div>
</div>
<div className="text-center p-4 border rounded-lg">
<div className="w-16 h-16 mx-auto mb-3 bg-yellow-100 rounded-full flex items-center justify-center">
<Star className="w-8 h-8 text-yellow-600" />
</div>
<h4 className="font-medium text-gray-900 mb-2">Recomendados</h4>
<p className="text-2xl font-bold text-yellow-600 mb-1">
{launchReadiness.recommended.completed}/{launchReadiness.recommended.total}
</p>
<p className="text-sm text-gray-600">{launchReadiness.recommended.percentage}% completado</p>
<div className="w-full bg-gray-200 rounded-full h-2 mt-2">
<div
className="bg-yellow-600 h-2 rounded-full"
style={{ width: `${launchReadiness.recommended.percentage}%` }}
></div>
</div>
</div>
<div className="text-center p-4 border rounded-lg">
<div className="w-16 h-16 mx-auto mb-3 bg-blue-100 rounded-full flex items-center justify-center">
<Users className="w-8 h-8 text-blue-600" />
</div>
<h4 className="font-medium text-gray-900 mb-2">Opcionales</h4>
<p className="text-2xl font-bold text-blue-600 mb-1">
{launchReadiness.optional.completed}/{launchReadiness.optional.total}
</p>
<p className="text-sm text-gray-600">{launchReadiness.optional.percentage}% completado</p>
<div className="w-full bg-gray-200 rounded-full h-2 mt-2">
<div
className="bg-blue-600 h-2 rounded-full"
style={{ width: `${launchReadiness.optional.percentage}%` }}
></div>
</div>
</div>
</div>
<div className="bg-green-50 border border-green-200 rounded-lg p-6">
<div className="flex items-center space-x-3 mb-4">
<CheckCircle className="w-6 h-6 text-green-600" />
<h4 className="text-lg font-semibold text-green-900">¡Listo para Lanzar!</h4>
</div>
<p className="text-green-800 mb-4">
Tu configuración está lista para el lanzamiento. Todos los elementos esenciales están completados
y el sistema está preparado para comenzar a operar.
</p>
<div className="flex space-x-3">
<Button className="bg-green-600 hover:bg-green-700">
<Zap className="w-4 h-4 mr-2" />
Lanzar Ahora
</Button>
<Button variant="outline">
Ejecutar Pruebas Finales
</Button>
</div>
</div>
</Card>
</div>
)}
</div>
);
};
export default OnboardingReviewPage;

View File

@@ -1 +0,0 @@
export { default as OnboardingReviewPage } from './OnboardingReviewPage';

View File

@@ -1,906 +0,0 @@
import React, { useState, useEffect, useRef } from 'react';
import { ChevronRight, ChevronLeft, Check, Store, Upload, Brain } from 'lucide-react';
import { Button, Card, Input } from '../../../../components/ui';
import { PageHeader } from '../../../../components/layout';
const OnboardingSetupPage: React.FC = () => {
const [currentStep, setCurrentStep] = useState(1);
const [formData, setFormData] = useState({
bakery: {
name: 'Panadería Artesanal El Buen Pan',
type: 'artisan',
size: 'medium',
location: 'Av. Principal 123, Centro Histórico',
phone: '+1 234 567 8900',
email: 'info@elbuenpan.com'
},
upload: {
salesData: null,
inventoryData: null,
recipeData: null
}
});
const [trainingState, setTrainingState] = useState({
isTraining: false,
progress: 0,
currentStep: 'Iniciando entrenamiento...',
status: 'pending', // 'pending', 'running', 'completed', 'error'
logs: [] as string[]
});
const wsRef = useRef<WebSocket | null>(null);
const steps = [
{
id: 1,
title: 'Información de la Panadería',
description: 'Detalles básicos sobre tu negocio',
icon: Store,
fields: ['name', 'type', 'location', 'contact']
},
{
id: 2,
title: 'Carga de Datos',
description: 'Importa tus datos existentes para acelerar la configuración inicial',
icon: Upload,
fields: ['files', 'templates', 'validation']
},
{
id: 3,
title: 'Entrenamiento IA',
description: 'Configurando tu modelo de inteligencia artificial personalizado',
icon: Brain,
fields: ['training', 'progress', 'completion']
}
];
const bakeryTypes = [
{
value: 'artisan',
label: 'Panadería Artesanal Local',
description: 'Producción propia y tradicional en el local'
},
{
value: 'dependent',
label: 'Panadería Dependiente',
description: 'Dependiente de un panadero central'
}
];
const handleInputChange = (section: string, field: string, value: any) => {
setFormData(prev => ({
...prev,
[section]: {
...prev[section as keyof typeof prev],
[field]: value
}
}));
};
const handleArrayToggle = (section: string, field: string, value: string) => {
setFormData(prev => {
const currentArray = prev[section as keyof typeof prev][field] || [];
const newArray = currentArray.includes(value)
? currentArray.filter((item: string) => item !== value)
: [...currentArray, value];
return {
...prev,
[section]: {
...prev[section as keyof typeof prev],
[field]: newArray
}
};
});
};
const nextStep = () => {
if (currentStep < steps.length) {
setCurrentStep(currentStep + 1);
}
};
const prevStep = () => {
if (currentStep > 1) {
setCurrentStep(currentStep - 1);
}
};
const handleFinish = () => {
console.log('Onboarding completed:', formData);
// Navigate to dashboard - onboarding is complete
window.location.href = '/app/dashboard';
};
const downloadTemplate = (type: 'sales' | 'inventory' | 'recipes') => {
let csvContent = '';
let fileName = '';
switch (type) {
case 'sales':
csvContent = `fecha,producto,cantidad,precio_unitario,precio_total,cliente,canal_venta
2024-01-15,Pan Integral,5,2.50,12.50,Cliente A,Tienda
2024-01-15,Croissant,3,1.80,5.40,Cliente B,Online
2024-01-15,Baguette,2,3.00,6.00,Cliente C,Tienda
2024-01-16,Pan de Centeno,4,2.80,11.20,Cliente A,Tienda
2024-01-16,Empanadas,6,4.50,27.00,Cliente D,Delivery`;
fileName = 'plantilla_ventas.csv';
break;
case 'inventory':
csvContent = `nombre,categoria,unidad_medida,stock_actual,stock_minimo,stock_maximo,precio_compra,proveedor,fecha_vencimiento
Harina de Trigo,Ingrediente,kg,50,20,100,1.20,Molinos del Sur,2024-12-31
Azúcar Blanca,Ingrediente,kg,25,10,50,0.85,Dulces SA,2024-12-31
Levadura Fresca,Ingrediente,kg,5,2,10,3.50,Levaduras Pro,2024-03-15
Mantequilla,Ingrediente,kg,15,5,30,4.20,Lácteos Premium,2024-02-28
Pan Integral,Producto Final,unidad,20,10,50,0.00,Producción Propia,2024-01-20`;
fileName = 'plantilla_inventario.csv';
break;
case 'recipes':
csvContent = `nombre_receta,categoria,tiempo_preparacion,tiempo_coccion,porciones,ingrediente,cantidad,unidad
Pan Integral,Panadería,30,45,2,Harina Integral,500,g
Pan Integral,Panadería,30,45,2,Agua,325,ml
Pan Integral,Panadería,30,45,2,Sal,10,g
Pan Integral,Panadería,30,45,2,Levadura,7,g
Croissant,Bollería,120,20,12,Harina,400,g
Croissant,Bollería,120,20,12,Mantequilla,250,g
Croissant,Bollería,120,20,12,Agua,200,ml
Croissant,Bollería,120,20,12,Sal,8,g`;
fileName = 'plantilla_recetas.csv';
break;
}
// Create and download the file
const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
const link = document.createElement('a');
const url = URL.createObjectURL(blob);
link.setAttribute('href', url);
link.setAttribute('download', fileName);
link.style.visibility = 'hidden';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
};
// WebSocket connection for ML training updates
const connectWebSocket = () => {
if (wsRef.current?.readyState === WebSocket.OPEN) {
return;
}
const wsUrl = process.env.NODE_ENV === 'production'
? 'wss://api.bakeryai.com/ws/training'
: 'ws://localhost:8000/ws/training';
wsRef.current = new WebSocket(wsUrl);
wsRef.current.onopen = () => {
console.log('WebSocket connected for ML training');
setTrainingState(prev => ({ ...prev, status: 'running' }));
};
wsRef.current.onmessage = (event) => {
try {
const data = JSON.parse(event.data);
setTrainingState(prev => ({
...prev,
progress: data.progress || prev.progress,
currentStep: data.step || prev.currentStep,
status: data.status || prev.status,
logs: data.log ? [...prev.logs, data.log] : prev.logs
}));
} catch (error) {
console.error('Error parsing WebSocket message:', error);
}
};
wsRef.current.onclose = () => {
console.log('WebSocket connection closed');
};
wsRef.current.onerror = (error) => {
console.error('WebSocket error:', error);
setTrainingState(prev => ({ ...prev, status: 'error' }));
};
};
const startTraining = () => {
setTrainingState(prev => ({ ...prev, isTraining: true, progress: 0 }));
connectWebSocket();
// Send training configuration to server
if (wsRef.current?.readyState === WebSocket.OPEN) {
wsRef.current.send(JSON.stringify({
action: 'start_training',
bakery_data: formData.bakery,
upload_data: formData.upload
}));
}
};
// Cleanup WebSocket on unmount
useEffect(() => {
return () => {
if (wsRef.current) {
wsRef.current.close();
}
};
}, []);
const isStepComplete = (stepId: number) => {
// Basic validation logic
switch (stepId) {
case 1:
return formData.bakery.name && formData.bakery.location;
case 2:
return true; // Upload step is now optional - users can skip if they want
case 3:
return trainingState.status === 'completed';
default:
return false;
}
};
const renderStepContent = () => {
switch (currentStep) {
case 1:
return (
<div className="space-y-8">
<div className="space-y-3">
<label className="block text-lg font-semibold text-[var(--text-primary)] mb-3">
Nombre de la Panadería *
</label>
<div className="relative">
<Input
value={formData.bakery.name}
onChange={(e) => handleInputChange('bakery', 'name', e.target.value)}
placeholder="Ej: Panadería Artesanal El Buen Pan"
className="w-full transition-all duration-300 focus:scale-[1.02] focus:shadow-lg"
/>
{formData.bakery.name && (
<div className="absolute right-3 top-1/2 transform -translate-y-1/2">
<Check className="w-5 h-5 text-[var(--color-success)]" />
</div>
)}
</div>
</div>
<div>
<label className="block text-lg font-semibold text-[var(--text-primary)] mb-4">
Tipo de Panadería *
</label>
<div className="space-y-4">
{bakeryTypes.map((type) => (
<label
key={type.value}
className={`
group relative flex p-6 border-2 rounded-xl cursor-pointer transition-all duration-300 hover:scale-[1.02] hover:shadow-lg
${formData.bakery.type === type.value
? 'border-[var(--color-primary)] bg-[var(--color-primary)]/5 shadow-lg'
: 'border-[var(--border-secondary)] bg-[var(--bg-secondary)] hover:border-[var(--color-primary)]/50 hover:bg-[var(--bg-tertiary)]'
}
`}
>
<div className="flex items-start space-x-4 w-full">
<div className="relative flex-shrink-0 mt-1">
<input
type="radio"
name="bakeryType"
value={type.value}
checked={formData.bakery.type === type.value}
onChange={(e) => handleInputChange('bakery', 'type', e.target.value)}
className="sr-only"
/>
<div className={`
w-6 h-6 rounded-full border-2 flex items-center justify-center transition-all duration-300
${formData.bakery.type === type.value
? 'border-[var(--color-primary)] bg-[var(--color-primary)]'
: 'border-[var(--border-tertiary)] group-hover:border-[var(--color-primary)]'
}
`}>
{formData.bakery.type === type.value && (
<div className="w-3 h-3 bg-white rounded-full"></div>
)}
</div>
</div>
<div className="flex-1">
<h3 className={`
text-lg font-semibold mb-2 transition-colors duration-300
${formData.bakery.type === type.value ? 'text-[var(--text-primary)]' : 'text-[var(--text-secondary)] group-hover:text-[var(--text-primary)]'}
`}>
{type.label}
</h3>
<p className={`text-sm transition-colors duration-300 ${
formData.bakery.type === type.value ? 'text-[var(--text-secondary)]' : 'text-[var(--text-tertiary)] group-hover:text-[var(--text-secondary)]'
}`}>
{type.description}
</p>
</div>
</div>
{/* Selection indicator */}
{formData.bakery.type === type.value && (
<div className="absolute top-4 right-4">
<Check className="w-5 h-5 text-[var(--color-primary)]" />
</div>
)}
</label>
))}
</div>
</div>
<div>
<label className="block text-sm font-medium text-[var(--text-secondary)] mb-2">
Ubicación *
</label>
<Input
value={formData.bakery.location}
onChange={(e) => handleInputChange('bakery', 'location', e.target.value)}
placeholder="Dirección completa"
/>
</div>
<div className="grid grid-cols-2 gap-4">
<div>
<label className="block text-sm font-medium text-[var(--text-secondary)] mb-2">
Teléfono
</label>
<Input
value={formData.bakery.phone}
onChange={(e) => handleInputChange('bakery', 'phone', e.target.value)}
placeholder="+34 xxx xxx xxx"
/>
</div>
<div>
<label className="block text-sm font-medium text-[var(--text-secondary)] mb-2">
Email
</label>
<Input
value={formData.bakery.email}
onChange={(e) => handleInputChange('bakery', 'email', e.target.value)}
placeholder="contacto@panaderia.com"
/>
</div>
</div>
</div>
);
case 2:
return (
<div className="space-y-8">
{/* Upload Options */}
<div className="space-y-4">
<div className={`grid grid-cols-1 gap-4 ${
formData.bakery.type === 'dependent' ? 'md:grid-cols-2' : 'md:grid-cols-3'
}`}>
{/* Sales Data Upload */}
<div className="border-2 border-dashed border-[var(--border-secondary)] rounded-xl p-6 text-center transition-all duration-300 hover:border-[var(--color-primary)] hover:bg-[var(--color-primary)]/5">
<div className="w-12 h-12 bg-[var(--color-success)]/20 rounded-full flex items-center justify-center mx-auto mb-4">
<svg className="w-6 h-6 text-[var(--color-success)]" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z" />
</svg>
</div>
<h4 className="font-semibold text-[var(--text-primary)] mb-2">Datos de Ventas</h4>
<p className="text-sm text-[var(--text-secondary)] mb-3">
Historial de ventas (CSV, Excel)
</p>
<div className="text-xs text-[var(--text-tertiary)] mb-4 bg-[var(--bg-secondary)] rounded-lg p-3 text-left">
<div className="font-medium mb-1">Columnas requeridas:</div>
<div> <strong>Fecha</strong> (date, fecha)</div>
<div> <strong>Producto</strong> (product, producto)</div>
<div className="font-medium mt-2 mb-1">Columnas opcionales:</div>
<div> Cantidad, Precio, Categoría, Ubicación</div>
<div className="mt-2 text-[var(--color-info)]">Formatos: CSV, Excel | Máx: 10MB</div>
</div>
<input
type="file"
accept=".csv,.xlsx,.xls"
onChange={(e) => handleInputChange('upload', 'salesData', e.target.files?.[0] || null)}
className="hidden"
id="sales-upload"
/>
<label htmlFor="sales-upload" className="btn btn-outline text-sm cursor-pointer">
{formData.upload.salesData ? 'Archivo cargado ✓' : 'Seleccionar archivo'}
</label>
</div>
{/* Inventory Data Upload */}
<div className="border-2 border-dashed border-[var(--border-secondary)] rounded-xl p-6 text-center transition-all duration-300 hover:border-[var(--color-primary)] hover:bg-[var(--color-primary)]/5">
<div className="w-12 h-12 bg-[var(--color-warning)]/20 rounded-full flex items-center justify-center mx-auto mb-4">
<svg className="w-6 h-6 text-[var(--color-warning)]" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M20 7l-8-4-8 4m16 0l-8 4m8-4v10l-8 4m0-10L4 7m8 4v10M4 7v10l8 4" />
</svg>
</div>
<h4 className="font-semibold text-[var(--text-primary)] mb-2">Inventario</h4>
<p className="text-sm text-[var(--text-secondary)] mb-4">
Lista de productos e ingredientes
</p>
<input
type="file"
accept=".csv,.xlsx,.xls"
onChange={(e) => handleInputChange('upload', 'inventoryData', e.target.files?.[0] || null)}
className="hidden"
id="inventory-upload"
/>
<label htmlFor="inventory-upload" className="btn btn-outline text-sm cursor-pointer">
{formData.upload.inventoryData ? 'Archivo cargado ✓' : 'Seleccionar archivo'}
</label>
</div>
{/* Recipe Data Upload - Only for artisan bakeries */}
{formData.bakery.type === 'artisan' && (
<div className="border-2 border-dashed border-[var(--border-secondary)] rounded-xl p-6 text-center transition-all duration-300 hover:border-[var(--color-primary)] hover:bg-[var(--color-primary)]/5">
<div className="w-12 h-12 bg-[var(--color-info)]/20 rounded-full flex items-center justify-center mx-auto mb-4">
<svg className="w-6 h-6 text-[var(--color-info)]" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.746 0 3.332.477 4.5 1.253v13C19.832 18.477 18.246 18 16.5 18c-1.746 0-3.332.477-4.5 1.253" />
</svg>
</div>
<h4 className="font-semibold text-[var(--text-primary)] mb-2">Recetas</h4>
<p className="text-sm text-[var(--text-secondary)] mb-4">
Recetas y fórmulas existentes
</p>
<input
type="file"
accept=".csv,.xlsx,.xls,.pdf"
onChange={(e) => handleInputChange('upload', 'recipeData', e.target.files?.[0] || null)}
className="hidden"
id="recipe-upload"
/>
<label htmlFor="recipe-upload" className="btn btn-outline text-sm cursor-pointer">
{formData.upload.recipeData ? 'Archivo cargado ✓' : 'Seleccionar archivo'}
</label>
</div>
)}
</div>
</div>
{/* Template Downloads */}
<div className="space-y-4 pt-6 border-t border-[var(--border-secondary)]">
<div className="text-center">
<h4 className="font-semibold text-[var(--text-primary)] mb-2">¿Necesitas ayuda con el formato?</h4>
<p className="text-sm text-[var(--text-secondary)] mb-4">
Descarga nuestras plantillas para estructurar correctamente tus datos
</p>
<div className="flex flex-col sm:flex-row gap-3 justify-center">
<button
onClick={() => downloadTemplate('sales')}
className="flex items-center justify-center px-4 py-2 border border-[var(--color-success)] text-[var(--color-success)] rounded-lg hover:bg-[var(--color-success)]/10 transition-all duration-200"
>
<svg className="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
</svg>
Plantilla de Ventas
</button>
<button
onClick={() => downloadTemplate('inventory')}
className="flex items-center justify-center px-4 py-2 border border-[var(--color-warning)] text-[var(--color-warning)] rounded-lg hover:bg-[var(--color-warning)]/10 transition-all duration-200"
>
<svg className="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
</svg>
Plantilla de Inventario
</button>
{/* Recipe template - Only for artisan bakeries */}
{formData.bakery.type === 'artisan' && (
<button
onClick={() => downloadTemplate('recipes')}
className="flex items-center justify-center px-4 py-2 border border-[var(--color-info)] text-[var(--color-info)] rounded-lg hover:bg-[var(--color-info)]/10 transition-all duration-200"
>
<svg className="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
</svg>
Plantilla de Recetas
</button>
)}
</div>
</div>
</div>
</div>
);
case 3:
return (
<div className="space-y-8">
{/* Training Progress */}
{!trainingState.isTraining && trainingState.status === 'pending' && (
<div className="text-center">
<p className="text-[var(--text-secondary)] mb-6">
Presiona el botón para iniciar el entrenamiento de tu modelo de IA
</p>
<Button
onClick={startTraining}
className="px-8 py-3 bg-[var(--color-info)] hover:bg-[var(--color-info)]/80 text-white"
>
<Brain className="w-4 h-4 mr-2" />
Iniciar Entrenamiento
</Button>
</div>
)}
{/* Training in Progress */}
{trainingState.isTraining && (
<div className="space-y-6">
{/* Progress Bar */}
<div>
<div className="flex justify-between items-center mb-2">
<span className="text-sm font-medium text-[var(--text-primary)]">
Progreso del Entrenamiento
</span>
<span className="text-sm text-[var(--text-secondary)]">
{trainingState.progress}%
</span>
</div>
<div className="w-full bg-[var(--bg-quaternary)] rounded-full h-4 overflow-hidden">
<div
className="bg-gradient-to-r from-[var(--color-info)] to-[var(--color-primary)] h-full rounded-full transition-all duration-500 ease-out relative"
style={{ width: `${trainingState.progress}%` }}
>
<div className="absolute inset-0 bg-white opacity-20 rounded-full"></div>
{trainingState.progress > 0 && (
<div className="absolute inset-0 bg-gradient-to-r from-transparent via-white/30 to-transparent animate-pulse rounded-full"></div>
)}
</div>
</div>
</div>
{/* Current Step */}
<div className="flex items-center space-x-3 p-4 bg-[var(--bg-secondary)] rounded-lg border">
<div className="w-3 h-3 bg-[var(--color-info)] rounded-full animate-pulse"></div>
<span className="text-[var(--text-primary)] font-medium">
{trainingState.currentStep}
</span>
</div>
{/* Training Status */}
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
<div className={`p-4 rounded-lg border text-center ${
trainingState.progress >= 25
? 'bg-[var(--color-success)]/10 border-[var(--color-success)]'
: 'bg-[var(--bg-secondary)] border-[var(--border-secondary)]'
}`}>
<div className={`w-8 h-8 mx-auto mb-2 rounded-full flex items-center justify-center ${
trainingState.progress >= 25 ? 'bg-[var(--color-success)]' : 'bg-[var(--bg-tertiary)]'
}`}>
{trainingState.progress >= 25 ? (
<Check className="w-4 h-4 text-white" />
) : (
<span className="text-xs text-[var(--text-tertiary)]">1</span>
)}
</div>
<p className="text-xs font-medium">Carga de Datos</p>
</div>
<div className={`p-4 rounded-lg border text-center ${
trainingState.progress >= 75
? 'bg-[var(--color-success)]/10 border-[var(--color-success)]'
: 'bg-[var(--bg-secondary)] border-[var(--border-secondary)]'
}`}>
<div className={`w-8 h-8 mx-auto mb-2 rounded-full flex items-center justify-center ${
trainingState.progress >= 75 ? 'bg-[var(--color-success)]' : 'bg-[var(--bg-tertiary)]'
}`}>
{trainingState.progress >= 75 ? (
<Check className="w-4 h-4 text-white" />
) : (
<span className="text-xs text-[var(--text-tertiary)]">2</span>
)}
</div>
<p className="text-xs font-medium">Entrenamiento</p>
</div>
<div className={`p-4 rounded-lg border text-center ${
trainingState.status === 'completed'
? 'bg-[var(--color-success)]/10 border-[var(--color-success)]'
: 'bg-[var(--bg-secondary)] border-[var(--border-secondary)]'
}`}>
<div className={`w-8 h-8 mx-auto mb-2 rounded-full flex items-center justify-center ${
trainingState.status === 'completed' ? 'bg-[var(--color-success)]' : 'bg-[var(--bg-tertiary)]'
}`}>
{trainingState.status === 'completed' ? (
<Check className="w-4 h-4 text-white" />
) : (
<span className="text-xs text-[var(--text-tertiary)]">3</span>
)}
</div>
<p className="text-xs font-medium">Validación</p>
</div>
</div>
{/* Training Logs */}
{trainingState.logs.length > 0 && (
<div>
<h4 className="text-sm font-medium text-[var(--text-primary)] mb-3">
Log de Entrenamiento
</h4>
<div className="bg-[var(--bg-secondary)] rounded-lg p-4 max-h-32 overflow-y-auto border">
{trainingState.logs.slice(-5).map((log, index) => (
<div key={index} className="text-xs text-[var(--text-secondary)] mb-1 font-mono">
{log}
</div>
))}
</div>
</div>
)}
</div>
)}
{/* Training Completed */}
{trainingState.status === 'completed' && (
<div className="text-center p-6 bg-[var(--color-success)]/10 rounded-xl border border-[var(--color-success)]/20">
<Check className="w-12 h-12 text-[var(--color-success)] mx-auto mb-4" />
<h4 className="text-lg font-semibold text-[var(--text-primary)] mb-2">
¡Entrenamiento Completado!
</h4>
<p className="text-[var(--text-secondary)]">
Tu modelo de IA personalizado está listo para ayudarte a optimizar tu panadería
</p>
</div>
)}
{/* Training Error */}
{trainingState.status === 'error' && (
<div className="text-center p-6 bg-[var(--color-error)]/10 rounded-xl border border-[var(--color-error)]/20">
<div className="w-12 h-12 bg-[var(--color-error)] rounded-full flex items-center justify-center mx-auto mb-4">
<span className="text-white text-xl">!</span>
</div>
<h4 className="text-lg font-semibold text-[var(--text-primary)] mb-2">
Error en el Entrenamiento
</h4>
<p className="text-[var(--text-secondary)] mb-4">
Ocurrió un problema durante el entrenamiento. Por favor, inténtalo de nuevo.
</p>
<Button
onClick={startTraining}
className="px-6 py-2 bg-[var(--color-info)] hover:bg-[var(--color-info)]/80 text-white"
>
Reintentar
</Button>
</div>
)}
</div>
);
default:
return null;
}
};
return (
<div className="p-4 sm:p-6 max-w-5xl mx-auto">
<div className="text-center mb-8 sm:mb-12">
<div className="inline-flex items-center justify-center w-14 h-14 sm:w-16 sm:h-16 bg-[var(--color-primary)] rounded-full mb-4 sm:mb-6 shadow-lg">
<svg className="w-6 h-6 sm:w-8 sm:h-8 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M13 10V3L4 14h7v7l9-11h-7z" />
</svg>
</div>
<h1 className="text-2xl sm:text-3xl font-bold text-[var(--text-primary)] mb-3 sm:mb-4">
Configuración Inicial
</h1>
<p className="text-base sm:text-lg text-[var(--text-secondary)] max-w-2xl mx-auto px-4">
Configura tu panadería paso a paso para comenzar a usar la plataforma de manera óptima
</p>
</div>
<div className="max-w-4xl mx-auto">
{/* Single Integrated Card */}
<Card className="shadow-lg overflow-hidden">
{/* Progress Header Inside Card */}
<div className="bg-[var(--bg-secondary)] p-4 sm:p-6 border-b border-[var(--border-secondary)]">
{/* Step Indicators - Mobile Optimized */}
<div className="mb-4 sm:mb-6">
{/* Mobile: Vertical Step List */}
<div className="block sm:hidden space-y-3">
{steps.map((step, index) => (
<div key={step.id} className="flex items-center space-x-3">
<div className={`
w-8 h-8 rounded-full flex items-center justify-center text-xs font-medium transition-all duration-300 flex-shrink-0
${step.id <= currentStep
? 'bg-[var(--color-primary)] text-white'
: 'bg-[var(--bg-quaternary)] text-[var(--text-tertiary)] border border-[var(--border-secondary)]'
}
`}>
{step.id < currentStep ? (
<Check className="w-3 h-3" />
) : step.id === currentStep ? (
<step.icon className="w-3 h-3" />
) : (
<step.icon className="w-3 h-3" />
)}
</div>
<div className="flex-1">
<h3 className={`text-sm font-medium ${
step.id === currentStep
? 'text-[var(--color-primary)]'
: step.id < currentStep
? 'text-[var(--color-success)]'
: 'text-[var(--text-tertiary)]'
}`}>
{step.title}
</h3>
{step.id === currentStep && (
<p className="text-xs text-[var(--text-secondary)] mt-1">
{step.description}
</p>
)}
</div>
</div>
))}
</div>
{/* Desktop: Horizontal Step Indicators */}
<div className="hidden sm:flex items-center justify-center space-x-6">
{steps.map((step, index) => (
<div key={step.id} className="flex items-center">
<div className="flex flex-col items-center">
<div className={`
w-10 h-10 rounded-full flex items-center justify-center text-sm font-medium transition-all duration-300 shadow-sm
${step.id <= currentStep
? 'bg-[var(--color-primary)] text-white'
: 'bg-[var(--bg-quaternary)] text-[var(--text-tertiary)] border border-[var(--border-secondary)]'
}
`}>
{step.id < currentStep ? (
<Check className="w-4 h-4" />
) : step.id === currentStep ? (
<step.icon className="w-4 h-4" />
) : (
<step.icon className="w-4 h-4" />
)}
</div>
<p className={`text-xs mt-2 text-center max-w-16 leading-tight ${
step.id === currentStep
? 'text-[var(--color-primary)] font-semibold'
: 'text-[var(--text-tertiary)]'
}`}>
{step.title}
</p>
</div>
{/* Connection line - Desktop only */}
{index < steps.length - 1 && (
<div className={`
w-16 h-0.5 mx-3 transition-all duration-500
${step.id < currentStep ? 'bg-[var(--color-primary)]' : 'bg-[var(--border-secondary)]'}
`} />
)}
</div>
))}
</div>
</div>
{/* Step Info - Desktop Only (Mobile shows inline) */}
<div className="text-center hidden sm:block">
<h2 className="text-xl font-semibold text-[var(--text-primary)] mb-2">
Paso {currentStep}: {steps[currentStep - 1].title}
</h2>
<p className="text-[var(--text-secondary)] mb-4">
{steps[currentStep - 1].description}
</p>
</div>
{/* Mobile Step Info */}
<div className="text-center block sm:hidden mb-4">
<h2 className="text-lg font-semibold text-[var(--text-primary)] mb-2">
{steps[currentStep - 1].title}
</h2>
</div>
{/* Progress Bar */}
<div className="relative max-w-sm sm:max-w-md mx-auto">
<div className="w-full bg-[var(--bg-quaternary)] rounded-full h-2 overflow-hidden">
<div
className="bg-[var(--color-primary)] h-full rounded-full transition-all duration-700 ease-out"
style={{ width: `${(currentStep / steps.length) * 100}%` }}
/>
</div>
<div className="text-center mt-2">
<span className="text-sm font-medium text-[var(--color-primary)]">
{Math.round((currentStep / steps.length) * 100)}% Completado
</span>
</div>
</div>
</div>
{/* Form Content */}
<div className="p-4 sm:p-8">
<div className="space-y-4 sm:space-y-6">
{renderStepContent()}
</div>
</div>
{/* Navigation Footer */}
<div className="bg-[var(--bg-secondary)] px-4 sm:px-8 py-4 sm:py-6 border-t border-[var(--border-secondary)]">
<div className="flex flex-col sm:flex-row items-center justify-between gap-4 sm:gap-0">
{/* Mobile: Full width buttons stacked */}
<div className="flex w-full sm:w-auto gap-3 sm:hidden">
<Button
variant="outline"
onClick={prevStep}
disabled={currentStep === 1}
className="flex-1 py-3 disabled:opacity-50"
>
<ChevronLeft className="w-4 h-4 mr-2" />
Anterior
</Button>
{currentStep === steps.length ? (
<Button
onClick={handleFinish}
disabled={!isStepComplete(currentStep)}
className="flex-1 py-3 bg-[var(--color-success)] hover:bg-[var(--color-success)]/90 text-white disabled:opacity-50"
>
<Check className="w-4 h-4 mr-2" />
Finalizar
</Button>
) : (
<Button
onClick={nextStep}
disabled={!isStepComplete(currentStep)}
className="flex-1 py-3 disabled:opacity-50"
>
Siguiente
<ChevronRight className="w-4 h-4 ml-2" />
</Button>
)}
</div>
{/* Desktop: Original layout */}
<Button
variant="outline"
onClick={prevStep}
disabled={currentStep === 1}
className="hidden sm:flex px-6 py-3 disabled:opacity-50"
>
<ChevronLeft className="w-4 h-4 mr-2" />
Anterior
</Button>
{/* Step indicators - smaller on mobile */}
<div className="flex items-center space-x-1.5 sm:space-x-2">
{steps.map((_, index) => (
<div
key={index}
className={`
w-1.5 h-1.5 sm:w-2 sm:h-2 rounded-full transition-all duration-300
${index + 1 === currentStep
? 'bg-[var(--color-primary)] scale-125'
: index + 1 < currentStep
? 'bg-[var(--color-success)]'
: 'bg-[var(--border-secondary)]'
}
`}
/>
))}
</div>
{currentStep === steps.length ? (
<Button
onClick={handleFinish}
disabled={!isStepComplete(currentStep)}
className="hidden sm:flex px-8 py-3 bg-[var(--color-success)] hover:bg-[var(--color-success)]/90 text-white disabled:opacity-50"
>
<Check className="w-4 h-4 mr-2" />
Finalizar
</Button>
) : (
<Button
onClick={nextStep}
disabled={!isStepComplete(currentStep)}
className="hidden sm:flex px-6 py-3 disabled:opacity-50"
>
Siguiente
<ChevronRight className="w-4 h-4 ml-2" />
</Button>
)}
</div>
</div>
</Card>
</div>
</div>
);
};
export default OnboardingSetupPage;

View File

@@ -1,499 +0,0 @@
import React, { useState } from 'react';
import { ChevronRight, ChevronLeft, Check, Store, Users, Settings, Zap } from 'lucide-react';
import { Button, Card, Input } from '../../../../components/ui';
import { PageHeader } from '../../../../components/layout';
const OnboardingSetupPage: React.FC = () => {
const [currentStep, setCurrentStep] = useState(1);
const [formData, setFormData] = useState({
bakery: {
name: '',
type: 'traditional',
size: 'medium',
location: '',
phone: '',
email: ''
},
team: {
ownerName: '',
teamSize: '5-10',
roles: [],
experience: 'intermediate'
},
operations: {
openingHours: {
start: '07:00',
end: '20:00'
},
daysOpen: 6,
specialties: [],
dailyProduction: 'medium'
},
goals: {
primaryGoals: [],
expectedRevenue: '',
timeline: '6months'
}
});
const steps = [
{
id: 1,
title: 'Información de la Panadería',
description: 'Detalles básicos sobre tu negocio',
icon: Store,
fields: ['name', 'type', 'location', 'contact']
},
{
id: 2,
title: 'Equipo y Personal',
description: 'Información sobre tu equipo de trabajo',
icon: Users,
fields: ['owner', 'teamSize', 'roles', 'experience']
},
{
id: 3,
title: 'Operaciones',
description: 'Horarios y especialidades de producción',
icon: Settings,
fields: ['hours', 'specialties', 'production']
},
{
id: 4,
title: 'Objetivos',
description: 'Metas y expectativas para tu panadería',
icon: Zap,
fields: ['goals', 'revenue', 'timeline']
}
];
const bakeryTypes = [
{ value: 'traditional', label: 'Panadería Tradicional' },
{ value: 'artisan', label: 'Panadería Artesanal' },
{ value: 'cafe', label: 'Panadería-Café' },
{ value: 'industrial', label: 'Producción Industrial' }
];
const specialties = [
{ value: 'bread', label: 'Pan Tradicional' },
{ value: 'pastries', label: 'Bollería' },
{ value: 'cakes', label: 'Tartas y Pasteles' },
{ value: 'cookies', label: 'Galletas' },
{ value: 'savory', label: 'Productos Salados' },
{ value: 'gluten-free', label: 'Sin Gluten' },
{ value: 'vegan', label: 'Vegano' },
{ value: 'organic', label: 'Orgánico' }
];
const businessGoals = [
{ value: 'increase-sales', label: 'Aumentar Ventas' },
{ value: 'reduce-waste', label: 'Reducir Desperdicios' },
{ value: 'improve-efficiency', label: 'Mejorar Eficiencia' },
{ value: 'expand-menu', label: 'Ampliar Menú' },
{ value: 'digital-presence', label: 'Presencia Digital' },
{ value: 'customer-loyalty', label: 'Fidelización de Clientes' }
];
const handleInputChange = (section: string, field: string, value: any) => {
setFormData(prev => ({
...prev,
[section]: {
...prev[section as keyof typeof prev],
[field]: value
}
}));
};
const handleArrayToggle = (section: string, field: string, value: string) => {
setFormData(prev => {
const currentArray = prev[section as keyof typeof prev][field] || [];
const newArray = currentArray.includes(value)
? currentArray.filter((item: string) => item !== value)
: [...currentArray, value];
return {
...prev,
[section]: {
...prev[section as keyof typeof prev],
[field]: newArray
}
};
});
};
const nextStep = () => {
if (currentStep < steps.length) {
setCurrentStep(currentStep + 1);
}
};
const prevStep = () => {
if (currentStep > 1) {
setCurrentStep(currentStep - 1);
}
};
const handleFinish = () => {
console.log('Onboarding completed:', formData);
// Handle completion logic
};
const isStepComplete = (stepId: number) => {
// Basic validation logic
switch (stepId) {
case 1:
return formData.bakery.name && formData.bakery.location;
case 2:
return formData.team.ownerName;
case 3:
return formData.operations.specialties.length > 0;
case 4:
return formData.goals.primaryGoals.length > 0;
default:
return false;
}
};
const renderStepContent = () => {
switch (currentStep) {
case 1:
return (
<div className="space-y-6">
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Nombre de la Panadería *
</label>
<Input
value={formData.bakery.name}
onChange={(e) => handleInputChange('bakery', 'name', e.target.value)}
placeholder="Ej: Panadería San Miguel"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Tipo de Panadería
</label>
<div className="grid grid-cols-2 gap-4">
{bakeryTypes.map((type) => (
<label key={type.value} className="flex items-center space-x-3 p-3 border rounded-lg cursor-pointer hover:bg-gray-50">
<input
type="radio"
name="bakeryType"
value={type.value}
checked={formData.bakery.type === type.value}
onChange={(e) => handleInputChange('bakery', 'type', e.target.value)}
className="text-blue-600"
/>
<span className="text-sm">{type.label}</span>
</label>
))}
</div>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Ubicación *
</label>
<Input
value={formData.bakery.location}
onChange={(e) => handleInputChange('bakery', 'location', e.target.value)}
placeholder="Dirección completa"
/>
</div>
<div className="grid grid-cols-2 gap-4">
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Teléfono
</label>
<Input
value={formData.bakery.phone}
onChange={(e) => handleInputChange('bakery', 'phone', e.target.value)}
placeholder="+34 xxx xxx xxx"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Email
</label>
<Input
value={formData.bakery.email}
onChange={(e) => handleInputChange('bakery', 'email', e.target.value)}
placeholder="contacto@panaderia.com"
/>
</div>
</div>
</div>
);
case 2:
return (
<div className="space-y-6">
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Nombre del Propietario *
</label>
<Input
value={formData.team.ownerName}
onChange={(e) => handleInputChange('team', 'ownerName', e.target.value)}
placeholder="Tu nombre completo"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Tamaño del Equipo
</label>
<select
value={formData.team.teamSize}
onChange={(e) => handleInputChange('team', 'teamSize', e.target.value)}
className="w-full px-3 py-2 border border-gray-300 rounded-md"
>
<option value="1-2">Solo yo o 1-2 personas</option>
<option value="3-5">3-5 empleados</option>
<option value="5-10">5-10 empleados</option>
<option value="10+">Más de 10 empleados</option>
</select>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Experiencia en el Sector
</label>
<div className="space-y-2">
{[
{ value: 'beginner', label: 'Principiante (menos de 2 años)' },
{ value: 'intermediate', label: 'Intermedio (2-5 años)' },
{ value: 'experienced', label: 'Experimentado (5-10 años)' },
{ value: 'expert', label: 'Experto (más de 10 años)' }
].map((exp) => (
<label key={exp.value} className="flex items-center space-x-3">
<input
type="radio"
name="experience"
value={exp.value}
checked={formData.team.experience === exp.value}
onChange={(e) => handleInputChange('team', 'experience', e.target.value)}
className="text-blue-600"
/>
<span className="text-sm">{exp.label}</span>
</label>
))}
</div>
</div>
</div>
);
case 3:
return (
<div className="space-y-6">
<div className="grid grid-cols-2 gap-4">
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Hora de Apertura
</label>
<input
type="time"
value={formData.operations.openingHours.start}
onChange={(e) => handleInputChange('operations', 'openingHours', {
...formData.operations.openingHours,
start: e.target.value
})}
className="w-full px-3 py-2 border border-gray-300 rounded-md"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Hora de Cierre
</label>
<input
type="time"
value={formData.operations.openingHours.end}
onChange={(e) => handleInputChange('operations', 'openingHours', {
...formData.operations.openingHours,
end: e.target.value
})}
className="w-full px-3 py-2 border border-gray-300 rounded-md"
/>
</div>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Días de Operación por Semana
</label>
<select
value={formData.operations.daysOpen}
onChange={(e) => handleInputChange('operations', 'daysOpen', parseInt(e.target.value))}
className="w-full px-3 py-2 border border-gray-300 rounded-md"
>
<option value={5}>5 días</option>
<option value={6}>6 días</option>
<option value={7}>7 días</option>
</select>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-3">
Especialidades *
</label>
<div className="grid grid-cols-2 gap-3">
{specialties.map((specialty) => (
<label key={specialty.value} className="flex items-center space-x-3 p-3 border rounded-lg cursor-pointer hover:bg-gray-50">
<input
type="checkbox"
checked={formData.operations.specialties.includes(specialty.value)}
onChange={() => handleArrayToggle('operations', 'specialties', specialty.value)}
className="text-blue-600 rounded"
/>
<span className="text-sm">{specialty.label}</span>
</label>
))}
</div>
</div>
</div>
);
case 4:
return (
<div className="space-y-6">
<div>
<label className="block text-sm font-medium text-gray-700 mb-3">
Objetivos Principales *
</label>
<div className="grid grid-cols-2 gap-3">
{businessGoals.map((goal) => (
<label key={goal.value} className="flex items-center space-x-3 p-3 border rounded-lg cursor-pointer hover:bg-gray-50">
<input
type="checkbox"
checked={formData.goals.primaryGoals.includes(goal.value)}
onChange={() => handleArrayToggle('goals', 'primaryGoals', goal.value)}
className="text-blue-600 rounded"
/>
<span className="text-sm">{goal.label}</span>
</label>
))}
</div>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Ingresos Mensuales Esperados (opcional)
</label>
<select
value={formData.goals.expectedRevenue}
onChange={(e) => handleInputChange('goals', 'expectedRevenue', e.target.value)}
className="w-full px-3 py-2 border border-gray-300 rounded-md"
>
<option value="">Seleccionar rango</option>
<option value="0-5000">Menos de €5,000</option>
<option value="5000-15000">€5,000 - €15,000</option>
<option value="15000-30000">€15,000 - €30,000</option>
<option value="30000+">Más de €30,000</option>
</select>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Plazo para Alcanzar Objetivos
</label>
<select
value={formData.goals.timeline}
onChange={(e) => handleInputChange('goals', 'timeline', e.target.value)}
className="w-full px-3 py-2 border border-gray-300 rounded-md"
>
<option value="3months">3 meses</option>
<option value="6months">6 meses</option>
<option value="1year">1 año</option>
<option value="2years">2 años o más</option>
</select>
</div>
</div>
);
default:
return null;
}
};
return (
<div className="p-6 max-w-4xl mx-auto">
<PageHeader
title="Configuración Inicial"
description="Configura tu panadería paso a paso para comenzar"
/>
{/* Progress Steps */}
<Card className="p-6 mb-8">
<div className="flex items-center justify-between mb-6">
{steps.map((step, index) => (
<div key={step.id} className="flex items-center">
<div className={`flex items-center justify-center w-10 h-10 rounded-full ${
step.id === currentStep
? 'bg-blue-600 text-white'
: step.id < currentStep || isStepComplete(step.id)
? 'bg-green-600 text-white'
: 'bg-gray-200 text-gray-600'
}`}>
{step.id < currentStep || (step.id === currentStep && isStepComplete(step.id)) ? (
<Check className="w-5 h-5" />
) : (
<step.icon className="w-5 h-5" />
)}
</div>
{index < steps.length - 1 && (
<div className={`w-full h-1 mx-4 ${
step.id < currentStep ? 'bg-green-600' : 'bg-gray-200'
}`} />
)}
</div>
))}
</div>
<div className="text-center">
<h2 className="text-xl font-semibold text-gray-900 mb-2">
Paso {currentStep}: {steps[currentStep - 1].title}
</h2>
<p className="text-gray-600">
{steps[currentStep - 1].description}
</p>
</div>
</Card>
{/* Step Content */}
<Card className="p-8 mb-8">
{renderStepContent()}
</Card>
{/* Navigation */}
<div className="flex justify-between">
<Button
variant="outline"
onClick={prevStep}
disabled={currentStep === 1}
>
<ChevronLeft className="w-4 h-4 mr-2" />
Anterior
</Button>
{currentStep === steps.length ? (
<Button onClick={handleFinish}>
<Check className="w-4 h-4 mr-2" />
Finalizar Configuración
</Button>
) : (
<Button
onClick={nextStep}
disabled={!isStepComplete(currentStep)}
>
Siguiente
<ChevronRight className="w-4 h-4 ml-2" />
</Button>
)}
</div>
</div>
);
};
export default OnboardingSetupPage;

View File

@@ -1 +0,0 @@
export { default as OnboardingSetupPage } from './OnboardingSetupPage';

View File

@@ -1,454 +0,0 @@
import React, { useState } from 'react';
import { Upload, File, Check, AlertCircle, Download, RefreshCw, Trash2, Eye } from 'lucide-react';
import { Button, Card, Badge } from '../../../../components/ui';
import { PageHeader } from '../../../../components/layout';
const OnboardingUploadPage: React.FC = () => {
const [dragActive, setDragActive] = useState(false);
const [uploadProgress, setUploadProgress] = useState(0);
const [isProcessing, setIsProcessing] = useState(false);
const uploadedFiles = [
{
id: '1',
name: 'productos_menu.csv',
type: 'productos',
size: '45 KB',
status: 'completed',
uploadedAt: '2024-01-26 10:30:00',
records: 127,
errors: 3,
warnings: 8
},
{
id: '2',
name: 'inventario_inicial.xlsx',
type: 'inventario',
size: '82 KB',
status: 'completed',
uploadedAt: '2024-01-26 10:25:00',
records: 89,
errors: 0,
warnings: 2
},
{
id: '3',
name: 'empleados.csv',
type: 'empleados',
size: '12 KB',
status: 'processing',
uploadedAt: '2024-01-26 10:35:00',
records: 8,
errors: 0,
warnings: 0
},
{
id: '4',
name: 'ventas_historicas.csv',
type: 'ventas',
size: '256 KB',
status: 'error',
uploadedAt: '2024-01-26 10:20:00',
records: 0,
errors: 1,
warnings: 0,
errorMessage: 'Formato de fecha incorrecto en la columna "fecha_venta"'
}
];
const supportedFormats = [
{
type: 'productos',
name: 'Productos y Menú',
formats: ['CSV', 'Excel'],
description: 'Lista de productos con precios, categorías y descripciones',
template: 'template_productos.csv',
requiredFields: ['nombre', 'categoria', 'precio', 'descripcion']
},
{
type: 'inventario',
name: 'Inventario Inicial',
formats: ['CSV', 'Excel'],
description: 'Stock inicial de ingredientes y materias primas',
template: 'template_inventario.xlsx',
requiredFields: ['item', 'cantidad', 'unidad', 'costo_unitario']
},
{
type: 'empleados',
name: 'Empleados',
formats: ['CSV'],
description: 'Información del personal y roles',
template: 'template_empleados.csv',
requiredFields: ['nombre', 'cargo', 'email', 'telefono']
},
{
type: 'ventas',
name: 'Historial de Ventas',
formats: ['CSV'],
description: 'Datos históricos de ventas para análisis',
template: 'template_ventas.csv',
requiredFields: ['fecha', 'producto', 'cantidad', 'total']
},
{
type: 'proveedores',
name: 'Proveedores',
formats: ['CSV', 'Excel'],
description: 'Lista de proveedores y datos de contacto',
template: 'template_proveedores.csv',
requiredFields: ['nombre', 'contacto', 'telefono', 'email']
}
];
const uploadStats = {
totalFiles: uploadedFiles.length,
completedFiles: uploadedFiles.filter(f => f.status === 'completed').length,
totalRecords: uploadedFiles.reduce((sum, f) => sum + f.records, 0),
totalErrors: uploadedFiles.reduce((sum, f) => sum + f.errors, 0),
totalWarnings: uploadedFiles.reduce((sum, f) => sum + f.warnings, 0)
};
const getStatusIcon = (status: string) => {
const iconProps = { className: "w-5 h-5" };
switch (status) {
case 'completed': return <Check {...iconProps} className="w-5 h-5 text-[var(--color-success)]" />;
case 'processing': return <RefreshCw {...iconProps} className="w-5 h-5 text-[var(--color-info)] animate-spin" />;
case 'error': return <AlertCircle {...iconProps} className="w-5 h-5 text-[var(--color-error)]" />;
default: return <File {...iconProps} />;
}
};
const getStatusColor = (status: string) => {
switch (status) {
case 'completed': return 'green';
case 'processing': return 'blue';
case 'error': return 'red';
default: return 'gray';
}
};
const getStatusLabel = (status: string) => {
switch (status) {
case 'completed': return 'Completado';
case 'processing': return 'Procesando';
case 'error': return 'Error';
default: return status;
}
};
const handleDragOver = (e: React.DragEvent) => {
e.preventDefault();
setDragActive(true);
};
const handleDragLeave = (e: React.DragEvent) => {
e.preventDefault();
setDragActive(false);
};
const handleDrop = (e: React.DragEvent) => {
e.preventDefault();
setDragActive(false);
const files = Array.from(e.dataTransfer.files);
handleFiles(files);
};
const handleFileInput = (e: React.ChangeEvent<HTMLInputElement>) => {
if (e.target.files) {
const files = Array.from(e.target.files);
handleFiles(files);
}
};
const handleFiles = (files: File[]) => {
console.log('Files selected:', files);
// Simulate upload progress
setIsProcessing(true);
setUploadProgress(0);
const interval = setInterval(() => {
setUploadProgress(prev => {
if (prev >= 100) {
clearInterval(interval);
setIsProcessing(false);
return 100;
}
return prev + 10;
});
}, 200);
};
const downloadTemplate = (template: string) => {
console.log('Downloading template:', template);
// Handle template download
};
const retryUpload = (fileId: string) => {
console.log('Retrying upload for file:', fileId);
// Handle retry logic
};
const deleteFile = (fileId: string) => {
console.log('Deleting file:', fileId);
// Handle delete logic
};
const viewDetails = (fileId: string) => {
console.log('Viewing details for file:', fileId);
// Handle view details logic
};
return (
<div className="p-6 space-y-6">
<PageHeader
title="Carga de Datos"
description="Importa tus datos existentes para acelerar la configuración inicial"
/>
{/* Upload Stats */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-5 gap-6">
<Card className="p-6">
<div className="flex items-center justify-between">
<div>
<p className="text-sm font-medium text-[var(--text-secondary)]">Archivos Subidos</p>
<p className="text-3xl font-bold text-[var(--color-info)]">{uploadStats.totalFiles}</p>
</div>
<div className="h-12 w-12 bg-[var(--color-info)]/10 rounded-full flex items-center justify-center">
<Upload className="h-6 w-6 text-[var(--color-info)]" />
</div>
</div>
</Card>
<Card className="p-6">
<div className="flex items-center justify-between">
<div>
<p className="text-sm font-medium text-[var(--text-secondary)]">Completados</p>
<p className="text-3xl font-bold text-[var(--color-success)]">{uploadStats.completedFiles}</p>
</div>
<div className="h-12 w-12 bg-[var(--color-success)]/10 rounded-full flex items-center justify-center">
<Check className="h-6 w-6 text-[var(--color-success)]" />
</div>
</div>
</Card>
<Card className="p-6">
<div className="flex items-center justify-between">
<div>
<p className="text-sm font-medium text-[var(--text-secondary)]">Registros</p>
<p className="text-3xl font-bold text-purple-600">{uploadStats.totalRecords}</p>
</div>
<div className="h-12 w-12 bg-purple-100 rounded-full flex items-center justify-center">
<File className="h-6 w-6 text-purple-600" />
</div>
</div>
</Card>
<Card className="p-6">
<div className="flex items-center justify-between">
<div>
<p className="text-sm font-medium text-[var(--text-secondary)]">Errores</p>
<p className="text-3xl font-bold text-[var(--color-error)]">{uploadStats.totalErrors}</p>
</div>
<div className="h-12 w-12 bg-[var(--color-error)]/10 rounded-full flex items-center justify-center">
<AlertCircle className="h-6 w-6 text-[var(--color-error)]" />
</div>
</div>
</Card>
<Card className="p-6">
<div className="flex items-center justify-between">
<div>
<p className="text-sm font-medium text-[var(--text-secondary)]">Advertencias</p>
<p className="text-3xl font-bold text-yellow-600">{uploadStats.totalWarnings}</p>
</div>
<div className="h-12 w-12 bg-yellow-100 rounded-full flex items-center justify-center">
<AlertCircle className="h-6 w-6 text-yellow-600" />
</div>
</div>
</Card>
</div>
{/* Upload Area */}
<Card className="p-8">
<div
className={`border-2 border-dashed rounded-lg p-8 text-center transition-colors ${
dragActive
? 'border-blue-400 bg-[var(--color-info)]/5'
: 'border-[var(--border-secondary)] hover:border-[var(--border-tertiary)]'
}`}
onDragOver={handleDragOver}
onDragLeave={handleDragLeave}
onDrop={handleDrop}
>
<Upload className="w-12 h-12 text-[var(--text-tertiary)] mx-auto mb-4" />
<h3 className="text-lg font-semibold text-[var(--text-primary)] mb-2">
Arrastra archivos aquí o haz clic para seleccionar
</h3>
<p className="text-[var(--text-secondary)] mb-4">
Formatos soportados: CSV, Excel (XLSX). Tamaño máximo: 10MB por archivo
</p>
<input
type="file"
multiple
accept=".csv,.xlsx,.xls"
onChange={handleFileInput}
className="hidden"
id="file-upload"
/>
<label htmlFor="file-upload">
<Button className="cursor-pointer">
Seleccionar Archivos
</Button>
</label>
{isProcessing && (
<div className="mt-4">
<div className="w-full bg-[var(--bg-quaternary)] rounded-full h-2 mb-2">
<div
className="bg-blue-600 h-2 rounded-full transition-all duration-300"
style={{ width: `${uploadProgress}%` }}
></div>
</div>
<p className="text-sm text-[var(--text-secondary)]">Procesando... {uploadProgress}%</p>
</div>
)}
</div>
</Card>
{/* Supported Formats */}
<Card className="p-6">
<h3 className="text-lg font-semibold text-[var(--text-primary)] mb-4">Formatos Soportados</h3>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{supportedFormats.map((format, index) => (
<div key={index} className="border rounded-lg p-4">
<div className="flex items-center justify-between mb-2">
<h4 className="font-medium text-[var(--text-primary)]">{format.name}</h4>
<div className="flex space-x-1">
{format.formats.map((fmt, idx) => (
<Badge key={idx} variant="blue" className="text-xs">{fmt}</Badge>
))}
</div>
</div>
<p className="text-sm text-[var(--text-secondary)] mb-3">{format.description}</p>
<div className="mb-3">
<p className="text-xs font-medium text-[var(--text-secondary)] mb-1">Campos requeridos:</p>
<div className="flex flex-wrap gap-1">
{format.requiredFields.map((field, idx) => (
<span key={idx} className="px-2 py-1 bg-[var(--bg-tertiary)] text-[var(--text-secondary)] text-xs rounded">
{field}
</span>
))}
</div>
</div>
<Button
size="sm"
variant="outline"
className="w-full"
onClick={() => downloadTemplate(format.template)}
>
<Download className="w-3 h-3 mr-2" />
Descargar Plantilla
</Button>
</div>
))}
</div>
</Card>
{/* Uploaded Files */}
<Card className="p-6">
<h3 className="text-lg font-semibold text-[var(--text-primary)] mb-4">Archivos Cargados</h3>
<div className="space-y-3">
{uploadedFiles.map((file) => (
<div key={file.id} className="flex items-center justify-between p-4 border rounded-lg">
<div className="flex items-center space-x-4 flex-1">
{getStatusIcon(file.status)}
<div>
<div className="flex items-center space-x-3 mb-1">
<h4 className="text-sm font-medium text-[var(--text-primary)]">{file.name}</h4>
<Badge variant={getStatusColor(file.status)}>
{getStatusLabel(file.status)}
</Badge>
<span className="text-xs text-[var(--text-tertiary)]">{file.size}</span>
</div>
<div className="flex items-center space-x-4 text-xs text-[var(--text-secondary)]">
<span>{file.records} registros</span>
{file.errors > 0 && (
<span className="text-[var(--color-error)]">{file.errors} errores</span>
)}
{file.warnings > 0 && (
<span className="text-yellow-600">{file.warnings} advertencias</span>
)}
<span>Subido: {new Date(file.uploadedAt).toLocaleString('es-ES')}</span>
</div>
{file.status === 'error' && file.errorMessage && (
<div className="mt-2 p-2 bg-red-50 border border-red-200 rounded text-xs text-[var(--color-error)]">
{file.errorMessage}
</div>
)}
</div>
</div>
<div className="flex space-x-2">
<Button size="sm" variant="outline" onClick={() => viewDetails(file.id)}>
<Eye className="w-3 h-3" />
</Button>
{file.status === 'error' && (
<Button size="sm" variant="outline" onClick={() => retryUpload(file.id)}>
<RefreshCw className="w-3 h-3" />
</Button>
)}
<Button size="sm" variant="outline" onClick={() => deleteFile(file.id)}>
<Trash2 className="w-3 h-3" />
</Button>
</div>
</div>
))}
</div>
</Card>
{/* Help Section */}
<Card className="p-6 bg-[var(--color-info)]/5 border-[var(--color-info)]/20">
<h3 className="text-lg font-semibold text-blue-900 mb-4">Tips para la Carga de Datos</h3>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 text-sm text-[var(--color-info)]">
<ul className="space-y-2">
<li> Usa las plantillas proporcionadas para garantizar el formato correcto</li>
<li> Verifica que todos los campos requeridos estén completos</li>
<li> Los archivos CSV deben usar codificación UTF-8</li>
<li> Las fechas deben estar en formato DD/MM/YYYY</li>
</ul>
<ul className="space-y-2">
<li> Los precios deben usar punto (.) como separador decimal</li>
<li> Evita caracteres especiales en los nombres de productos</li>
<li> Mantén los nombres de archivos descriptivos</li>
<li> Puedes cargar múltiples archivos del mismo tipo</li>
</ul>
</div>
</Card>
{/* Navigation */}
<div className="flex justify-between items-center">
<Button
variant="outline"
onClick={() => window.location.href = '/app/onboarding/setup'}
>
Volver a Configuración
</Button>
<Button
onClick={() => window.location.href = '/app/onboarding/analysis'}
>
Continuar al Análisis
</Button>
</div>
</div>
);
};
export default OnboardingUploadPage;

View File

@@ -1,438 +0,0 @@
import React, { useState } from 'react';
import { Upload, File, Check, AlertCircle, Download, RefreshCw, Trash2, Eye } from 'lucide-react';
import { Button, Card, Badge } from '../../../../components/ui';
import { PageHeader } from '../../../../components/layout';
const OnboardingUploadPage: React.FC = () => {
const [dragActive, setDragActive] = useState(false);
const [uploadProgress, setUploadProgress] = useState(0);
const [isProcessing, setIsProcessing] = useState(false);
const uploadedFiles = [
{
id: '1',
name: 'productos_menu.csv',
type: 'productos',
size: '45 KB',
status: 'completed',
uploadedAt: '2024-01-26 10:30:00',
records: 127,
errors: 3,
warnings: 8
},
{
id: '2',
name: 'inventario_inicial.xlsx',
type: 'inventario',
size: '82 KB',
status: 'completed',
uploadedAt: '2024-01-26 10:25:00',
records: 89,
errors: 0,
warnings: 2
},
{
id: '3',
name: 'empleados.csv',
type: 'empleados',
size: '12 KB',
status: 'processing',
uploadedAt: '2024-01-26 10:35:00',
records: 8,
errors: 0,
warnings: 0
},
{
id: '4',
name: 'ventas_historicas.csv',
type: 'ventas',
size: '256 KB',
status: 'error',
uploadedAt: '2024-01-26 10:20:00',
records: 0,
errors: 1,
warnings: 0,
errorMessage: 'Formato de fecha incorrecto en la columna "fecha_venta"'
}
];
const supportedFormats = [
{
type: 'productos',
name: 'Productos y Menú',
formats: ['CSV', 'Excel'],
description: 'Lista de productos con precios, categorías y descripciones',
template: 'template_productos.csv',
requiredFields: ['nombre', 'categoria', 'precio', 'descripcion']
},
{
type: 'inventario',
name: 'Inventario Inicial',
formats: ['CSV', 'Excel'],
description: 'Stock inicial de ingredientes y materias primas',
template: 'template_inventario.xlsx',
requiredFields: ['item', 'cantidad', 'unidad', 'costo_unitario']
},
{
type: 'empleados',
name: 'Empleados',
formats: ['CSV'],
description: 'Información del personal y roles',
template: 'template_empleados.csv',
requiredFields: ['nombre', 'cargo', 'email', 'telefono']
},
{
type: 'ventas',
name: 'Historial de Ventas',
formats: ['CSV'],
description: 'Datos históricos de ventas para análisis',
template: 'template_ventas.csv',
requiredFields: ['fecha', 'producto', 'cantidad', 'total']
},
{
type: 'proveedores',
name: 'Proveedores',
formats: ['CSV', 'Excel'],
description: 'Lista de proveedores y datos de contacto',
template: 'template_proveedores.csv',
requiredFields: ['nombre', 'contacto', 'telefono', 'email']
}
];
const uploadStats = {
totalFiles: uploadedFiles.length,
completedFiles: uploadedFiles.filter(f => f.status === 'completed').length,
totalRecords: uploadedFiles.reduce((sum, f) => sum + f.records, 0),
totalErrors: uploadedFiles.reduce((sum, f) => sum + f.errors, 0),
totalWarnings: uploadedFiles.reduce((sum, f) => sum + f.warnings, 0)
};
const getStatusIcon = (status: string) => {
const iconProps = { className: "w-5 h-5" };
switch (status) {
case 'completed': return <Check {...iconProps} className="w-5 h-5 text-green-600" />;
case 'processing': return <RefreshCw {...iconProps} className="w-5 h-5 text-blue-600 animate-spin" />;
case 'error': return <AlertCircle {...iconProps} className="w-5 h-5 text-red-600" />;
default: return <File {...iconProps} />;
}
};
const getStatusColor = (status: string) => {
switch (status) {
case 'completed': return 'green';
case 'processing': return 'blue';
case 'error': return 'red';
default: return 'gray';
}
};
const getStatusLabel = (status: string) => {
switch (status) {
case 'completed': return 'Completado';
case 'processing': return 'Procesando';
case 'error': return 'Error';
default: return status;
}
};
const handleDragOver = (e: React.DragEvent) => {
e.preventDefault();
setDragActive(true);
};
const handleDragLeave = (e: React.DragEvent) => {
e.preventDefault();
setDragActive(false);
};
const handleDrop = (e: React.DragEvent) => {
e.preventDefault();
setDragActive(false);
const files = Array.from(e.dataTransfer.files);
handleFiles(files);
};
const handleFileInput = (e: React.ChangeEvent<HTMLInputElement>) => {
if (e.target.files) {
const files = Array.from(e.target.files);
handleFiles(files);
}
};
const handleFiles = (files: File[]) => {
console.log('Files selected:', files);
// Simulate upload progress
setIsProcessing(true);
setUploadProgress(0);
const interval = setInterval(() => {
setUploadProgress(prev => {
if (prev >= 100) {
clearInterval(interval);
setIsProcessing(false);
return 100;
}
return prev + 10;
});
}, 200);
};
const downloadTemplate = (template: string) => {
console.log('Downloading template:', template);
// Handle template download
};
const retryUpload = (fileId: string) => {
console.log('Retrying upload for file:', fileId);
// Handle retry logic
};
const deleteFile = (fileId: string) => {
console.log('Deleting file:', fileId);
// Handle delete logic
};
const viewDetails = (fileId: string) => {
console.log('Viewing details for file:', fileId);
// Handle view details logic
};
return (
<div className="p-6 space-y-6">
<PageHeader
title="Carga de Datos"
description="Importa tus datos existentes para acelerar la configuración inicial"
/>
{/* Upload Stats */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-5 gap-6">
<Card className="p-6">
<div className="flex items-center justify-between">
<div>
<p className="text-sm font-medium text-gray-600">Archivos Subidos</p>
<p className="text-3xl font-bold text-blue-600">{uploadStats.totalFiles}</p>
</div>
<div className="h-12 w-12 bg-blue-100 rounded-full flex items-center justify-center">
<Upload className="h-6 w-6 text-blue-600" />
</div>
</div>
</Card>
<Card className="p-6">
<div className="flex items-center justify-between">
<div>
<p className="text-sm font-medium text-gray-600">Completados</p>
<p className="text-3xl font-bold text-green-600">{uploadStats.completedFiles}</p>
</div>
<div className="h-12 w-12 bg-green-100 rounded-full flex items-center justify-center">
<Check className="h-6 w-6 text-green-600" />
</div>
</div>
</Card>
<Card className="p-6">
<div className="flex items-center justify-between">
<div>
<p className="text-sm font-medium text-gray-600">Registros</p>
<p className="text-3xl font-bold text-purple-600">{uploadStats.totalRecords}</p>
</div>
<div className="h-12 w-12 bg-purple-100 rounded-full flex items-center justify-center">
<File className="h-6 w-6 text-purple-600" />
</div>
</div>
</Card>
<Card className="p-6">
<div className="flex items-center justify-between">
<div>
<p className="text-sm font-medium text-gray-600">Errores</p>
<p className="text-3xl font-bold text-red-600">{uploadStats.totalErrors}</p>
</div>
<div className="h-12 w-12 bg-red-100 rounded-full flex items-center justify-center">
<AlertCircle className="h-6 w-6 text-red-600" />
</div>
</div>
</Card>
<Card className="p-6">
<div className="flex items-center justify-between">
<div>
<p className="text-sm font-medium text-gray-600">Advertencias</p>
<p className="text-3xl font-bold text-yellow-600">{uploadStats.totalWarnings}</p>
</div>
<div className="h-12 w-12 bg-yellow-100 rounded-full flex items-center justify-center">
<AlertCircle className="h-6 w-6 text-yellow-600" />
</div>
</div>
</Card>
</div>
{/* Upload Area */}
<Card className="p-8">
<div
className={`border-2 border-dashed rounded-lg p-8 text-center transition-colors ${
dragActive
? 'border-blue-400 bg-blue-50'
: 'border-gray-300 hover:border-gray-400'
}`}
onDragOver={handleDragOver}
onDragLeave={handleDragLeave}
onDrop={handleDrop}
>
<Upload className="w-12 h-12 text-gray-400 mx-auto mb-4" />
<h3 className="text-lg font-semibold text-gray-900 mb-2">
Arrastra archivos aquí o haz clic para seleccionar
</h3>
<p className="text-gray-600 mb-4">
Formatos soportados: CSV, Excel (XLSX). Tamaño máximo: 10MB por archivo
</p>
<input
type="file"
multiple
accept=".csv,.xlsx,.xls"
onChange={handleFileInput}
className="hidden"
id="file-upload"
/>
<label htmlFor="file-upload">
<Button className="cursor-pointer">
Seleccionar Archivos
</Button>
</label>
{isProcessing && (
<div className="mt-4">
<div className="w-full bg-gray-200 rounded-full h-2 mb-2">
<div
className="bg-blue-600 h-2 rounded-full transition-all duration-300"
style={{ width: `${uploadProgress}%` }}
></div>
</div>
<p className="text-sm text-gray-600">Procesando... {uploadProgress}%</p>
</div>
)}
</div>
</Card>
{/* Supported Formats */}
<Card className="p-6">
<h3 className="text-lg font-semibold text-gray-900 mb-4">Formatos Soportados</h3>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{supportedFormats.map((format, index) => (
<div key={index} className="border rounded-lg p-4">
<div className="flex items-center justify-between mb-2">
<h4 className="font-medium text-gray-900">{format.name}</h4>
<div className="flex space-x-1">
{format.formats.map((fmt, idx) => (
<Badge key={idx} variant="blue" className="text-xs">{fmt}</Badge>
))}
</div>
</div>
<p className="text-sm text-gray-600 mb-3">{format.description}</p>
<div className="mb-3">
<p className="text-xs font-medium text-gray-700 mb-1">Campos requeridos:</p>
<div className="flex flex-wrap gap-1">
{format.requiredFields.map((field, idx) => (
<span key={idx} className="px-2 py-1 bg-gray-100 text-gray-700 text-xs rounded">
{field}
</span>
))}
</div>
</div>
<Button
size="sm"
variant="outline"
className="w-full"
onClick={() => downloadTemplate(format.template)}
>
<Download className="w-3 h-3 mr-2" />
Descargar Plantilla
</Button>
</div>
))}
</div>
</Card>
{/* Uploaded Files */}
<Card className="p-6">
<h3 className="text-lg font-semibold text-gray-900 mb-4">Archivos Cargados</h3>
<div className="space-y-3">
{uploadedFiles.map((file) => (
<div key={file.id} className="flex items-center justify-between p-4 border rounded-lg">
<div className="flex items-center space-x-4 flex-1">
{getStatusIcon(file.status)}
<div>
<div className="flex items-center space-x-3 mb-1">
<h4 className="text-sm font-medium text-gray-900">{file.name}</h4>
<Badge variant={getStatusColor(file.status)}>
{getStatusLabel(file.status)}
</Badge>
<span className="text-xs text-gray-500">{file.size}</span>
</div>
<div className="flex items-center space-x-4 text-xs text-gray-600">
<span>{file.records} registros</span>
{file.errors > 0 && (
<span className="text-red-600">{file.errors} errores</span>
)}
{file.warnings > 0 && (
<span className="text-yellow-600">{file.warnings} advertencias</span>
)}
<span>Subido: {new Date(file.uploadedAt).toLocaleString('es-ES')}</span>
</div>
{file.status === 'error' && file.errorMessage && (
<div className="mt-2 p-2 bg-red-50 border border-red-200 rounded text-xs text-red-700">
{file.errorMessage}
</div>
)}
</div>
</div>
<div className="flex space-x-2">
<Button size="sm" variant="outline" onClick={() => viewDetails(file.id)}>
<Eye className="w-3 h-3" />
</Button>
{file.status === 'error' && (
<Button size="sm" variant="outline" onClick={() => retryUpload(file.id)}>
<RefreshCw className="w-3 h-3" />
</Button>
)}
<Button size="sm" variant="outline" onClick={() => deleteFile(file.id)}>
<Trash2 className="w-3 h-3" />
</Button>
</div>
</div>
))}
</div>
</Card>
{/* Help Section */}
<Card className="p-6 bg-blue-50 border-blue-200">
<h3 className="text-lg font-semibold text-blue-900 mb-4">Tips para la Carga de Datos</h3>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 text-sm text-blue-800">
<ul className="space-y-2">
<li>• Usa las plantillas proporcionadas para garantizar el formato correcto</li>
<li>• Verifica que todos los campos requeridos estén completos</li>
<li>• Los archivos CSV deben usar codificación UTF-8</li>
<li>• Las fechas deben estar en formato DD/MM/YYYY</li>
</ul>
<ul className="space-y-2">
<li>• Los precios deben usar punto (.) como separador decimal</li>
<li>• Evita caracteres especiales en los nombres de productos</li>
<li>• Mantén los nombres de archivos descriptivos</li>
<li>• Puedes cargar múltiples archivos del mismo tipo</li>
</ul>
</div>
</Card>
</div>
);
};
export default OnboardingUploadPage;

View File

@@ -1 +0,0 @@
export { default as OnboardingUploadPage } from './OnboardingUploadPage';

View File

@@ -1,710 +0,0 @@
import React from 'react';
import { Link } from 'react-router-dom';
import { Button } from '../../components/ui';
import { PublicLayout } from '../../components/layout';
import {
BarChart3,
TrendingUp,
Shield,
Zap,
Users,
Award,
ChevronRight,
Check,
Star,
ArrowRight,
Play,
Calendar,
Clock,
DollarSign,
Package,
PieChart,
Settings
} from 'lucide-react';
const LandingPage: React.FC = () => {
const scrollToSection = (sectionId: string) => {
const element = document.getElementById(sectionId);
if (element) {
element.scrollIntoView({ behavior: 'smooth' });
}
};
return (
<PublicLayout
variant="full-width"
contentPadding="none"
headerProps={{
showThemeToggle: true,
showAuthButtons: true,
variant: "default",
navigationItems: [
{ id: 'features', label: 'Características', href: '#features' },
{ id: 'benefits', label: 'Beneficios', href: '#benefits' },
{ id: 'pricing', label: 'Precios', href: '#pricing' },
{ id: 'testimonials', label: 'Testimonios', href: '#testimonials' }
]
}}
>
{/* Hero Section */}
<section className="relative py-20 lg:py-32 bg-gradient-to-br from-[var(--bg-primary)] via-[var(--bg-secondary)] to-[var(--color-primary)]/5">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="text-center">
<div className="mb-6">
<span className="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium bg-[var(--color-primary)]/10 text-[var(--color-primary)]">
<Zap className="w-4 h-4 mr-2" />
IA Avanzada para Panaderías
</span>
</div>
<h1 className="text-4xl tracking-tight font-extrabold text-[var(--text-primary)] sm:text-5xl lg:text-7xl">
<span className="block">Revoluciona tu</span>
<span className="block text-[var(--color-primary)]">Panadería con IA</span>
</h1>
<p className="mt-6 max-w-3xl mx-auto text-lg text-[var(--text-secondary)] sm:text-xl">
Optimiza automáticamente tu producción, reduce desperdicios hasta un 35%,
predice demanda con precisión del 92% y aumenta tus ventas con inteligencia artificial.
</p>
<div className="mt-10 flex flex-col sm:flex-row gap-4 justify-center">
<Link to="/register">
<Button size="lg" className="px-8 py-4 text-lg font-semibold bg-[var(--color-primary)] hover:bg-[var(--color-primary-dark)] text-white shadow-lg hover:shadow-xl transform hover:scale-105 transition-all duration-200">
Comenzar Gratis 14 Días
<ArrowRight className="ml-2 w-5 h-5" />
</Button>
</Link>
<Button
variant="outline"
size="lg"
className="px-8 py-4 text-lg font-semibold border-2 border-[var(--color-primary)] text-[var(--color-primary)] hover:bg-[var(--color-primary)] hover:text-white transition-all duration-200"
onClick={() => scrollToSection('demo')}
>
<Play className="mr-2 w-5 h-5" />
Ver Demo en Vivo
</Button>
</div>
<div className="mt-12 flex items-center justify-center space-x-6 text-sm text-[var(--text-tertiary)]">
<div className="flex items-center">
<Check className="w-4 h-4 text-green-500 mr-2" />
Sin tarjeta de crédito
</div>
<div className="flex items-center">
<Check className="w-4 h-4 text-green-500 mr-2" />
Configuración en 5 minutos
</div>
<div className="flex items-center">
<Check className="w-4 h-4 text-green-500 mr-2" />
Soporte 24/7 en español
</div>
</div>
</div>
</div>
{/* Background decoration */}
<div className="absolute top-0 left-0 right-0 h-full overflow-hidden -z-10">
<div className="absolute -top-40 -right-40 w-80 h-80 bg-[var(--color-primary)]/5 rounded-full blur-3xl"></div>
<div className="absolute -bottom-40 -left-40 w-80 h-80 bg-[var(--color-secondary)]/5 rounded-full blur-3xl"></div>
</div>
</section>
{/* Stats Section */}
<section className="py-16 bg-[var(--bg-primary)]">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="grid grid-cols-2 gap-8 md:grid-cols-4">
<div className="text-center">
<div className="text-3xl font-bold text-[var(--color-primary)]">500+</div>
<div className="mt-2 text-sm text-[var(--text-secondary)]">Panaderías Activas</div>
</div>
<div className="text-center">
<div className="text-3xl font-bold text-[var(--color-primary)]">35%</div>
<div className="mt-2 text-sm text-[var(--text-secondary)]">Reducción de Desperdicios</div>
</div>
<div className="text-center">
<div className="text-3xl font-bold text-[var(--color-primary)]">92%</div>
<div className="mt-2 text-sm text-[var(--text-secondary)]">Precisión de Predicciones</div>
</div>
<div className="text-center">
<div className="text-3xl font-bold text-[var(--color-primary)]">4.8★</div>
<div className="mt-2 text-sm text-[var(--text-secondary)]">Satisfacción de Clientes</div>
</div>
</div>
</div>
</section>
{/* Main Features Section */}
<section id="features" className="py-24 bg-[var(--bg-secondary)]">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="text-center">
<h2 className="text-3xl lg:text-5xl font-extrabold text-[var(--text-primary)]">
Gestión Completa con
<span className="block text-[var(--color-primary)]">Inteligencia Artificial</span>
</h2>
<p className="mt-6 max-w-3xl mx-auto text-lg text-[var(--text-secondary)]">
Automatiza procesos, optimiza recursos y toma decisiones inteligentes basadas en datos reales de tu panadería.
</p>
</div>
<div className="mt-20 grid grid-cols-1 lg:grid-cols-3 gap-8">
{/* AI Forecasting */}
<div className="group relative bg-[var(--bg-primary)] rounded-2xl p-8 shadow-lg hover:shadow-2xl transition-all duration-300 border border-[var(--border-primary)] hover:border-[var(--color-primary)]/30">
<div className="absolute -top-4 left-8">
<div className="w-12 h-12 bg-gradient-to-r from-[var(--color-primary)] to-[var(--color-primary-dark)] rounded-xl flex items-center justify-center shadow-lg">
<TrendingUp className="w-6 h-6 text-white" />
</div>
</div>
<div className="mt-6">
<h3 className="text-xl font-bold text-[var(--text-primary)]">Predicción Inteligente</h3>
<p className="mt-4 text-[var(--text-secondary)]">
Algoritmos de IA analizan patrones históricos, clima, eventos locales y tendencias para predecir la demanda exacta de cada producto.
</p>
<div className="mt-6">
<div className="flex items-center text-sm text-[var(--color-primary)]">
<Check className="w-4 h-4 mr-2" />
Precisión del 92% en predicciones
</div>
<div className="flex items-center text-sm text-[var(--color-primary)] mt-2">
<Check className="w-4 h-4 mr-2" />
Reduce desperdicios hasta 35%
</div>
<div className="flex items-center text-sm text-[var(--color-primary)] mt-2">
<Check className="w-4 h-4 mr-2" />
Aumenta ventas promedio 22%
</div>
</div>
</div>
</div>
{/* Smart Inventory */}
<div className="group relative bg-[var(--bg-primary)] rounded-2xl p-8 shadow-lg hover:shadow-2xl transition-all duration-300 border border-[var(--border-primary)] hover:border-[var(--color-primary)]/30">
<div className="absolute -top-4 left-8">
<div className="w-12 h-12 bg-gradient-to-r from-[var(--color-secondary)] to-[var(--color-secondary-dark)] rounded-xl flex items-center justify-center shadow-lg">
<Package className="w-6 h-6 text-white" />
</div>
</div>
<div className="mt-6">
<h3 className="text-xl font-bold text-[var(--text-primary)]">Inventario Inteligente</h3>
<p className="mt-4 text-[var(--text-secondary)]">
Control automático de stock con alertas predictivas, órdenes de compra automatizadas y optimización de costos.
</p>
<div className="mt-6">
<div className="flex items-center text-sm text-[var(--color-secondary)]">
<Check className="w-4 h-4 mr-2" />
Alertas automáticas de stock bajo
</div>
<div className="flex items-center text-sm text-[var(--color-secondary)] mt-2">
<Check className="w-4 h-4 mr-2" />
Órdenes de compra automatizadas
</div>
<div className="flex items-center text-sm text-[var(--color-secondary)] mt-2">
<Check className="w-4 h-4 mr-2" />
Optimización de costos de materias primas
</div>
</div>
</div>
</div>
{/* Production Planning */}
<div className="group relative bg-[var(--bg-primary)] rounded-2xl p-8 shadow-lg hover:shadow-2xl transition-all duration-300 border border-[var(--border-primary)] hover:border-[var(--color-primary)]/30">
<div className="absolute -top-4 left-8">
<div className="w-12 h-12 bg-gradient-to-r from-[var(--color-accent)] to-[var(--color-accent-dark)] rounded-xl flex items-center justify-center shadow-lg">
<Calendar className="w-6 h-6 text-white" />
</div>
</div>
<div className="mt-6">
<h3 className="text-xl font-bold text-[var(--text-primary)]">Planificación de Producción</h3>
<p className="mt-4 text-[var(--text-secondary)]">
Programa automáticamente la producción diaria basada en predicciones, optimiza horarios y recursos disponibles.
</p>
<div className="mt-6">
<div className="flex items-center text-sm text-[var(--color-accent)]">
<Check className="w-4 h-4 mr-2" />
Programación automática de horneado
</div>
<div className="flex items-center text-sm text-[var(--color-accent)] mt-2">
<Check className="w-4 h-4 mr-2" />
Optimización de uso de hornos
</div>
<div className="flex items-center text-sm text-[var(--color-accent)] mt-2">
<Check className="w-4 h-4 mr-2" />
Gestión de personal y turnos
</div>
</div>
</div>
</div>
</div>
{/* Additional Features Grid */}
<div className="mt-16 grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-8">
<div className="text-center p-6 bg-[var(--bg-primary)] rounded-xl border border-[var(--border-primary)]">
<div className="w-12 h-12 bg-[var(--color-primary)]/10 rounded-lg flex items-center justify-center mx-auto mb-4">
<BarChart3 className="w-6 h-6 text-[var(--color-primary)]" />
</div>
<h4 className="font-semibold text-[var(--text-primary)]">Analytics Avanzado</h4>
<p className="text-sm text-[var(--text-secondary)] mt-2">Dashboards en tiempo real con métricas clave</p>
</div>
<div className="text-center p-6 bg-[var(--bg-primary)] rounded-xl border border-[var(--border-primary)]">
<div className="w-12 h-12 bg-[var(--color-secondary)]/10 rounded-lg flex items-center justify-center mx-auto mb-4">
<DollarSign className="w-6 h-6 text-[var(--color-secondary)]" />
</div>
<h4 className="font-semibold text-[var(--text-primary)]">POS Integrado</h4>
<p className="text-sm text-[var(--text-secondary)] mt-2">Sistema de ventas completo y fácil de usar</p>
</div>
<div className="text-center p-6 bg-[var(--bg-primary)] rounded-xl border border-[var(--border-primary)]">
<div className="w-12 h-12 bg-[var(--color-accent)]/10 rounded-lg flex items-center justify-center mx-auto mb-4">
<Shield className="w-6 h-6 text-[var(--color-accent)]" />
</div>
<h4 className="font-semibold text-[var(--text-primary)]">Control de Calidad</h4>
<p className="text-sm text-[var(--text-secondary)] mt-2">Trazabilidad completa y gestión HACCP</p>
</div>
<div className="text-center p-6 bg-[var(--bg-primary)] rounded-xl border border-[var(--border-primary)]">
<div className="w-12 h-12 bg-[var(--color-info)]/10 rounded-lg flex items-center justify-center mx-auto mb-4">
<Settings className="w-6 h-6 text-[var(--color-info)]" />
</div>
<h4 className="font-semibold text-[var(--text-primary)]">Automatización</h4>
<p className="text-sm text-[var(--text-secondary)] mt-2">Procesos automáticos que ahorran tiempo</p>
</div>
</div>
</div>
</section>
{/* Benefits Section */}
<section id="benefits" className="py-24 bg-[var(--bg-primary)]">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="lg:grid lg:grid-cols-2 lg:gap-16 items-center">
<div>
<h2 className="text-3xl lg:text-4xl font-extrabold text-[var(--text-primary)]">
Resultados Comprobados
<span className="block text-[var(--color-primary)]">en Cientos de Panaderías</span>
</h2>
<p className="mt-6 text-lg text-[var(--text-secondary)]">
Nuestros clientes han logrado transformaciones significativas en sus operaciones,
mejorando rentabilidad y reduciendo desperdicios desde el primer mes.
</p>
<div className="mt-10 space-y-8">
<div className="flex items-start">
<div className="flex-shrink-0">
<div className="w-8 h-8 bg-green-100 rounded-full flex items-center justify-center">
<TrendingUp className="w-5 h-5 text-green-600" />
</div>
</div>
<div className="ml-4">
<h4 className="text-lg font-semibold text-[var(--text-primary)]">Aumenta Ventas 22% Promedio</h4>
<p className="text-[var(--text-secondary)]">Optimización de producción y mejor disponibilidad de productos populares</p>
</div>
</div>
<div className="flex items-start">
<div className="flex-shrink-0">
<div className="w-8 h-8 bg-blue-100 rounded-full flex items-center justify-center">
<Shield className="w-5 h-5 text-blue-600" />
</div>
</div>
<div className="ml-4">
<h4 className="text-lg font-semibold text-[var(--text-primary)]">Reduce Desperdicios 35%</h4>
<p className="text-[var(--text-secondary)]">Predicciones precisas evitan sobreproducción y productos vencidos</p>
</div>
</div>
<div className="flex items-start">
<div className="flex-shrink-0">
<div className="w-8 h-8 bg-purple-100 rounded-full flex items-center justify-center">
<Clock className="w-5 h-5 text-purple-600" />
</div>
</div>
<div className="ml-4">
<h4 className="text-lg font-semibold text-[var(--text-primary)]">Ahorra 8 Horas Semanales</h4>
<p className="text-[var(--text-secondary)]">Automatización de tareas administrativas y de planificación</p>
</div>
</div>
</div>
</div>
<div className="mt-12 lg:mt-0">
<div className="bg-gradient-to-r from-[var(--color-primary)]/10 to-[var(--color-secondary)]/10 rounded-2xl p-8">
<div className="grid grid-cols-2 gap-8">
<div className="text-center">
<div className="text-3xl font-bold text-[var(--color-primary)]">€127k</div>
<div className="text-sm text-[var(--text-secondary)]">Ahorro promedio anual por panadería</div>
</div>
<div className="text-center">
<div className="text-3xl font-bold text-[var(--color-secondary)]">98%</div>
<div className="text-sm text-[var(--text-secondary)]">Satisfacción de clientes</div>
</div>
<div className="text-center">
<div className="text-3xl font-bold text-[var(--color-accent)]">2.3x</div>
<div className="text-sm text-[var(--text-secondary)]">ROI promedio en 12 meses</div>
</div>
<div className="text-center">
<div className="text-3xl font-bold text-[var(--color-info)]">24/7</div>
<div className="text-sm text-[var(--text-secondary)]">Soporte técnico especializado</div>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
{/* Testimonials Section */}
<section id="testimonials" className="py-24 bg-[var(--bg-secondary)]">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="text-center">
<h2 className="text-3xl lg:text-4xl font-extrabold text-[var(--text-primary)]">
Lo que Dicen Nuestros Clientes
</h2>
<p className="mt-4 max-w-2xl mx-auto text-lg text-[var(--text-secondary)]">
Panaderías de toda España han transformado sus negocios con nuestra plataforma
</p>
</div>
<div className="mt-16 grid grid-cols-1 lg:grid-cols-3 gap-8">
{/* Testimonial 1 */}
<div className="bg-[var(--bg-primary)] rounded-2xl p-8 shadow-lg border border-[var(--border-primary)]">
<div className="flex items-center mb-4">
{[...Array(5)].map((_, i) => (
<Star key={i} className="w-5 h-5 text-yellow-400 fill-current" />
))}
</div>
<blockquote className="text-[var(--text-secondary)] italic">
"Desde que implementamos Panadería IA, nuestros desperdicios se redujeron un 40% y las ventas aumentaron un 28%.
La predicción de demanda es increíblemente precisa."
</blockquote>
<div className="mt-6 flex items-center">
<div className="w-12 h-12 bg-[var(--color-primary)] rounded-full flex items-center justify-center text-white font-bold">
M
</div>
<div className="ml-4">
<div className="font-semibold text-[var(--text-primary)]">María González</div>
<div className="text-sm text-[var(--text-secondary)]">Panadería Santa María, Madrid</div>
</div>
</div>
</div>
{/* Testimonial 2 */}
<div className="bg-[var(--bg-primary)] rounded-2xl p-8 shadow-lg border border-[var(--border-primary)]">
<div className="flex items-center mb-4">
{[...Array(5)].map((_, i) => (
<Star key={i} className="w-5 h-5 text-yellow-400 fill-current" />
))}
</div>
<blockquote className="text-[var(--text-secondary)] italic">
"El sistema nos ahorra 10 horas semanales en planificación. Ahora puedo enfocarme en mejorar nuestros productos
mientras la IA maneja la logística."
</blockquote>
<div className="mt-6 flex items-center">
<div className="w-12 h-12 bg-[var(--color-secondary)] rounded-full flex items-center justify-center text-white font-bold">
C
</div>
<div className="ml-4">
<div className="font-semibold text-[var(--text-primary)]">Carlos Ruiz</div>
<div className="text-sm text-[var(--text-secondary)]">Horno de Oro, Valencia</div>
</div>
</div>
</div>
{/* Testimonial 3 */}
<div className="bg-[var(--bg-primary)] rounded-2xl p-8 shadow-lg border border-[var(--border-primary)]">
<div className="flex items-center mb-4">
{[...Array(5)].map((_, i) => (
<Star key={i} className="w-5 h-5 text-yellow-400 fill-current" />
))}
</div>
<blockquote className="text-[var(--text-secondary)] italic">
"Increíble cómo predice exactamente cuántos panes necesitamos cada día. Nuestros clientes siempre encuentran
sus productos favoritos disponibles."
</blockquote>
<div className="mt-6 flex items-center">
<div className="w-12 h-12 bg-[var(--color-accent)] rounded-full flex items-center justify-center text-white font-bold">
A
</div>
<div className="ml-4">
<div className="font-semibold text-[var(--text-primary)]">Ana Martínez</div>
<div className="text-sm text-[var(--text-secondary)]">Pan & Tradición, Sevilla</div>
</div>
</div>
</div>
</div>
{/* Trust indicators */}
<div className="mt-16 text-center">
<p className="text-sm text-[var(--text-tertiary)] mb-8">Confiado por más de 500 panaderías en España</p>
<div className="flex items-center justify-center space-x-8 opacity-60">
<div className="font-semibold text-[var(--text-secondary)]">Panadería Real</div>
<div className="font-semibold text-[var(--text-secondary)]">Horno Artesanal</div>
<div className="font-semibold text-[var(--text-secondary)]">Pan de Casa</div>
<div className="font-semibold text-[var(--text-secondary)]">Dulce Tradición</div>
</div>
</div>
</div>
</section>
{/* Pricing Section */}
<section id="pricing" className="py-24 bg-[var(--bg-primary)]">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="text-center">
<h2 className="text-3xl lg:text-4xl font-extrabold text-[var(--text-primary)]">
Planes que se Adaptan a tu Negocio
</h2>
<p className="mt-4 max-w-2xl mx-auto text-lg text-[var(--text-secondary)]">
Sin costos ocultos, sin compromisos largos. Comienza gratis y escala según crezcas.
</p>
</div>
<div className="mt-16 grid grid-cols-1 lg:grid-cols-3 gap-8">
{/* Starter Plan */}
<div className="bg-[var(--bg-secondary)] rounded-2xl p-8 border border-[var(--border-primary)]">
<h3 className="text-lg font-semibold text-[var(--text-primary)]">Starter</h3>
<p className="mt-2 text-sm text-[var(--text-secondary)]">Perfecto para panaderías pequeñas</p>
<div className="mt-6">
<span className="text-3xl font-bold text-[var(--text-primary)]">€49</span>
<span className="text-[var(--text-secondary)]">/mes</span>
</div>
<div className="mt-8 space-y-4">
<div className="flex items-center">
<Check className="w-5 h-5 text-green-500 mr-3" />
<span className="text-sm text-[var(--text-secondary)]">Hasta 50 productos</span>
</div>
<div className="flex items-center">
<Check className="w-5 h-5 text-green-500 mr-3" />
<span className="text-sm text-[var(--text-secondary)]">Predicción básica de demanda</span>
</div>
<div className="flex items-center">
<Check className="w-5 h-5 text-green-500 mr-3" />
<span className="text-sm text-[var(--text-secondary)]">Control de inventario</span>
</div>
<div className="flex items-center">
<Check className="w-5 h-5 text-green-500 mr-3" />
<span className="text-sm text-[var(--text-secondary)]">Reportes básicos</span>
</div>
<div className="flex items-center">
<Check className="w-5 h-5 text-green-500 mr-3" />
<span className="text-sm text-[var(--text-secondary)]">Soporte por email</span>
</div>
</div>
<Button className="w-full mt-8" variant="outline">
Comenzar Gratis
</Button>
</div>
{/* Professional Plan - Highlighted */}
<div className="bg-gradient-to-b from-[var(--color-primary)] to-[var(--color-primary-dark)] rounded-2xl p-8 relative shadow-2xl">
<div className="absolute -top-4 left-1/2 transform -translate-x-1/2">
<span className="bg-[var(--color-secondary)] text-white px-4 py-1 rounded-full text-sm font-semibold">
Más Popular
</span>
</div>
<h3 className="text-lg font-semibold text-white">Professional</h3>
<p className="mt-2 text-sm text-white/80">Para panaderías en crecimiento</p>
<div className="mt-6">
<span className="text-3xl font-bold text-white">€149</span>
<span className="text-white/80">/mes</span>
</div>
<div className="mt-8 space-y-4">
<div className="flex items-center">
<Check className="w-5 h-5 text-white mr-3" />
<span className="text-sm text-white">Productos ilimitados</span>
</div>
<div className="flex items-center">
<Check className="w-5 h-5 text-white mr-3" />
<span className="text-sm text-white">IA avanzada con 92% precisión</span>
</div>
<div className="flex items-center">
<Check className="w-5 h-5 text-white mr-3" />
<span className="text-sm text-white">Gestión completa de producción</span>
</div>
<div className="flex items-center">
<Check className="w-5 h-5 text-white mr-3" />
<span className="text-sm text-white">POS integrado</span>
</div>
<div className="flex items-center">
<Check className="w-5 h-5 text-white mr-3" />
<span className="text-sm text-white">Analytics avanzado</span>
</div>
<div className="flex items-center">
<Check className="w-5 h-5 text-white mr-3" />
<span className="text-sm text-white">Soporte prioritario 24/7</span>
</div>
</div>
<Button className="w-full mt-8 bg-white text-[var(--color-primary)] hover:bg-gray-100">
Comenzar Prueba Gratuita
</Button>
</div>
{/* Enterprise Plan */}
<div className="bg-[var(--bg-secondary)] rounded-2xl p-8 border border-[var(--border-primary)]">
<h3 className="text-lg font-semibold text-[var(--text-primary)]">Enterprise</h3>
<p className="mt-2 text-sm text-[var(--text-secondary)]">Para cadenas y grandes operaciones</p>
<div className="mt-6">
<span className="text-3xl font-bold text-[var(--text-primary)]">€399</span>
<span className="text-[var(--text-secondary)]">/mes</span>
</div>
<div className="mt-8 space-y-4">
<div className="flex items-center">
<Check className="w-5 h-5 text-green-500 mr-3" />
<span className="text-sm text-[var(--text-secondary)]">Multi-locación ilimitada</span>
</div>
<div className="flex items-center">
<Check className="w-5 h-5 text-green-500 mr-3" />
<span className="text-sm text-[var(--text-secondary)]">IA personalizada por ubicación</span>
</div>
<div className="flex items-center">
<Check className="w-5 h-5 text-green-500 mr-3" />
<span className="text-sm text-[var(--text-secondary)]">API personalizada</span>
</div>
<div className="flex items-center">
<Check className="w-5 h-5 text-green-500 mr-3" />
<span className="text-sm text-[var(--text-secondary)]">Integración ERPs</span>
</div>
<div className="flex items-center">
<Check className="w-5 h-5 text-green-500 mr-3" />
<span className="text-sm text-[var(--text-secondary)]">Manager dedicado</span>
</div>
</div>
<Button className="w-full mt-8" variant="outline">
Contactar Ventas
</Button>
</div>
</div>
<div className="mt-16 text-center">
<p className="text-sm text-[var(--text-tertiary)]">
🔒 Todos los planes incluyen cifrado de datos, backups automáticos y cumplimiento RGPD
</p>
</div>
</div>
</section>
{/* FAQ Section */}
<section className="py-24 bg-[var(--bg-secondary)]">
<div className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="text-center">
<h2 className="text-3xl lg:text-4xl font-extrabold text-[var(--text-primary)]">
Preguntas Frecuentes
</h2>
<p className="mt-4 text-lg text-[var(--text-secondary)]">
Todo lo que necesitas saber sobre Panadería IA
</p>
</div>
<div className="mt-16 space-y-8">
<div className="bg-[var(--bg-primary)] rounded-xl p-8 border border-[var(--border-primary)]">
<h3 className="text-lg font-semibold text-[var(--text-primary)]">
¿Qué tan precisa es la predicción de demanda?
</h3>
<p className="mt-4 text-[var(--text-secondary)]">
Nuestra IA alcanza una precisión del 92% en predicciones de demanda, analizando más de 50 variables incluyendo
histórico de ventas, clima, eventos locales, estacionalidad y tendencias de mercado. La precisión mejora continuamente
con más datos de tu panadería.
</p>
</div>
<div className="bg-[var(--bg-primary)] rounded-xl p-8 border border-[var(--border-primary)]">
<h3 className="text-lg font-semibold text-[var(--text-primary)]">
¿Cuánto tiempo toma implementar el sistema?
</h3>
<p className="mt-4 text-[var(--text-secondary)]">
La configuración inicial toma solo 5 minutos. Nuestro equipo te ayuda a migrar tus datos históricos en 24-48 horas.
La IA comienza a generar predicciones útiles después de una semana de datos, alcanzando máxima precisión en 30 días.
</p>
</div>
<div className="bg-[var(--bg-primary)] rounded-xl p-8 border border-[var(--border-primary)]">
<h3 className="text-lg font-semibold text-[var(--text-primary)]">
¿Se integra con mi sistema POS actual?
</h3>
<p className="mt-4 text-[var(--text-secondary)]">
Sí, nos integramos con más de 50 sistemas POS populares en España. También incluimos nuestro propio POS optimizado
para panaderías. Si usas un sistema específico, nuestro equipo técnico puede crear una integración personalizada.
</p>
</div>
<div className="bg-[var(--bg-primary)] rounded-xl p-8 border border-[var(--border-primary)]">
<h3 className="text-lg font-semibold text-[var(--text-primary)]">
¿Qué soporte técnico ofrecen?
</h3>
<p className="mt-4 text-[var(--text-secondary)]">
Ofrecemos soporte 24/7 en español por chat, email y teléfono. Todos nuestros técnicos son expertos en operaciones
de panadería. Además, incluimos onboarding personalizado y training para tu equipo sin costo adicional.
</p>
</div>
<div className="bg-[var(--bg-primary)] rounded-xl p-8 border border-[var(--border-primary)]">
<h3 className="text-lg font-semibold text-[var(--text-primary)]">
¿Mis datos están seguros?
</h3>
<p className="mt-4 text-[var(--text-secondary)]">
Absolutamente. Utilizamos cifrado AES-256, servidores en la UE, cumplimos 100% con RGPD y realizamos auditorías
de seguridad trimestrales. Tus datos nunca se comparten con terceros y tienes control total sobre tu información.
</p>
</div>
</div>
</div>
</section>
{/* Final CTA Section */}
<section className="py-24 bg-gradient-to-r from-[var(--color-primary)] to-[var(--color-primary-dark)] relative overflow-hidden">
<div className="absolute inset-0">
<div className="absolute -top-40 -right-40 w-80 h-80 bg-white/5 rounded-full blur-3xl"></div>
<div className="absolute -bottom-40 -left-40 w-80 h-80 bg-white/5 rounded-full blur-3xl"></div>
</div>
<div className="max-w-4xl mx-auto text-center px-4 sm:px-6 lg:px-8 relative">
<h2 className="text-3xl lg:text-5xl font-extrabold text-white">
Transforma tu Panadería
<span className="block text-white/90">Comenzando Hoy</span>
</h2>
<p className="mt-6 text-lg text-white/80 max-w-2xl mx-auto">
Únete a más de 500 panaderías que ya están reduciendo desperdicios, aumentando ventas y
optimizando operaciones con inteligencia artificial.
</p>
<div className="mt-12 flex flex-col sm:flex-row gap-6 justify-center">
<Link to="/register">
<Button
size="lg"
className="px-10 py-4 text-lg font-semibold bg-white text-[var(--color-primary)] hover:bg-gray-100 shadow-lg hover:shadow-xl transform hover:scale-105 transition-all duration-200"
>
Comenzar Prueba Gratuita 14 Días
<ArrowRight className="ml-2 w-5 h-5" />
</Button>
</Link>
<Button
size="lg"
variant="outline"
className="px-10 py-4 text-lg font-semibold border-2 border-white text-white hover:bg-white hover:text-[var(--color-primary)] transition-all duration-200"
onClick={() => scrollToSection('demo')}
>
<Play className="mr-2 w-5 h-5" />
Ver Demo
</Button>
</div>
<div className="mt-12 grid grid-cols-1 sm:grid-cols-3 gap-8 text-center">
<div>
<div className="text-2xl font-bold text-white">14 días</div>
<div className="text-white/70 text-sm">Prueba gratuita</div>
</div>
<div>
<div className="text-2xl font-bold text-white">5 min</div>
<div className="text-white/70 text-sm">Configuración</div>
</div>
<div>
<div className="text-2xl font-bold text-white">24/7</div>
<div className="text-white/70 text-sm">Soporte incluido</div>
</div>
</div>
</div>
</section>
</PublicLayout>
);
};
export default LandingPage;

View File

@@ -7,7 +7,7 @@ const RegisterPage: React.FC = () => {
const navigate = useNavigate();
const handleRegistrationSuccess = () => {
navigate('/app/onboarding/setup');
navigate('/app/onboarding');
};
const handleLoginClick = () => {