import React, { useState, useMemo } from 'react'; import { Plus, Minus, ShoppingCart, CreditCard, Banknote, Calculator, User, Receipt, Package, Euro, TrendingUp, Clock, ToggleLeft, ToggleRight, Settings, Zap, Wifi, WifiOff, AlertCircle, CheckCircle, Loader, Trash2, ChevronDown, ChevronUp } from 'lucide-react'; import { Button, Card, StatsGrid, StatusCard, getStatusColor } from '../../../../components/ui'; import { PageHeader } from '../../../../components/layout'; import { LoadingSpinner } from '../../../../components/ui'; import { formatters } from '../../../../components/ui/Stats/StatsPresets'; import { useIngredients } from '../../../../api/hooks/inventory'; import { useTenantId } from '../../../../hooks/useTenantId'; import { ProductType, ProductCategory, IngredientResponse } from '../../../../api/types/inventory'; import { useToast } from '../../../../hooks/ui/useToast'; import { usePOSConfigurationData, usePOSConfigurationManager } from '../../../../api/hooks/pos'; import { POSConfiguration } from '../../../../api/types/pos'; import { posService } from '../../../../api/services/pos'; import { bakeryColors } from '../../../../styles/colors'; // Import new POS components import { POSProductCard } from '../../../../components/domain/pos/POSProductCard'; import { POSCart } from '../../../../components/domain/pos/POSCart'; import { POSPayment } from '../../../../components/domain/pos/POSPayment'; import { CreatePOSConfigModal } from '../../../../components/domain/pos/CreatePOSConfigModal'; interface CartItem { id: string; name: string; price: number; quantity: number; category: string; stock: number; } const POSPage: React.FC = () => { const [cart, setCart] = useState([]); const [selectedCategory, setSelectedCategory] = useState('all'); const [posMode, setPosMode] = useState<'manual' | 'automatic'>('manual'); const [showPOSConfig, setShowPOSConfig] = useState(false); const [showStats, setShowStats] = useState(false); // POS Configuration State const [showPosConfigModal, setShowPosConfigModal] = useState(false); const [selectedPosConfig, setSelectedPosConfig] = useState(null); const [posConfigMode, setPosConfigMode] = useState<'create' | 'edit'>('create'); const [testingConnection, setTestingConnection] = useState(null); const tenantId = useTenantId(); const { addToast } = useToast(); // POS Configuration hooks const posData = usePOSConfigurationData(tenantId); const posManager = usePOSConfigurationManager(tenantId); // Fetch finished products from API const { data: ingredientsData, isLoading: productsLoading, error: productsError } = useIngredients(tenantId, { category: undefined, search: undefined }); // Filter for finished products and convert to POS format const products = useMemo(() => { if (!ingredientsData) return []; return ingredientsData .filter(ingredient => ingredient.product_type === ProductType.FINISHED_PRODUCT) .map(ingredient => ({ id: ingredient.id, name: ingredient.name, price: Number(ingredient.average_cost) || 0, category: ingredient.category.toLowerCase(), stock: Number(ingredient.current_stock) || 0, ingredient: ingredient })) .filter(product => product.stock > 0); }, [ingredientsData]); // Generate categories from actual product data with bakery colors const categories = useMemo(() => { const categoryMap = new Map(); categoryMap.set('all', { id: 'all', name: 'Todos', color: bakeryColors.flour, icon: '🛍️' }); // Define category colors from bakery palette const categoryColors: Record = { bread: bakeryColors.sourdough, pastry: bakeryColors.brioche, cake: bakeryColors.strawberry, cookie: bakeryColors.caramel, beverage: bakeryColors.espresso, default: bakeryColors.wheat }; const categoryIcons: Record = { bread: '🍞', pastry: '🥐', cake: '🎂', cookie: '🍪', beverage: '☕', default: '📦' }; products.forEach(product => { if (!categoryMap.has(product.category)) { const categoryName = product.category.charAt(0).toUpperCase() + product.category.slice(1); categoryMap.set(product.category, { id: product.category, name: categoryName, color: categoryColors[product.category] || categoryColors.default, icon: categoryIcons[product.category] || categoryIcons.default }); } }); return Array.from(categoryMap.values()); }, [products]); // Load POS configurations function for refetching after updates const loadPosConfigurations = () => { console.log('POS configurations updated, consider implementing refetch if needed'); }; const filteredProducts = useMemo(() => { return products.filter(product => selectedCategory === 'all' || product.category === selectedCategory ); }, [products, selectedCategory]); // POS Configuration Handlers const handleAddPosConfiguration = () => { setSelectedPosConfig(null); setPosConfigMode('create'); setShowPosConfigModal(true); }; const handleEditPosConfiguration = (config: POSConfiguration) => { setSelectedPosConfig(config); setPosConfigMode('edit'); setShowPosConfigModal(true); }; const handlePosConfigSuccess = () => { loadPosConfigurations(); setShowPosConfigModal(false); }; const handleTestPosConnection = async (configId: string) => { try { setTestingConnection(configId); const response = await posService.testPOSConnection({ tenant_id: tenantId, config_id: configId, }); if (response.success) { addToast('Conexión exitosa', { type: 'success' }); } else { addToast(`Error en la conexión: ${response.message || 'Error desconocido'}`, { type: 'error' }); } } catch (error) { addToast('Error al probar la conexión', { type: 'error' }); } finally { setTestingConnection(null); } }; const handleDeletePosConfiguration = async (configId: string) => { if (!window.confirm('¿Estás seguro de que deseas eliminar esta configuración?')) { return; } try { await posService.deletePOSConfiguration({ tenant_id: tenantId, config_id: configId, }); addToast('Configuración eliminada correctamente', { type: 'success' }); loadPosConfigurations(); } catch (error) { addToast('Error al eliminar la configuración', { type: 'error' }); } }; const addToCart = (product: typeof products[0]) => { setCart(prevCart => { const existingItem = prevCart.find(item => item.id === product.id); if (existingItem) { if (existingItem.quantity >= product.stock) { return prevCart; } return prevCart.map(item => item.id === product.id ? { ...item, quantity: item.quantity + 1 } : item ); } else { return [...prevCart, { id: product.id, name: product.name, price: product.price, quantity: 1, category: product.category, stock: product.stock }]; } }); }; const updateQuantity = (id: string, quantity: number) => { if (quantity <= 0) { setCart(prevCart => prevCart.filter(item => item.id !== id)); } else { setCart(prevCart => prevCart.map(item => { if (item.id === id) { const maxQuantity = Math.min(quantity, item.stock); return { ...item, quantity: maxQuantity }; } return item; }) ); } }; const clearCart = () => { setCart([]); }; const subtotal = cart.reduce((sum, item) => sum + (item.price * item.quantity), 0); const taxRate = 0.21; const tax = subtotal * taxRate; const total = subtotal + tax; const processPayment = (paymentData: any) => { if (cart.length === 0) return; console.log('Processing payment:', { cart, ...paymentData, total, }); setCart([]); addToast('Venta procesada exitosamente', { type: 'success' }); }; // Calculate stats for the POS dashboard const posStats = useMemo(() => { const totalProducts = products.length; const totalStock = products.reduce((sum, product) => sum + product.stock, 0); const cartValue = cart.reduce((sum, item) => sum + (item.price * item.quantity), 0); const cartItems = cart.reduce((sum, item) => sum + item.quantity, 0); const lowStockProducts = products.filter(product => product.stock <= 5).length; const avgProductPrice = totalProducts > 0 ? products.reduce((sum, product) => sum + product.price, 0) / totalProducts : 0; return { totalProducts, totalStock, cartValue, cartItems, lowStockProducts, avgProductPrice }; }, [products, cart]); const stats = [ { title: 'Productos Disponibles', value: posStats.totalProducts, variant: 'default' as const, icon: Package, }, { title: 'Stock Total', value: posStats.totalStock, variant: 'info' as const, icon: Package, }, { title: 'Artículos en Carrito', value: posStats.cartItems, variant: 'success' as const, icon: ShoppingCart, }, { title: 'Valor del Carrito', value: formatters.currency(posStats.cartValue), variant: 'success' as const, icon: Euro, }, { title: 'Stock Bajo', value: posStats.lowStockProducts, variant: 'warning' as const, icon: Clock, }, { title: 'Precio Promedio', value: formatters.currency(posStats.avgProductPrice), variant: 'info' as const, icon: TrendingUp, }, ]; // Loading and error states if (productsLoading || !tenantId) { return (
); } if (productsError) { return (

Error al cargar productos

{productsError.message || 'Ha ocurrido un error inesperado'}

); } return (
{/* POS Mode Toggle */}

Modo de Operación

{posMode === 'manual' ? 'Sistema POS manual interno - gestiona las ventas directamente desde la aplicación' : 'Integración automática con sistemas POS externos - sincroniza datos automáticamente' }

Manual Automático
{posMode === 'automatic' && ( )}
{posMode === 'manual' ? ( <> {/* Collapsible Stats Grid */} {showStats && (
)}
{/* Main 2-Column Layout */}
{/* Left Column: Products (2/3 width on desktop) */}
{/* Category Pills with Bakery Colors */}
{categories.map(category => ( ))}
{/* Products Grid - Large Touch-Friendly Cards */}
{filteredProducts.map(product => { const cartItem = cart.find(item => item.id === product.id); const cartQuantity = cartItem?.quantity || 0; return ( addToCart(product)} /> ); })}
{/* Empty State */} {filteredProducts.length === 0 && (

No hay productos disponibles

{selectedCategory === 'all' ? 'No hay productos en stock en este momento' : `No hay productos en la categoría "${categories.find(c => c.id === selectedCategory)?.name}"` }

)}
{/* Right Column: Cart & Payment (1/3 width on desktop) */}
{/* Cart Component */} {/* Payment Component */}
) : ( /* Automatic POS Integration Section with StatusCard */

Sistemas POS Integrados

Gestiona tus sistemas POS externos y configuraciones de integración

{posData.isLoading ? (
) : posData.configurations.length === 0 ? (

No hay sistemas POS configurados

Configura tu primer sistema POS para comenzar a sincronizar datos de ventas.

) : (
{posData.configurations.map(config => { const provider = posData.supportedSystems.find(p => p.id === config.pos_system); const isConnected = config.is_connected && config.is_active; return ( handleTestPosConnection(config.id), priority: 'secondary' as const, disabled: testingConnection === config.id, }, { label: 'Editar', icon: Settings, onClick: () => handleEditPosConfiguration(config), priority: 'secondary' as const, }, { label: 'Eliminar', icon: Trash2, onClick: () => handleDeletePosConfiguration(config.id), priority: 'tertiary' as const, destructive: true, }, ]} /> ); })}
)}
)} {/* POS Configuration Modal */} setShowPosConfigModal(false)} tenantId={tenantId} onSuccess={handlePosConfigSuccess} existingConfig={selectedPosConfig} mode={posConfigMode} />
); }; export default POSPage;