Files
goldenChart/frontend/src/components/trendSearch/TrendSearchCardSignalPanel.tsx
T
2026-05-29 00:46:20 +09:00

83 lines
3.0 KiB
TypeScript

import React, { useMemo } from 'react';
import { useSignalVisualRailHeight } from '../../hooks/useSignalVisualRailHeight';
import type { TrendSearchConditionDto } from '../../utils/trendSearchApi';
import { computeMatchRate, getTrafficLightState } from '../../utils/virtualSignalMetrics';
import { buildTrendSearchMetrics } from '../../utils/trendSearchMetrics';
import VirtualSignalEqualizer from '../virtual/VirtualSignalEqualizer';
import VirtualConditionList from '../virtual/VirtualConditionList';
import VirtualSignalTrafficLight from '../virtual/VirtualSignalTrafficLight';
import VirtualIndicatorCompareTable from '../virtual/VirtualIndicatorCompareTable';
interface Props {
conditions: TrendSearchConditionDto[];
matchRate: number;
matchedCount: number;
totalConditions: number;
updatedAt?: number;
}
const TrendSearchCardSignalPanel: React.FC<Props> = ({
conditions,
matchRate,
matchedCount,
totalConditions,
updatedAt,
}) => {
const metrics = useMemo(() => buildTrendSearchMetrics(conditions), [conditions]);
const resolvedMatch = useMemo(
() => computeMatchRate(metrics, matchRate),
[metrics, matchRate],
);
const trafficState = useMemo(
() => getTrafficLightState(resolvedMatch, metrics),
[resolvedMatch, metrics],
);
const { visualRef, lightRef } = useSignalVisualRailHeight([
conditions.length,
resolvedMatch,
trafficState,
]);
if (conditions.length === 0) {
return <p className="vtd-muted vtd-card-empty">조건 데이터 없음</p>;
}
const updatedLabel = updatedAt
? new Date(updatedAt).toLocaleTimeString('ko-KR', {
hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false,
})
: '—';
return (
<div className="vtd-sig-panel-wrap vtd-sig-panel-wrap--detail">
<div className="vtd-sig-panel vtd-sig-panel--detail-top">
<div className="vtd-sig-panel-title">SIGNAL INTELLIGENCE &amp; MATCH RATES</div>
<div className="vtd-sig-visual" ref={visualRef}>
<div className="vtd-sig-visual-pane vtd-sig-visual-eq">
<VirtualSignalEqualizer matchRate={resolvedMatch} />
</div>
<div className="vtd-sig-visual-pane vtd-sig-visual-heat">
<VirtualConditionList metrics={metrics} />
</div>
<div className="vtd-sig-visual-pane vtd-sig-visual-light">
<div ref={lightRef} className="vtd-sig-light-rail">
<VirtualSignalTrafficLight state={trafficState} matchRate={resolvedMatch} />
</div>
</div>
</div>
</div>
<div className="vtd-sig-detail-table" aria-label="지표별 상태 목록">
<VirtualIndicatorCompareTable metrics={metrics} showStrategyCondition={false} />
</div>
<div className="vtd-card-foot">
<span className="vtd-card-updated">갱신 {updatedLabel}</span>
<span className="vtd-card-match-summary">
{matchedCount}/{totalConditions} 조건 · 일치율 {resolvedMatch}%
</span>
</div>
</div>
);
};
export default TrendSearchCardSignalPanel;