| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255 |
- 'use client';
- import './style.scss';
- import { useState, useEffect } from 'react';
- import { LoginLogType, WalletLogCategory } from '@/constants/common';
- import { fetchApi, getDateTime } from '@/lib/utils/client';
- import type { ChargeLogsResponse } from '@/types/response/account/chargeLogs';
- import Loading from '@/app/component/Loading';
- import Pagination from '@/app/component/Pagination';
- import NavTabs from '../navTabs';
- const STATUS_MAP: Record<string, { label: string; cls: string }> = {
- Paid: { label: '완료', cls: 'status--paid' },
- Pending: { label: '대기', cls: 'status--pending' },
- WaitingDeposit: { label: '입금대기', cls: 'status--pending' },
- Failed: { label: '실패', cls: 'status--failed' },
- Cancelled: { label: '취소', cls: 'status--cancelled' }
- };
- const METHOD_MAP: Record<string, string> = {
- Card: '신용카드',
- VirtualAccount: '가상계좌',
- Mobile: '휴대폰',
- Transfer: '계좌이체',
- NaverPay: '네이버페이',
- KakaoPay: '카카오페이',
- Payco: '페이코',
- Integrated: '통합결제'
- };
- const TX_TYPE_MAP: Record<string, string> = {
- Charge: '충전',
- RewardEarned: '보상 적립',
- Spend: '사용',
- Refund: '환불',
- Lock: '잠금',
- Unlock: '잠금 해제',
- Adjusted: '운영자 조정',
- OrderPay: '상점 결제',
- OrderRefund: '주문 환불',
- OrderChannelReward: '상점 채널 보상',
- OrderRefundChannelReward: '상점 보상 차감'
- };
- const BALANCE_TYPE_MAP: Record<string, string> = {
- PgCharged: 'PG 충전',
- Deposit: '예치금',
- Reward: '보상',
- Airdrop: '에어드랍',
- Locked: '잠금',
- Adjusted: '운영자 조정',
- StoreRevenue: '상점 수익'
- };
- // 적립류 (+), 사용류 (-) 분류 — UI 표시용 부호
- const EARN_TX_TYPES = new Set(['Charge', 'RewardEarned', 'Unlock', 'OrderChannelReward', 'OrderRefund', 'Refund']);
- export default function ChargeLogs()
- {
- const [error, setError] = useState<string>('');
- const [loading, setLoading] = useState<boolean>(true);
- const [page, setPage] = useState<number>(1);
- const [type, setType] = useState<LoginLogType>(LoginLogType.Today);
- const [category, setCategory] = useState<WalletLogCategory>(WalletLogCategory.Charge);
- const [data, setData] = useState<ChargeLogsResponse>({
- total: 0,
- list: []
- });
- useEffect(() => {
- if (error) {
- alert(error);
- setError('');
- }
- }, [error]);
- useEffect(() => {
- setLoading(true);
- fetchApi<ChargeLogsResponse>(`/api/mypage/charge-logs?type=${type}&category=${category}&page=${page}&perPage=20`).then((res) => {
- setData(res.data!);
- }).catch(err => {
- setError(err.message);
- }).finally(() => {
- setLoading(false);
- });
- }, [type, category, page]);
- useEffect(() => {
- setPage(1);
- }, [type, category]);
- const tabItems = [
- { label: "오늘", value: LoginLogType.Today },
- { label: "1주일", value: LoginLogType.Week },
- { label: "1개월", value: LoginLogType.Month },
- { label: "3개월", value: LoginLogType.QuarterYear },
- { label: "6개월", value: LoginLogType.HalfYear }
- ];
- const categoryItems = [
- { label: '전체', value: WalletLogCategory.All },
- { label: '충전', value: WalletLogCategory.Charge },
- { label: '적립', value: WalletLogCategory.Earn },
- { label: '사용', value: WalletLogCategory.Spend },
- { label: '환불', value: WalletLogCategory.Refund }
- ];
- const getStatus = (status: string|null) => status ? (STATUS_MAP[status] ?? { label: status, cls: '' }) : { label: '', cls: '' };
- const getMethod = (method: string|null) => method ? (METHOD_MAP[method] ?? method) : '';
- const getTxType = (txType: string|null) => txType ? (TX_TYPE_MAP[txType] ?? txType) : '';
- const getBalanceType = (bt: string|null) => bt ? (BALANCE_TYPE_MAP[bt] ?? bt) : '';
- const isEarn = (txType: string|null) => txType !== null && EARN_TX_TYPES.has(txType);
- const isCharge = category === WalletLogCategory.Charge;
- return (
- <>
- <NavTabs />
- <div id="chargeLogs">
- {loading && <Loading />}
- <h1>(P) 충전 내역</h1>
- <div className="charge-logs__header">
- <div className="charge-logs__summary">합계: {data.total}</div>
- <div className="charge-logs__tabs">
- <select
- className="charge-logs__filter"
- value={category}
- onChange={(e) => setCategory(e.target.value as WalletLogCategory)}
- >
- {categoryItems.map((item) => (
- <option key={item.value} value={item.value}>{item.label}</option>
- ))}
- </select>
- {tabItems.map((item, i) => (
- <button type="button" key={i} className={type === item.value ? 'active' : ''}
- onClick={() => setType(item.value)}>{item.label}
- </button>
- ))}
- </div>
- </div>
- <section className={`charge-logs__list ${isCharge ? 'charge-logs__list--charge' : 'charge-logs__list--wallet'}`}>
- <article>
- {isCharge ? (
- <ul>
- <li>일시</li>
- <li>주문번호</li>
- <li>결제 수단</li>
- <li>결제 금액</li>
- <li>캐시</li>
- <li>상태</li>
- </ul>
- ) : (
- <ul>
- <li>일시</li>
- <li>구분</li>
- <li>사유</li>
- <li>잔액 유형</li>
- <li>금액</li>
- <li>잔액 후</li>
- </ul>
- )}
- </article>
- <article>
- {data.list.length > 0 ? (
- data.list.map((row) => {
- if (isCharge || row.category === 'charge') {
- const st = getStatus(row.status);
- return (
- <section key={row.id}>
- {/* PC */}
- <ol>
- <li>{getDateTime(row.paidAt ?? row.createdAt)}</li>
- <li className="charge-logs__order-id">{row.orderID}</li>
- <li>{getMethod(row.paymentMethod)}</li>
- <li>{row.amount.toLocaleString()}원</li>
- <li className="amount-plus">+{(row.pointAmount ?? 0).toLocaleString()}P</li>
- <li><span className={st.cls}>{st.label}</span></li>
- </ol>
- {/* Mobile */}
- <dl hidden>
- <dt>
- <div className='flex justify-between'>
- <div>{row.amount.toLocaleString()}원 충전</div>
- <div>
- <small className="charge-logs__order-id">{row.orderID}</small>
- </div>
- </div>
- </dt>
- <dd>
- <ul>
- <li className="amount-plus">+{(row.pointAmount ?? 0).toLocaleString()}P</li>
- <li><span className={st.cls}>{st.label}</span></li>
- <li>{getDateTime(row.paidAt ?? row.createdAt)}</li>
- </ul>
- </dd>
- </dl>
- </section>
- );
- }
- // all/earn/spend/refund 행
- const earn = isEarn(row.txType);
- const sign = earn ? '+' : '-';
- const amountCls = earn ? 'amount-plus' : 'amount-minus';
- return (
- <section key={row.id}>
- {/* PC */}
- <ol>
- <li>{getDateTime(row.createdAt)}</li>
- <li>{getTxType(row.txType)}</li>
- <li className="charge-logs__reason" title={row.reason ?? ''}>{row.reason}</li>
- <li>{getBalanceType(row.balanceType)}</li>
- <li className={amountCls}>{sign}{row.amount.toLocaleString()}P</li>
- <li>{(row.balanceAfter ?? 0).toLocaleString()}P</li>
- </ol>
- {/* Mobile */}
- <dl hidden>
- <dt>
- <div className='flex justify-between'>
- <div>{getTxType(row.txType)}</div>
- <div className={amountCls}>{sign}{row.amount.toLocaleString()}P</div>
- </div>
- </dt>
- <dd>
- <ul>
- <li>{getBalanceType(row.balanceType)}</li>
- <li className="charge-logs__reason">{row.reason}</li>
- <li>{getDateTime(row.createdAt)}</li>
- </ul>
- </dd>
- </dl>
- </section>
- );
- })
- ) : (
- <p className="empty">기록이 없습니다.</p>
- )}
- </article>
- </section>
- {data.list.length > 0 && (
- <Pagination total={data.total} page={page} perPage={20} onChange={setPage} />
- )}
- </div>
- </>
- );
- }
|