모의투자 관리 기능 추가

This commit is contained in:
Macbook
2026-05-31 03:47:07 +09:00
parent 9d7dddfa57
commit 16d0e2c226
78 changed files with 4688 additions and 972 deletions
+62 -5
View File
@@ -36,6 +36,11 @@ import { getIndicatorListLabels } from '../utils/indicatorSettingsList';
import type { ChartType, Theme, Timeframe, ChartMode, IndicatorConfig } from '../types';
import { MarketSearchPanel } from './MarketSearchPanel';
import { getKoreanName } from '../utils/marketNameCache';
import {
formatChartLiveChangePct,
formatChartLivePrice,
type ChartLiveQuote,
} from '../utils/chartLiveQuote';
import LayoutPicker from './LayoutPicker';
import { DEFAULT_SYNC, type LayoutDef, type SyncOptions } from '../utils/layoutTypes';
import { loadStrategies } from '../utils/backendApi';
@@ -134,6 +139,11 @@ export interface ToolbarProps {
onOpenBulkIndicatorSettings?: () => void;
/** 지표 추가 팝업 항목 — 해당 지표 설정 모달 */
onOpenIndicatorSettings?: (type: string) => void;
/** 종목 선택 우측 시세 (실시간 차트 — 하단 upbit-bar 대체) */
showChartQuote?: boolean;
chartLiveQuote?: ChartLiveQuote | null;
chartQuoteLoading?: boolean;
chartQuoteError?: string | null;
}
// ─── SVG 아이콘 ────────────────────────────────────────────────────────────
@@ -800,6 +810,10 @@ const Toolbar: React.FC<ToolbarProps> = ({
liveStrategyActive = false,
onOpenBulkIndicatorSettings,
onOpenIndicatorSettings,
showChartQuote = false,
chartLiveQuote = null,
chartQuoteLoading = false,
chartQuoteError = null,
}) => {
const [showMarket, setShowMarket] = React.useState(false);
const [showTfMenu, setShowTfMenu] = React.useState(false);
@@ -854,6 +868,13 @@ const Toolbar: React.FC<ToolbarProps> = ({
setShowLayout(true);
}, [showLayout]);
const openMarketSearch = useCallback(() => {
setShowMarket(v => !v);
setShowTfMenu(false);
setShowCtMenu(false);
setShowIndMenu(false);
}, []);
return (
<>
{/* 종목 검색 패널 */}
@@ -869,18 +890,54 @@ const Toolbar: React.FC<ToolbarProps> = ({
{/* ── Left ─────────────────────────────────────────────────────────── */}
<div className="tv-toolbar-left">
{/* Symbol Search → 클릭 시 MarketSearchPanel 열기 */}
{/* 종목 검색(아이콘) · 선택 종목 표시 — 클릭 시 MarketSearchPanel */}
<div className="tv-sym-wrap">
<button
className="tv-sym-btn"
onClick={() => { setShowMarket(v => !v); setShowTfMenu(false); setShowCtMenu(false); setShowIndMenu(false); }}
title="종목 변경"
type="button"
className="tv-sym-btn tv-sym-btn--search"
onClick={openMarketSearch}
title="종목 검색"
aria-expanded={showMarket}
aria-haspopup="dialog"
>
<IcSearch />
<span className="tv-sym-text">{getKoreanName(symbol)}</span>
</button>
</div>
<div className="tv-toolbar-quote" aria-label="선택 종목">
<button
type="button"
className="tv-quote-ko"
onClick={openMarketSearch}
title="종목 변경"
aria-expanded={showMarket}
>
{getKoreanName(symbol)}
</button>
<span className="tv-quote-en">{symbol}</span>
{showChartQuote && chartQuoteLoading && (
<span className="tv-quote-status"> </span>
)}
{showChartQuote && chartQuoteError && (
<span className="tv-quote-status tv-quote-status--err"> {chartQuoteError}</span>
)}
{showChartQuote && !chartQuoteLoading && !chartQuoteError && chartLiveQuote?.price != null && (
<>
<span
className="tv-quote-price"
style={{ color: chartLiveQuote.isUp ? 'var(--up)' : 'var(--down)' }}
>
{formatChartLivePrice(chartLiveQuote.price)}
</span>
{chartLiveQuote.changePct != null && (
<span className={`tv-quote-change ${chartLiveQuote.isUp ? 'up' : 'down'}`}>
{formatChartLiveChangePct(chartLiveQuote.changePct)}
</span>
)}
</>
)}
</div>
{/* Compare */}
<button className="tv-icon-btn" title="워치리스트" onClick={onToggleWatch}>
<IcPlus />