"use client"; // 최근 본 종목 카드 그리드. localStorage 의 stockchart.recent 를 구독. // 각 코드별로 /api/chart 2일치 받아서 현재가/전일대비 표시. // 빈 리스트면 패널 자체를 안 그림. import Link from "next/link"; import { useEffect, useState } from "react"; import { api } from "../lib/api"; import { recent } from "../lib/recent"; type Tile = { code: string; name: string; market: string; current: number | null; prev: number | null; }; export function RecentTiles() { const [codes, setCodes] = useState([]); const [tiles, setTiles] = useState([]); const [hydrated, setHydrated] = useState(false); useEffect(() => { setHydrated(true); const refresh = () => setCodes(recent.get()); refresh(); return recent.subscribe(refresh); }, []); useEffect(() => { if (!hydrated || codes.length === 0) { setTiles([]); return; } let alive = true; Promise.allSettled( codes.map(async (code) => { const c = await api.getChart(code, 5, "1d"); const valid = c.ohlcv.filter((p) => p.close != null); const last = valid[valid.length - 1] ?? null; const prev = valid.length >= 2 ? valid[valid.length - 2] : null; return { code, name: c.name, market: c.market, current: last?.close ?? null, prev: prev?.close ?? null, }; }), ).then((settled) => { if (!alive) return; setTiles( settled.map((r, i) => { if (r.status === "fulfilled") return r.value; return { code: codes[i], name: codes[i], market: "—", current: null, prev: null, }; }), ); }); return () => { alive = false; }; }, [codes, hydrated]); if (!hydrated || codes.length === 0) return null; return (
최근 본 종목
{tiles.map((t) => ( ))}
); } function Tile({ t }: { t: Tile }) { const change = t.current != null && t.prev != null ? t.current - t.prev : null; const pct = change != null && t.prev != null && t.prev !== 0 ? (change / t.prev) * 100 : null; const tone = change == null || change === 0 ? "text-zinc-400" : change > 0 ? "text-rose-400" : "text-sky-400"; return (
{t.name}
{t.code} · {t.market}
{t.current != null ? t.current.toLocaleString(undefined, { maximumFractionDigits: 0 }) : "—"}
{pct == null ? " " : `${pct > 0 ? "+" : ""}${pct.toFixed(2)}%`}
); }