'use client'; import { useEffect, useState } from 'react'; import { api } from '@/lib/api'; import { Card, PageHeader, Input, Select, Button, Toggle, Banner } from '@/components/ui'; import { Send, Bell, Target, Droplet, BarChart3 } from 'lucide-react'; const SYMBOLS = ['BTCUSDT', 'ETHUSDT', 'SOLUSDT', 'BNBUSDT']; const TFS = ['1m', '3m', '5m', '15m', '30m', '1h', '4h']; const TABS = [ { key: 'tg', label: '텔레그램', icon: Send }, { key: 'alert', label: '알림 / 모니터링', icon: Bell }, { key: 'signal', label: '신호 임계값', icon: Target }, { key: 'vol', label: '거래량 / 펀딩비', icon: Droplet }, { key: 'chart', label: '차트', icon: BarChart3 }, ]; export default function SettingsPage() { const [s, setS] = useState({}); const [tab, setTab] = useState('tg'); const [msg, setMsg] = useState(null); useEffect(() => { api.get('/api/settings').then(setS); }, []); function set(k: string, v: any) { setS({ ...s, [k]: v }); } function setTfs(arr: string[]) { setS({ ...s, alert_timeframes: arr.join(',') }); } function tfList() { return (s.alert_timeframes || '').split(',').filter(Boolean); } function toggleTf(tf: string) { const list = tfList(); setTfs(list.includes(tf) ? list.filter(x => x !== tf) : [...list, tf]); } async function save() { await api.put('/api/settings', { values: s }); setMsg('✅ 저장 완료. 다음 폴링부터 반영'); setTimeout(() => setMsg(null), 3000); } return (
💾 전체 저장} /> {msg &&
{msg}
}
{TABS.map(t => ( ))}
{tab === 'tg' && (
set('telegram_token', e.target.value)} placeholder="예: 1234567890:ABCDEF..." /> set('telegram_chat_id', e.target.value)} placeholder="예: -1001234567890" />

⚠️ Token 은 plain text 표시. DB 에 저장됨 — 노출 주의.

)} {tab === 'alert' && (
{TFS.map(tf => { const on = tfList().includes(tf); return ( ); })}
set('alert_cooldown_sec', e.target.value)} /> set('stop_loss_pct', (parseFloat(e.target.value) / 100).toFixed(6))} /> set('polling_interval_sec', e.target.value)} /> set('forming_stable_polls', e.target.value)} />
set('alert_enabled', v ? '1' : '0')} label="알림 활성화" /> set('daily_report_enabled', v ? '1' : '0')} label="일일 리포트 활성화" />
)} {tab === 'signal' && (
RSI 임계값
set('long_rsi_max', e.target.value)} /> set('short_rsi_min', e.target.value)} /> set('strong_long_rsi_max', e.target.value)} /> set('strong_short_rsi_min', e.target.value)} />
캔들 body / 추세 꺾임
set('body_pct_min', (parseFloat(e.target.value) / 100).toFixed(6))} /> set('reversal_body_pct', (parseFloat(e.target.value) / 100).toFixed(6))} /> set('reversal_vol_mult', e.target.value)} />
)} {tab === 'vol' && (
거래량 배수
set('vol_exhaustion_mult', e.target.value)} /> set('vol_net_mult', e.target.value)} /> set('oi_active_pct', (parseFloat(e.target.value) / 100).toFixed(6))} />
펀딩비 임계 (단위: %)
set('fr_long_overheat', e.target.value)} /> set('fr_short_caution', e.target.value)} /> set('fr_short_extreme', e.target.value)} />
)} {tab === 'chart' && (
set('candle_limit_desktop', e.target.value)} /> set('candle_limit_mobile', e.target.value)} />
)}
); }