가상투자 메뉴 기능 구현

This commit is contained in:
Macbook
2026-05-25 06:41:45 +09:00
parent 0cfe7fc84c
commit 8b373b11e3
33 changed files with 3897 additions and 99 deletions
@@ -0,0 +1,102 @@
import React, { useEffect, useState } from 'react';
import VirtualTargetCard from './VirtualTargetCard';
import VirtualStrategyChartPopup from './VirtualStrategyChartPopup';
import type { StrategyDto } from '../../utils/backendApi';
import type { Theme } from '../../types';
import type { VirtualTargetItem, VirtualCardViewMode } from '../../utils/virtualTradingStorage';
import { loadVirtualCardViewMode, saveVirtualCardViewMode } from '../../utils/virtualTradingStorage';
import type { VirtualLiveStatus } from '../../hooks/useVirtualTargetLiveStatus';
import type { VirtualIndicatorSnapshot } from '../../hooks/useVirtualIndicatorSnapshots';
interface Props {
targets: VirtualTargetItem[];
strategies: StrategyDto[];
snapshots: Record<string, VirtualIndicatorSnapshot>;
running: boolean;
globalStrategyId: number | null;
liveStatusByMarket?: Record<string, VirtualLiveStatus>;
theme?: Theme;
}
const VirtualTargetGrid: React.FC<Props> = ({
targets, strategies, snapshots, running, globalStrategyId, liveStatusByMarket = {}, theme = 'dark',
}) => {
const [viewMode, setViewMode] = useState<VirtualCardViewMode>(() => loadVirtualCardViewMode());
const [chartTarget, setChartTarget] = useState<{ market: string; strategy: StrategyDto | undefined } | null>(null);
useEffect(() => {
saveVirtualCardViewMode(viewMode);
}, [viewMode]);
if (targets.length === 0) {
return (
<div className="vtd-grid-empty">
<p className="vtd-muted"> .</p>
</div>
);
}
return (
<>
<div className="vtd-grid-wrap">
<div className="vtd-grid-head">
<span className="vtd-grid-head-title"> </span>
<div className="vtd-grid-head-right">
<div className="vtd-view-toggle" role="group" aria-label="카드 표시 방식">
<button
type="button"
className={`vtd-view-toggle-btn${viewMode === 'summary' ? ' vtd-view-toggle-btn--on' : ''}`}
onClick={() => setViewMode('summary')}
>
</button>
<button
type="button"
className={`vtd-view-toggle-btn${viewMode === 'detail' ? ' vtd-view-toggle-btn--on' : ''}`}
onClick={() => setViewMode('detail')}
>
</button>
</div>
{viewMode === 'detail' && (
<div className="vtd-grid-head-legend">
<span className="vtd-legend vtd-legend--match"> </span>
<span className="vtd-legend vtd-legend--pending"> </span>
<span className="vtd-legend vtd-legend--nomatch"> </span>
</div>
)}
{running && <span className="vtd-grid-live"> 3</span>}
</div>
</div>
<div className="vtd-grid">
{targets.map(t => {
const strat = strategies.find(s => s.id === (t.strategyId ?? globalStrategyId));
return (
<VirtualTargetCard
key={t.market}
market={t.market}
strategy={strat}
snapshot={snapshots[t.market]}
running={running}
liveStatus={liveStatusByMarket[t.market] ?? (running ? 'connecting' : 'idle')}
viewMode={viewMode}
onOpenChart={() => setChartTarget({ market: t.market, strategy: strat })}
/>
);
})}
</div>
</div>
{chartTarget && (
<VirtualStrategyChartPopup
market={chartTarget.market}
strategy={chartTarget.strategy}
theme={theme}
running={running}
onClose={() => setChartTarget(null)}
/>
)}
</>
);
};
export default VirtualTargetGrid;