import { AppLayout } from '@/components/AppLayout' import { Badge } from '@/components/ui/badge' import { Button } from '@/components/ui/button' import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' import { Combobox, type ComboboxOption } from '@/components/ui/combobox' import { Dialog, DialogContent, DialogHeader, DialogTitle, } from '@/components/ui/dialog' import { Input } from '@/components/ui/input' import { Label } from '@/components/ui/label' import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/select' import { Skeleton } from '@/components/ui/skeleton' import { useAuth } from '@/contexts/AuthContext' import { useTheme } from '@/hooks/use-theme' import { useLocale } from '@/hooks/use-locale' import { getSupportedTimezones } from '@/utils/locale' import { type ApiTokenStatusResponse, type UserProvider, authService, } from '@/lib/api/services/auth' import { CheckCircle2, Copy, Eye, EyeOff, Github, Key, Mail, Palette, Shield, Trash2, User, } from 'lucide-react' import { useEffect, useState } from 'react' import { toast } from 'sonner' import { formatDate } from '@/utils/format-date' export function AccountPage() { const { user, setUser } = useAuth() const { theme, setTheme } = useTheme() const { locale, timezone, setLocale, setTimezone } = useLocale() // Profile state const [profileName, setProfileName] = useState('') const [profileSaving, setProfileSaving] = useState(false) // Password state const [passwordData, setPasswordData] = useState({ current_password: '', new_password: '', confirm_password: '', }) const [passwordSaving, setPasswordSaving] = useState(false) const [showCurrentPassword, setShowCurrentPassword] = useState(false) const [showNewPassword, setShowNewPassword] = useState(false) // API Token state const [apiTokenStatus, setApiTokenStatus] = useState(null) const [apiTokenLoading, setApiTokenLoading] = useState(true) const [generatedToken, setGeneratedToken] = useState('') const [showGeneratedToken, setShowGeneratedToken] = useState(false) const [tokenExpireDays, setTokenExpireDays] = useState('365') // Providers state const [providers, setProviders] = useState([]) const [providersLoading, setProvidersLoading] = useState(true) // Prepare timezone options for combobox const timezoneOptions: ComboboxOption[] = getSupportedTimezones().map((tz) => ({ value: tz, label: tz.replace('_', ' '), searchValue: tz.replace('_', ' ') })) useEffect(() => { if (user) { setProfileName(user.name) } loadApiTokenStatus() loadProviders() }, [user]) const loadApiTokenStatus = async () => { try { const status = await authService.getApiTokenStatus() setApiTokenStatus(status) } catch (error) { console.error('Failed to load API token status:', error) } finally { setApiTokenLoading(false) } } const loadProviders = async () => { try { const userProviders = await authService.getUserProviders() setProviders(userProviders) } catch (error) { console.error('Failed to load providers:', error) setProviders([]) } finally { setProvidersLoading(false) } } const handleProfileSave = async () => { if (!user || !profileName.trim()) return setProfileSaving(true) try { const updatedUser = await authService.updateProfile({ name: profileName.trim(), }) setUser?.(updatedUser) toast.success('Profile updated successfully') } catch (error) { toast.error('Failed to update profile') console.error('Profile update error:', error) } finally { setProfileSaving(false) } } const handlePasswordChange = async () => { // Check if user has password authentication from providers const hasPasswordProvider = providers.some( provider => provider.provider === 'password', ) // Validate required fields if (hasPasswordProvider && !passwordData.current_password) { toast.error('Current password is required') return } if (!passwordData.new_password) { toast.error('New password is required') return } if (passwordData.new_password !== passwordData.confirm_password) { toast.error('New passwords do not match') return } if (passwordData.new_password.length < 8) { toast.error('New password must be at least 8 characters long') return } setPasswordSaving(true) try { await authService.changePassword({ current_password: hasPasswordProvider ? passwordData.current_password : undefined, new_password: passwordData.new_password, }) setPasswordData({ current_password: '', new_password: '', confirm_password: '', }) toast.success( hasPasswordProvider ? 'Password changed successfully' : 'Password set successfully', ) // Reload providers since password status might have changed loadProviders() } catch (error) { toast.error('Failed to change password') console.error('Password change error:', error) } finally { setPasswordSaving(false) } } const handleGenerateApiToken = async () => { try { const response = await authService.generateApiToken({ expires_days: parseInt(tokenExpireDays), }) setGeneratedToken(response.api_token) setShowGeneratedToken(true) await loadApiTokenStatus() toast.success('API token generated successfully') } catch (error) { toast.error('Failed to generate API token') console.error('API token generation error:', error) } } const handleDeleteApiToken = async () => { try { await authService.deleteApiToken() await loadApiTokenStatus() toast.success('API token deleted successfully') } catch (error) { toast.error('Failed to delete API token') console.error('API token deletion error:', error) } } const copyToClipboard = (text: string) => { navigator.clipboard.writeText(text) toast.success('Copied to clipboard') } const getProviderIcon = (provider: string) => { switch (provider.toLowerCase()) { case 'github': return case 'google': return case 'password': return default: return } } if (!user) { return (
{Array.from({ length: 4 }).map((_, i) => (
))}
) } return (

Account Settings

{/* Profile Information */} Profile Information

Email cannot be changed

setProfileName(e.target.value)} placeholder="Enter your display name" />
Role:{' '} {user.role}
Credits:{' '} {user.credits.toLocaleString()}
Plan: {user.plan.name}
Member since:{' '} {formatDate(user.created_at)}
{/* Theme Settings */} Appearance

