구조적인 부분 수정
This commit is contained in:
@@ -10,7 +10,7 @@ import './Dashboard.css'
|
||||
const formatTime = (isoStr?: string) => {
|
||||
if (!isoStr) return null;
|
||||
const d = new Date(isoStr);
|
||||
return d.toLocaleTimeString('ko-KR', { hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false });
|
||||
return d.toLocaleTimeString('ko-KR', { hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false, timeZone: 'Asia/Seoul' });
|
||||
};
|
||||
|
||||
// 마켓 운영 시간 체크 (한국장 + 미국장)
|
||||
@@ -70,10 +70,11 @@ const Dashboard = () => {
|
||||
const { data: mcpStatus } = useQuery({
|
||||
queryKey: ['mcp-status'],
|
||||
queryFn: async () => (await api.get('/mcp/status')).data,
|
||||
refetchInterval: isMarketOpen ? 5000 : false,
|
||||
refetchInterval: isMarketOpen ? 30000 : false, // 5초 → 30초 (MCP 상태는 빈번한 폴링 불필요)
|
||||
staleTime: 20000,
|
||||
})
|
||||
|
||||
const { data: ruleBasedRecommendations } = useQuery({
|
||||
const { data: ruleBasedRecommendations, refetch: refetchRecommendations, isError: isRecommendationsError } = useQuery({
|
||||
queryKey: ['rule-based-recommendations'],
|
||||
queryFn: async () => {
|
||||
const response = await api.get('/recommendations/rule-based', {
|
||||
@@ -95,8 +96,9 @@ const Dashboard = () => {
|
||||
|
||||
return data;
|
||||
},
|
||||
refetchInterval: isMarketOpen ? 600000 : false, // 10분마다 배치 실행
|
||||
// 장외에도 초기 로드는 실행 (DB 조회는 빠름)
|
||||
refetchInterval: isMarketOpen ? 120000 : 300000, // 장중 2분, 장외 5분
|
||||
staleTime: isMarketOpen ? 110000 : 290000, // refetchInterval보다 약간 짧게 → 재진입 시 즉시 표시
|
||||
refetchOnWindowFocus: false, // 탭 전환 시 불필요한 재요청 방지 (자동매매 중복 트리거 차단)
|
||||
})
|
||||
|
||||
const { data: chartData, isLoading: chartLoading } = useQuery({
|
||||
@@ -201,13 +203,11 @@ const Dashboard = () => {
|
||||
}
|
||||
}, [wsMessages, queryClient])
|
||||
|
||||
// 보유종목 + 분석종목 전체 코드 수집 (현재가 일괄 조회용)
|
||||
// 보유종목 코드만 수집 (현재가 일괄 실시간 조회용)
|
||||
// 분석종목은 stock_analysis.current_price (DB값)를 폴백으로 사용하므로 실시간 조회 불필요
|
||||
const allStockCodes = useMemo(() => {
|
||||
const codes = new Set<string>()
|
||||
;(portfolio || []).forEach((s: any) => codes.add(s.stock_code))
|
||||
;(ruleBasedRecommendations?.analyzed_stock_list || []).forEach((s: any) => codes.add(s.stock_code))
|
||||
return Array.from(codes)
|
||||
}, [portfolio, ruleBasedRecommendations])
|
||||
return (portfolio || []).map((s: any) => s.stock_code)
|
||||
}, [portfolio])
|
||||
|
||||
// 현재가 일괄 실시간 조회 (보유종목·분석종목 동기화)
|
||||
const { data: livePrices } = useQuery({
|
||||
@@ -698,10 +698,23 @@ const Dashboard = () => {
|
||||
<span className="text-xs bg-purple-500/20 text-purple-300 px-2 py-0.5 rounded-full">
|
||||
{ruleBasedRecommendations?.analyzed_stocks || 0}
|
||||
</span>
|
||||
<button
|
||||
onClick={() => refetchRecommendations()}
|
||||
className="p-1 hover:bg-gray-700/50 rounded transition-colors"
|
||||
title="분석 종목 새로고침"
|
||||
>
|
||||
<RefreshCw className="w-3 h-3 text-gray-400 hover:text-purple-400" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="divide-y divide-gray-700/50 overflow-y-auto custom-scrollbar flex-1">
|
||||
{ruleBasedRecommendations === undefined ? (
|
||||
{isRecommendationsError ? (
|
||||
<div className="h-full flex flex-col items-center justify-center text-red-500 text-xs p-4 text-center">
|
||||
<p>⚠️ API 오류</p>
|
||||
<p className="text-[10px] text-gray-500 mt-1">백엔드 로그 확인 필요</p>
|
||||
<button onClick={() => refetchRecommendations()} className="mt-2 px-2 py-1 bg-red-500/20 text-red-400 rounded text-[10px]">재시도</button>
|
||||
</div>
|
||||
) : ruleBasedRecommendations === undefined ? (
|
||||
<div className="h-full flex flex-col items-center justify-center text-gray-500 text-xs p-4">
|
||||
<RefreshCw className="w-6 h-6 animate-spin mb-2 text-purple-400" />
|
||||
<p>분석 종목 로딩 중...</p>
|
||||
@@ -790,8 +803,8 @@ const Dashboard = () => {
|
||||
{journals && journals.length > 0 ? (
|
||||
journals.map((journal: any) => {
|
||||
const journalDate = new Date(journal.journal_date || journal.created_at);
|
||||
const dateStr = journalDate.toLocaleDateString('ko-KR', { month: '2-digit', day: '2-digit' });
|
||||
const timeStr = journalDate.toLocaleTimeString('ko-KR', { hour: '2-digit', minute: '2-digit', hour12: false });
|
||||
const dateStr = journalDate.toLocaleDateString('ko-KR', { month: '2-digit', day: '2-digit', timeZone: 'Asia/Seoul' });
|
||||
const timeStr = journalDate.toLocaleTimeString('ko-KR', { hour: '2-digit', minute: '2-digit', hour12: false, timeZone: 'Asia/Seoul' });
|
||||
const isGoodMood = journal.mood === '좋음' || journal.mood === 'good' || journal.mood === 'positive';
|
||||
const isBadMood = journal.mood === '나쁨' || journal.mood === 'bad' || journal.mood === 'negative';
|
||||
const tags: string[] = Array.isArray(journal.tags) ? journal.tags : [];
|
||||
|
||||
Reference in New Issue
Block a user