import React, { useState, useMemo } from 'react'; import { Plus, Minus, ShoppingCart, CreditCard, Banknote, Calculator, User, Receipt, Package, Euro, TrendingUp, Clock } from 'lucide-react'; import { Button, Input, Card, Badge, StatsGrid, StatusCard, getStatusColor } from '../../../../components/ui'; import { PageHeader } from '../../../../components/layout'; import { LoadingSpinner } from '../../../../components/shared'; 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'; 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 [customerInfo, setCustomerInfo] = useState({ name: '', email: '', phone: '', }); const [paymentMethod, setPaymentMethod] = useState<'cash' | 'card' | 'transfer'>('cash'); const [cashReceived, setCashReceived] = useState(''); const tenantId = useTenantId(); // Fetch finished products from API const { data: ingredientsData, isLoading: productsLoading, error: productsError } = useIngredients(tenantId, { // Filter for finished products only category: undefined, // We'll filter client-side for now 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); // Only show products in stock }, [ingredientsData]); // Generate categories from actual product data const categories = useMemo(() => { const categoryMap = new Map(); categoryMap.set('all', { id: 'all', name: 'Todos' }); 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 }); } }); return Array.from(categoryMap.values()); }, [products]); const filteredProducts = useMemo(() => { return products.filter(product => selectedCategory === 'all' || product.category === selectedCategory ); }, [products, selectedCategory]); const addToCart = (product: typeof products[0]) => { setCart(prevCart => { const existingItem = prevCart.find(item => item.id === product.id); if (existingItem) { // Check if we have enough stock if (existingItem.quantity >= product.stock) { return prevCart; // Don't add if no stock available } 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) { // Don't allow quantity to exceed stock 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; // 21% IVA const tax = subtotal * taxRate; const total = subtotal + tax; const change = cashReceived ? Math.max(0, parseFloat(cashReceived) - total) : 0; const processPayment = () => { if (cart.length === 0) return; // TODO: Integrate with real POS API endpoint console.log('Processing payment:', { cart, customerInfo, paymentMethod, total, cashReceived: paymentMethod === 'cash' ? parseFloat(cashReceived) : undefined, change: paymentMethod === 'cash' ? change : undefined, }); // Clear cart after successful payment setCart([]); setCustomerInfo({ name: '', email: '', phone: '' }); setCashReceived(''); alert('Venta procesada exitosamente'); }; // 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 (
{/* Stats Grid */}
{/* Products Section */}
{/* Categories */}
{categories.map(category => ( ))}
{/* Products Grid */}
{filteredProducts.map(product => { const cartItem = cart.find(item => item.id === product.id); const inCart = !!cartItem; const cartQuantity = cartItem?.quantity || 0; const remainingStock = product.stock - cartQuantity; const getStockStatusConfig = () => { if (remainingStock <= 0) { return { color: getStatusColor('cancelled'), text: 'Sin Stock', icon: Package, isCritical: true, isHighlight: false }; } else if (remainingStock <= 5) { return { color: getStatusColor('pending'), text: `${remainingStock} disponibles`, icon: Package, isCritical: false, isHighlight: true }; } else { return { color: getStatusColor('completed'), text: `${remainingStock} disponibles`, icon: Package, isCritical: false, isHighlight: false }; } }; 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}"` }

)}
{/* Cart and Checkout Section */}
{/* Cart */}

Carrito ({cart.length})

{cart.length > 0 && ( )}
{cart.length === 0 ? (

Carrito vacío

) : ( cart.map(item => { const product = products.find(p => p.id === item.id); const maxQuantity = product?.stock || item.stock; return (

{item.name}

€{item.price.toFixed(2)} c/u

Stock: {maxQuantity}

{item.quantity}

€{(item.price * item.quantity).toFixed(2)}

); }) )}
{cart.length > 0 && (
Subtotal: €{subtotal.toFixed(2)}
IVA (21%): €{tax.toFixed(2)}
Total: €{total.toFixed(2)}
)}
{/* Customer Info */}

Cliente (Opcional)

setCustomerInfo(prev => ({ ...prev, name: e.target.value }))} /> setCustomerInfo(prev => ({ ...prev, email: e.target.value }))} /> setCustomerInfo(prev => ({ ...prev, phone: e.target.value }))} />
{/* Payment */}

Método de Pago

{paymentMethod === 'cash' && (
setCashReceived(e.target.value)} /> {cashReceived && parseFloat(cashReceived) >= total && (

Cambio: €{change.toFixed(2)}

)}
)}
); }; export default POSPage;