This commit is contained in:
2026-02-01 20:24:04 +09:00
parent 498bddb4fe
commit 3c2f4a0371
10 changed files with 276 additions and 215 deletions

View File

@@ -22,17 +22,17 @@ interface DiscoveryProps {
const DISCOVERY_CATEGORIES = [
{ id: 'trading_value', name: '거래대금 상위', icon: <Flame size={16} /> },
{ id: 'gainers', name: '급상승 종목', icon: <Zap size={16} /> },
{ id: 'continuous_rise', name: '연속 상승세', icon: <Trophy size={16} />, badge: '인기' },
{ id: 'undervalued_growth', name: '저평가 성장주', icon: <Sparkles size={16} />, badge: '인기' },
{ id: 'cheap_value', name: '아직 저렴한 가치주', icon: <Sparkles size={16} /> },
{ id: 'stable_dividends', name: '꾸준한 배당주', icon: <Sparkles size={16} />, badge: '인기' },
{ id: 'profitable_companies', name: '돈 잘버는 회사 찾기', icon: <Sparkles size={16} /> },
{ id: 'undervalued_recovery', name: '저평가 탈출', icon: <Sparkles size={16} /> },
{ id: 'future_dividend_kings', name: '미래의 배당왕 찾기', icon: <Sparkles size={16} /> },
{ id: 'growth_prospects', name: '성장 기대주', icon: <Sparkles size={16} /> },
{ id: 'buy_at_cheap', name: '싼값에 매수', icon: <Sparkles size={16} /> },
{ id: 'high_yield_undervalued', name: '고수익 저평가', icon: <Sparkles size={16} /> },
{ id: 'popular_growth', name: '인기 성장주', icon: <Sparkles size={16} /> },
{ id: 'continuous_rise', name: '연속 상승세', icon: <Trophy size={16} />, isPending: true },
{ id: 'undervalued_growth', name: '저평가 성장주', icon: <Sparkles size={16} />, isPending: true },
{ id: 'cheap_value', name: '아직 저렴한 가치주', icon: <Sparkles size={16} />, isPending: true },
{ id: 'stable_dividends', name: '꾸준한 배당주', icon: <Sparkles size={16} />, isPending: true },
{ id: 'profitable_companies', name: '돈 잘버는 회사 찾기', icon: <Sparkles size={16} />, isPending: true },
{ id: 'undervalued_recovery', name: '저평가 탈출', icon: <Sparkles size={16} />, isPending: true },
{ id: 'future_dividend_kings', name: '미래의 배당왕 찾기', icon: <Sparkles size={16} />, isPending: true },
{ id: 'growth_prospects', name: '성장 기대주', icon: <Sparkles size={16} />, isPending: true },
{ id: 'buy_at_cheap', name: '싼값에 매수', icon: <Sparkles size={16} />, isPending: true },
{ id: 'high_yield_undervalued', name: '고수익 저평가', icon: <Sparkles size={16} />, isPending: true },
{ id: 'popular_growth', name: '인기 성장주', icon: <Sparkles size={16} />, isPending: true },
];
const Discovery: React.FC<DiscoveryProps> = ({ stocks, orders, onUpdateStock, settings }) => {
@@ -122,34 +122,37 @@ const Discovery: React.FC<DiscoveryProps> = ({ stocks, orders, onUpdateStock, se
{/* 1. 좌측 사이드바 메뉴 */}
<div className="w-full lg:w-[240px] shrink-0 space-y-8">
<div>
<h3 className="text-[11px] font-black text-slate-400 uppercase tracking-widest px-4 mb-4"> </h3>
<h3 className="text-[12px] font-black text-slate-400 uppercase tracking-widest px-4 mb-4"> </h3>
<div className="space-y-1">
<p className="text-[10px] font-black text-slate-300 px-4 mb-2 uppercase italic"> </p>
<button className="w-full flex items-center gap-3 px-4 py-2 text-blue-600 font-bold text-[13px] hover:bg-blue-50/50 rounded-xl transition-all">
<div className="w-6 h-6 rounded-lg bg-blue-50 flex items-center justify-center">+</div>
<p className="text-[11px] font-black text-slate-300 px-4 mb-2 uppercase italic"> </p>
<button disabled className="w-full flex items-center gap-3 px-4 py-2 text-slate-300 font-bold text-[14px] cursor-not-allowed rounded-xl transition-all">
<div className="w-6 h-6 rounded-lg bg-slate-50 flex items-center justify-center">+</div>
()
</button>
</div>
</div>
<div>
<p className="text-[10px] font-black text-slate-300 px-4 mb-3 uppercase italic"> </p>
<p className="text-[11px] font-black text-slate-300 px-4 mb-3 uppercase italic"> </p>
<div className="space-y-0.5">
{DISCOVERY_CATEGORIES.map(cat => (
<button
key={cat.id}
onClick={() => setActiveCategoryId(cat.id)}
className={`w-full flex items-center justify-between px-4 py-2.5 rounded-xl transition-all group ${activeCategoryId === cat.id ? 'bg-slate-900 text-white' : 'text-slate-600 hover:bg-slate-50'}`}
disabled={cat.isPending}
onClick={() => !cat.isPending && setActiveCategoryId(cat.id)}
className={`w-full flex items-center justify-between px-4 py-2.5 rounded-xl transition-all group ${activeCategoryId === cat.id ? 'bg-slate-900 text-white' : 'text-slate-600 hover:bg-slate-50'} ${cat.isPending ? 'opacity-40 cursor-not-allowed' : ''}`}
>
<div className="flex items-center gap-3">
<span className={`${activeCategoryId === cat.id ? 'text-blue-400' : 'text-slate-400 group-hover:text-slate-900'}`}>{cat.icon}</span>
<span className="text-[13px] font-black tracking-tight">{cat.name}</span>
<span className="text-[14px] font-black tracking-tight">{cat.name}</span>
</div>
{cat.badge && (
<span className={`text-[9px] px-1.5 py-0.5 rounded-md font-black italic ${activeCategoryId === cat.id ? 'bg-blue-500 text-white' : 'bg-blue-50 text-blue-500'}`}>
{cat.isPending ? (
<span className="text-[9px] px-1.5 py-0.5 rounded-md font-black bg-slate-100 text-slate-400 uppercase"></span>
) : cat.badge ? (
<span className={`text-[10px] px-1.5 py-0.5 rounded-md font-black italic ${activeCategoryId === cat.id ? 'bg-blue-500 text-white' : 'bg-blue-50 text-blue-500'}`}>
{cat.badge}
</span>
)}
) : null}
</button>
))}
</div>
@@ -161,7 +164,7 @@ const Discovery: React.FC<DiscoveryProps> = ({ stocks, orders, onUpdateStock, se
<div className="flex items-center justify-between gap-4">
<div>
<h2 className="text-2xl font-black text-slate-900 italic tracking-tighter">{activeCategory.name}</h2>
<p className="text-[11px] font-bold text-slate-400 mt-1 uppercase tracking-tight"> .</p>
<p className="text-[12px] font-bold text-slate-400 mt-1 uppercase tracking-tight"> .</p>
</div>
<div className="flex gap-1.5 bg-slate-100 p-1 rounded-xl">
<FilterChip active={marketFilter === 'all'} onClick={() => setMarketFilter('all')} label="전체" />
@@ -173,7 +176,7 @@ const Discovery: React.FC<DiscoveryProps> = ({ stocks, orders, onUpdateStock, se
<div className="bg-white rounded-2xl border border-slate-100 shadow-sm overflow-hidden">
<table className="w-full text-left">
<thead>
<tr className="text-[10px] font-black text-slate-400 uppercase tracking-widest border-b bg-slate-50/50">
<tr className="text-[11px] font-black text-slate-400 uppercase tracking-widest border-b bg-slate-50/50">
<th className="pl-6 py-4 w-12 text-center"></th>
<th className="px-4 py-4"></th>
<th className="px-4 py-4 text-right"></th>
@@ -206,7 +209,7 @@ const Discovery: React.FC<DiscoveryProps> = ({ stocks, orders, onUpdateStock, se
<div className="w-12 h-12 bg-slate-900 rounded-xl flex items-center justify-center text-white shadow-md text-sm">{selectedStock.name[0]}</div>
<div className="min-w-0">
<h4 className="text-lg font-black text-slate-900 italic tracking-tighter cursor-pointer hover:text-blue-600 leading-tight truncate" onClick={() => setDetailStock(selectedStock)}>{selectedStock.name}</h4>
<p className="text-[11px] font-black text-slate-400">{selectedStock.code}</p>
<p className="text-[12px] font-black text-slate-400">{selectedStock.code}</p>
</div>
</div>
<button onClick={handleToggleHide} className={`p-2 rounded-xl transition-all shrink-0 ${selectedStock.isHidden ? 'bg-rose-50 text-rose-500' : 'bg-slate-50 text-slate-400 hover:text-rose-500'}`}>
@@ -221,13 +224,13 @@ const Discovery: React.FC<DiscoveryProps> = ({ stocks, orders, onUpdateStock, se
<div className="space-y-3">
<div className="flex items-center justify-between">
<div className="flex items-center gap-1.5 text-[13px] font-black text-slate-800">
<div className="flex items-center gap-1.5 text-[14px] font-black text-slate-800">
<StickyNote size={14} className="text-amber-500" />
</div>
<button onClick={handleSaveMemo} className="text-[10px] font-black text-blue-600 hover:underline"></button>
<button onClick={handleSaveMemo} className="text-[11px] font-black text-blue-600 hover:underline"></button>
</div>
<textarea
className="w-full h-20 p-4 bg-slate-50 border border-slate-100 rounded-2xl text-[12px] text-slate-600 font-medium resize-none focus:bg-white outline-none transition-all"
className="w-full h-20 p-4 bg-slate-50 border border-slate-100 rounded-2xl text-[13px] text-slate-600 font-medium resize-none focus:bg-white outline-none transition-all"
placeholder="메모를 입력하세요..."
value={memo}
onChange={(e) => setMemo(e.target.value)}
@@ -235,7 +238,7 @@ const Discovery: React.FC<DiscoveryProps> = ({ stocks, orders, onUpdateStock, se
</div>
<div className="space-y-3 pt-4 border-t border-slate-50">
<div className="flex items-center gap-1.5 text-[13px] font-black text-slate-800">
<div className="flex items-center gap-1.5 text-[14px] font-black text-slate-800">
<HistoryIcon size={14} className="text-blue-500" />
</div>
<div className="space-y-2">
@@ -243,26 +246,26 @@ const Discovery: React.FC<DiscoveryProps> = ({ stocks, orders, onUpdateStock, se
stockOrders.slice(0, 2).map((order) => (
<div key={order.id} className="p-3 bg-slate-50/50 border border-slate-100 rounded-xl flex items-center justify-between">
<div className="flex items-center gap-2">
<div className={`p-1.5 rounded-lg ${order.type === OrderType.BUY ? 'bg-rose-50 text-rose-500' : 'bg-blue-50 text-blue-500'}`}>
<div className={`p-1.5 rounded-lg ${order.type === OrderType.BUY ? 'bg-rose-50 text-rose-500' : 'bg-blue-50 text-blue-600'}`}>
{order.type === OrderType.BUY ? <ArrowDownRight size={12} /> : <ArrowUpRight size={12} />}
</div>
<div>
<p className="text-[11px] font-black text-slate-800">{order.type === OrderType.BUY ? '매수' : '매도'} {order.quantity}</p>
<p className="text-[9px] text-slate-400 font-bold">{new Date(order.timestamp).toLocaleDateString()}</p>
<p className="text-[12px] font-black text-slate-800">{order.type === OrderType.BUY ? '매수' : '매도'} {order.quantity}</p>
<p className="text-[10px] text-slate-400 font-bold">{new Date(order.timestamp).toLocaleDateString()}</p>
</div>
</div>
<p className="text-[11px] font-black text-slate-900">{order.price.toLocaleString()}</p>
<p className="text-[12px] font-black text-slate-900">{order.price.toLocaleString()}</p>
</div>
))
) : (
<p className="text-[10px] font-black text-slate-300 text-center py-4 italic"> </p>
<p className="text-[11px] font-black text-slate-300 text-center py-4 italic"> </p>
)}
</div>
</div>
<div className="space-y-3 pt-4 border-t border-slate-50">
<div className="flex items-center justify-between">
<div className="flex items-center gap-1.5 text-[13px] font-black text-slate-800">
<div className="flex items-center gap-1.5 text-[14px] font-black text-slate-800">
<Sparkles size={14} className="text-purple-500" /> AI
</div>
<button onClick={handleGenerateAnalysis} disabled={isAnalyzing} className="p-1.5 bg-purple-50 text-purple-600 rounded-lg shrink-0">
@@ -270,12 +273,12 @@ const Discovery: React.FC<DiscoveryProps> = ({ stocks, orders, onUpdateStock, se
</button>
</div>
{selectedStock.aiAnalysis ? (
<div className="bg-slate-900 p-4 rounded-2xl text-slate-100 text-[11px] leading-relaxed font-medium italic">
<div className="bg-slate-900 p-4 rounded-2xl text-slate-100 text-[12px] leading-relaxed font-medium italic">
{selectedStock.aiAnalysis}
</div>
) : (
<div className="p-6 border border-dashed border-slate-100 rounded-2xl flex flex-col items-center gap-2 opacity-30 text-center">
<p className="text-[10px] font-black uppercase"> </p>
<p className="text-[11px] font-black uppercase"> </p>
</div>
)}
</div>