import { useState, useEffect } from 'react' import { toast } from 'sonner' import { User, Cpu, Building2, MessageCircle, Eye, EyeOff, ChevronRight, Plus, Pencil } from 'lucide-react' import { useAuth } from '../contexts/AuthContext' import { useLanguage } from '../contexts/LanguageContext' import { api } from '../lib/api' import { ExchangeConfigModal } from '../components/trader/ExchangeConfigModal' import { TelegramConfigModal } from '../components/trader/TelegramConfigModal' import { ModelConfigModal } from '../components/trader/AITradersPage' import type { Exchange, AIModel } from '../types' type Tab = 'account' | 'models' | 'exchanges' | 'telegram' export function SettingsPage() { const { user } = useAuth() const { language } = useLanguage() const [activeTab, setActiveTab] = useState('account') // Account state const [newPassword, setNewPassword] = useState('') const [showPassword, setShowPassword] = useState(false) const [changingPassword, setChangingPassword] = useState(false) // AI Models state const [configuredModels, setConfiguredModels] = useState([]) const [supportedModels, setSupportedModels] = useState([]) const [showModelModal, setShowModelModal] = useState(false) const [editingModel, setEditingModel] = useState(null) // Exchanges state const [exchanges, setExchanges] = useState([]) const [showExchangeModal, setShowExchangeModal] = useState(false) const [editingExchange, setEditingExchange] = useState(null) // Telegram state const [showTelegramModal, setShowTelegramModal] = useState(false) // Fetch data when tabs are visited useEffect(() => { if (activeTab === 'models') { Promise.all([api.getModelConfigs(), api.getSupportedModels()]) .then(([configs, supported]) => { setConfiguredModels(configs) setSupportedModels(supported) }) .catch(() => toast.error('Failed to load AI models')) } if (activeTab === 'exchanges') { api.getExchangeConfigs() .then(setExchanges) .catch(() => toast.error('Failed to load exchanges')) } }, [activeTab]) const handleChangePassword = async (e: React.FormEvent) => { e.preventDefault() if (newPassword.length < 8) { toast.error('Password must be at least 8 characters') return } setChangingPassword(true) try { const res = await fetch('/api/user/password', { method: 'PUT', headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${localStorage.getItem('token') || ''}`, }, body: JSON.stringify({ new_password: newPassword }), }) if (!res.ok) { const data = await res.json().catch(() => ({})) throw new Error(data.error || 'Failed to update password') } toast.success('Password updated successfully') setNewPassword('') } catch (err) { toast.error(err instanceof Error ? err.message : 'Failed to update password') } finally { setChangingPassword(false) } } const handleSaveModel = async ( modelId: string, apiKey: string, customApiUrl?: string, customModelName?: string ) => { try { const existingModel = configuredModels.find((m) => m.id === modelId) const modelTemplate = supportedModels.find((m) => m.id === modelId) const modelToUpdate = existingModel || modelTemplate if (!modelToUpdate) { toast.error('Model not found'); return } let updatedModels: AIModel[] if (existingModel) { updatedModels = configuredModels.map((m) => m.id === modelId ? { ...m, apiKey, customApiUrl: customApiUrl || '', customModelName: customModelName || '', enabled: true } : m ) } else { updatedModels = [...configuredModels, { ...modelToUpdate, apiKey, customApiUrl: customApiUrl || '', customModelName: customModelName || '', enabled: true, }] } const request = { models: Object.fromEntries( updatedModels.map((m) => [m.provider, { enabled: m.enabled, api_key: m.apiKey || '', custom_api_url: m.customApiUrl || '', custom_model_name: m.customModelName || '', }]) ), } await toast.promise(api.updateModelConfigs(request), { loading: 'Saving model config...', success: 'Model config saved', error: 'Failed to save model config', }) const refreshed = await api.getModelConfigs() setConfiguredModels(refreshed) setShowModelModal(false) setEditingModel(null) } catch { toast.error('Failed to save model config') } } const handleDeleteModel = async (modelId: string) => { try { const updatedModels = configuredModels.map((m) => m.id === modelId ? { ...m, apiKey: '', customApiUrl: '', customModelName: '', enabled: false } : m ) const request = { models: Object.fromEntries( updatedModels.map((m) => [m.provider, { enabled: m.enabled, api_key: m.apiKey || '', custom_api_url: m.customApiUrl || '', custom_model_name: m.customModelName || '', }]) ), } await api.updateModelConfigs(request) const refreshed = await api.getModelConfigs() setConfiguredModels(refreshed) setShowModelModal(false) setEditingModel(null) toast.success('Model config removed') } catch { toast.error('Failed to remove model config') } } const handleSaveExchange = async ( exchangeId: string | null, exchangeType: string, accountName: string, apiKey: string, secretKey?: string, passphrase?: string, testnet?: boolean, hyperliquidWalletAddr?: string, asterUser?: string, asterSigner?: string, asterPrivateKey?: string, lighterWalletAddr?: string, lighterPrivateKey?: string, lighterApiKeyPrivateKey?: string, lighterApiKeyIndex?: number ) => { try { if (exchangeId) { const request = { exchanges: { [exchangeId]: { enabled: true, api_key: apiKey || '', secret_key: secretKey || '', passphrase: passphrase || '', testnet: testnet || false, hyperliquid_wallet_addr: hyperliquidWalletAddr || '', aster_user: asterUser || '', aster_signer: asterSigner || '', aster_private_key: asterPrivateKey || '', lighter_wallet_addr: lighterWalletAddr || '', lighter_private_key: lighterPrivateKey || '', lighter_api_key_private_key: lighterApiKeyPrivateKey || '', lighter_api_key_index: lighterApiKeyIndex || 0, }, }, } await toast.promise(api.updateExchangeConfigsEncrypted(request), { loading: 'Updating exchange config...', success: 'Exchange config updated', error: 'Failed to update exchange config', }) } else { const createRequest = { exchange_type: exchangeType, account_name: accountName, enabled: true, api_key: apiKey || '', secret_key: secretKey || '', passphrase: passphrase || '', testnet: testnet || false, hyperliquid_wallet_addr: hyperliquidWalletAddr || '', aster_user: asterUser || '', aster_signer: asterSigner || '', aster_private_key: asterPrivateKey || '', lighter_wallet_addr: lighterWalletAddr || '', lighter_private_key: lighterPrivateKey || '', lighter_api_key_private_key: lighterApiKeyPrivateKey || '', lighter_api_key_index: lighterApiKeyIndex || 0, } await toast.promise(api.createExchangeEncrypted(createRequest), { loading: 'Creating exchange account...', success: 'Exchange account created', error: 'Failed to create exchange account', }) } const refreshed = await api.getExchangeConfigs() setExchanges(refreshed) setShowExchangeModal(false) setEditingExchange(null) } catch { toast.error('Failed to save exchange config') } } const handleDeleteExchange = async (exchangeId: string) => { try { await toast.promise(api.deleteExchange(exchangeId), { loading: 'Deleting exchange account...', success: 'Exchange account deleted', error: 'Failed to delete exchange account', }) const refreshed = await api.getExchangeConfigs() setExchanges(refreshed) setShowExchangeModal(false) setEditingExchange(null) } catch { toast.error('Failed to delete exchange account') } } const tabs: { key: Tab; label: string; icon: React.ReactNode }[] = [ { key: 'account', label: 'Account', icon: }, { key: 'models', label: 'AI Models', icon: }, { key: 'exchanges', label: 'Exchanges', icon: }, { key: 'telegram', label: 'Telegram', icon: }, ] return (

Settings

{/* Tabs */}
{tabs.map((tab) => ( ))}
{/* Tab Content */}
{/* Account Tab */} {activeTab === 'account' && (

Email

{user?.email}

Change Password

setNewPassword(e.target.value)} className="w-full bg-zinc-950/80 border border-zinc-700/80 rounded-xl px-4 py-3 pr-11 text-sm text-white placeholder-zinc-600 focus:outline-none focus:border-nofx-gold/60 focus:ring-1 focus:ring-nofx-gold/30 transition-all" placeholder="At least 8 characters" required />
)} {/* AI Models Tab */} {activeTab === 'models' && (

{configuredModels.length} model{configuredModels.length !== 1 ? 's' : ''} configured

{configuredModels.length === 0 ? (
No AI models configured yet
) : (
{configuredModels.map((model) => ( ))}
)}
)} {/* Exchanges Tab */} {activeTab === 'exchanges' && (

{exchanges.length} account{exchanges.length !== 1 ? 's' : ''} connected

{exchanges.length === 0 ? (
No exchange accounts connected yet
) : (
{exchanges.map((exchange) => ( ))}
)}
)} {/* Telegram Tab */} {activeTab === 'telegram' && (

Connect a Telegram bot to receive trading notifications and interact with your traders.

)}
{/* AI Model Modal */} {showModelModal && (
{ setShowModelModal(false); setEditingModel(null) }} language={language} />
)} {/* Exchange Modal */} {showExchangeModal && (
{ setShowExchangeModal(false); setEditingExchange(null) }} language={language} />
)} {/* Telegram Modal */} {showTelegramModal && (
setShowTelegramModal(false)} language={language} />
)}
) }