/** * JournalPanel.tsx — 매매내역 및 매매일지 탭 패널 컴포넌트 * * 역할: * - 매매내역 탭: 최근 거래 이력 표시 (매수/매도, 손익, 체결 시간, 사유) * - 매매일지 탭: AI 자동 생성 매매일지 표시 (기분, 태그, 내용) * - 미체결 주문 정리 버튼 제공 * * 최적화: * - React.memo로 불필요한 리렌더 방지 * * 데이터 흐름: * - journals: react-query 'journals' 쿼리 결과 * - tradeHistory: react-query 'trade-history' 쿼리 결과 */ import React, { useMemo, useState } from 'react' import { BookOpen, ArrowUpDown } from 'lucide-react' import { Journal, TradeHistory } from '../types/trading' import { parseKst } from '../utils/formatUtils' const kstDateKey = (d: Date | null): string => d ? d.toLocaleDateString('sv-SE', { timeZone: 'Asia/Seoul' }) : '' const kstDateLabel = (d: Date | null, todayKey: string): string => { if (!d) return '날짜 미상' const key = kstDateKey(d) const md = d.toLocaleDateString('ko-KR', { month: '2-digit', day: '2-digit', weekday: 'short', timeZone: 'Asia/Seoul' }) return key === todayKey ? `${md} (오늘)` : md } /** JournalPanel Props 정의 */ interface JournalPanelProps { /** 매매일지 배열 (장 마감 후 자동 생성) */ journals: Journal[] | undefined /** 매매 내역 배열 (최근 거래 이력) */ tradeHistory?: TradeHistory[] | undefined /** 미체결 주문 정리 콜백 */ onResetPending: () => void /** true면 미체결 정리 버튼 비활성화 (예: 사용자 모드 확정 전) */ resetDisabled?: boolean } /** * JournalPanel — 매매내역/매매일지 탭 전환 패널 * * 상태(State): * - tab: 현재 활성 탭 ('trades' 또는 'journal') */ const JournalPanel = React.memo(({ journals, tradeHistory, onResetPending, resetDisabled }: JournalPanelProps) => { const [tab, setTab] = useState<'trades' | 'journal'>('trades') // 최신순 정렬 (created_at/executed_at 기준, KST 보정). 시각 파싱 불가 건은 id 로 보조 정렬 const sortedTrades = useMemo(() => { const todayKey = kstDateKey(new Date()) return (tradeHistory ?? []) .map((t: TradeHistory) => { const dt = parseKst(t.created_at || t.executed_at || '') return { t, dt, key: kstDateKey(dt) } }) .sort((a, b) => { const d = (b.dt?.getTime() ?? 0) - (a.dt?.getTime() ?? 0) return d !== 0 ? d : Number(b.t.id || 0) - Number(a.t.id || 0) }) .map((x, i, arr) => ({ ...x, // 날짜가 바뀌는 첫 행에 구분선 라벨 header: i === 0 || arr[i - 1].key !== x.key ? kstDateLabel(x.dt, todayKey) : null, })) }, [tradeHistory]) return (