import { useState, useEffect, useCallback } from "react";
const API = "http://192.168.0.60:8989/api";
const SENTIMENT_MAP = { "호재": { color: "#00E676", icon: "▲", bg: "rgba(0,230,118,0.08)" }, "악재": { color: "#FF5252", icon: "▼", bg: "rgba(255,82,82,0.08)" }, "중립": { color: "#78909C", icon: "●", bg: "rgba(120,144,156,0.06)" } };
const REC_MAP = { "강력매수": { color: "#00E676", weight: 900 }, "매수관심": { color: "#69F0AE", weight: 700 }, "관망": { color: "#78909C", weight: 500 }, "매도관심": { color: "#FF8A80", weight: 700 }, "강력매도": { color: "#FF5252", weight: 900 } };
function useFetch(endpoint, interval = 60000) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const load = useCallback(() => {
fetch(`${API}${endpoint}`).then(r => r.json()).then(d => { setData(d); setLoading(false); }).catch(() => setLoading(false));
}, [endpoint]);
useEffect(() => { load(); const t = setInterval(load, interval); return () => clearInterval(t); }, [load, interval]);
return { data, loading, reload: load };
}
function formatTime(ts) {
if (!ts) return "-";
const d = new Date(ts);
const now = new Date();
const diff = (now - d) / 1000 / 60;
if (diff < 60) return `${Math.floor(diff)}분 전`;
if (diff < 1440) return `${Math.floor(diff / 60)}시간 전`;
return d.toLocaleDateString("ko-KR", { month: "short", day: "numeric" });
}
function ScoreBar({ value, max = 100 }) {
const pct = Math.min(100, Math.max(0, ((value + max) / (2 * max)) * 100));
const color = value >= 30 ? "#00E676" : value >= -30 ? "#78909C" : "#FF5252";
return (
);
}
function SummaryCard({ icon, label, value, sub, color }) {
return (
{icon} {label}
{value}
{sub &&
{sub}
}
);
}
function Header({ onRefresh }) {
const [time, setTime] = useState(new Date());
useEffect(() => { const t = setInterval(() => setTime(new Date()), 1000); return () => clearInterval(t); }, []);
return (
◆ Trading AI Dashboard
뉴스 + DART 공시 기반 종목 분석 시스템
{time.toLocaleString("ko-KR", { hour: "2-digit", minute: "2-digit", second: "2-digit" })}
);
}
function RecommendationCard({ item }) {
const rec = REC_MAP[item.recommendation] || REC_MAP["관망"];
return (
{item.stock_name}
{item.stock_code}
{item.recommendation}
종합 {item.total_score?.toFixed(1)}
뉴스 {item.news_score?.toFixed(0)}
공시 {item.dart_score?.toFixed(0)}
가격 {item.price_score?.toFixed(0)}
{item.top_reasons && (
{item.top_reasons.split("|")[0]?.trim().substring(0, 120)}...
)}
);
}
function NewsItem({ item }) {
const s = SENTIMENT_MAP[item.sentiment] || SENTIMENT_MAP["중립"];
return (
{s.icon}
{item.intensity || 0}
{item.title}
{item.primary_stock && {item.primary_stock}}
{item.source}
{formatTime(item.analyzed_at)}
{item.reason &&
{item.reason.substring(0, 100)}
}
{item.investment_action || "관망"}
);
}
function AlertItem({ item }) {
const s = SENTIMENT_MAP[item.sentiment] || SENTIMENT_MAP["중립"];
return (
{s.icon} {item.sentiment} (강도 {item.intensity})
{formatTime(item.analyzed_at)}
{item.title}
{item.reason?.substring(0, 100)}
);
}
function Tab({ active, label, onClick }) {
return (
);
}
export default function Dashboard() {
const [tab, setTab] = useState("overview");
const { data: summary, reload: r1 } = useFetch("/summary", 30000);
const { data: ranking } = useFetch("/ranking", 60000);
const { data: recs, reload: r2 } = useFetch("/recommendations", 60000);
const { data: recent } = useFetch("/recent?limit=30", 30000);
const { data: alerts } = useFetch("/alerts", 30000);
const { data: timeline } = useFetch("/timeline?hours=48", 60000);
const refresh = () => { r1(); r2(); };
return (
{/* Tabs */}
setTab("overview")} />
setTab("recommend")} />
setTab("news")} />
setTab("alerts")} />
{/* Overview */}
{tab === "overview" && (
{/* Summary Cards */}
{/* Two column: Ranking + Recent */}
{/* Ranking */}
🏆 종목 랭킹 (최신)
{ranking && ranking.length > 0 ? ranking.slice(0, 10).map((item, i) => (
{i + 1}
{item.stock_name}
{item.stock_code}
{item.total_score?.toFixed(1)}
{item.recommendation}
)) :
데이터 수집 중...
}
{/* Recent News */}
📰 최근 분석
{recent && recent.length > 0 ? recent.slice(0, 15).map((item, i) => (
)) :
데이터 수집 중...
}
)}
{/* Recommendations */}
{tab === "recommend" && (
뉴스 감성 + DART 공시 + 가격 모멘텀 기반 종합 점수
{recs && recs.length > 0 ? recs.map((item, i) => (
)) :
추천 데이터가 아직 없습니다. 16:30 자동 산출 후 업데이트됩니다.
}
)}
{/* News Feed */}
{tab === "news" && (
전체 뉴스 피드
{recent && recent.length > 0 ? recent.map((item, i) => (
)) :
뉴스 데이터 수집 중...
}
)}
{/* Alerts */}
{tab === "alerts" && (
강도 3 이상 뉴스/공시 (최근 24시간)
{alerts && alerts.length > 0 ? alerts.map((item, i) => (
)) :
현재 긴급 알림이 없습니다.
}
)}
{/* Footer */}
Trading AI System • 뉴스/공시 기반 자동 분석 • 투자 판단의 참고 자료로만 활용하세요
);
}