'use client'; import { useEffect, useState } from 'react'; import { useLanguage } from '@/context/LanguageContext'; import { useAuth } from '@/context/AuthContext'; import Card from '@/components/ui/Card'; import Button from '@/components/ui/Button'; import Input from '@/components/ui/Input'; import { dashboardApi, authApi, UserProfile, UserSession, } from '@/lib/api'; import { parseDate } from '@/lib/utils'; import toast from 'react-hot-toast'; interface AccountTabProps { onUpdate?: () => void; } /** * Merged Profile + Security tab: account info, edit-profile form, authentication * methods, change/set password, active sessions and account deletion. */ export default function AccountTab({ onUpdate }: AccountTabProps) { const { locale } = useLanguage(); const { user, updateUser } = useAuth(); const [profile, setProfile] = useState(null); const [sessions, setSessions] = useState([]); const [loading, setLoading] = useState(true); const [saving, setSaving] = useState(false); const [changingPassword, setChangingPassword] = useState(false); const [settingPassword, setSettingPassword] = useState(false); const [formData, setFormData] = useState({ name: '', phone: '', languagePreference: '', rucNumber: '', }); const [passwordForm, setPasswordForm] = useState({ currentPassword: '', newPassword: '', confirmPassword: '', }); const [newPasswordForm, setNewPasswordForm] = useState({ password: '', confirmPassword: '', }); useEffect(() => { loadData(); }, []); const loadData = async () => { try { const [profileRes, sessionsRes] = await Promise.all([ dashboardApi.getProfile(), dashboardApi.getSessions(), ]); setProfile(profileRes.profile); setSessions(sessionsRes.sessions); setFormData({ name: profileRes.profile.name || '', phone: profileRes.profile.phone || '', languagePreference: profileRes.profile.languagePreference || '', rucNumber: profileRes.profile.rucNumber || '', }); } catch (error) { toast.error(locale === 'es' ? 'Error al cargar datos' : 'Failed to load data'); } finally { setLoading(false); } }; const handleSaveProfile = async (e: React.FormEvent) => { e.preventDefault(); setSaving(true); try { await dashboardApi.updateProfile(formData); toast.success(locale === 'es' ? 'Perfil actualizado' : 'Profile updated'); if (user) { updateUser({ ...user, name: formData.name, phone: formData.phone, languagePreference: formData.languagePreference, rucNumber: formData.rucNumber, }); } onUpdate?.(); } catch (error: any) { toast.error(error.message || (locale === 'es' ? 'Error al actualizar' : 'Update failed')); } finally { setSaving(false); } }; const handleChangePassword = async (e: React.FormEvent) => { e.preventDefault(); if (passwordForm.newPassword !== passwordForm.confirmPassword) { toast.error(locale === 'es' ? 'Las contraseñas no coinciden' : 'Passwords do not match'); return; } if (passwordForm.newPassword.length < 10) { toast.error( locale === 'es' ? 'La contraseña debe tener al menos 10 caracteres' : 'Password must be at least 10 characters' ); return; } setChangingPassword(true); try { await authApi.changePassword(passwordForm.currentPassword, passwordForm.newPassword); toast.success(locale === 'es' ? 'Contraseña actualizada' : 'Password updated'); setPasswordForm({ currentPassword: '', newPassword: '', confirmPassword: '' }); } catch (error: any) { toast.error( error.message || (locale === 'es' ? 'Error al cambiar contraseña' : 'Failed to change password') ); } finally { setChangingPassword(false); } }; const handleSetPassword = async (e: React.FormEvent) => { e.preventDefault(); if (newPasswordForm.password !== newPasswordForm.confirmPassword) { toast.error(locale === 'es' ? 'Las contraseñas no coinciden' : 'Passwords do not match'); return; } if (newPasswordForm.password.length < 10) { toast.error( locale === 'es' ? 'La contraseña debe tener al menos 10 caracteres' : 'Password must be at least 10 characters' ); return; } setSettingPassword(true); try { await dashboardApi.setPassword(newPasswordForm.password); toast.success(locale === 'es' ? 'Contraseña establecida' : 'Password set'); setNewPasswordForm({ password: '', confirmPassword: '' }); loadData(); } catch (error: any) { toast.error( error.message || (locale === 'es' ? 'Error al establecer contraseña' : 'Failed to set password') ); } finally { setSettingPassword(false); } }; const handleUnlinkGoogle = async () => { if ( !confirm( locale === 'es' ? '¿Estás seguro de que quieres desvincular tu cuenta de Google?' : 'Are you sure you want to unlink your Google account?' ) ) return; try { await dashboardApi.unlinkGoogle(); toast.success(locale === 'es' ? 'Google desvinculado' : 'Google unlinked'); loadData(); } catch (error: any) { toast.error(error.message || (locale === 'es' ? 'Error' : 'Failed')); } }; const handleRevokeSession = async (sessionId: string) => { try { await dashboardApi.revokeSession(sessionId); setSessions((prev) => prev.filter((s) => s.id !== sessionId)); toast.success(locale === 'es' ? 'Sesión cerrada' : 'Session revoked'); } catch (error) { toast.error(locale === 'es' ? 'Error' : 'Failed'); } }; const handleRevokeAllSessions = async () => { if ( !confirm( locale === 'es' ? '¿Cerrar todas las otras sesiones? Esta sesión permanecerá activa.' : 'Log out of all other sessions? This session stays signed in.' ) ) return; try { await dashboardApi.revokeAllSessions(); toast.success( locale === 'es' ? 'Todas las otras sesiones cerradas' : 'All other sessions revoked' ); loadData(); } catch (error) { toast.error(locale === 'es' ? 'Error' : 'Failed'); } }; const formatDateTime = (dateStr: string) => parseDate(dateStr).toLocaleString(locale === 'es' ? 'es-ES' : 'en-US', { year: 'numeric', month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit', timeZone: 'America/Asuncion', }); if (loading) { return (
); } const isActive = profile?.accountStatus === 'active'; return (
{/* Account info */}

{locale === 'es' ? 'Información de la cuenta' : 'Account information'}

{locale === 'es' ? 'Correo' : 'Email'} {profile?.email}
{locale === 'es' ? 'Estado' : 'Status'} {isActive ? locale === 'es' ? 'Activa' : 'Active' : profile?.accountStatus}
{locale === 'es' ? 'Miembro desde' : 'Member since'} {profile?.memberSince ? parseDate(profile.memberSince).toLocaleDateString( locale === 'es' ? 'es-ES' : 'en-US', { year: 'numeric', month: 'long', day: 'numeric', timeZone: 'America/Asuncion' } ) : '-'}
{/* Edit profile */}

{locale === 'es' ? 'Editar perfil' : 'Edit profile'}

setFormData({ ...formData, name: e.target.value })} required /> setFormData({ ...formData, phone: e.target.value })} />
setFormData({ ...formData, rucNumber: e.target.value })} placeholder={locale === 'es' ? 'Opcional' : 'Optional'} />

