import React, { useState } from 'react'; import { User, Mail, Phone, Lock, Globe, Clock, Camera, Save, X, Bell, MessageSquare, Smartphone, RotateCcw, CreditCard, Crown, Package, MapPin, Users, TrendingUp, Calendar, CheckCircle, AlertCircle, ArrowRight, Star, RefreshCw, Settings, Download, ExternalLink } from 'lucide-react'; import { Button, Card, Avatar, Input, Select, Tabs, Badge, Modal } from '../../../../components/ui'; import { PageHeader } from '../../../../components/layout'; import { useAuthUser } from '../../../../stores/auth.store'; import { useCurrentTenant } from '../../../../stores'; import { useToast } from '../../../../hooks/ui/useToast'; import { useAuthProfile, useUpdateProfile, useChangePassword } from '../../../../api/hooks/auth'; import { subscriptionService, type UsageSummary, type AvailablePlans } from '../../../../api'; import { useTranslation } from 'react-i18next'; interface ProfileFormData { first_name: string; last_name: string; email: string; phone: string; language: string; timezone: string; } interface PasswordData { currentPassword: string; newPassword: string; confirmPassword: string; } interface NotificationPreferences { notifications: { inventory: { app: boolean; email: boolean; sms: boolean; frequency: string; }; sales: { app: boolean; email: boolean; sms: boolean; frequency: string; }; production: { app: boolean; email: boolean; sms: boolean; frequency: string; }; system: { app: boolean; email: boolean; sms: boolean; frequency: string; }; marketing: { app: boolean; email: boolean; sms: boolean; frequency: string; }; }; global: { doNotDisturb: boolean; quietHours: { enabled: boolean; start: string; end: string; }; language: string; timezone: string; soundEnabled: boolean; vibrationEnabled: boolean; }; channels: { email: string; phone: string; slack: boolean; webhook: string; }; } const ProfilePage: React.FC = () => { const user = useAuthUser(); const { t } = useTranslation('auth'); const { addToast } = useToast(); const { data: profile, isLoading: profileLoading, error: profileError } = useAuthProfile(); const updateProfileMutation = useUpdateProfile(); const changePasswordMutation = useChangePassword(); const [isEditing, setIsEditing] = useState(false); const [isLoading, setIsLoading] = useState(false); const [showPasswordForm, setShowPasswordForm] = useState(false); const [activeTab, setActiveTab] = useState('profile'); const [hasPreferencesChanges, setHasPreferencesChanges] = useState(false); const [usageSummary, setUsageSummary] = useState(null); const [availablePlans, setAvailablePlans] = useState(null); const [subscriptionLoading, setSubscriptionLoading] = useState(false); const [upgradeDialogOpen, setUpgradeDialogOpen] = useState(false); const [selectedPlan, setSelectedPlan] = useState(''); const [upgrading, setUpgrading] = useState(false); const currentTenant = useCurrentTenant(); const [profileData, setProfileData] = useState({ first_name: '', last_name: '', email: '', phone: '', language: 'es', timezone: 'Europe/Madrid' }); // Update profile data when profile is loaded React.useEffect(() => { if (profile) { setProfileData({ first_name: profile.first_name || '', last_name: profile.last_name || '', email: profile.email || '', phone: profile.phone || '', language: profile.language || 'es', timezone: profile.timezone || 'Europe/Madrid' }); // Update preferences with profile data setPreferences(prev => ({ ...prev, global: { ...prev.global, language: profile.language || 'es', timezone: profile.timezone || 'Europe/Madrid' }, channels: { ...prev.channels, email: profile.email || '', phone: profile.phone || '' } })); } }, [profile]); // Load subscription data when needed React.useEffect(() => { if (activeTab === 'subscription' && (currentTenant?.id || user?.tenant_id) && !usageSummary) { loadSubscriptionData(); } }, [activeTab, currentTenant, user?.tenant_id]); const [passwordData, setPasswordData] = useState({ currentPassword: '', newPassword: '', confirmPassword: '' }); const [errors, setErrors] = useState>({}); const [preferences, setPreferences] = useState({ notifications: { inventory: { app: true, email: false, sms: true, frequency: 'immediate' }, sales: { app: true, email: true, sms: false, frequency: 'hourly' }, production: { app: true, email: false, sms: true, frequency: 'immediate' }, system: { app: true, email: true, sms: false, frequency: 'daily' }, marketing: { app: false, email: true, sms: false, frequency: 'weekly' } }, global: { doNotDisturb: false, quietHours: { enabled: false, start: '22:00', end: '07:00' }, language: 'es', timezone: 'Europe/Madrid', soundEnabled: true, vibrationEnabled: true }, channels: { email: '', phone: '', slack: false, webhook: '' } }); const languageOptions = [ { value: 'es', label: 'Español' }, { value: 'ca', label: 'Català' }, { value: 'en', label: 'English' } ]; const timezoneOptions = [ { value: 'Europe/Madrid', label: 'Madrid (CET/CEST)' }, { value: 'Atlantic/Canary', label: 'Canarias (WET/WEST)' }, { value: 'Europe/London', label: 'Londres (GMT/BST)' } ]; const validateProfile = (): boolean => { const newErrors: Record = {}; if (!profileData.first_name.trim()) { newErrors.first_name = 'El nombre es requerido'; } if (!profileData.last_name.trim()) { newErrors.last_name = 'Los apellidos son requeridos'; } if (!profileData.email.trim()) { newErrors.email = 'El email es requerido'; } else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(profileData.email)) { newErrors.email = 'Email inválido'; } setErrors(newErrors); return Object.keys(newErrors).length === 0; }; const validatePassword = (): boolean => { const newErrors: Record = {}; if (!passwordData.currentPassword) { newErrors.currentPassword = 'Contraseña actual requerida'; } if (!passwordData.newPassword) { newErrors.newPassword = 'Nueva contraseña requerida'; } else if (passwordData.newPassword.length < 8) { newErrors.newPassword = 'Mínimo 8 caracteres'; } if (passwordData.newPassword !== passwordData.confirmPassword) { newErrors.confirmPassword = 'Las contraseñas no coinciden'; } setErrors(newErrors); return Object.keys(newErrors).length === 0; }; const handleSaveProfile = async () => { if (!validateProfile()) return; setIsLoading(true); try { await updateProfileMutation.mutateAsync(profileData); setIsEditing(false); addToast('Perfil actualizado correctamente', 'success'); } catch (error) { addToast('No se pudo actualizar tu perfil', 'error'); } finally { setIsLoading(false); } }; const handleChangePasswordSubmit = async () => { if (!validatePassword()) return; setIsLoading(true); try { await changePasswordMutation.mutateAsync({ current_password: passwordData.currentPassword, new_password: passwordData.newPassword, confirm_password: passwordData.confirmPassword }); setShowPasswordForm(false); setPasswordData({ currentPassword: '', newPassword: '', confirmPassword: '' }); addToast('Contraseña actualizada correctamente', 'success'); } catch (error) { addToast('No se pudo cambiar tu contraseña', 'error'); } finally { setIsLoading(false); } }; const handleInputChange = (field: keyof ProfileFormData) => (e: React.ChangeEvent) => { setProfileData(prev => ({ ...prev, [field]: e.target.value })); if (errors[field]) { setErrors(prev => ({ ...prev, [field]: '' })); } }; const handleSelectChange = (field: keyof ProfileFormData) => (value: string) => { setProfileData(prev => ({ ...prev, [field]: value })); }; const handlePasswordChange = (field: keyof PasswordData) => (e: React.ChangeEvent) => { setPasswordData(prev => ({ ...prev, [field]: e.target.value })); if (errors[field]) { setErrors(prev => ({ ...prev, [field]: '' })); } }; // Communication Preferences handlers const categories = [ { id: 'inventory', name: 'Inventario', description: 'Alertas de stock, reposiciones y vencimientos', icon: '📦' }, { id: 'sales', name: 'Ventas', description: 'Pedidos, transacciones y reportes de ventas', icon: '💰' }, { id: 'production', name: 'Producción', description: 'Hornadas, calidad y tiempos de producción', icon: '🍞' }, { id: 'system', name: 'Sistema', description: 'Actualizaciones, mantenimiento y errores', icon: '⚙️' }, { id: 'marketing', name: 'Marketing', description: 'Campañas, promociones y análisis', icon: '📢' } ]; const frequencies = [ { value: 'immediate', label: 'Inmediato' }, { value: 'hourly', label: 'Cada hora' }, { value: 'daily', label: 'Diario' }, { value: 'weekly', label: 'Semanal' } ]; const handleNotificationChange = (category: string, channel: string, value: boolean) => { setPreferences(prev => ({ ...prev, notifications: { ...prev.notifications, [category]: { ...prev.notifications[category as keyof typeof prev.notifications], [channel]: value } } })); setHasPreferencesChanges(true); }; const handleFrequencyChange = (category: string, frequency: string) => { setPreferences(prev => ({ ...prev, notifications: { ...prev.notifications, [category]: { ...prev.notifications[category as keyof typeof prev.notifications], frequency } } })); setHasPreferencesChanges(true); }; const handleGlobalChange = (setting: string, value: any) => { setPreferences(prev => ({ ...prev, global: { ...prev.global, [setting]: value } })); setHasPreferencesChanges(true); }; const handleChannelChange = (channel: string, value: string | boolean) => { setPreferences(prev => ({ ...prev, channels: { ...prev.channels, [channel]: value } })); setHasPreferencesChanges(true); }; const handleSavePreferences = async () => { try { await updateProfileMutation.mutateAsync({ language: preferences.global.language, timezone: preferences.global.timezone, phone: preferences.channels.phone, notification_preferences: preferences.notifications }); addToast('Preferencias guardadas correctamente', 'success'); setHasPreferencesChanges(false); } catch (error) { addToast('Error al guardar las preferencias', 'error'); } }; const handleResetPreferences = () => { if (profile) { setPreferences({ notifications: { inventory: { app: true, email: false, sms: true, frequency: 'immediate' }, sales: { app: true, email: true, sms: false, frequency: 'hourly' }, production: { app: true, email: false, sms: true, frequency: 'immediate' }, system: { app: true, email: true, sms: false, frequency: 'daily' }, marketing: { app: false, email: true, sms: false, frequency: 'weekly' } }, global: { doNotDisturb: false, quietHours: { enabled: false, start: '22:00', end: '07:00' }, language: profile.language || 'es', timezone: profile.timezone || 'Europe/Madrid', soundEnabled: true, vibrationEnabled: true }, channels: { email: profile.email || '', phone: profile.phone || '', slack: false, webhook: '' } }); } setHasPreferencesChanges(false); }; const getChannelIcon = (channel: string) => { switch (channel) { case 'app': return ; case 'email': return ; case 'sms': return ; default: return ; } }; // Subscription handlers const loadSubscriptionData = async () => { let tenantId = currentTenant?.id || user?.tenant_id; if (!tenantId) { addToast('No se encontró información del tenant', 'error'); return; } try { setSubscriptionLoading(true); const [usage, plans] = await Promise.all([ subscriptionService.getUsageSummary(tenantId), subscriptionService.getAvailablePlans() ]); setUsageSummary(usage); setAvailablePlans(plans); } catch (error) { console.error('Error loading subscription data:', error); addToast("No se pudo cargar la información de suscripción", 'error'); } finally { setSubscriptionLoading(false); } }; const handleUpgradeClick = (planKey: string) => { setSelectedPlan(planKey); setUpgradeDialogOpen(true); }; const handleUpgradeConfirm = async () => { let tenantId = currentTenant?.id || user?.tenant_id; if (!tenantId || !selectedPlan) { addToast('Información de tenant no disponible', 'error'); return; } try { setUpgrading(true); const validation = await subscriptionService.validatePlanUpgrade( tenantId, selectedPlan ); if (!validation.can_upgrade) { addToast(validation.reason || 'No se puede actualizar el plan', 'error'); return; } const result = await subscriptionService.upgradePlan(tenantId, selectedPlan); if (result.success) { addToast(result.message, 'success'); await loadSubscriptionData(); setUpgradeDialogOpen(false); setSelectedPlan(''); } else { addToast('Error al cambiar el plan', 'error'); } } catch (error) { console.error('Error upgrading plan:', error); addToast('Error al procesar el cambio de plan', 'error'); } finally { setUpgrading(false); } }; const ProgressBar: React.FC<{ value: number; className?: string }> = ({ value, className = '' }) => { const getProgressColor = () => { if (value >= 90) return 'bg-red-500'; if (value >= 80) return 'bg-yellow-500'; return 'bg-green-500'; }; return (
); }; const tabItems = [ { id: 'profile', label: 'Información Personal' }, { id: 'preferences', label: 'Preferencias de Comunicación' }, { id: 'subscription', label: 'Suscripción y Facturación' } ]; return (
{/* Tab Navigation */} {/* Profile Header */} {activeTab === 'profile' && (

{profileData.first_name} {profileData.last_name}

{profileData.email}

{user?.role && (

{t(`global_roles.${user.role}`)}

)}
En línea
{!isEditing && ( )}
)} {/* Profile Form */} {activeTab === 'profile' && (

Información Personal

} /> } /> } /> } />
{isEditing && (
)}
)} {/* Password Change Form */} {activeTab === 'profile' && showPasswordForm && (

Cambiar Contraseña

} /> } /> } />
)} {/* Communication Preferences Tab */} {activeTab === 'preferences' && ( <> {/* Action Buttons */}
{/* Global Settings */}

Configuración General

Silencia todas las notificaciones

Reproducir sonidos de notificación

{preferences.global.quietHours.enabled && (
handleGlobalChange('quietHours', { ...preferences.global.quietHours, start: e.target.value })} className="px-3 py-1 border border-[var(--border-secondary)] rounded-md text-sm" />
handleGlobalChange('quietHours', { ...preferences.global.quietHours, end: e.target.value })} className="px-3 py-1 border border-[var(--border-secondary)] rounded-md text-sm" />
)}
{/* Channel Settings */}

