import React, { useMemo } from 'react'; import { getKoreanName } from '../../utils/marketNameCache'; import type { TrendSearchResultDto } from '../../utils/trendSearchApi'; import { tfLabelShort } from '../../utils/trendSearchMetrics'; import type { Theme } from '../../types'; import type { ChartRealtimeSource } from '../../hooks/useChartRealtimeData'; import type { TickerData } from '../../hooks/useMarketTicker'; import VirtualTargetQuote from '../virtual/VirtualTargetQuote'; import VirtualLiveBadge from '../virtual/VirtualLiveBadge'; import TrendSearchCardSignalPanel from './TrendSearchCardSignalPanel'; import TrendSearchCardChart from './TrendSearchCardChart'; import TrendSearchTargetActions from './TrendSearchTargetActions'; export type TrendSearchDisplayMode = 'summary' | 'chart'; interface Props { result: TrendSearchResultDto; timeframe: string; displayMode: TrendSearchDisplayMode; selected?: boolean; onSelect?: () => void; theme?: Theme; chartRealtimeSource?: ChartRealtimeSource; chartCandleAreaPriceLabels?: boolean; chartSeriesPriceLabels?: boolean; chartPaneSeparator?: import('../../types/chartPaneSeparator').ChartPaneSeparatorOptions; ticker?: TickerData; updatedAt?: number; flash?: boolean; inTargets?: boolean; targetPinned?: boolean; targetBusy?: boolean; onAddTarget?: () => void; onRemoveTarget?: () => void; } function resultToTicker(result: TrendSearchResultDto): TickerData { const rate = result.changeRate / 100; return { market: result.market, koreanName: result.koreanName, tradePrice: result.currentPrice, changeRate: rate, changePrice: null, accTradePrice24: 0, accTradeVolume24: null, openingPrice: null, highPrice: null, lowPrice: null, change: result.changeRate > 0 ? 'RISE' : result.changeRate < 0 ? 'FALL' : 'EVEN', }; } const TrendSearchResultCard: React.FC = ({ result, timeframe, displayMode, selected = false, onSelect, theme = 'dark', chartRealtimeSource = 'BACKEND_STOMP', chartCandleAreaPriceLabels = true, chartSeriesPriceLabels = true, chartPaneSeparator, ticker, updatedAt, flash = false, inTargets = false, targetPinned = false, targetBusy = false, onAddTarget, onRemoveTarget, }) => { const en = result.market.replace(/^KRW-/, ''); const quoteTicker = ticker ?? resultToTicker(result); const ko = quoteTicker.koreanName || getKoreanName(result.market) || en; const isChart = displayMode === 'chart'; const flashCls = useMemo(() => { if (!flash) return ''; return result.changeRate >= 0 ? 'tsd-card--flash-up' : 'tsd-card--flash-down'; }, [flash, result.changeRate]); return (
{ if (onSelect && (e.key === 'Enter' || e.key === ' ')) { e.preventDefault(); onSelect(); } }} role={onSelect ? 'button' : undefined} tabIndex={onSelect ? 0 : undefined} aria-pressed={onSelect ? selected : undefined} >
{ko} {en}
시간봉 {tfLabelShort(timeframe)} {result.matchRate}점 {result.highMatch && HIGH MATCH}
{(onAddTarget || onRemoveTarget) && (
onAddTarget?.()} onRemove={() => onRemoveTarget?.()} />
)}
{!isChart && ( 추세점수 {result.matchedCount}/{result.totalConditions} )}
{isChart ? ( ) : ( )}
); }; export default TrendSearchResultCard;