'use client'; import { useState, useMemo, useCallback } from 'react'; import { format, subDays, subMonths, startOfMonth, endOfMonth } from 'date-fns'; import { CreditCard, RefreshCw, } from 'lucide-react'; import { Button } from '@/components/ui/button'; import { TableRow, TableCell } from '@/components/ui/table'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/select'; import { IntegratedListTemplateV2, type TableColumn, type StatCard, } from '@/components/templates/IntegratedListTemplateV2'; import { DateRangeSelector } from '@/components/molecules/DateRangeSelector'; import { ListMobileCard, InfoField } from '@/components/organisms/ListMobileCard'; import type { CardTransaction, SortOption, } from './types'; import { SORT_OPTIONS, } from './types'; // ===== Mock 데이터 생성 (결정론적: seed 기반으로 일관된 값 생성) ===== const generateMockData = (): CardTransaction[] => { const cards = ['신한 1234', '국민 5678', '우리 9012', '하나 3456']; const cardNames = ['법인카드1', '법인카드2', '대표이사카드', '영업팀카드']; const users = ['홍길동', '김철수', '이영희', '박민수', '최지영']; const merchants = ['GS25 강남점', '스타벅스 역삼', '교보문고', '쿠팡', '네이버페이']; // 고정된 금액 배열 (Math.random() 대신 결정론적 값 사용) const amounts = [150000, 230000, 45000, 320000, 78000, 190000, 420000, 55000, 280000, 125000, 340000, 67000, 215000, 89000, 175000, 395000, 52000, 268000, 112000, 445000, 73000, 198000, 310000, 48000, 256000, 135000, 380000, 62000, 225000, 95000]; // 고정된 날짜들 (new Date() 대신 고정 기준일 사용) const baseDate = new Date('2025-12-01'); return Array.from({ length: 30 }, (_, i) => { return { id: `card-txn-${i + 1}`, card: cards[i % cards.length], cardName: cardNames[i % cardNames.length], user: users[i % users.length], usedAt: format(subDays(baseDate, i % 30), 'yyyy-MM-dd HH:mm'), merchantName: merchants[i % merchants.length], amount: amounts[i], createdAt: baseDate.toISOString(), updatedAt: baseDate.toISOString(), }; }); }; export function CardTransactionInquiry() { // ===== 상태 관리 ===== const [searchQuery, setSearchQuery] = useState(''); const [sortOption, setSortOption] = useState('latest'); const [cardFilter, setCardFilter] = useState('all'); // 카드명 필터 const [currentPage, setCurrentPage] = useState(1); const itemsPerPage = 20; // 날짜 범위 상태 const [startDate, setStartDate] = useState('2025-09-01'); const [endDate, setEndDate] = useState('2025-09-03'); // Mock 데이터 (SSR-safe: 결정론적 데이터) const [data] = useState(() => generateMockData()); // ===== 카드명 옵션 ===== const cardOptions = useMemo(() => { const uniqueCards = [...new Set(data.map(d => d.cardName))]; return [ { value: 'all', label: '전체' }, ...uniqueCards.map(card => ({ value: card, label: card })) ]; }, [data]); // ===== 필터링된 데이터 ===== const filteredData = useMemo(() => { let result = data.filter(item => item.card.includes(searchQuery) || item.cardName.includes(searchQuery) || item.user.includes(searchQuery) || item.merchantName.includes(searchQuery) ); // 카드명 필터 if (cardFilter !== 'all') { result = result.filter(item => item.cardName === cardFilter); } // 정렬 switch (sortOption) { case 'latest': result.sort((a, b) => new Date(b.usedAt).getTime() - new Date(a.usedAt).getTime()); break; case 'oldest': result.sort((a, b) => new Date(a.usedAt).getTime() - new Date(b.usedAt).getTime()); break; case 'amountHigh': result.sort((a, b) => b.amount - a.amount); break; case 'amountLow': result.sort((a, b) => a.amount - b.amount); break; } return result; }, [data, searchQuery, cardFilter, sortOption]); const paginatedData = useMemo(() => { const startIndex = (currentPage - 1) * itemsPerPage; return filteredData.slice(startIndex, startIndex + itemsPerPage); }, [filteredData, currentPage, itemsPerPage]); const totalPages = Math.ceil(filteredData.length / itemsPerPage); // 새로고침 핸들러 const handleRefresh = useCallback(() => { console.log('새로고침: 카드 사용 내역 최신 데이터 조회'); // TODO: API 호출로 최신 데이터 조회 }, []); // ===== 통계 카드 (전월/당월 사용액) ===== // Mock 데이터 기준일(2025-12-01)에 맞춰 고정된 날짜 사용 (Hydration 오류 방지) const statCards: StatCard[] = useMemo(() => { // Mock 데이터가 2025-12-01 기준이므로, 해당 월 기준으로 계산 const referenceDate = new Date('2025-12-01'); const currentMonthStart = startOfMonth(referenceDate); const currentMonthEnd = endOfMonth(referenceDate); const lastMonthStart = startOfMonth(subMonths(referenceDate, 1)); const lastMonthEnd = endOfMonth(subMonths(referenceDate, 1)); // 전월 사용액 계산 const lastMonthTotal = data .filter(d => { const date = new Date(d.usedAt); return date >= lastMonthStart && date <= lastMonthEnd; }) .reduce((sum, d) => sum + d.amount, 0); // 당월 사용액 계산 const currentMonthTotal = data .filter(d => { const date = new Date(d.usedAt); return date >= currentMonthStart && date <= currentMonthEnd; }) .reduce((sum, d) => sum + d.amount, 0); return [ { label: '전월 사용액', value: `${lastMonthTotal.toLocaleString()}원`, icon: CreditCard, iconColor: 'text-gray-500' }, { label: '당월 사용액', value: `${currentMonthTotal.toLocaleString()}원`, icon: CreditCard, iconColor: 'text-blue-500' }, ]; }, [data]); // ===== 테이블 컬럼 (체크박스/번호 없음) ===== const tableColumns: TableColumn[] = useMemo(() => [ { key: 'card', label: '카드' }, { key: 'cardName', label: '카드명' }, { key: 'user', label: '사용자' }, { key: 'usedAt', label: '사용일시' }, { key: 'merchantName', label: '가맹점명' }, { key: 'amount', label: '사용금액', className: 'text-right' }, ], []); // ===== 테이블 행 렌더링 (체크박스/번호 없음) ===== const renderTableRow = useCallback((item: CardTransaction) => { return ( {/* 카드 */} {item.card} {/* 카드명 */} {item.cardName} {/* 사용자 */} {item.user} {/* 사용일시 */} {item.usedAt} {/* 가맹점명 */} {item.merchantName} {/* 사용금액 */} {item.amount.toLocaleString()} ); }, []); // ===== 모바일 카드 렌더링 ===== const renderMobileCard = useCallback(( item: CardTransaction, index: number, globalIndex: number, isSelected: boolean, onToggle: () => void ) => { return ( {}} showCheckbox={false} infoGrid={
} /> ); }, []); // ===== 헤더 액션 (날짜 선택만) ===== const headerActions = ( ); // ===== 테이블 상단 새로고침 버튼 (출금관리 스타일) ===== const beforeTableContent = (
); // ===== 테이블 헤더 액션 (2개 필터) ===== const tableHeaderActions = (
{/* 카드명 필터 */} {/* 정렬 */}
); // ===== 테이블 합계 계산 ===== const totalAmount = useMemo(() => { return filteredData.reduce((sum, item) => sum + item.amount, 0); }, [filteredData]); // ===== 테이블 하단 합계 행 ===== const tableFooter = ( 합계 {totalAmount.toLocaleString()} ); return ( {}} onToggleSelectAll={() => {}} getItemId={(item: CardTransaction) => item.id} renderTableRow={renderTableRow} renderMobileCard={renderMobileCard} showCheckbox={false} showRowNumber={false} pagination={{ currentPage, totalPages, totalItems: filteredData.length, itemsPerPage, onPageChange: setCurrentPage, }} /> ); }