diff --git a/CLAUDE.md b/CLAUDE.md index dd35849..3828027 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -16,7 +16,7 @@ | `/blog` | `Blog` | 마크다운 기반 블로그 | | `/lotto` | `Lotto` | 로또 추천/통계 | | `/stock` | `Stock` | 주식 뉴스/지수 | -| `/stock/trade` | `StockTrade` | 주식 트레이딩 | +| `/stock/trade` | `StockTrade` | 주식 트레이딩 (포트폴리오·리포트·어드바이저·보유종목 인텔·관심종목 5탭) | | `/stock/screener` | `Screener` | 노드 기반 강세주 스크리너 (폼 ↔ n8n 스타일 캔버스 모드 토글, 점수 노드 7 + 위생 게이트 + ATR 포지션 사이저) | | `/realestate` | `Subscription` | 청약 자격·일정 관리
• **프로필 탭**: 자치구 5티어 분류(드래그&드롭, PC 전용 / 모바일 read-only), 매칭 임계값 슬라이더, 텔레그램 알림 토글
• **카드/매칭 결과**: district 뱃지 + 5티어(S/A/B/C/D) 뱃지 표시
• **상세 모달**: 매칭 분석 섹션 (점수 + 사유 + 신청 자격) | | `/realestate/property` | `RealEstate` | 관심 단지 정보 | @@ -102,6 +102,10 @@ proxy: { | 스크리너 | POST | `/api/stock/screener/snapshot/refresh` | | 스크리너 | GET | `/api/stock/screener/runs?limit=N` | | 스크리너 | GET | `/api/stock/screener/runs/:id` | +| 관심종목 | GET | `/api/stock/watchlist` — { watchlist: [{ ticker, name, note, params, added_at }] } | +| 관심종목 | POST | `/api/stock/watchlist` — body: { ticker, name?, note? } | +| 관심종목 | DELETE | `/api/stock/watchlist/:ticker` | +| 매매 시그널 | GET | `/api/stock/trade-alerts?days=N` — { alerts: [{ id, ticker, name, kind, condition, price, detail, fired_at }] } | | 포트폴리오 | GET/POST | `/api/portfolio` | | 포트폴리오 | PUT/DELETE | `/api/portfolio/:id` | | 예수금 | PUT | `/api/portfolio/cash` — body: `{ broker, cash }` | diff --git a/src/pages/stock/StockTrade.jsx b/src/pages/stock/StockTrade.jsx index e751d72..17d53a0 100644 --- a/src/pages/stock/StockTrade.jsx +++ b/src/pages/stock/StockTrade.jsx @@ -6,7 +6,7 @@ import SwipeableView from '../../components/SwipeableView'; import { formatNumber, formatPercent, toNumeric, profitColorClass, - TAB_PORTFOLIO, TAB_REPORT, TAB_ADVISOR, TAB_HOLDINGS_INTEL, + TAB_PORTFOLIO, TAB_REPORT, TAB_ADVISOR, TAB_HOLDINGS_INTEL, TAB_WATCHLIST, } from './stockUtils'; /* ── hooks ──────────────────────────────────────────────────────── */ @@ -17,12 +17,14 @@ import useAssetHistory from './hooks/useAssetHistory'; import useMarketContext from './hooks/useMarketContext'; import useReportData from './hooks/useReportData'; import useAdvisor from './hooks/useAdvisor'; +import useWatchlist from './hooks/useWatchlist'; /* ── tab components ─────────────────────────────────────────────── */ import PortfolioTab from './components/PortfolioTab'; import ReportTab from './components/ReportTab'; import AdvisorTab from './components/AdvisorTab'; import HoldingsIntelTab from './components/HoldingsIntelTab'; +import WatchlistTab from './components/WatchlistTab'; import SellHistoryDrawer from './components/SellHistoryDrawer'; /* ── component ───────────────────────────────────────────────────── */ @@ -31,8 +33,8 @@ const StockTrade = () => { const [activeTab, setActiveTab] = React.useState(TAB_REPORT); const isMobile = useIsMobile(); - const TAB_ORDER = [TAB_PORTFOLIO, TAB_REPORT, TAB_ADVISOR, TAB_HOLDINGS_INTEL]; - const tabLabels = ['포트폴리오', '리포트', '어드바이저', '보유종목 인텔']; + const TAB_ORDER = [TAB_PORTFOLIO, TAB_REPORT, TAB_ADVISOR, TAB_HOLDINGS_INTEL, TAB_WATCHLIST]; + const tabLabels = ['포트폴리오', '리포트', '어드바이저', '보유종목 인텔', '관심종목']; const tabIndex = TAB_ORDER.indexOf(activeTab); const handleTabChange = useCallback((idx) => setActiveTab(TAB_ORDER[idx]), []); // eslint-disable-line react-hooks/exhaustive-deps @@ -62,6 +64,7 @@ const StockTrade = () => { totalAssets: pf.totalAssets, marketCtx, }); + const wl = useWatchlist(); /* ── sell history filter derived ─────────────────────────────── */ const sellHistoryBrokers = useMemo(() => { @@ -169,7 +172,9 @@ const StockTrade = () => { ? : tabId === TAB_ADVISOR ? - : , + : tabId === TAB_HOLDINGS_INTEL + ? + : , }))} activeIndex={tabIndex} onTabChange={handleTabChange} @@ -182,6 +187,7 @@ const StockTrade = () => { { id: TAB_REPORT, icon: '📊', label: '리포트', sub: '분석·AI코치' }, { id: TAB_ADVISOR, icon: '🧠', label: 'AI 어드바이저', sub: 'Gemini Pro', className: 'stock-main-tab--advisor' }, { id: TAB_HOLDINGS_INTEL, icon: '🔍', label: '보유종목 인텔', sub: '신호·이슈', className: 'stock-main-tab--holdings-intel' }, + { id: TAB_WATCHLIST, icon: '⭐', label: '관심종목', sub: '관리·시그널', badge: wl.items.length || null }, ].map(({ id, icon, label, sub, badge, className: cls }) => (