import { useState } from 'react' import { Plus, X, Database, TrendingUp, TrendingDown, List, Ban, Zap } from 'lucide-react' import type { CoinSourceConfig } from '../../types' import { coinSource, ts } from '../../i18n/strategy-translations' import { NofxSelect } from '../ui/select' interface CoinSourceEditorProps { config: CoinSourceConfig onChange: (config: CoinSourceConfig) => void disabled?: boolean language: string } export function CoinSourceEditor({ config, onChange, disabled, language, }: CoinSourceEditorProps) { const [newCoin, setNewCoin] = useState('') const [newExcludedCoin, setNewExcludedCoin] = useState('') const sourceTypes = [ { value: 'static', icon: List, color: '#848E9C' }, { value: 'ai500', icon: Database, color: '#F0B90B' }, { value: 'oi_top', icon: TrendingUp, color: '#0ECB81' }, { value: 'oi_low', icon: TrendingDown, color: '#F6465D' }, ] as const // xyz dex assets (stocks, forex, commodities) - should NOT get USDT suffix const xyzDexAssets = new Set([ // Stocks 'TSLA', 'NVDA', 'AAPL', 'MSFT', 'META', 'AMZN', 'GOOGL', 'AMD', 'COIN', 'NFLX', 'PLTR', 'HOOD', 'INTC', 'MSTR', 'TSM', 'ORCL', 'MU', 'RIVN', 'COST', 'LLY', 'CRCL', 'SKHX', 'SNDK', // Forex 'EUR', 'JPY', // Commodities 'GOLD', 'SILVER', // Index 'XYZ100', ]) const isXyzDexAsset = (symbol: string): boolean => { const base = symbol.toUpperCase().replace(/^XYZ:/, '').replace(/USDT$|USD$|-USDC$/, '') return xyzDexAssets.has(base) } const MAX_STATIC_COINS = 10 const showToast = (msg: string) => { const toast = document.createElement('div') toast.textContent = msg toast.className = 'fixed top-4 left-1/2 -translate-x-1/2 px-4 py-2 rounded-lg text-sm z-50 shadow-lg' toast.style.cssText = 'background:#F6465D;color:#fff;' document.body.appendChild(toast) setTimeout(() => toast.remove(), 2000) } const handleAddCoin = () => { if (!newCoin.trim()) return const currentCoins = config.static_coins || [] if (currentCoins.length >= MAX_STATIC_COINS) { showToast(language === 'zh' ? `最多添加 ${MAX_STATIC_COINS} 个币种` : `Maximum ${MAX_STATIC_COINS} coins allowed`) return } const symbol = newCoin.toUpperCase().trim() // For xyz dex assets (stocks, forex, commodities), use xyz: prefix without USDT let formattedSymbol: string if (isXyzDexAsset(symbol)) { // Remove xyz: prefix (case-insensitive) and any USD suffixes const base = symbol.replace(/^xyz:/i, '').replace(/USDT$|USD$|-USDC$/i, '') formattedSymbol = `xyz:${base}` } else { formattedSymbol = symbol.endsWith('USDT') ? symbol : `${symbol}USDT` } if (!currentCoins.includes(formattedSymbol)) { onChange({ ...config, static_coins: [...currentCoins, formattedSymbol], }) } setNewCoin('') } const handleRemoveCoin = (coin: string) => { onChange({ ...config, static_coins: (config.static_coins || []).filter((c) => c !== coin), }) } const handleAddExcludedCoin = () => { if (!newExcludedCoin.trim()) return const symbol = newExcludedCoin.toUpperCase().trim() // For xyz dex assets, use xyz: prefix without USDT let formattedSymbol: string if (isXyzDexAsset(symbol)) { const base = symbol.replace(/^xyz:/i, '').replace(/USDT$|USD$|-USDC$/i, '') formattedSymbol = `xyz:${base}` } else { formattedSymbol = symbol.endsWith('USDT') ? symbol : `${symbol}USDT` } const currentExcluded = config.excluded_coins || [] if (!currentExcluded.includes(formattedSymbol)) { onChange({ ...config, excluded_coins: [...currentExcluded, formattedSymbol], }) } setNewExcludedCoin('') } const handleRemoveExcludedCoin = (coin: string) => { onChange({ ...config, excluded_coins: (config.excluded_coins || []).filter((c) => c !== coin), }) } // NofxOS badge component const NofxOSBadge = () => ( NofxOS ) return (
{ts(coinSource.excludedCoinsDesc, language)}
{ts(coinSource.nofxosNote, language)}
{ts(coinSource.nofxosNote, language)}
{ts(coinSource.nofxosNote, language)}