83 lines
3.0 KiB
TypeScript
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 & 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;
|