Canales de Comunicación

handleChannelChange('email', e.target.value)} className="w-full px-3 py-2 border border-[var(--border-secondary)] rounded-md" placeholder="tu-email@ejemplo.com" />
handleChannelChange('phone', e.target.value)} className="w-full px-3 py-2 border border-[var(--border-secondary)] rounded-md" placeholder="+34 600 123 456" />
handleChannelChange('webhook', e.target.value)} className="w-full px-3 py-2 border border-[var(--border-secondary)] rounded-md" placeholder="https://tu-webhook.com/notifications" />

URL para recibir notificaciones JSON

{/* Category Preferences */}
{categories.map((category) => { const categoryPrefs = preferences.notifications[category.id as keyof typeof preferences.notifications]; return (
{category.icon}

{category.name}

{category.description}

{/* Channel toggles */}

Canales

{['app', 'email', 'sms'].map((channel) => ( ))}
{/* Frequency */}

Frecuencia

); })}
{/* Save Changes Banner */} {hasPreferencesChanges && (
Tienes cambios sin guardar
)} )} {/* Subscription Tab */} {activeTab === 'subscription' && ( <> {subscriptionLoading ? (

Cargando información de suscripción...

) : !usageSummary || !availablePlans ? (

No se pudo cargar la información

Hubo un problema al cargar los datos de suscripción

) : ( <> {/* Current Plan Overview */}

Plan Actual: {subscriptionService.getPlanDisplayInfo(usageSummary.plan).name}

{usageSummary.status === 'active' ? 'Activo' : usageSummary.status}
Precio Mensual {subscriptionService.formatPrice(usageSummary.monthly_price)}
Próxima Facturación {new Date(usageSummary.next_billing_date).toLocaleDateString('es-ES', { day: '2-digit', month: '2-digit' })}
Usuarios {usageSummary.usage.users.current}/{usageSummary.usage.users.unlimited ? '∞' : usageSummary.usage.users.limit}
Ubicaciones {usageSummary.usage.locations.current}/{usageSummary.usage.locations.unlimited ? '∞' : usageSummary.usage.locations.limit}
{/* Usage Details */}

Uso de Recursos

{/* Users */}
Usuarios
{usageSummary.usage.users.current}/ {usageSummary.usage.users.unlimited ? '∞' : usageSummary.usage.users.limit}

{usageSummary.usage.users.usage_percentage}% utilizado {usageSummary.usage.users.unlimited ? 'Ilimitado' : `${usageSummary.usage.users.limit - usageSummary.usage.users.current} restantes`}

{/* Locations */}
Ubicaciones
{usageSummary.usage.locations.current}/ {usageSummary.usage.locations.unlimited ? '∞' : usageSummary.usage.locations.limit}

{usageSummary.usage.locations.usage_percentage}% utilizado {usageSummary.usage.locations.unlimited ? 'Ilimitado' : `${usageSummary.usage.locations.limit - usageSummary.usage.locations.current} restantes`}

{/* Products */}
Productos
{usageSummary.usage.products.current}/ {usageSummary.usage.products.unlimited ? '∞' : usageSummary.usage.products.limit}

{usageSummary.usage.products.usage_percentage}% utilizado {usageSummary.usage.products.unlimited ? 'Ilimitado' : 'Ilimitado'}

{/* Available Plans */}

Planes Disponibles

{Object.entries(availablePlans.plans).map(([planKey, plan]) => { const isCurrentPlan = usageSummary.plan === planKey; const getPlanColor = () => { switch (planKey) { case 'starter': return 'border-blue-500/30 bg-blue-500/5'; case 'professional': return 'border-purple-500/30 bg-purple-500/5'; case 'enterprise': return 'border-amber-500/30 bg-amber-500/5'; default: return 'border-[var(--border-primary)] bg-[var(--bg-secondary)]'; } }; return ( {plan.popular && (
Más Popular
)}

{plan.name}

{subscriptionService.formatPrice(plan.monthly_price)} /mes

{plan.description}

{plan.max_users === -1 ? 'Usuarios ilimitados' : `${plan.max_users} usuarios`}
{plan.max_locations === -1 ? 'Ubicaciones ilimitadas' : `${plan.max_locations} ubicación${plan.max_locations > 1 ? 'es' : ''}`}
{plan.max_products === -1 ? 'Productos ilimitados' : `${plan.max_products} productos`}
{isCurrentPlan ? ( Plan Actual ) : ( )}
); })}
)} )} {/* Upgrade Modal */} {upgradeDialogOpen && selectedPlan && availablePlans && ( setUpgradeDialogOpen(false)} title="Confirmar Cambio de Plan" >

¿Estás seguro de que quieres cambiar tu plan de suscripción?

{availablePlans.plans[selectedPlan] && usageSummary && (
Plan actual: {subscriptionService.getPlanDisplayInfo(usageSummary.plan).name}
Nuevo plan: {availablePlans.plans[selectedPlan].name}
Nuevo precio: {subscriptionService.formatPrice(availablePlans.plans[selectedPlan].monthly_price)}/mes
)}
)}
); }; export default ProfilePage;