page.tsx 2.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667
  1. import '../style.scss';
  2. import { Metadata } from 'next';
  3. import MarketNav from '../_components/MarketNav';
  4. import Sparkline from '../_components/Sparkline';
  5. import { fetchElsRisk } from '@/lib/api/seibro';
  6. import { codeLabel, formatAmountCompact, formatShares } from '@/lib/utils/market';
  7. import { ELS_SECN_TPCD_LABEL } from '@/types/seibro';
  8. export const metadata: Metadata = {
  9. title: 'ELS 리스크보드 — 개미투자',
  10. description: 'ELS/DLS 기초자산별 미상환잔액 추이 — 지수 하락 리스크 지표 (SEIBro 수집)'
  11. };
  12. export default async function MarketElsRiskPage()
  13. {
  14. const res = await fetchElsRisk({});
  15. const assets = res.success ? (res.data?.assets ?? []) : [];
  16. return (
  17. <div className='market'>
  18. <div className='market__head'>
  19. <h1 className='market__title'>ELS 리스크보드</h1>
  20. <span className='market__basis'>기초자산별 미상환잔액 추이 · 지수 하락 리스크 (SEIBro)</span>
  21. </div>
  22. <MarketNav />
  23. {!res.success ? (
  24. <div className='market__error' role='alert'>
  25. <strong>ELS 리스크 데이터를 불러오지 못했습니다.</strong>
  26. 잠시 후 다시 시도해 주세요.
  27. </div>
  28. ) : assets.length === 0 ? (
  29. <div className='market__empty'>표시할 ELS 리스크 데이터가 없습니다.</div>
  30. ) : (
  31. <div className='market__series-grid'>
  32. {assets.map((series, index) => {
  33. const latest = series.points.length > 0 ? series.points[series.points.length - 1] : null;
  34. return (
  35. <div key={`${series.bassetCd}-${series.secnTpcd}-${index}`} className='market__series-card'>
  36. <div className='market__series-head'>
  37. <span className='market__series-name' title={series.bassetContent ?? series.bassetCd}>
  38. {series.bassetContent ?? series.bassetCd}
  39. </span>
  40. <span className='market__cls-badge'>{codeLabel(series.secnTpcd, ELS_SECN_TPCD_LABEL)}</span>
  41. </div>
  42. <span className='market__series-latest'>{formatAmountCompact(latest?.unredeemedRemaining)}</span>
  43. <Sparkline
  44. values={series.points.map(p => p.unredeemedRemaining)}
  45. label={`${series.bassetContent ?? series.bassetCd} 미상환잔액 추이`}
  46. />
  47. <div className='market__series-sub'>
  48. <span>미상환 {formatShares(latest?.unredeemedCount)}종목</span>
  49. <span>{latest?.stdDt ?? '—'} 기준</span>
  50. </div>
  51. </div>
  52. );
  53. })}
  54. </div>
  55. )}
  56. </div>
  57. );
  58. }