목록 로딩 로직 개선
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import React, { useMemo, useState } from 'react';
|
||||
import { VirtualScroll } from '../common/VirtualScroll';
|
||||
import type { BacktestResultRecord } from '../../utils/backendApi';
|
||||
import type { LiveExecutionItem } from '../../utils/liveExecutionGroups';
|
||||
import BacktestSparkline from './BacktestSparkline';
|
||||
@@ -164,9 +165,14 @@ export default function BacktestExecutionList({
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="btd-exec-scroll">
|
||||
{tab === 'backtest' ? (
|
||||
filteredBacktests.length === 0 ? (
|
||||
{tab === 'backtest' ? (
|
||||
<VirtualScroll
|
||||
className="btd-exec-scroll vl-scroll"
|
||||
items={filteredBacktests}
|
||||
estimateSize={88}
|
||||
measureDynamic
|
||||
getItemKey={r => r.id ?? r.createdAt}
|
||||
empty={(
|
||||
<div className="btd-sidebar-empty">
|
||||
{normalizedQuery ? (
|
||||
<p>'{query}'에 해당하는 결과가 없습니다.</p>
|
||||
@@ -177,51 +183,62 @@ export default function BacktestExecutionList({
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
filteredBacktests.map(r => {
|
||||
const ret = r.totalReturn ?? 0;
|
||||
const positive = ret >= 0;
|
||||
const active = selectedBacktestId === r.id;
|
||||
const market = toUpbitMarket(r.symbol);
|
||||
const ko = getKoreanName(market);
|
||||
const strategy = r.strategyName || '전략 없음';
|
||||
return (
|
||||
<button key={r.id} type="button" className={`btd-history-card${active ? ' btd-history-card--active' : ''}`} onClick={() => onSelectBacktest(r)}>
|
||||
<div className="btd-history-card-row">
|
||||
<div className="btd-history-card-main">
|
||||
<span className="btd-history-ko">{ko}</span>
|
||||
<span className="btd-history-strategy">{strategy} · {formatTimeframeKo(r.timeframe)}</span>
|
||||
<span className="btd-history-date">{fmtListTimestamp(r.createdAt)}</span>
|
||||
</div>
|
||||
<div className="btd-history-card-side">
|
||||
<BacktestSparkline curve={parseBacktestSpark(r)} positive={positive} width={56} height={22} />
|
||||
<span className="btd-history-win">승률 {pctAbs(r.winRate ?? 0)}</span>
|
||||
<span className={`btd-history-roi${positive ? ' up' : ' down'}`}>총 수익률 {pct(ret)}</span>
|
||||
</div>
|
||||
)}
|
||||
aria-label="백테스팅 실행 목록"
|
||||
>
|
||||
{r => {
|
||||
const ret = r.totalReturn ?? 0;
|
||||
const positive = ret >= 0;
|
||||
const active = selectedBacktestId === r.id;
|
||||
const market = toUpbitMarket(r.symbol);
|
||||
const ko = getKoreanName(market);
|
||||
const strategy = r.strategyName || '전략 없음';
|
||||
return (
|
||||
<button type="button" className={`btd-history-card${active ? ' btd-history-card--active' : ''}`} onClick={() => onSelectBacktest(r)}>
|
||||
<div className="btd-history-card-row">
|
||||
<div className="btd-history-card-main">
|
||||
<span className="btd-history-ko">{ko}</span>
|
||||
<span className="btd-history-strategy">{strategy} · {formatTimeframeKo(r.timeframe)}</span>
|
||||
<span className="btd-history-date">{fmtListTimestamp(r.createdAt)}</span>
|
||||
</div>
|
||||
</button>
|
||||
);
|
||||
})
|
||||
)
|
||||
) : filteredLiveItems.length === 0 ? (
|
||||
<div className="btd-sidebar-empty">
|
||||
{normalizedQuery ? (
|
||||
<p>'{query}'에 해당하는 결과가 없습니다.</p>
|
||||
) : (
|
||||
<>
|
||||
<p>실시간 매매 이력이 없습니다.</p>
|
||||
<p className="btd-sidebar-hint">가상투자 화면에서 자동·수동<br />매매를 실행하세요.</p>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
filteredLiveItems.map(item => {
|
||||
<div className="btd-history-card-side">
|
||||
<BacktestSparkline curve={parseBacktestSpark(r)} positive={positive} width={56} height={22} />
|
||||
<span className="btd-history-win">승률 {pctAbs(r.winRate ?? 0)}</span>
|
||||
<span className={`btd-history-roi${positive ? ' up' : ' down'}`}>총 수익률 {pct(ret)}</span>
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
);
|
||||
}}
|
||||
</VirtualScroll>
|
||||
) : (
|
||||
<VirtualScroll
|
||||
className="btd-exec-scroll vl-scroll"
|
||||
items={filteredLiveItems}
|
||||
estimateSize={88}
|
||||
measureDynamic
|
||||
getItemKey={item => item.id}
|
||||
empty={(
|
||||
<div className="btd-sidebar-empty">
|
||||
{normalizedQuery ? (
|
||||
<p>'{query}'에 해당하는 결과가 없습니다.</p>
|
||||
) : (
|
||||
<>
|
||||
<p>실시간 매매 이력이 없습니다.</p>
|
||||
<p className="btd-sidebar-hint">가상투자 화면에서 자동·수동<br />매매를 실행하세요.</p>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
aria-label="실시간 매매 목록"
|
||||
>
|
||||
{item => {
|
||||
const positive = item.totalReturnPct >= 0;
|
||||
const active = selectedLiveId === item.id;
|
||||
const market = toUpbitMarket(item.symbol);
|
||||
const ko = getKoreanName(market);
|
||||
return (
|
||||
<button key={item.id} type="button" className={`btd-history-card${active ? ' btd-history-card--active' : ''}`} onClick={() => onSelectLive(item)}>
|
||||
<button type="button" className={`btd-history-card${active ? ' btd-history-card--active' : ''}`} onClick={() => onSelectLive(item)}>
|
||||
<div className="btd-history-card-row">
|
||||
<div className="btd-history-card-main">
|
||||
<span className="btd-history-ko">{ko}</span>
|
||||
@@ -236,9 +253,9 @@ export default function BacktestExecutionList({
|
||||
</div>
|
||||
</button>
|
||||
);
|
||||
})
|
||||
)}
|
||||
</div>
|
||||
}}
|
||||
</VirtualScroll>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user