kcg-monitoring/frontend/src/components/common/LiveControls.tsx
htlee 7fa4e2bfb1 feat: 센서 그래프 개선 + 지진 마커 + 시설 아이콘 정렬 + SSH 재시도 v2
- SensorChart: 히스토리 1H/2H/3H/6H, 기압 SLP 보정, 데이터 범위 확장(y축 시작)
- SensorChart Tooltip: KST 시간 포맷, 위치 상단 고정, 스타일 통일
- 지진 포인트 클릭 → 지도 flyTo + SeismicMarker 진도별 펄스 원형 표시
- SatelliteMap flyTo 지원 추가
- OilFacilityLayer: planned ring SVG 내부로 이동 (아이콘 중심 정렬 수정)
- 밝은 테마 text-shadow CSS 변수 분리 (dark/light)
- deploy.yml: SSH SCP+실행 각 3회 재시도 (kex_exchange 거부 대응)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 11:02:55 +09:00

81 lines
2.3 KiB
TypeScript

import { useTranslation } from 'react-i18next';
interface Props {
currentTime: number;
historyMinutes: number;
onHistoryChange: (minutes: number) => void;
aircraftCount: number;
shipCount: number;
satelliteCount: number;
timeZone: 'KST' | 'UTC';
onTimeZoneChange: (tz: 'KST' | 'UTC') => void;
}
const HISTORY_PRESETS = [
{ label: '1H', minutes: 60 },
{ label: '2H', minutes: 120 },
{ label: '3H', minutes: 180 },
{ label: '6H', minutes: 360 },
];
function formatTime(epoch: number, tz: 'KST' | 'UTC'): string {
const d = new Date(epoch);
const pad = (n: number) => String(n).padStart(2, '0');
if (tz === 'UTC') {
return `${d.getUTCFullYear()}-${pad(d.getUTCMonth() + 1)}-${pad(d.getUTCDate())} ${pad(d.getUTCHours())}:${pad(d.getUTCMinutes())}:${pad(d.getUTCSeconds())}`;
}
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
}
export function LiveControls({
currentTime,
historyMinutes,
onHistoryChange,
timeZone,
onTimeZoneChange,
}: Props) {
const { t } = useTranslation();
return (
<div className="live-controls">
<div className="live-indicator">
<span className="live-dot" />
<span className="live-label">{t('header.live')}</span>
</div>
<div className="live-clock" style={{ display: 'flex', alignItems: 'center', gap: '6px' }}>
<span>{formatTime(currentTime, timeZone)}</span>
<div className="tz-radio-group">
{(['KST', 'UTC'] as const).map(tz => (
<button
key={tz}
type="button"
className={`tz-radio-btn ${timeZone === tz ? 'active' : ''}`}
onClick={() => onTimeZoneChange(tz)}
>
{tz}
</button>
))}
</div>
</div>
<div className="flex-1" />
<div className="history-controls">
<span className="history-label">{t('time.history')}</span>
<div className="history-presets">
{HISTORY_PRESETS.map(p => (
<button
key={p.label}
className={`history-btn ${historyMinutes === p.minutes ? 'active' : ''}`}
onClick={() => onHistoryChange(p.minutes)}
>
{p.label}
</button>
))}
</div>
</div>
</div>
);
}