'use client'; /** * 계좌관리 - 종합 계좌 관리 목록 페이지 * * - 통계카드 5개 (전체/은행/대출/증권/보험) * - 구분/금융기관 필터 * - 수기 계좌 등록 버튼 * - 범례 (수기/연동) * - 체크박스 없음 */ import { useState, useMemo, useCallback } from 'react'; import { useRouter } from 'next/navigation'; import { format, startOfMonth, endOfMonth } from 'date-fns'; import { Landmark, Building2, CreditCard, TrendingUp, Shield, Plus, } from 'lucide-react'; import { Button } from '@/components/ui/button'; import { Badge } from '@/components/ui/badge'; import { TableRow, TableCell } from '@/components/ui/table'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/select'; import { UniversalListPage, type UniversalListConfig, type SelectionHandlers, type RowClickHandlers, type ListParams, type StatCard, } from '@/components/templates/UniversalListPage'; import { ListMobileCard, InfoField } from '@/components/organisms/MobileCard'; import type { Account, AccountCategory } from './types'; import { BANK_LABELS, ACCOUNT_CATEGORY_LABELS, ACCOUNT_CATEGORY_FILTER_OPTIONS, ACCOUNT_TYPE_LABELS, ACCOUNT_STATUS_LABELS, ACCOUNT_STATUS_COLORS, ALL_FINANCIAL_INSTITUTION_OPTIONS, } from './types'; import { getBankAccounts } from './actions'; // ===== 계좌번호 마스킹 ===== const maskAccountNumber = (accountNumber: string): string => { if (!accountNumber || accountNumber.length <= 8) return accountNumber || ''; const parts = accountNumber.split('-'); if (parts.length >= 3) { return parts.map((part, idx) => { if (idx === 0 || idx === parts.length - 1) return part; return '****'; }).join('-'); } const first = accountNumber.slice(0, 4); const last = accountNumber.slice(-4); return `${first}-****-${last}`; }; export function AccountManagement() { const router = useRouter(); const itemsPerPage = 20; // ===== 날짜 범위 상태 ===== const today = new Date(); const [startDate, setStartDate] = useState(() => format(startOfMonth(today), 'yyyy-MM-dd')); const [endDate, setEndDate] = useState(() => format(endOfMonth(today), 'yyyy-MM-dd')); // ===== 필터 상태 ===== const [categoryFilter, setCategoryFilter] = useState('all'); const [institutionFilter, setInstitutionFilter] = useState('all'); // ===== 핸들러 ===== const handleRowClick = useCallback((item: Account) => { router.push(`/ko/settings/accounts/${item.id}?mode=view`); }, [router]); const handleCreate = useCallback(() => { router.push('/ko/settings/accounts?mode=new'); }, [router]); // ===== 금융기관 필터 옵션 ===== const institutionFilterOptions = useMemo(() => [ { value: 'all', label: '전체' }, ...ALL_FINANCIAL_INSTITUTION_OPTIONS, ], []); // ===== UniversalListPage Config ===== const config: UniversalListConfig = useMemo( () => ({ title: '계좌 관리', description: '계좌 목록을 관리합니다', icon: Landmark, basePath: '/settings/accounts', idField: 'id', getItemId: (item: Account) => String(item.id), // 체크박스 없음 showCheckbox: false, // 날짜 범위 선택기 + 프리셋 버튼 dateRangeSelector: { enabled: true, showPresets: true, presets: ['thisMonth', 'lastMonth', 'twoMonthsAgo', 'threeMonthsAgo', 'fourMonthsAgo', 'fiveMonthsAgo'], presetLabels: { thisMonth: '이번달', lastMonth: '지난달', }, startDate, endDate, onStartDateChange: setStartDate, onEndDateChange: setEndDate, }, // API 액션 actions: { getList: async (params?: ListParams) => { try { const result = await getBankAccounts(); if (result.success && result.data) { let filteredData = result.data; // 구분 필터 if (categoryFilter && categoryFilter !== 'all') { filteredData = filteredData.filter(item => item.category === categoryFilter); } // 금융기관 필터 if (institutionFilter && institutionFilter !== 'all') { filteredData = filteredData.filter(item => item.bankCode === institutionFilter); } // 검색 필터 if (params?.search) { const s = params.search.toLowerCase(); filteredData = filteredData.filter(item => item.accountName?.toLowerCase().includes(s) || item.accountNumber?.includes(s) || item.accountHolder?.toLowerCase().includes(s) || BANK_LABELS[item.bankCode]?.toLowerCase().includes(s) ); } return { success: true, data: filteredData, totalCount: filteredData.length, totalPages: Math.ceil(filteredData.length / itemsPerPage), }; } return { success: false, error: result.error || '계좌 목록을 불러오는데 실패했습니다.' }; } catch { return { success: false, error: '서버 오류가 발생했습니다.' }; } }, }, // 통계카드 computeStats: (data: Account[], totalCount: number): StatCard[] => { // 전체 데이터 기준 (필터 무관하게) return [ { label: '전체계좌', value: totalCount, icon: Landmark, iconColor: 'text-blue-500', onClick: () => setCategoryFilter('all'), isActive: categoryFilter === 'all', }, { label: '은행계좌', value: data.filter(a => a.category === 'bank_account').length, icon: Building2, iconColor: 'text-green-500', onClick: () => setCategoryFilter('bank_account'), isActive: categoryFilter === 'bank_account', }, { label: '대출계좌', value: data.filter(a => a.category === 'loan_account').length, icon: CreditCard, iconColor: 'text-orange-500', onClick: () => setCategoryFilter('loan_account'), isActive: categoryFilter === 'loan_account', }, { label: '증권계좌', value: data.filter(a => a.category === 'securities_account').length, icon: TrendingUp, iconColor: 'text-purple-500', onClick: () => setCategoryFilter('securities_account'), isActive: categoryFilter === 'securities_account', }, { label: '보험계좌', value: data.filter(a => a.category === 'insurance_account').length, icon: Shield, iconColor: 'text-red-500', onClick: () => setCategoryFilter('insurance_account'), isActive: categoryFilter === 'insurance_account', }, ]; }, // 테이블 컬럼 columns: [ { key: 'no', label: 'No.', className: 'text-center w-[60px]' }, { key: 'category', label: '구분', className: 'min-w-[80px]' }, { key: 'accountType', label: '유형', className: 'min-w-[80px]' }, { key: 'institution', label: '금융기관', className: 'min-w-[100px]' }, { key: 'accountNumber', label: '계좌번호', className: 'min-w-[160px]' }, { key: 'accountName', label: '계좌명', className: 'min-w-[120px]' }, { key: 'status', label: '상태', className: 'min-w-[70px]' }, ], clientSideFiltering: true, itemsPerPage, searchPlaceholder: '금융기관, 계좌번호, 계좌명 검색...', searchFilter: (item: Account, search: string) => { const s = search.toLowerCase(); return ( item.bankName?.toLowerCase().includes(s) || item.accountNumber?.toLowerCase().includes(s) || item.accountName?.toLowerCase().includes(s) || item.accountHolder?.toLowerCase().includes(s) || false ); }, // 헤더 액션 - 수기 계좌 등록 버튼 headerActions: () => ( ), // 테이블 카드 내부 필터 (구분, 금융기관 Select) - "총 N건" 옆에 배치 tableHeaderActions: (
), // 테이블 행 렌더링 renderTableRow: ( item: Account, _index: number, globalIndex: number, handlers: SelectionHandlers & RowClickHandlers ) => { return ( handleRowClick(item)} > {globalIndex} {ACCOUNT_CATEGORY_LABELS[item.category] || item.category} {ACCOUNT_TYPE_LABELS[item.accountType] || item.accountType || '-'}
{BANK_LABELS[item.bankCode] || item.bankCode}
{maskAccountNumber(item.accountNumber)} {item.accountName} {ACCOUNT_STATUS_LABELS[item.status]}
); }, // 모바일 카드 renderMobileCard: ( item: Account, _index: number, globalIndex: number, handlers: SelectionHandlers & RowClickHandlers ) => { return ( {ACCOUNT_CATEGORY_LABELS[item.category]} {BANK_LABELS[item.bankCode] || item.bankCode} } statusBadge={ {ACCOUNT_STATUS_LABELS[item.status]} } showCheckbox={false} isSelected={false} onToggleSelection={() => {}} onClick={() => handleRowClick(item)} infoGrid={
} /> ); }, // 테이블 카드 내부 하단 - 범례 (수기/연동) tableFooter: (
수기 계좌
연동 계좌
), }), [handleCreate, handleRowClick, categoryFilter, institutionFilter, institutionFilterOptions, startDate, endDate] ); return ; }