| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132 |
- 'use client';
- import { useCallback, useEffect, useState } from 'react';
- import { fetchApi, cn } from '@/lib/utils/client';
- import { formatBpPercent, bpDirection, formatTokenNumber } from '@/lib/utils/paper';
- import type {
- PaperAccountResponse,
- PaperMyRankResponse
- } from '@/types/paper';
- import Loading from '@/app/component/Loading';
- import PaperDepositWithdrawModal from './PaperDepositWithdrawModal';
- // 내 계좌 카드 — 잔액/예약/평가자산/좌수/누적수익률/순위 + 입금·출금 모달.
- // 계좌 미개설(hasAccount=false) 시 첫 입금 안내.
- export default function PaperAccountCard()
- {
- const [account, setAccount] = useState<PaperAccountResponse|null>(null);
- const [rank, setRank] = useState<PaperMyRankResponse|null>(null);
- const [loading, setLoading] = useState(true);
- const [modal, setModal] = useState<'deposit'|'withdraw'|null>(null);
- const load = useCallback(async () => {
- setLoading(true);
- const [accRes, rankRes] = await Promise.all([
- fetchApi<PaperAccountResponse>('/api/paper/account', { silent: true }),
- fetchApi<PaperMyRankResponse>('/api/paper/leaderboard/me?sort=return&period=all', { silent: true })
- ]);
- setAccount(accRes.success ? (accRes.data ?? null) : null);
- setRank(rankRes.success ? (rankRes.data ?? null) : null);
- setLoading(false);
- }, []);
- useEffect(() => {
- load();
- }, [load]);
- const handleDone = useCallback(() => {
- setModal(null);
- load();
- }, [load]);
- if (loading) {
- return (
- <div className='paper__account-card'>
- <Loading type={2} />
- </div>
- );
- }
- if (!account || !account.hasAccount) {
- return (
- <>
- <div className='paper__empty-account'>
- <div className='paper__empty-account-title'>아직 모의투자 계좌가 없어요</div>
- <p className='paper__empty-account-desc'>
- 지갑 토큰을 입금하면 계좌가 자동으로 개설됩니다.<br />
- 입금한 토큰으로 실제 종목을 매매하고, 언제든 다시 지갑으로 출금할 수 있어요.
- </p>
- <button type='button' className='paper__btn paper__btn--primary' onClick={() => setModal('deposit')}>
- 첫 입금하고 시작하기
- </button>
- </div>
- {modal === 'deposit' && (
- <PaperDepositWithdrawModal
- mode='deposit'
- account={null}
- onClose={() => setModal(null)}
- onDone={handleDone}
- />
- )}
- </>
- );
- }
- const returnDir = bpDirection(account.cumReturnBp);
- return (
- <>
- <div className='paper__account-card'>
- <div className='paper__metrics'>
- <div className='paper__metric'>
- <span className='paper__metric-label'>가용 토큰</span>
- <span className='paper__metric-value'>{formatTokenNumber(account.token)}</span>
- </div>
- <div className='paper__metric'>
- <span className='paper__metric-label'>매수 예약</span>
- <span className='paper__metric-value'>{formatTokenNumber(account.reservedToken)}</span>
- </div>
- <div className='paper__metric'>
- <span className='paper__metric-label'>평가자산</span>
- <span className='paper__metric-value'>{formatTokenNumber(account.equity)}</span>
- </div>
- <div className='paper__metric'>
- <span className='paper__metric-label'>보유 좌수</span>
- <span className='paper__metric-value'>{account.units.toLocaleString('ko-KR', { maximumFractionDigits: 4 })}</span>
- </div>
- <div className='paper__metric'>
- <span className='paper__metric-label'>누적 수익률</span>
- <span className={cn('paper__metric-value', `paper__value--${returnDir}`)}>{formatBpPercent(account.cumReturnBp)}</span>
- </div>
- <div className='paper__metric'>
- <span className='paper__metric-label'>순위 (수익률·전체)</span>
- <span className='paper__metric-value'>
- {rank && rank.hasRank ? `${rank.rank.toLocaleString()}위 / ${rank.total.toLocaleString()}` : '미등재'}
- </span>
- </div>
- </div>
- <div className='paper__actions'>
- <button type='button' className='paper__btn paper__btn--primary' onClick={() => setModal('deposit')}>
- 입금
- </button>
- <button type='button' className='paper__btn' onClick={() => setModal('withdraw')}>
- 출금
- </button>
- </div>
- </div>
- {modal && (
- <PaperDepositWithdrawModal
- mode={modal}
- account={account}
- onClose={() => setModal(null)}
- onDone={handleDone}
- />
- )}
- </>
- );
- }
|