주식 주요 지수 가져오기 추가
This commit is contained in:
@@ -69,3 +69,7 @@ export function triggerStockScrap() {
|
|||||||
export function getStockHealth() {
|
export function getStockHealth() {
|
||||||
return apiGet("/api/stock/health");
|
return apiGet("/api/stock/health");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getStockIndices() {
|
||||||
|
return apiGet("/api/stock/indices");
|
||||||
|
}
|
||||||
|
|||||||
@@ -176,6 +176,12 @@
|
|||||||
background: rgba(0, 0, 0, 0.2);
|
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 {
|
.stock-snapshot__card p {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
@@ -193,6 +199,19 @@
|
|||||||
font-size: 12px;
|
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 {
|
.stock-schedule {
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: 12px;
|
gap: 12px;
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import React, { useEffect, useMemo, useState } from 'react';
|
import React, { useEffect, useMemo, useState } from 'react';
|
||||||
import {
|
import {
|
||||||
getStockHealth,
|
getStockHealth,
|
||||||
|
getStockIndices,
|
||||||
getStockNews,
|
getStockNews,
|
||||||
triggerStockScrap,
|
triggerStockScrap,
|
||||||
} from '../../api';
|
} from '../../api';
|
||||||
@@ -34,6 +35,10 @@ const Stock = () => {
|
|||||||
status: 'unknown',
|
status: 'unknown',
|
||||||
message: '',
|
message: '',
|
||||||
});
|
});
|
||||||
|
const [indices, setIndices] = useState([]);
|
||||||
|
const [indicesLoading, setIndicesLoading] = useState(false);
|
||||||
|
const [indicesAt, setIndicesAt] = useState('');
|
||||||
|
const [autoRefreshMs] = useState(180000);
|
||||||
|
|
||||||
const latestCrawled = useMemo(
|
const latestCrawled = useMemo(
|
||||||
() => getLatestBy(news, 'crawled_at'),
|
() => 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 () => {
|
const onScrap = async () => {
|
||||||
setScraping(true);
|
setScraping(true);
|
||||||
setError('');
|
setError('');
|
||||||
@@ -94,7 +113,12 @@ const Stock = () => {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
loadHealth();
|
loadHealth();
|
||||||
}, []);
|
loadIndices();
|
||||||
|
const timer = window.setInterval(() => {
|
||||||
|
loadIndices();
|
||||||
|
}, autoRefreshMs);
|
||||||
|
return () => window.clearInterval(timer);
|
||||||
|
}, [autoRefreshMs]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="stock">
|
<div className="stock">
|
||||||
@@ -160,18 +184,71 @@ const Stock = () => {
|
|||||||
<p className="stock-panel__eyebrow">Snapshot</p>
|
<p className="stock-panel__eyebrow">Snapshot</p>
|
||||||
<h3>시장 스냅샷</h3>
|
<h3>시장 스냅샷</h3>
|
||||||
<p className="stock-panel__sub">
|
<p className="stock-panel__sub">
|
||||||
지수/가격 API 연동을 위한 준비 구간입니다.
|
주요 지표의 현재가와 등락을 빠르게 확인합니다.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="stock-panel__actions">
|
||||||
|
{indicesLoading ? (
|
||||||
|
<span className="stock-chip">갱신 중</span>
|
||||||
|
) : null}
|
||||||
|
{indicesAt ? (
|
||||||
|
<span className="stock-chip">{indicesAt}</span>
|
||||||
|
) : null}
|
||||||
|
<button
|
||||||
|
className="button ghost small"
|
||||||
|
onClick={loadIndices}
|
||||||
|
disabled={indicesLoading}
|
||||||
|
>
|
||||||
|
지표 새로고침
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="stock-snapshot">
|
<div className="stock-snapshot">
|
||||||
{['KOSPI', 'KOSDAQ', 'NASDAQ'].map((label) => (
|
{indices.length === 0 ? (
|
||||||
<div key={label} className="stock-snapshot__card">
|
<p className="stock-empty">지표 데이터를 불러오지 못했습니다.</p>
|
||||||
<p>{label}</p>
|
) : (
|
||||||
<strong>--</strong>
|
[...indices]
|
||||||
<span>연동 예정</span>
|
.sort((a, b) => {
|
||||||
</div>
|
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) => (
|
||||||
|
<div
|
||||||
|
key={item.name}
|
||||||
|
className={`stock-snapshot__card ${
|
||||||
|
item.name?.toUpperCase?.() ===
|
||||||
|
'KOSPI200'
|
||||||
|
? 'is-highlight'
|
||||||
|
: ''
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<p>{item.name}</p>
|
||||||
|
<strong>{item.value ?? '--'}</strong>
|
||||||
|
<span
|
||||||
|
className={`stock-snapshot__change ${
|
||||||
|
item.direction === 'red'
|
||||||
|
? 'is-up'
|
||||||
|
: item.direction === 'blue'
|
||||||
|
? 'is-down'
|
||||||
|
: ''
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{item.direction === 'red'
|
||||||
|
? '▲'
|
||||||
|
: item.direction === 'blue'
|
||||||
|
? '▼'
|
||||||
|
: '■'}{' '}
|
||||||
|
{item.change_value ?? '--'}{' '}
|
||||||
|
{item.change_percent ?? ''}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
))
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user