- {item.title} -
-- {item.summary} -
-해외 뉴스 없음
+ ) : ( ++ {item.title} +
++ {item.summary} +
+diff --git a/src/api.js b/src/api.js index d132658..b3d361f 100644 --- a/src/api.js +++ b/src/api.js @@ -58,12 +58,16 @@ export function deleteHistory(id) { return apiDelete(`/api/history/${id}`); } -export function getStockNews(limit = 20) { - return apiGet(`/api/stock/news?limit=${limit}`); +export function getStockNews(limit = 20, category) { + const qs = new URLSearchParams({ limit: String(limit) }); + if (category) { + qs.set("category", category); + } + return apiGet(`/api/stock/news?${qs.toString()}`); } export function triggerStockScrap() { - return apiPost("/api/admin/stock/scrap"); + return apiPost("/api/stock/scrap"); } export function getStockHealth() { diff --git a/src/pages/stock/Stock.css b/src/pages/stock/Stock.css index 96761df..43b5451 100644 --- a/src/pages/stock/Stock.css +++ b/src/pages/stock/Stock.css @@ -116,7 +116,7 @@ .stock-grid { display: grid; - grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); + grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 18px; } @@ -174,6 +174,7 @@ .stock-snapshot { display: grid; gap: 12px; + grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); } .stock-snapshot__card { @@ -183,6 +184,7 @@ display: grid; gap: 6px; background: rgba(0, 0, 0, 0.2); + min-height: 94px; } .stock-snapshot__card.is-highlight { @@ -262,6 +264,28 @@ gap: 14px; } +.stock-tabs { + display: flex; + gap: 8px; + margin-bottom: 10px; +} + +.stock-tab { + border: 1px solid var(--line); + background: rgba(0, 0, 0, 0.2); + color: var(--muted); + border-radius: 999px; + padding: 6px 12px; + font-size: 12px; + cursor: pointer; + transition: border-color 0.2s ease, color 0.2s ease; +} + +.stock-tab.is-active { + border-color: rgba(255, 255, 255, 0.5); + color: var(--text); +} + .stock-news__item { border: 1px solid var(--line); border-radius: 16px; diff --git a/src/pages/stock/Stock.jsx b/src/pages/stock/Stock.jsx index 4f56584..9c65ce3 100644 --- a/src/pages/stock/Stock.jsx +++ b/src/pages/stock/Stock.jsx @@ -26,7 +26,9 @@ const getLatestBy = (items, key) => { }; const Stock = () => { - const [news, setNews] = useState([]); + const [newsDomestic, setNewsDomestic] = useState([]); + const [newsOverseas, setNewsOverseas] = useState([]); + const [newsCategory, setNewsCategory] = useState('domestic'); const [limit, setLimit] = useState(20); const [loading, setLoading] = useState(false); const [scraping, setScraping] = useState(false); @@ -43,21 +45,29 @@ const Stock = () => { const [indicesAt, setIndicesAt] = useState(''); const [autoRefreshMs] = useState(180000); + const combinedNews = useMemo( + () => [...newsDomestic, ...newsOverseas], + [newsDomestic, newsOverseas] + ); const latestCrawled = useMemo( - () => getLatestBy(news, 'crawled_at'), - [news] + () => getLatestBy(combinedNews, 'crawled_at'), + [combinedNews] ); const latestPublished = useMemo( - () => getLatestBy(news, 'pub_date'), - [news] + () => getLatestBy(combinedNews, 'pub_date'), + [combinedNews] ); const loadNews = async () => { setLoading(true); setNewsError(''); try { - const data = await getStockNews(limit); - setNews(Array.isArray(data) ? data : []); + const [domestic, overseas] = await Promise.all([ + getStockNews(limit, 'domestic'), + getStockNews(limit, 'overseas'), + ]); + setNewsDomestic(Array.isArray(domestic) ? domestic : []); + setNewsOverseas(Array.isArray(overseas) ? overseas : []); } catch (err) { setNewsError(err?.message ?? String(err)); } finally { @@ -175,7 +185,7 @@ const Stock = () => {
Schedule
-- 매일 오전 8시에 자동 스크랩이 실행됩니다. -
-뉴스를 불러오는 중...
) : newsError ? ({newsError}
- ) : news.length === 0 ? ( + ) : combinedNews.length === 0 ? (표시할 뉴스가 없습니다.
) : ( -- {item.title} -
-- {item.summary} -
-해외 뉴스 없음
+ ) : ( ++ {item.title} +
++ {item.summary} +
+