매매탭 주문패널을 스플릿 카드 레이아웃으로 복원 (상단 매수, 하단 매도)

- TradeTabOrderPanel(탭 방식) → TradeSplitOrderPanel(카드 분리 방식)으로 전환
- VirtualTradingPage·PaperTradingPage에 '거래' 외부 탭 복원
- 거래 내역(미체결/체결/원장)을 '거래' 탭 하위로 원복

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Macbook
2026-06-11 13:10:33 +09:00
parent c601293ed7
commit 9f9e025598
4 changed files with 50 additions and 56 deletions
+31 -33
View File
@@ -23,7 +23,7 @@ import PaperPendingOrdersList from './paper/PaperPendingOrdersList';
import PaperLedgerTab from './paper/PaperLedgerTab'; import PaperLedgerTab from './paper/PaperLedgerTab';
import PaperAssetTrendChart from './paper/PaperAssetTrendChart'; import PaperAssetTrendChart from './paper/PaperAssetTrendChart';
import { computePaperMetrics } from '../utils/paperMetrics'; import { computePaperMetrics } from '../utils/paperMetrics';
import { TradeTabOrderPanel, TradeOrderbookPanelContent, TradeRightPanelBody } from './trade'; import { TradeSplitOrderPanel, TradeOrderbookPanelContent, TradeRightPanelBody } from './trade';
import type { Theme, TradeOrderFillRequest } from '../types'; import type { Theme, TradeOrderFillRequest } from '../types';
import PaperAnalysisChart from './paper/PaperAnalysisChart'; import PaperAnalysisChart from './paper/PaperAnalysisChart';
import PaperLeftStrategyTab from './paper/PaperLeftStrategyTab'; import PaperLeftStrategyTab from './paper/PaperLeftStrategyTab';
@@ -38,7 +38,7 @@ import { formatVirtualTargetOptionLabel } from '../utils/virtualTargetNames';
type HistoryTab = 'open' | 'recent' | 'ledger'; type HistoryTab = 'open' | 'recent' | 'ledger';
type CenterTab = 'invest' | 'chart'; type CenterTab = 'invest' | 'chart';
type RightTab = 'trade' | 'orderbook'; type RightTab = 'trade' | 'orderbook' | 'history';
type LeftTab = 'strategy' | 'settings'; type LeftTab = 'strategy' | 'settings';
interface Props { interface Props {
@@ -140,7 +140,7 @@ const PaperTradingPage: React.FC<Props> = ({
} finally { } finally {
setInitialLoading(false); setInitialLoading(false);
} }
if (opts?.focusHistory) setRightTab('trade'); if (opts?.focusHistory) setRightTab('history');
}, []); }, []);
useEffect(() => { useEffect(() => {
@@ -293,33 +293,7 @@ const PaperTradingPage: React.FC<Props> = ({
<div className="bps-right-tabs"> <div className="bps-right-tabs">
<button type="button" className={`bps-right-tab${rightTab === 'trade' ? ' bps-right-tab--on' : ''}`} onClick={() => setRightTab('trade')}></button> <button type="button" className={`bps-right-tab${rightTab === 'trade' ? ' bps-right-tab--on' : ''}`} onClick={() => setRightTab('trade')}></button>
<button type="button" className={`bps-right-tab${rightTab === 'orderbook' ? ' bps-right-tab--on' : ''}`} onClick={() => setRightTab('orderbook')}></button> <button type="button" className={`bps-right-tab${rightTab === 'orderbook' ? ' bps-right-tab--on' : ''}`} onClick={() => setRightTab('orderbook')}></button>
</div> <button type="button" className={`bps-right-tab${rightTab === 'history' ? ' bps-right-tab--on' : ''}`} onClick={() => setRightTab('history')}></button>
);
const tradeHistorySlot = (
<div className="ptd-right-history">
<div className="ptd-tabs ptd-tabs--history">
<button type="button" className={`ptd-tab${historyTab === 'open' ? ' active' : ''}`} onClick={() => setHistoryTab('open')}></button>
<button type="button" className={`ptd-tab${historyTab === 'recent' ? ' active' : ''}`} onClick={() => setHistoryTab('recent')}>{trades.length > 0 ? ` (${trades.length})` : ''}</button>
<button type="button" className={`ptd-tab${historyTab === 'ledger' ? ' active' : ''}`} onClick={() => setHistoryTab('ledger')}></button>
</div>
<div className="ptd-right-history-body">
{historyTab === 'open' ? (
<PaperPendingOrdersList orders={pendingOrders} onChanged={() => void loadData()} />
) : historyTab === 'ledger' ? (
<PaperLedgerTab refreshKey={dataRefreshKey} />
) : (
<PaperTradeHistoryList
trades={trades}
strategyNames={strategyNames}
tickers={tickers}
selectedTradeId={selectedTradeId}
onSelectTrade={handleTradeHistorySelect}
emptyText="체결 내역이 없습니다."
className="ptd-trade-history--fill"
/>
)}
</div>
</div> </div>
); );
@@ -445,7 +419,7 @@ const PaperTradingPage: React.FC<Props> = ({
right={( right={(
<TradeRightPanelBody anchorRef={orderAnchorRef} embeddedInShell> <TradeRightPanelBody anchorRef={orderAnchorRef} embeddedInShell>
{rightTab === 'trade' ? ( {rightTab === 'trade' ? (
<TradeTabOrderPanel <TradeSplitOrderPanel
market={selectedMarket} market={selectedMarket}
tradePrice={tradePrice} tradePrice={tradePrice}
availableKrw={orderableCash} availableKrw={orderableCash}
@@ -457,9 +431,8 @@ const PaperTradingPage: React.FC<Props> = ({
paperTradingEnabled={paperTradingEnabled} paperTradingEnabled={paperTradingEnabled}
paperAutoTradeEnabled={paperAutoTradeEnabled} paperAutoTradeEnabled={paperAutoTradeEnabled}
onPaperOrderFilled={() => void loadData({ focusHistory: true })} onPaperOrderFilled={() => void loadData({ focusHistory: true })}
historySlot={tradeHistorySlot}
/> />
) : ( ) : rightTab === 'orderbook' ? (
<TradeOrderbookPanelContent <TradeOrderbookPanelContent
market={selectedMarket} market={selectedMarket}
asks={orderbook.asks} asks={orderbook.asks}
@@ -477,6 +450,31 @@ const PaperTradingPage: React.FC<Props> = ({
tradeStrength={tradeStrength ?? undefined} tradeStrength={tradeStrength ?? undefined}
onRowClick={handleOrderbookRowClick} onRowClick={handleOrderbookRowClick}
/> />
) : (
<div className="ptd-right-history">
<div className="ptd-tabs ptd-tabs--history">
<button type="button" className={`ptd-tab${historyTab === 'open' ? ' active' : ''}`} onClick={() => setHistoryTab('open')}></button>
<button type="button" className={`ptd-tab${historyTab === 'recent' ? ' active' : ''}`} onClick={() => setHistoryTab('recent')}>{trades.length > 0 ? ` (${trades.length})` : ''}</button>
<button type="button" className={`ptd-tab${historyTab === 'ledger' ? ' active' : ''}`} onClick={() => setHistoryTab('ledger')}></button>
</div>
<div className="ptd-right-history-body">
{historyTab === 'open' ? (
<PaperPendingOrdersList orders={pendingOrders} onChanged={() => void loadData()} />
) : historyTab === 'ledger' ? (
<PaperLedgerTab refreshKey={dataRefreshKey} />
) : (
<PaperTradeHistoryList
trades={trades}
strategyNames={strategyNames}
tickers={tickers}
selectedTradeId={selectedTradeId}
onSelectTrade={handleTradeHistorySelect}
emptyText="체결 내역이 없습니다."
className="ptd-trade-history--fill"
/>
)}
</div>
</div>
)} )}
</TradeRightPanelBody> </TradeRightPanelBody>
)} )}
+2 -3
View File
@@ -5,7 +5,7 @@
*/ */
import React, { useState, useEffect, useRef, useCallback } from 'react'; import React, { useState, useEffect, useRef, useCallback } from 'react';
import { import {
TradeTabOrderPanel, TradeSplitOrderPanel,
TradeOrderbookPanelContent, TradeOrderbookPanelContent,
TradeRightPanelTabs, TradeRightPanelTabs,
TradeRightPanelBody, TradeRightPanelBody,
@@ -158,7 +158,7 @@ const RightSidePanel: React.FC<RightSidePanelProps> = ({
<TradeRightPanelBody anchorRef={tradeSectionRef} className="rsp-body-wrap"> <TradeRightPanelBody anchorRef={tradeSectionRef} className="rsp-body-wrap">
{activeTab === 'trade' && ( {activeTab === 'trade' && (
<TradeTabOrderPanel <TradeSplitOrderPanel
market={market} market={market}
tradePrice={tradePrice} tradePrice={tradePrice}
availableKrw={availableKrw} availableKrw={availableKrw}
@@ -169,7 +169,6 @@ const RightSidePanel: React.FC<RightSidePanelProps> = ({
paperTradingEnabled={paperTradingEnabled} paperTradingEnabled={paperTradingEnabled}
paperAutoTradeEnabled={paperAutoTradeEnabled} paperAutoTradeEnabled={paperAutoTradeEnabled}
onPaperOrderFilled={onPaperOrderFilled} onPaperOrderFilled={onPaperOrderFilled}
hideHistory
/> />
)} )}
@@ -23,7 +23,7 @@ import BacktestAnalysisReportModal from './backtest/BacktestAnalysisReportModal'
import type { BacktestAnalysisReportModel } from './backtest/BacktestAnalysisReportModal'; import type { BacktestAnalysisReportModel } from './backtest/BacktestAnalysisReportModal';
import { coerceFiniteNumber } from '../utils/safeFormat'; import { coerceFiniteNumber } from '../utils/safeFormat';
import { import {
TradeTabOrderPanel, TradeSplitOrderPanel,
TradeOrderbookPanelContent, TradeOrderbookPanelContent,
TradeRightPanelTabs, TradeRightPanelTabs,
TradeRightPanelBody, TradeRightPanelBody,
@@ -753,7 +753,7 @@ export const TradeNotificationListPage: React.FC<Props> = ({
bodyClassName="tnl-right-body" bodyClassName="tnl-right-body"
> >
{rightTab === 'trade' ? ( {rightTab === 'trade' ? (
<TradeTabOrderPanel <TradeSplitOrderPanel
market={selectedMarket} market={selectedMarket}
tradePrice={tradePrice} tradePrice={tradePrice}
availableKrw={coerceFiniteNumber(summary?.cashBalance) ?? 0} availableKrw={coerceFiniteNumber(summary?.cashBalance) ?? 0}
@@ -764,7 +764,6 @@ export const TradeNotificationListPage: React.FC<Props> = ({
paperTradingEnabled={paperTradingEnabled} paperTradingEnabled={paperTradingEnabled}
paperAutoTradeEnabled={paperAutoTradeEnabled} paperAutoTradeEnabled={paperAutoTradeEnabled}
onPaperOrderFilled={() => void refreshPaperData()} onPaperOrderFilled={() => void refreshPaperData()}
hideHistory
/> />
) : ( ) : (
<TradeOrderbookPanelContent <TradeOrderbookPanelContent
+15 -17
View File
@@ -15,7 +15,7 @@ import {
} from '../utils/backendApi'; } from '../utils/backendApi';
import type { Theme, TradeOrderFillRequest } from '../types'; import type { Theme, TradeOrderFillRequest } from '../types';
import type { TickerData } from '../hooks/useMarketTicker'; import type { TickerData } from '../hooks/useMarketTicker';
import { TradeTabOrderPanel, TradeOrderbookPanelContent, TradeRightPanelBody } from './trade'; import { TradeSplitOrderPanel, TradeOrderbookPanelContent, TradeRightPanelBody } from './trade';
import { useUpbitOrderbook } from '../hooks/useUpbitOrderbook'; import { useUpbitOrderbook } from '../hooks/useUpbitOrderbook';
import { useUpbitRecentTrades } from '../hooks/useUpbitRecentTrades'; import { useUpbitRecentTrades } from '../hooks/useUpbitRecentTrades';
import PaperTradeHistoryList from './paper/PaperTradeHistoryList'; import PaperTradeHistoryList from './paper/PaperTradeHistoryList';
@@ -72,7 +72,7 @@ import {
} from '../utils/virtualTargetHydrate'; } from '../utils/virtualTargetHydrate';
import '../styles/virtualTradingDashboard.css'; import '../styles/virtualTradingDashboard.css';
type RightTab = 'trade' | 'orderbook'; type RightTab = 'trade' | 'orderbook' | 'history';
interface Props { interface Props {
theme?: Theme; theme?: Theme;
@@ -258,7 +258,7 @@ const VirtualTradingPage: React.FC<Props> = ({
setTrades(tr); setTrades(tr);
} catch { /* ignore */ } } catch { /* ignore */ }
onPaperOrderFilled?.(); onPaperOrderFilled?.();
if (opts?.focusHistory) setRightTab('trade'); if (opts?.focusHistory) setRightTab('history');
}, [onPaperOrderFilled]); }, [onPaperOrderFilled]);
const handleGlobalDisplayModeChange = useCallback((mode: VirtualCardDisplayMode) => { const handleGlobalDisplayModeChange = useCallback((mode: VirtualCardDisplayMode) => {
@@ -472,20 +472,10 @@ const VirtualTradingPage: React.FC<Props> = ({
<div className="bps-right-tabs"> <div className="bps-right-tabs">
<button type="button" className={`bps-right-tab${rightTab === 'trade' ? ' bps-right-tab--on' : ''}`} onClick={() => setRightTab('trade')}></button> <button type="button" className={`bps-right-tab${rightTab === 'trade' ? ' bps-right-tab--on' : ''}`} onClick={() => setRightTab('trade')}></button>
<button type="button" className={`bps-right-tab${rightTab === 'orderbook' ? ' bps-right-tab--on' : ''}`} onClick={() => setRightTab('orderbook')}></button> <button type="button" className={`bps-right-tab${rightTab === 'orderbook' ? ' bps-right-tab--on' : ''}`} onClick={() => setRightTab('orderbook')}></button>
<button type="button" className={`bps-right-tab${rightTab === 'history' ? ' bps-right-tab--on' : ''}`} onClick={() => setRightTab('history')}></button>
</div> </div>
); );
const tradeHistorySlot = (
<PaperTradeHistoryList
trades={trades}
strategyNames={strategyNames}
tickers={tickers}
onSelectMarket={handleSelectMarket}
emptyText="수동·자동 매매 체결 내역이 없습니다."
className="ptd-trade-history--fill"
/>
);
return ( return (
<BuilderPageShell <BuilderPageShell
theme={theme} theme={theme}
@@ -572,7 +562,7 @@ const VirtualTradingPage: React.FC<Props> = ({
right={( right={(
<TradeRightPanelBody anchorRef={orderAnchorRef} embeddedInShell> <TradeRightPanelBody anchorRef={orderAnchorRef} embeddedInShell>
{rightTab === 'trade' ? ( {rightTab === 'trade' ? (
<TradeTabOrderPanel <TradeSplitOrderPanel
market={selectedMarket} market={selectedMarket}
tradePrice={tradePrice} tradePrice={tradePrice}
availableKrw={coerceFiniteNumber(summary?.cashBalance) ?? 0} availableKrw={coerceFiniteNumber(summary?.cashBalance) ?? 0}
@@ -583,9 +573,8 @@ const VirtualTradingPage: React.FC<Props> = ({
paperTradingEnabled={paperTradingEnabled} paperTradingEnabled={paperTradingEnabled}
paperAutoTradeEnabled={paperAutoTradeEnabled} paperAutoTradeEnabled={paperAutoTradeEnabled}
onPaperOrderFilled={() => void refreshPaperData({ focusHistory: true })} onPaperOrderFilled={() => void refreshPaperData({ focusHistory: true })}
historySlot={tradeHistorySlot}
/> />
) : ( ) : rightTab === 'orderbook' ? (
<TradeOrderbookPanelContent <TradeOrderbookPanelContent
market={selectedMarket} market={selectedMarket}
asks={orderbook.asks} asks={orderbook.asks}
@@ -603,6 +592,15 @@ const VirtualTradingPage: React.FC<Props> = ({
tradeStrength={tradeStrength ?? undefined} tradeStrength={tradeStrength ?? undefined}
onRowClick={handleOrderbookRowClick} onRowClick={handleOrderbookRowClick}
/> />
) : (
<PaperTradeHistoryList
trades={trades}
strategyNames={strategyNames}
tickers={tickers}
onSelectMarket={handleSelectMarket}
emptyText="수동·자동 매매 체결 내역이 없습니다."
className="ptd-trade-history--fill"
/>
)} )}
</TradeRightPanelBody> </TradeRightPanelBody>
)} )}