{locale === 'es' ? 'Tu número de RUC paraguayo para facturas fiscales' : 'Your Paraguayan RUC number for fiscal invoices'}

{locale === 'es' ? 'Para cambiar tu correo, contacta al soporte.' : 'To change your email, please contact support.'}

{/* Authentication methods */}

{locale === 'es' ? 'Métodos de autenticación' : 'Authentication methods'}

{locale === 'es' ? 'Contraseña' : 'Password'}

{profile?.hasPassword ? locale === 'es' ? 'Configurada' : 'Set' : locale === 'es' ? 'No configurada' : 'Not set'}

Google

{profile?.hasGoogleLinked ? locale === 'es' ? 'Vinculado' : 'Linked' : locale === 'es' ? 'No vinculado' : 'Not linked'}

{profile?.hasGoogleLinked && profile?.hasPassword && ( )}
{/* Password management */}

{profile?.hasPassword ? locale === 'es' ? 'Cambiar contraseña' : 'Change password' : locale === 'es' ? 'Establecer contraseña' : 'Set password'}

{profile?.hasPassword ? (
setPasswordForm({ ...passwordForm, currentPassword: e.target.value })} required />
setPasswordForm({ ...passwordForm, newPassword: e.target.value })} required />

{locale === 'es' ? 'Mínimo 10 caracteres' : 'Minimum 10 characters'}

setPasswordForm({ ...passwordForm, confirmPassword: e.target.value })} required />
) : (

{locale === 'es' ? 'Actualmente inicias sesión con Google. Establece una contraseña para más opciones de acceso.' : 'You currently sign in with Google. Set a password for more access options.'}

setNewPasswordForm({ ...newPasswordForm, password: e.target.value })} required />

{locale === 'es' ? 'Mínimo 10 caracteres' : 'Minimum 10 characters'}

setNewPasswordForm({ ...newPasswordForm, confirmPassword: e.target.value })} required />
)}
{/* Active sessions */}

{locale === 'es' ? 'Sesiones activas' : 'Active sessions'}

{sessions.length > 1 && ( )}
{sessions.length === 0 ? (

{locale === 'es' ? 'No hay sesiones activas' : 'No active sessions'}

) : (
{sessions.map((session) => (

{session.userAgent ? session.userAgent.substring(0, 50) + (session.userAgent.length > 50 ? '…' : '') : locale === 'es' ? 'Dispositivo desconocido' : 'Unknown device'}

{locale === 'es' ? 'Última actividad:' : 'Last active:'}{' '} {formatDateTime(session.lastActiveAt)} {session.ipAddress && ` • ${session.ipAddress}`}

{session.current ? ( {locale === 'es' ? 'Esta sesión' : 'This session'} ) : ( )}
))}
)}
{/* Danger zone */}

{locale === 'es' ? 'Zona de peligro' : 'Danger zone'}

{locale === 'es' ? 'Si deseas eliminar tu cuenta, contacta al soporte.' : 'If you want to delete your account, please contact support.'}

); }