Fix new services implementation 5
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import React, { useState, useEffect, useRef } from 'react';
|
||||
import {
|
||||
Sparkles, CheckCircle, Clock, ArrowRight, Coffee,
|
||||
TrendingUp, Target, Loader, AlertTriangle, Mail,
|
||||
@@ -18,6 +18,11 @@ interface SimplifiedTrainingProgressProps {
|
||||
onTimeout?: () => void;
|
||||
onBackgroundMode?: () => void;
|
||||
onEmailNotification?: (email: string) => void;
|
||||
// Optional WebSocket debugging info
|
||||
websocketStatus?: string;
|
||||
connectionError?: string;
|
||||
isConnected?: boolean;
|
||||
onRetryConnection?: () => void;
|
||||
}
|
||||
|
||||
// Proceso simplificado de entrenamiento en 3 etapas
|
||||
@@ -79,13 +84,18 @@ export default function SimplifiedTrainingProgress({
|
||||
progress,
|
||||
onTimeout,
|
||||
onBackgroundMode,
|
||||
onEmailNotification
|
||||
onEmailNotification,
|
||||
websocketStatus,
|
||||
connectionError,
|
||||
isConnected,
|
||||
onRetryConnection
|
||||
}: SimplifiedTrainingProgressProps) {
|
||||
const [showDetails, setShowDetails] = useState(false);
|
||||
const [showTimeoutOptions, setShowTimeoutOptions] = useState(false);
|
||||
const [emailForNotification, setEmailForNotification] = useState('');
|
||||
const [celebratingStage, setCelebratingStage] = useState<string | null>(null);
|
||||
const [startTime] = useState(Date.now());
|
||||
const celebratedStagesRef = useRef<Set<string>>(new Set());
|
||||
|
||||
// Show timeout options after 7 minutes for better UX
|
||||
useEffect(() => {
|
||||
@@ -98,17 +108,18 @@ export default function SimplifiedTrainingProgress({
|
||||
return () => clearTimeout(timer);
|
||||
}, [progress.status, progress.progress]);
|
||||
|
||||
// Celebrate stage completions
|
||||
// Celebrate stage completions - fixed to prevent infinite re-renders
|
||||
useEffect(() => {
|
||||
TRAINING_STAGES.forEach(stage => {
|
||||
if (progress.progress >= stage.progressRange[1] &&
|
||||
celebratingStage !== stage.id &&
|
||||
!celebratedStagesRef.current.has(stage.id) &&
|
||||
progress.progress > 0) {
|
||||
setCelebratingStage(stage.id);
|
||||
celebratedStagesRef.current.add(stage.id);
|
||||
setTimeout(() => setCelebratingStage(null), 3000);
|
||||
}
|
||||
});
|
||||
}, [progress.progress, celebratingStage]);
|
||||
}, [progress.progress]);
|
||||
|
||||
const getCurrentStage = () => {
|
||||
return TRAINING_STAGES.find(stage =>
|
||||
@@ -258,6 +269,36 @@ export default function SimplifiedTrainingProgress({
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Connection Status Debug Info */}
|
||||
{(websocketStatus || connectionError) && (
|
||||
<div className={`mb-4 p-3 rounded-lg text-sm ${
|
||||
connectionError
|
||||
? 'bg-red-50 text-red-800 border border-red-200'
|
||||
: isConnected
|
||||
? 'bg-green-50 text-green-800 border border-green-200'
|
||||
: 'bg-yellow-50 text-yellow-800 border border-yellow-200'
|
||||
}`}>
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<strong>Estado de conexión:</strong>
|
||||
{connectionError
|
||||
? ` Error - ${connectionError}`
|
||||
: isConnected
|
||||
? ' ✅ Conectado a tiempo real'
|
||||
: ' ⏳ Conectando...'}
|
||||
</div>
|
||||
{connectionError && onRetryConnection && (
|
||||
<button
|
||||
onClick={onRetryConnection}
|
||||
className="ml-2 px-3 py-1 bg-red-600 text-white text-xs rounded hover:bg-red-700 transition-colors"
|
||||
>
|
||||
Reintentar
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Optional Details */}
|
||||
<button
|
||||
onClick={() => setShowDetails(!showDetails)}
|
||||
|
||||
@@ -10,7 +10,9 @@ import {
|
||||
User,
|
||||
Bell,
|
||||
ChevronDown,
|
||||
ChefHat
|
||||
ChefHat,
|
||||
Warehouse,
|
||||
ShoppingCart
|
||||
} from 'lucide-react';
|
||||
|
||||
interface LayoutProps {
|
||||
@@ -42,6 +44,8 @@ const Layout: React.FC<LayoutProps> = ({
|
||||
{ id: 'dashboard', label: 'Inicio', icon: Home, href: '/dashboard' },
|
||||
{ id: 'orders', label: 'Pedidos', icon: Package, href: '/orders' },
|
||||
{ id: 'production', label: 'Producción', icon: ChefHat, href: '/production' },
|
||||
{ id: 'inventory', label: 'Inventario', icon: Warehouse, href: '/inventory' },
|
||||
{ id: 'sales', label: 'Ventas', icon: ShoppingCart, href: '/sales' },
|
||||
{ id: 'reports', label: 'Informes', icon: TrendingUp, href: '/reports' },
|
||||
{ id: 'settings', label: 'Configuración', icon: Settings, href: '/settings' },
|
||||
];
|
||||
|
||||
@@ -245,7 +245,8 @@ const SmartHistoricalDataImport: React.FC<SmartHistoricalDataImportProps> = ({
|
||||
color: 'blue',
|
||||
bgColor: 'bg-blue-50',
|
||||
borderColor: 'border-blue-200',
|
||||
textColor: 'text-blue-900'
|
||||
textColor: 'text-blue-900',
|
||||
businessType: 'bakery'
|
||||
},
|
||||
central_baker_satellite: {
|
||||
icon: Truck,
|
||||
@@ -254,7 +255,8 @@ const SmartHistoricalDataImport: React.FC<SmartHistoricalDataImportProps> = ({
|
||||
color: 'amber',
|
||||
bgColor: 'bg-amber-50',
|
||||
borderColor: 'border-amber-200',
|
||||
textColor: 'text-amber-900'
|
||||
textColor: 'text-amber-900',
|
||||
businessType: 'bakery'
|
||||
},
|
||||
retail_bakery: {
|
||||
icon: Store,
|
||||
@@ -263,7 +265,8 @@ const SmartHistoricalDataImport: React.FC<SmartHistoricalDataImportProps> = ({
|
||||
color: 'green',
|
||||
bgColor: 'bg-green-50',
|
||||
borderColor: 'border-green-200',
|
||||
textColor: 'text-green-900'
|
||||
textColor: 'text-green-900',
|
||||
businessType: 'bakery'
|
||||
},
|
||||
hybrid_bakery: {
|
||||
icon: Settings2,
|
||||
@@ -272,7 +275,28 @@ const SmartHistoricalDataImport: React.FC<SmartHistoricalDataImportProps> = ({
|
||||
color: 'purple',
|
||||
bgColor: 'bg-purple-50',
|
||||
borderColor: 'border-purple-200',
|
||||
textColor: 'text-purple-900'
|
||||
textColor: 'text-purple-900',
|
||||
businessType: 'bakery'
|
||||
},
|
||||
coffee_shop_individual: {
|
||||
icon: Coffee,
|
||||
title: 'Cafetería Individual',
|
||||
description: 'Servicio de bebidas y comida ligera con preparación in-situ',
|
||||
color: 'amber',
|
||||
bgColor: 'bg-amber-50',
|
||||
borderColor: 'border-amber-200',
|
||||
textColor: 'text-amber-900',
|
||||
businessType: 'coffee_shop'
|
||||
},
|
||||
coffee_shop_chain: {
|
||||
icon: Building2,
|
||||
title: 'Cafetería en Cadena',
|
||||
description: 'Múltiples ubicaciones con productos estandarizados',
|
||||
color: 'indigo',
|
||||
bgColor: 'bg-indigo-50',
|
||||
borderColor: 'border-indigo-200',
|
||||
textColor: 'text-indigo-900',
|
||||
businessType: 'coffee_shop'
|
||||
},
|
||||
// Legacy fallbacks
|
||||
production: {
|
||||
@@ -282,7 +306,8 @@ const SmartHistoricalDataImport: React.FC<SmartHistoricalDataImportProps> = ({
|
||||
color: 'blue',
|
||||
bgColor: 'bg-blue-50',
|
||||
borderColor: 'border-blue-200',
|
||||
textColor: 'text-blue-900'
|
||||
textColor: 'text-blue-900',
|
||||
businessType: 'bakery'
|
||||
},
|
||||
retail: {
|
||||
icon: Store,
|
||||
@@ -291,7 +316,8 @@ const SmartHistoricalDataImport: React.FC<SmartHistoricalDataImportProps> = ({
|
||||
color: 'green',
|
||||
bgColor: 'bg-green-50',
|
||||
borderColor: 'border-green-200',
|
||||
textColor: 'text-green-900'
|
||||
textColor: 'text-green-900',
|
||||
businessType: 'bakery'
|
||||
},
|
||||
hybrid: {
|
||||
icon: Settings2,
|
||||
@@ -300,7 +326,8 @@ const SmartHistoricalDataImport: React.FC<SmartHistoricalDataImportProps> = ({
|
||||
color: 'purple',
|
||||
bgColor: 'bg-purple-50',
|
||||
borderColor: 'border-purple-200',
|
||||
textColor: 'text-purple-900'
|
||||
textColor: 'text-purple-900',
|
||||
businessType: 'bakery'
|
||||
}
|
||||
};
|
||||
|
||||
@@ -331,12 +358,28 @@ const SmartHistoricalDataImport: React.FC<SmartHistoricalDataImportProps> = ({
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center space-x-2">
|
||||
<Coffee className="w-4 h-4 text-brown-500" />
|
||||
<Package className="w-4 h-4 text-green-500" />
|
||||
<span className="text-sm text-gray-700">
|
||||
{analysis.finished_product_count} productos finales
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Enhanced business intelligence insights if available */}
|
||||
{config.businessType === 'coffee_shop' && (
|
||||
<div className="mb-4 p-3 bg-amber-100 border border-amber-200 rounded-lg">
|
||||
<div className="flex items-center space-x-2 mb-2">
|
||||
<Coffee className="w-4 h-4 text-amber-600" />
|
||||
<span className="text-sm font-medium text-amber-800">
|
||||
Negocio de Cafetería Detectado
|
||||
</span>
|
||||
</div>
|
||||
<p className="text-xs text-amber-700">
|
||||
Hemos detectado que tu negocio se enfoca principalmente en bebidas y comida ligera.
|
||||
El sistema se optimizará para gestión de inventario de cafetería.
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{analysis.recommendations.length > 0 && (
|
||||
<div>
|
||||
|
||||
@@ -19,7 +19,7 @@ import {
|
||||
} from 'lucide-react';
|
||||
|
||||
import { useSales } from '../../api/hooks/useSales';
|
||||
import { useInventory } from '../../api/hooks/useInventory';
|
||||
import { useInventory, useInventoryProducts } from '../../api/hooks/useInventory';
|
||||
import { useAuth } from '../../api/hooks/useAuth';
|
||||
|
||||
import Card from '../ui/Card';
|
||||
@@ -37,10 +37,13 @@ const SalesAnalyticsDashboard: React.FC = () => {
|
||||
const {
|
||||
getSalesAnalytics,
|
||||
getSalesData,
|
||||
getProductsList,
|
||||
isLoading: salesLoading,
|
||||
error: salesError
|
||||
} = useSales();
|
||||
|
||||
const {
|
||||
getProductsList
|
||||
} = useInventoryProducts();
|
||||
|
||||
const {
|
||||
items: products,
|
||||
|
||||
Reference in New Issue
Block a user