import '../style.scss'; import { Metadata } from 'next'; import Link from 'next/link'; import PaperNav from '../_components/PaperNav'; import { fetchPaperLeaderboard } from '@/lib/api/paper'; import { changeDirection, changeSymbol } from '@/lib/utils/stock'; import { formatBpPercent, formatBpAbs, bpDirection } from '@/lib/utils/paper'; import type { PaperLeaderboardSort, PaperLeaderboardPeriod } from '@/types/paper'; export const metadata: Metadata = { title: '모의투자 리더보드 — 개미투자', description: '수익률·승률·MDD 기준 모의투자 순위 (주간/월간/전체)' }; const SORT_TABS: { value: PaperLeaderboardSort; label: string }[] = [ { value: 'return', label: '수익률' }, { value: 'winrate', label: '승률' }, { value: 'mdd', label: 'MDD' } ]; const PERIOD_TABS: { value: PaperLeaderboardPeriod; label: string }[] = [ { value: 'week', label: '주간' }, { value: 'month', label: '월간' }, { value: 'all', label: '전체' } ]; const PAGE_SIZE = 50; type Props = { searchParams: Promise<{ sort?: string; period?: string; page?: string; }>; }; export default async function PaperLeaderboardPage({ searchParams }: Props) { const query = await searchParams; const sort: PaperLeaderboardSort = SORT_TABS.some(c => c.value === query.sort) ? (query.sort as PaperLeaderboardSort) : 'return'; const period: PaperLeaderboardPeriod = PERIOD_TABS.some(c => c.value === query.period) ? (query.period as PaperLeaderboardPeriod) : 'all'; const page = Math.max(Number(query.page) || 1, 1); const res = await fetchPaperLeaderboard({ sort, period, page }); const rows = res.success ? (res.data?.rows ?? []) : []; const buildHref = (next: { sort?: PaperLeaderboardSort; period?: PaperLeaderboardPeriod }) => { const params = new URLSearchParams(); params.set('sort', next.sort ?? sort); params.set('period', next.period ?? period); return `/paper/leaderboard?${params.toString()}`; }; return (

리더보드

순위는 명예·프로필 배지만 부여됩니다 (별도 상금 없음)
정렬 {SORT_TABS.map(tab => ( {tab.label} ))}
기간 {PERIOD_TABS.map(tab => ( {tab.label} ))}
{!res.success ? (
리더보드를 불러오지 못했습니다. 잠시 후 다시 시도해 주세요.
) : rows.length === 0 ? (
아직 등재된 참여자가 없습니다.
) : (
{rows.map(row => { const dir = bpDirection(row.returnBp); const symbol = changeSymbol(changeDirection(row.returnBp)); return ( ); })}
모의투자 리더보드 — 순위·닉네임·수익률·승률·MDD·체결수
순위 닉네임 수익률 승률 MDD 체결수
{row.rank} {row.thumb ? ( // eslint-disable-next-line @next/next/no-img-element ) : null} {row.nickname || '익명'} {symbol} {formatBpPercent(row.returnBp)} {formatBpAbs(row.winRateBp)} {formatBpAbs(row.mddBp)} {row.fillCount.toLocaleString()}
)} {res.success && rows.length > 0 && ( )}
); }