Choose how the interface appears to you

Choose your preferred language for the interface

Choose your timezone for date and time display

Current theme:{' '} {theme}
Current language:{' '} {locale === 'en-US' ? 'English (US)' : 'Français (FR)'}
Current timezone:{' '} {timezone.replace('_', ' ')}
{/* Password Management */} Security {providers.some(provider => provider.provider === 'password') ? ( <>
setPasswordData(prev => ({ ...prev, current_password: e.target.value, })) } placeholder="Enter current password" />
setPasswordData(prev => ({ ...prev, new_password: e.target.value, })) } placeholder="Enter new password" />
setPasswordData(prev => ({ ...prev, confirm_password: e.target.value, })) } placeholder="Confirm new password" />
) : ( <>

💡 Set up password authentication
You signed up with OAuth and don't have a password yet. Set one now to enable password login.

setPasswordData(prev => ({ ...prev, new_password: e.target.value, })) } placeholder="Enter your new password" />
setPasswordData(prev => ({ ...prev, confirm_password: e.target.value, })) } placeholder="Confirm your password" />
)}
{/* API Token Management */} API Token {apiTokenLoading ? (
) : ( <> {apiTokenStatus?.has_token ? (
API Token Active {apiTokenStatus.expires_at && ( (Expires:{' '} {formatDate(apiTokenStatus.expires_at, false)} ) )}
) : (
)} )}
API tokens allow external applications to access your account programmatically
{/* Authentication Providers */} Authentication Methods

Available methods to sign in to your account. Use any of these to access your account.

{providersLoading ? (
{Array.from({ length: 2 }).map((_, i) => (
))}
) : (
{/* All Authentication Providers from API */} {providers.map(provider => { const isOAuth = provider.provider !== 'password' return (
{getProviderIcon(provider.provider)} {provider.display_name} {isOAuth ? 'OAuth' : 'Password Authentication'} {provider.connected_at && ( Connected{' '} {new Date( provider.connected_at, ).toLocaleDateString()} )}
Available
) })} {/* API Token Provider */} {apiTokenStatus?.has_token && (
API Token API Access {apiTokenStatus.expires_at && ( Expires{' '} {new Date( apiTokenStatus.expires_at, ).toLocaleDateString()} )}
Available
)} {providers.length === 0 && !apiTokenStatus?.has_token && (
No authentication methods configured
)}
)}
{/* Generated Token Dialog */} API Token Generated

⚠️ Important: This token will only be shown once. Copy it now and store it securely.

) }