71 lines
2.6 KiB
TypeScript
71 lines
2.6 KiB
TypeScript
import React from 'react';
|
|
import type { StrategyDto } from '../../lib/shared';
|
|
import type { VirtualIndicatorSnapshot } from '@frontend/hooks/useVirtualIndicatorSnapshots';
|
|
import type { VirtualLiveStatus } from '@frontend/hooks/useVirtualTargetLiveStatus';
|
|
import type { VirtualTargetItem } from '@frontend/utils/virtualTradingStorage';
|
|
import { resolveVirtualTargetStrategyId } from '@frontend/utils/virtualTargetStrategy';
|
|
import type { TradeSide } from '../../contexts/NavigationContext';
|
|
|
|
interface Props {
|
|
target: VirtualTargetItem;
|
|
globalStrategyId: number | null;
|
|
strategies: StrategyDto[];
|
|
snapshot?: VirtualIndicatorSnapshot;
|
|
liveStatus?: VirtualLiveStatus;
|
|
onDetail: () => void;
|
|
onTrade: (side: TradeSide) => void;
|
|
onTogglePin: () => void;
|
|
onRemove: () => void;
|
|
}
|
|
|
|
export default function VirtualTargetListRow({
|
|
target,
|
|
globalStrategyId,
|
|
strategies,
|
|
snapshot,
|
|
liveStatus,
|
|
onDetail,
|
|
onTrade,
|
|
onTogglePin,
|
|
onRemove,
|
|
}: Props) {
|
|
const strategyId = resolveVirtualTargetStrategyId(target, globalStrategyId);
|
|
const strategyName = strategies.find(s => s.id === strategyId)?.name ?? '전략 없음';
|
|
const matchPct = snapshot?.matchRate ?? 0;
|
|
|
|
return (
|
|
<article className="mobile-list-row card">
|
|
<div className="mobile-list-row-main">
|
|
<div className="mobile-list-row-info">
|
|
<div className="mobile-list-row-title">
|
|
<span className={`live-dot ${liveStatus ?? 'idle'}`} aria-hidden />
|
|
<span>{target.koreanName ?? target.market}</span>
|
|
</div>
|
|
<div className="text-muted mobile-list-row-meta">
|
|
{target.market} · {strategyName} · {matchPct.toFixed(0)}%
|
|
</div>
|
|
</div>
|
|
<div className="mobile-list-row-pin">
|
|
<button type="button" className="icon-btn" onClick={onTogglePin} aria-label="고정">
|
|
{target.pinned ? '📌' : '○'}
|
|
</button>
|
|
{!target.pinned && (
|
|
<button type="button" className="icon-btn icon-btn--danger" onClick={onRemove} aria-label="삭제">✕</button>
|
|
)}
|
|
</div>
|
|
</div>
|
|
<div className="mobile-list-row-actions">
|
|
<button type="button" className="btn-secondary mobile-action-btn" onClick={onDetail}>
|
|
상세보기
|
|
</button>
|
|
<button type="button" className="btn-primary mobile-action-btn mobile-action-btn--buy" onClick={() => onTrade('BUY')}>
|
|
매수
|
|
</button>
|
|
<button type="button" className="btn-danger mobile-action-btn" onClick={() => onTrade('SELL')}>
|
|
매도
|
|
</button>
|
|
</div>
|
|
</article>
|
|
);
|
|
}
|