import React, { useState, useEffect, useMemo } from 'react'; import { Card, Button, Badge, Input, Select, Avatar, Tooltip, Modal } from '../../ui'; import { SalesRecord, SalesChannel, PaymentMethod } from '../../../types/sales.types'; import { salesService } from '../../../services/api/sales.service'; import { useSales } from '../../../hooks/api/useSales'; // Customer interfaces interface Customer { id: string; name: string; email?: string; phone?: string; address?: string; city?: string; postal_code?: string; birth_date?: string; registration_date: string; status: CustomerStatus; segment: CustomerSegment; loyalty_points: number; preferred_channel: SalesChannel; notes?: string; tags: string[]; communication_preferences: CommunicationPreferences; } interface CustomerOrder { id: string; date: string; total: number; items_count: number; status: OrderStatus; channel: SalesChannel; payment_method?: PaymentMethod; products: string[]; } interface CustomerStats { total_orders: number; total_spent: number; average_order_value: number; last_order_date?: string; favorite_products: string[]; preferred_times: string[]; loyalty_tier: LoyaltyTier; lifetime_value: number; churn_risk: ChurnRisk; } interface CommunicationPreferences { email_marketing: boolean; sms_notifications: boolean; push_notifications: boolean; promotional_offers: boolean; order_updates: boolean; loyalty_updates: boolean; } interface PaymentMethod { id: string; type: 'credit_card' | 'debit_card' | 'digital_wallet' | 'bank_account'; last_four: string; brand?: string; is_default: boolean; expires_at?: string; } interface LoyaltyProgram { current_points: number; points_to_next_tier: number; current_tier: LoyaltyTier; benefits: string[]; rewards_history: LoyaltyTransaction[]; } interface LoyaltyTransaction { id: string; date: string; type: 'earned' | 'redeemed'; points: number; description: string; order_id?: string; } enum CustomerStatus { ACTIVE = 'active', INACTIVE = 'inactive', SUSPENDED = 'suspended', VIP = 'vip' } enum CustomerSegment { NEW = 'new', REGULAR = 'regular', PREMIUM = 'premium', ENTERPRISE = 'enterprise', AT_RISK = 'at_risk' } enum OrderStatus { PENDING = 'pending', CONFIRMED = 'confirmed', PREPARING = 'preparing', READY = 'ready', DELIVERED = 'delivered', CANCELLED = 'cancelled' } enum LoyaltyTier { BRONZE = 'bronze', SILVER = 'silver', GOLD = 'gold', PLATINUM = 'platinum' } enum ChurnRisk { LOW = 'low', MEDIUM = 'medium', HIGH = 'high', CRITICAL = 'critical' } interface CustomerInfoProps { customerId?: string; onCustomerSelect?: (customer: Customer) => void; onCustomerUpdate?: (customerId: string, updates: Partial) => void; showOrderHistory?: boolean; showLoyaltyProgram?: boolean; allowEditing?: boolean; className?: string; } const StatusColors = { [CustomerStatus.ACTIVE]: 'green', [CustomerStatus.INACTIVE]: 'gray', [CustomerStatus.SUSPENDED]: 'red', [CustomerStatus.VIP]: 'purple' } as const; const StatusLabels = { [CustomerStatus.ACTIVE]: 'Activo', [CustomerStatus.INACTIVE]: 'Inactivo', [CustomerStatus.SUSPENDED]: 'Suspendido', [CustomerStatus.VIP]: 'VIP' } as const; const SegmentColors = { [CustomerSegment.NEW]: 'blue', [CustomerSegment.REGULAR]: 'gray', [CustomerSegment.PREMIUM]: 'gold', [CustomerSegment.ENTERPRISE]: 'purple', [CustomerSegment.AT_RISK]: 'red' } as const; const SegmentLabels = { [CustomerSegment.NEW]: 'Nuevo', [CustomerSegment.REGULAR]: 'Regular', [CustomerSegment.PREMIUM]: 'Premium', [CustomerSegment.ENTERPRISE]: 'Empresa', [CustomerSegment.AT_RISK]: 'En Riesgo' } as const; const TierColors = { [LoyaltyTier.BRONZE]: 'orange', [LoyaltyTier.SILVER]: 'gray', [LoyaltyTier.GOLD]: 'yellow', [LoyaltyTier.PLATINUM]: 'purple' } as const; const TierLabels = { [LoyaltyTier.BRONZE]: 'Bronce', [LoyaltyTier.SILVER]: 'Plata', [LoyaltyTier.GOLD]: 'Oro', [LoyaltyTier.PLATINUM]: 'Platino' } as const; const ChurnRiskColors = { [ChurnRisk.LOW]: 'green', [ChurnRisk.MEDIUM]: 'yellow', [ChurnRisk.HIGH]: 'orange', [ChurnRisk.CRITICAL]: 'red' } as const; const ChurnRiskLabels = { [ChurnRisk.LOW]: 'Bajo', [ChurnRisk.MEDIUM]: 'Medio', [ChurnRisk.HIGH]: 'Alto', [ChurnRisk.CRITICAL]: 'Crítico' } as const; const ChannelLabels = { [SalesChannel.STORE_FRONT]: 'Tienda', [SalesChannel.ONLINE]: 'Online', [SalesChannel.PHONE_ORDER]: 'Teléfono', [SalesChannel.DELIVERY]: 'Delivery', [SalesChannel.CATERING]: 'Catering', [SalesChannel.WHOLESALE]: 'Mayorista', [SalesChannel.FARMERS_MARKET]: 'Mercado', [SalesChannel.THIRD_PARTY]: 'Terceros' } as const; export const CustomerInfo: React.FC = ({ customerId, onCustomerSelect, onCustomerUpdate, showOrderHistory = true, showLoyaltyProgram = true, allowEditing = true, className = '' }) => { // State const [customer, setCustomer] = useState(null); const [customerStats, setCustomerStats] = useState(null); const [orderHistory, setOrderHistory] = useState([]); const [loyaltyProgram, setLoyaltyProgram] = useState(null); const [paymentMethods, setPaymentMethods] = useState([]); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); // UI State const [isEditing, setIsEditing] = useState(false); const [editForm, setEditForm] = useState>({}); const [activeTab, setActiveTab] = useState<'info' | 'orders' | 'loyalty' | 'communication'>('info'); const [showAddNote, setShowAddNote] = useState(false); const [newNote, setNewNote] = useState(''); const [showLoyaltyModal, setShowLoyaltyModal] = useState(false); const [rewardToRedeem, setRewardToRedeem] = useState(null); // Pagination for order history const [orderPage, setOrderPage] = useState(1); const [orderPageSize, setOrderPageSize] = useState(10); // Effects useEffect(() => { if (customerId) { loadCustomerData(customerId); } }, [customerId]); // Load customer data const loadCustomerData = async (id: string) => { setLoading(true); setError(null); try { // In a real app, these would be separate API calls const mockCustomer: Customer = { id, name: 'María García López', email: 'maria.garcia@email.com', phone: '+34 612 345 678', address: 'Calle Mayor, 123', city: 'Madrid', postal_code: '28001', birth_date: '1985-05-15', registration_date: '2023-01-15T00:00:00Z', status: CustomerStatus.VIP, segment: CustomerSegment.PREMIUM, loyalty_points: 2450, preferred_channel: SalesChannel.ONLINE, notes: 'Cliente preferente. Le gusta el pan integral sin gluten.', tags: ['gluten-free', 'premium', 'weekly-order'], communication_preferences: { email_marketing: true, sms_notifications: true, push_notifications: false, promotional_offers: true, order_updates: true, loyalty_updates: true } }; const mockStats: CustomerStats = { total_orders: 47, total_spent: 1247.85, average_order_value: 26.55, last_order_date: '2024-01-20T10:30:00Z', favorite_products: ['Pan Integral', 'Croissant de Chocolate', 'Tarta de Santiago'], preferred_times: ['09:00-11:00', '17:00-19:00'], loyalty_tier: LoyaltyTier.GOLD, lifetime_value: 1850.00, churn_risk: ChurnRisk.LOW }; const mockOrders: CustomerOrder[] = Array.from({ length: 47 }, (_, i) => ({ id: `order_${i + 1}`, date: new Date(Date.now() - i * 7 * 24 * 60 * 60 * 1000).toISOString(), total: Math.random() * 50 + 15, items_count: Math.floor(Math.random() * 5) + 1, status: Object.values(OrderStatus)[Math.floor(Math.random() * Object.values(OrderStatus).length)], channel: Object.values(SalesChannel)[Math.floor(Math.random() * Object.values(SalesChannel).length)], payment_method: Object.values(PaymentMethod)[Math.floor(Math.random() * Object.values(PaymentMethod).length)], products: ['Pan Integral', 'Croissant', 'Magdalenas'].slice(0, Math.floor(Math.random() * 3) + 1) })); const mockLoyalty: LoyaltyProgram = { current_points: 2450, points_to_next_tier: 550, current_tier: LoyaltyTier.GOLD, benefits: [ 'Descuento 10% en todos los productos', 'Producto gratis cada 10 compras', 'Reserva prioritaria para productos especiales', 'Invitaciones exclusivas a eventos' ], rewards_history: Array.from({ length: 20 }, (_, i) => ({ id: `loyalty_${i + 1}`, date: new Date(Date.now() - i * 14 * 24 * 60 * 60 * 1000).toISOString(), type: Math.random() > 0.7 ? 'redeemed' as const : 'earned' as const, points: Math.floor(Math.random() * 200) + 50, description: Math.random() > 0.7 ? 'Canje por producto gratis' : 'Puntos ganados por compra', order_id: `order_${Math.floor(Math.random() * 47) + 1}` })) }; const mockPaymentMethods: PaymentMethod[] = [ { id: 'pm_1', type: 'credit_card', last_four: '4242', brand: 'Visa', is_default: true, expires_at: '2026-12-31' }, { id: 'pm_2', type: 'digital_wallet', last_four: 'PayPal', is_default: false } ]; setCustomer(mockCustomer); setCustomerStats(mockStats); setOrderHistory(mockOrders); setLoyaltyProgram(mockLoyalty); setPaymentMethods(mockPaymentMethods); } catch (err) { setError('Error al cargar datos del cliente'); console.error('Error loading customer data:', err); } finally { setLoading(false); } }; // Handle edit const handleEditStart = () => { if (customer) { setEditForm({ ...customer }); setIsEditing(true); } }; const handleEditSave = async () => { if (!customer || !editForm) return; try { const updatedCustomer = { ...customer, ...editForm }; setCustomer(updatedCustomer); onCustomerUpdate?.(customer.id, editForm); setIsEditing(false); setEditForm({}); } catch (err) { setError('Error al actualizar cliente'); } }; const handleEditCancel = () => { setIsEditing(false); setEditForm({}); }; // Handle notes const handleAddNote = () => { if (!customer || !newNote.trim()) return; const updatedNotes = customer.notes ? `${customer.notes}\n\n${new Date().toLocaleDateString('es-ES')}: ${newNote}` : newNote; const updatedCustomer = { ...customer, notes: updatedNotes }; setCustomer(updatedCustomer); onCustomerUpdate?.(customer.id, { notes: updatedNotes }); setNewNote(''); setShowAddNote(false); }; // Handle loyalty redemption const handleRedeemPoints = (rewardId: string, pointsCost: number) => { if (!customer || !loyaltyProgram) return; if (loyaltyProgram.current_points >= pointsCost) { const newTransaction: LoyaltyTransaction = { id: `loyalty_${Date.now()}`, date: new Date().toISOString(), type: 'redeemed', points: -pointsCost, description: `Canje: ${rewardId}`, }; const updatedLoyalty = { ...loyaltyProgram, current_points: loyaltyProgram.current_points - pointsCost, rewards_history: [newTransaction, ...loyaltyProgram.rewards_history] }; const updatedCustomer = { ...customer, loyalty_points: loyaltyProgram.current_points - pointsCost }; setLoyaltyProgram(updatedLoyalty); setCustomer(updatedCustomer); onCustomerUpdate?.(customer.id, { loyalty_points: updatedCustomer.loyalty_points }); } }; // Filtered order history const paginatedOrders = useMemo(() => { const start = (orderPage - 1) * orderPageSize; return orderHistory.slice(start, start + orderPageSize); }, [orderHistory, orderPage, orderPageSize]); if (loading) { return (
Cargando información del cliente...
); } if (error || !customer) { return (

Error

{error || 'Cliente no encontrado'}

); } return (
{/* Header */}

{customer.name}

{StatusLabels[customer.status]} {SegmentLabels[customer.segment]} {customerStats && ( {TierLabels[customerStats.loyalty_tier]} )}
{allowEditing && ( <> {isEditing ? ( <> ) : ( )} )}
{/* Stats Overview */} {customerStats && (

Total Gastado

€{customerStats.total_spent.toFixed(2)}

Pedidos Totales

{customerStats.total_orders}

Ticket Promedio

€{customerStats.average_order_value.toFixed(2)}

Riesgo de Fuga

{ChurnRiskLabels[customerStats.churn_risk]}
)} {/* Tabs */}
{/* Tab Content */} {activeTab === 'info' && (

Datos Personales

{isEditing ? ( setEditForm(prev => ({ ...prev, name: e.target.value }))} /> ) : (

{customer.name}

)}
{isEditing ? ( setEditForm(prev => ({ ...prev, email: e.target.value }))} /> ) : (

{customer.email}

)}
{isEditing ? ( setEditForm(prev => ({ ...prev, phone: e.target.value }))} /> ) : (

{customer.phone}

)}
{isEditing ? ( setEditForm(prev => ({ ...prev, birth_date: e.target.value }))} /> ) : (

{customer.birth_date ? new Date(customer.birth_date).toLocaleDateString('es-ES') : 'No especificado'}

)}
{isEditing ? ( setEditForm(prev => ({ ...prev, address: e.target.value }))} /> ) : (

{customer.address}

)}
{isEditing ? ( setEditForm(prev => ({ ...prev, city: e.target.value }))} /> ) : (

{customer.city}

)}
{isEditing ? ( setEditForm(prev => ({ ...prev, postal_code: e.target.value }))} /> ) : (

{customer.postal_code}

)}

Preferencias y Segmentación

{isEditing ? ( { if (isEditing || allowEditing) { const updated = { ...customer, communication_preferences: { ...customer.communication_preferences, email_marketing: e.target.checked } }; setCustomer(updated); onCustomerUpdate?.(customer.id, { communication_preferences: updated.communication_preferences }); } }} className="rounded border-[var(--border-secondary)] text-[var(--color-info)] focus:ring-blue-500" disabled={!isEditing && !allowEditing} /> Marketing por email

Tipos de Comunicación

{paymentMethods.length > 0 && (

Métodos de Pago

{paymentMethods.map(method => (

{method.brand} •••• {method.last_four}

{method.expires_at && (

Expira {new Date(method.expires_at).toLocaleDateString('es-ES')}

)}
{method.is_default && ( Por defecto )}
))}
)}
)} {/* Add Note Modal */} setShowAddNote(false)} title="Agregar Nota" >