From 159d172d59e2017230226cb862a111c5d75be336 Mon Sep 17 00:00:00 2001 From: shuim Date: Tue, 6 Oct 2026 13:36:42 +0900 Subject: [PATCH] =?UTF-8?q?feat(frontend):=20=EB=B3=B4=EC=9C=A0=EC=A2=85?= =?UTF-8?q?=EB=AA=A9=EC=9D=84=20=EB=8D=B0=EC=9D=B4=ED=8A=B8=EB=A0=88?= =?UTF-8?q?=EC=9D=B4=EB=94=A9/=EC=8A=A4=EC=9C=99/=EC=88=98=EB=8F=99=C2=B7?= =?UTF-8?q?=EC=99=B8=EB=B6=80/=ED=95=B4=EC=99=B8=EB=A1=9C=20=EB=B6=84?= =?UTF-8?q?=EB=A6=AC=20=ED=91=9C=EC=8B=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - portfolioGrouping.ts: DT 엔진 포지션 → 데이트레이딩, strategy_type 으로 swing(+NULL)/external·manual/us_swing 분류 - 스윙+DT 합산 행(Portfolio stock_code UNIQUE)은 DT 엔진 수량만큼 분할 표시, 분할 카드·DT 수량 미상 카드는 청산 버튼 비활성 (수동 매도는 DT 몫부터 차감되므로 분할 매도 불가 안내) - 그룹별 종목 수·평가금액·손익 소계, 상단 카운트 데이/스윙/수동/해외 정정 - 매매 로직·백엔드 변경 없음 Co-Authored-By: Claude Opus 5.5 --- frontend/src/components/PortfolioPanel.tsx | 146 +++++++++++++++------ frontend/src/utils/portfolioGrouping.ts | 77 +++++++++++ 2 files changed, 180 insertions(+), 43 deletions(-) create mode 100644 frontend/src/utils/portfolioGrouping.ts diff --git a/frontend/src/components/PortfolioPanel.tsx b/frontend/src/components/PortfolioPanel.tsx index 7bbb956..6a57474 100644 --- a/frontend/src/components/PortfolioPanel.tsx +++ b/frontend/src/components/PortfolioPanel.tsx @@ -13,12 +13,13 @@ * 한국/미국 시장 구분: * - 종목 코드가 6자리 숫자면 KR, 그 외는 US */ -import React from 'react' +import React, { useMemo } from 'react' import { useQuery } from '@tanstack/react-query' import api from '../lib/api' -import { Activity, TrendingUp, Clock } from 'lucide-react' +import { Activity, TrendingUp, Clock, UserCircle, Globe } from 'lucide-react' import { Position } from '../types/trading' import { isUsStock } from '../utils/stockUtils' +import { groupHoldings, GroupedHolding } from '../utils/portfolioGrouping' import AccountSetupNotice from './AccountSetupNotice' import DayTradingStatusBar from './DayTradingStatusBar' @@ -95,9 +96,12 @@ const PortfolioItem = React.memo(({ const isSelected = selectedStock === stock.stock_code const isUp = profitLossRate >= 0 + const splitLocked = Number((stock as GroupedHolding).isSplit) > 0 || Number((stock as GroupedHolding).dtIncluded) > 0 + const sellBlocked = !!sellDisabled || splitLocked + const handleSellClick = async (e: React.MouseEvent) => { e.stopPropagation() - if (sellDisabled) return + if (sellBlocked) return try { await onSell(stock.stock_code, stock.stock_name, quantity) } catch (error: unknown) { @@ -126,9 +130,25 @@ const PortfolioItem = React.memo(({ {isUs ? 'US' : 'KR'} - {stock.strategy_type === 'daytrading' && ( + {(stock.strategy_type === 'daytrading' || stock.strategy_type === 'day_trade') && ( 데이 )} + {(stock.strategy_type === 'external' || stock.strategy_type === 'manual') && ( + + {stock.strategy_type === 'external' ? '외부' : '수동'} + + )} + {Number((stock as GroupedHolding).dtIncluded) > 0 && ( + + DT 포함? + + )} {profitLossRate > 0 ? '+' : ''}{profitLossRate.toFixed(2)}% @@ -154,7 +174,8 @@ const PortfolioItem = React.memo(({