| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176 |
- 'use client';
- import { useMemo, useState, useEffect } from 'react';
- import Link from 'next/link';
- import { LogOut } from 'lucide-react';
- import { fetchApi, getDateTime } from '@/lib/utils/client';
- import type { WalletBalanceResponse, TransactionCategory } from '@/types/response/wallet/balance';
- import Loading from '@/app/component/Loading';
- type FilterKey = 'all'|TransactionCategory;
- const FILTER_TABS: { key: FilterKey; label: string }[] = [
- { key: 'all', label: '전체' },
- { key: 'donation', label: '후원 수익' },
- { key: 'commission', label: '판매 수수료' }
- ];
- const CATEGORY_LABEL: Record<TransactionCategory, string> = {
- donation: '후원',
- commission: '판매 수수료',
- other: '기타'
- };
- const TX_TYPE_LABEL: Record<string, string> = {
- DonationIn: '후원 받음',
- DonationOut: '후원 보냄',
- OrderChannelReward: '상점 판매 보상',
- OrderRefundChannelReward: '상점 환불 차감',
- WithdrawalStoreRevenue: '판매 수수료 출금',
- Charge: '충전',
- RewardEarned: '보상 적립',
- Spend: '사용',
- Refund: '환불',
- Lock: '잠금',
- Unlock: '잠금 해제',
- Adjusted: '운영자 조정'
- };
- export default function WalletBalancePage() {
- const [loading, setLoading] = useState(true);
- const [data, setData] = useState<WalletBalanceResponse|null>(null);
- const [filter, setFilter] = useState<FilterKey>('all');
- useEffect(() => {
- fetchApi<WalletBalanceResponse>('/api/studio/wallet/balance')
- .then(res => {
- if (res.data) {
- setData(res.data);
- }
- })
- .catch(() => {})
- .finally(() => setLoading(false));
- }, []);
- const filteredTx = useMemo(() => {
- if (!data) {
- return [];
- }
- if (filter === 'all') {
- return data.recentTransactions;
- }
- return data.recentTransactions.filter(t => t.category === filter);
- }, [data, filter]);
- if (loading || !data) {
- return <Loading />;
- }
- return (
- <div className="studio-page wallet">
- <div className="studio-page__header">
- <h1 className="studio-page__title">잔액 현황</h1>
- </div>
- {/* Summary Cards */}
- <div className="wallet__cards">
- <div className="wallet__card">
- <span className="wallet__card-label">출금 가능 잔액 (M)</span>
- <div className="wallet__card-value wallet__card-value--money">
- {data.withdrawableBalance.toLocaleString()}원
- </div>
- </div>
- <div className="wallet__card">
- <span className="wallet__card-label">누적 수익</span>
- <div className="wallet__card-value">
- {data.totalEarned.toLocaleString()}원
- </div>
- </div>
- <div className="wallet__card">
- <span className="wallet__card-label">누적 출금</span>
- <div className="wallet__card-value">
- {data.totalWithdrawn.toLocaleString()}원
- </div>
- </div>
- <div className="wallet__card">
- <span className="wallet__card-label">누적 판매 수수료</span>
- <div className="wallet__card-value">
- {data.totalStoreCommission.toLocaleString()}원
- </div>
- </div>
- </div>
- {/* Actions */}
- <div className="wallet__actions">
- <Link href="/studio/wallet/withdraw" className="wallet__action-btn wallet__action-btn--primary">
- <LogOut className="size-4" />
- 출금하기
- </Link>
- </div>
- {/* Recent Transactions */}
- <h2 className="wallet__section-title">최근 거래 내역</h2>
- <div className="wallet__filter flex gap-2 mb-3 flex-wrap">
- {FILTER_TABS.map(t => (
- <button
- key={t.key}
- type="button"
- onClick={() => setFilter(t.key)}
- className={`px-3 py-1.5 rounded-full text-xs font-semibold border transition-colors ${
- filter === t.key
- ? 'bg-blue-600 border-blue-600 text-white dark:bg-blue-500 dark:border-blue-500'
- : 'bg-transparent border-neutral-300 text-neutral-700 hover:bg-neutral-100 dark:border-neutral-700 dark:text-neutral-300 dark:hover:bg-neutral-800'
- }`}
- >
- {t.label}
- </button>
- ))}
- </div>
- <div className="wallet__table-wrap">
- <table className="wallet__table">
- <thead>
- <tr>
- <th>일시</th>
- <th>카테고리</th>
- <th>유형</th>
- <th>내용</th>
- <th className="text-right">금액</th>
- <th className="text-right">잔액</th>
- </tr>
- </thead>
- <tbody>
- {filteredTx.length > 0 ? (
- filteredTx.map((tx) => (
- <tr key={tx.id}>
- <td>{getDateTime(tx.createdAt)}</td>
- <td>{CATEGORY_LABEL[tx.category]}</td>
- <td>{TX_TYPE_LABEL[tx.type] ?? tx.type}</td>
- <td>
- {tx.refID && (
- <span className="font-mono text-xs text-neutral-500 mr-2">{tx.refID}</span>
- )}
- {tx.description}
- </td>
- <td className="text-right">
- <span className={tx.amount >= 0 ? 'wallet__amount--plus' : 'wallet__amount--minus'}>
- {tx.amount >= 0 ? '+' : ''}{tx.amount.toLocaleString()}원
- </span>
- </td>
- <td className="text-right">{tx.balance.toLocaleString()}원</td>
- </tr>
- ))
- ) : (
- <tr>
- <td colSpan={6} className="wallet__empty">거래 내역이 없습니다.</td>
- </tr>
- )}
- </tbody>
- </table>
- </div>
- </div>
- );
- }
|