import React, { useState, useMemo, useEffect } from 'react'; import { Trophy, Flame, Users, Search, Info, MessageSquare, Sparkles, Zap, Save, EyeOff, Eye, RefreshCw, FileText, StickyNote, History as HistoryIcon, ArrowUpRight, ArrowDownRight } from 'lucide-react'; import { StockItem, MarketType, OrderType, ApiSettings, TradeOrder } from '../types'; import StockDetailModal from '../components/StockDetailModal'; import TradeModal from '../components/TradeModal'; import { FilterChip, TabButton } from '../components/CommonUI'; import { StockRow } from '../components/StockRow'; import { AiService } from '../services/aiService'; interface DiscoveryProps { stocks: StockItem[]; orders: TradeOrder[]; onUpdateStock: (code: string, updates: Partial) => void; settings: ApiSettings; } const DISCOVERY_CATEGORIES = [ { id: 'trading_value', name: '거래대금 상위', icon: }, { id: 'gainers', name: '급상승 종목', icon: }, { id: 'continuous_rise', name: '연속 상승세', icon: , isPending: true }, { id: 'undervalued_growth', name: '저평가 성장주', icon: , isPending: true }, { id: 'cheap_value', name: '아직 저렴한 가치주', icon: , isPending: true }, { id: 'stable_dividends', name: '꾸준한 배당주', icon: , isPending: true }, { id: 'profitable_companies', name: '돈 잘버는 회사 찾기', icon: , isPending: true }, { id: 'undervalued_recovery', name: '저평가 탈출', icon: , isPending: true }, { id: 'future_dividend_kings', name: '미래의 배당왕 찾기', icon: , isPending: true }, { id: 'growth_prospects', name: '성장 기대주', icon: , isPending: true }, { id: 'buy_at_cheap', name: '싼값에 매수', icon: , isPending: true }, { id: 'high_yield_undervalued', name: '고수익 저평가', icon: , isPending: true }, { id: 'popular_growth', name: '인기 성장주', icon: , isPending: true }, ]; const Discovery: React.FC = ({ stocks, orders, onUpdateStock, settings }) => { const [activeCategoryId, setActiveCategoryId] = useState('trading_value'); const [marketFilter, setMarketFilter] = useState<'all' | 'domestic' | 'overseas'>('all'); const [selectedStockCode, setSelectedStockCode] = useState(stocks[0]?.code || ''); const [detailStock, setDetailStock] = useState(null); const [tradeContext, setTradeContext] = useState<{ stock: StockItem, type: OrderType } | null>(null); const activeCategory = useMemo(() => DISCOVERY_CATEGORIES.find(c => c.id === activeCategoryId) || DISCOVERY_CATEGORIES[0] , [activeCategoryId]); const selectedStock = useMemo(() => { return stocks.find(s => s.code === selectedStockCode) || null; }, [stocks, selectedStockCode]); const stockOrders = useMemo(() => { return orders.filter(o => o.stockCode === selectedStockCode).sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime()); }, [orders, selectedStockCode]); const [memo, setMemo] = useState(''); const [isAnalyzing, setIsAnalyzing] = useState(false); useEffect(() => { if (selectedStock) { setMemo(selectedStock.memo || ''); } }, [selectedStockCode, stocks]); const enrichedStocks = useMemo(() => { return stocks.filter(s => !s.isHidden).map((s) => ({ ...s, tradingValue: (s.volume * s.price) / (s.market === MarketType.DOMESTIC ? 100000000 : 1000000), buyRatio: 50 + Math.floor(Math.random() * 45), sellRatio: 100 - (50 + Math.floor(Math.random() * 45)) })).filter(s => { if (marketFilter === 'domestic') return s.market === MarketType.DOMESTIC; if (marketFilter === 'overseas') return s.market === MarketType.OVERSEAS; return true; }).sort((a, b) => { // 메뉴별 기본 정렬 (사용자가 나중에 필터링 로직 제공하기 전까지는 기존 로직 활용) if (activeCategoryId === 'gainers') return b.changePercent - a.changePercent; return (b.tradingValue || 0) - (a.tradingValue || 0); }); }, [stocks, marketFilter, activeCategoryId]); const handleSaveMemo = () => { if (selectedStock) { onUpdateStock(selectedStock.code, { memo }); alert('메모가 저장되었습니다.'); } }; const handleToggleHide = () => { if (selectedStock) { const newHidden = !selectedStock.isHidden; onUpdateStock(selectedStock.code, { isHidden: newHidden }); if (newHidden) { alert(`${selectedStock.name} 종목이 숨김 처리되었습니다.`); const next = enrichedStocks.find(s => s.code !== selectedStock.code); if (next) setSelectedStockCode(next.code); } } }; const handleGenerateAnalysis = async () => { if (!selectedStock) return; const config = settings.aiConfigs.find(c => c.id === settings.preferredStockAiId) || settings.aiConfigs[0]; if (!config) return; setIsAnalyzing(true); try { const prompt = `주식 전문가로서 ${selectedStock.name}(${selectedStock.code}) 리포트를 작성해줘.`; const result = await AiService.analyzeNewsSentiment(config, [prompt]); onUpdateStock(selectedStock.code, { aiAnalysis: result }); } catch (e) { alert('AI 분석 생성 실패'); } finally { setIsAnalyzing(false); } }; return (
{/* 1. 좌측 사이드바 메뉴 */}

주식 골라보기 목록

내가 만든

토스증권이 만든

{DISCOVERY_CATEGORIES.map(cat => ( ))}
{/* 2. 중앙 목록 영역 */}

{activeCategory.name}

수천 개의 주식 중 조건에 맞는 종목을 선별했습니다.

setMarketFilter('all')} label="전체" /> setMarketFilter('domestic')} label="국내" /> setMarketFilter('overseas')} label="해외" />
{enrichedStocks.map((stock, idx) => ( setSelectedStockCode(stock.code)} /> ))}
순위 종목 현재가 등락률 매수/매도 비율
{/* 3. 우측 상세 패널 */}
{selectedStock && (
{selectedStock.name[0]}

setDetailStock(selectedStock)}>{selectedStock.name}

{selectedStock.code}

종목 메모