diff --git a/src/api.js b/src/api.js index 6f20c45..d132658 100644 --- a/src/api.js +++ b/src/api.js @@ -69,3 +69,7 @@ export function triggerStockScrap() { export function getStockHealth() { return apiGet("/api/stock/health"); } + +export function getStockIndices() { + return apiGet("/api/stock/indices"); +} diff --git a/src/pages/stock/Stock.css b/src/pages/stock/Stock.css index 4b53c7f..6db4d54 100644 --- a/src/pages/stock/Stock.css +++ b/src/pages/stock/Stock.css @@ -176,6 +176,12 @@ background: rgba(0, 0, 0, 0.2); } +.stock-snapshot__card.is-highlight { + border-color: rgba(255, 255, 255, 0.4); + background: rgba(255, 255, 255, 0.05); + box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.08); +} + .stock-snapshot__card p { margin: 0; font-size: 12px; @@ -193,6 +199,19 @@ font-size: 12px; } +.stock-snapshot__change { + font-size: 12px; + color: var(--muted); +} + +.stock-snapshot__change.is-up { + color: #f3a7a7; +} + +.stock-snapshot__change.is-down { + color: #9fc5ff; +} + .stock-schedule { display: grid; gap: 12px; diff --git a/src/pages/stock/Stock.jsx b/src/pages/stock/Stock.jsx index 3d9300b..7f830a7 100644 --- a/src/pages/stock/Stock.jsx +++ b/src/pages/stock/Stock.jsx @@ -1,6 +1,7 @@ import React, { useEffect, useMemo, useState } from 'react'; import { getStockHealth, + getStockIndices, getStockNews, triggerStockScrap, } from '../../api'; @@ -34,6 +35,10 @@ const Stock = () => { status: 'unknown', message: '', }); + const [indices, setIndices] = useState([]); + const [indicesLoading, setIndicesLoading] = useState(false); + const [indicesAt, setIndicesAt] = useState(''); + const [autoRefreshMs] = useState(180000); const latestCrawled = useMemo( () => getLatestBy(news, 'crawled_at'), @@ -72,6 +77,20 @@ const Stock = () => { } }; + const loadIndices = async () => { + setIndicesLoading(true); + setError(''); + try { + const data = await getStockIndices(); + setIndices(Array.isArray(data?.indices) ? data.indices : []); + setIndicesAt(data?.crawled_at ?? ''); + } catch (err) { + setError(err?.message ?? String(err)); + } finally { + setIndicesLoading(false); + } + }; + const onScrap = async () => { setScraping(true); setError(''); @@ -94,7 +113,12 @@ const Stock = () => { useEffect(() => { loadHealth(); - }, []); + loadIndices(); + const timer = window.setInterval(() => { + loadIndices(); + }, autoRefreshMs); + return () => window.clearInterval(timer); + }, [autoRefreshMs]); return (
@@ -160,18 +184,71 @@ const Stock = () => {

Snapshot

시장 스냅샷

- 지수/가격 API 연동을 위한 준비 구간입니다. + 주요 지표의 현재가와 등락을 빠르게 확인합니다.

+
+ {indicesLoading ? ( + 갱신 중 + ) : null} + {indicesAt ? ( + {indicesAt} + ) : null} + +
- {['KOSPI', 'KOSDAQ', 'NASDAQ'].map((label) => ( -
-

{label}

- -- - 연동 예정 -
- ))} + {indices.length === 0 ? ( +

지표 데이터를 불러오지 못했습니다.

+ ) : ( + [...indices] + .sort((a, b) => { + const order = ['KOSPI', 'KOSDAQ', 'KOSPI200']; + const aIdx = order.indexOf(a.name); + const bIdx = order.indexOf(b.name); + if (aIdx === -1 && bIdx === -1) return 0; + if (aIdx === -1) return 1; + if (bIdx === -1) return -1; + return aIdx - bIdx; + }) + .map((item) => ( +
+

{item.name}

+ {item.value ?? '--'} + + {item.direction === 'red' + ? '▲' + : item.direction === 'blue' + ? '▼' + : '■'}{' '} + {item.change_value ?? '--'}{' '} + {item.change_percent ?? ''} + +
+ )) + )}