'use client'; /** * AccountDetailForm - 계좌 등록/수정/보기 조건부 폼 * * 구분(category) 선택에 따라 유형(accountType) 옵션과 하단 상세 섹션이 동적 변경됨 * - 은행계좌: 계좌 정보 (계약금액, 이율, 시작일, 만기일, 이월잔액) * - 대출계좌: 대출 정보 (대출금액, 이율, 상환방식, 거치기간 등) * - 증권계좌: 증권 정보 (투자금액, 수익율, 평가액) * - 보험계좌: 보험 정보 (단체/화재/CEO별 다른 필드) */ import { useState, useCallback, useMemo } from 'react'; import { useRouter } from 'next/navigation'; import { Landmark, Save, Trash2, ArrowLeft } from 'lucide-react'; import { toast } from 'sonner'; import { Button } from '@/components/ui/button'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { DeleteConfirmDialog } from '@/components/ui/confirm-dialog'; import { PageLayout } from '@/components/organisms/PageLayout'; import { PageHeader } from '@/components/organisms/PageHeader'; import { FormField } from '@/components/molecules/FormField'; import type { Account, AccountCategory, AccountFormData } from './types'; import { ACCOUNT_CATEGORY_OPTIONS, ACCOUNT_TYPE_OPTIONS_BY_CATEGORY, ACCOUNT_STATUS_OPTIONS, FINANCIAL_INSTITUTION_OPTIONS_BY_CATEGORY, BANK_LABELS, HOLDER_LABEL_BY_CATEGORY, } from './types'; // ===== Props ===== interface AccountDetailFormProps { mode: 'create' | 'edit' | 'view'; initialData?: Account; onSubmit: (data: AccountFormData) => Promise<{ success: boolean; error?: string }>; onDelete?: () => Promise<{ success: boolean; error?: string }>; isLoading?: boolean; } // ===== 초기 폼 데이터 생성 ===== function getInitialFormData(initialData?: Account): AccountFormData { if (initialData) { return { category: initialData.category || 'bank_account', accountType: initialData.accountType || '', bankCode: initialData.bankCode || '', bankName: initialData.bankName || '', accountNumber: initialData.accountNumber || '', accountName: initialData.accountName || '', accountHolder: initialData.accountHolder || '', status: initialData.status || 'active', contractAmount: initialData.contractAmount, interestRate: initialData.interestRate, startDate: initialData.startDate, maturityDate: initialData.maturityDate, carryoverBalance: initialData.carryoverBalance, loanAmount: initialData.loanAmount, loanBalance: initialData.loanBalance, interestPaymentCycle: initialData.interestPaymentCycle, repaymentMethod: initialData.repaymentMethod, gracePeriod: initialData.gracePeriod, monthlyRepayment: initialData.monthlyRepayment, collateral: initialData.collateral, investmentAmount: initialData.investmentAmount, returnRate: initialData.returnRate, evaluationAmount: initialData.evaluationAmount, surrenderValue: initialData.surrenderValue, policyNumber: initialData.policyNumber, paymentCycle: initialData.paymentCycle, premiumPerCycle: initialData.premiumPerCycle, premiumPerPerson: initialData.premiumPerPerson, enrolledCount: initialData.enrolledCount, insuredProperty: initialData.insuredProperty, propertyAddress: initialData.propertyAddress, beneficiary: initialData.beneficiary, note: initialData.note, }; } return { category: 'bank_account', accountType: '', bankCode: '', bankName: '', accountNumber: '', accountName: '', accountHolder: '', status: 'active', }; } export function AccountDetailForm({ mode: initialMode, initialData, onSubmit, onDelete, isLoading, }: AccountDetailFormProps) { const router = useRouter(); const [mode, setMode] = useState(initialMode); const [formData, setFormData] = useState(() => getInitialFormData(initialData)); const [isSaving, setIsSaving] = useState(false); const [showDeleteDialog, setShowDeleteDialog] = useState(false); const isViewMode = mode === 'view'; const isCreateMode = mode === 'create'; const disabled = isViewMode; // ===== 구분별 동적 옵션 ===== const typeOptions = useMemo( () => ACCOUNT_TYPE_OPTIONS_BY_CATEGORY[formData.category] || [], [formData.category] ); const institutionOptions = useMemo( () => FINANCIAL_INSTITUTION_OPTIONS_BY_CATEGORY[formData.category] || [], [formData.category] ); const holderLabel = HOLDER_LABEL_BY_CATEGORY[formData.category] || '예금주'; // ===== 핸들러 ===== const handleChange = useCallback((field: keyof AccountFormData, value: string | number | undefined) => { setFormData(prev => ({ ...prev, [field]: value })); }, []); const handleCategoryChange = useCallback((value: string) => { const category = value as AccountCategory; setFormData(prev => ({ ...prev, category, accountType: '', bankCode: '', bankName: '', })); }, []); const handleBankCodeChange = useCallback((value: string) => { setFormData(prev => ({ ...prev, bankCode: value, bankName: BANK_LABELS[value] || value, })); }, []); const handleSubmit = useCallback(async () => { if (!formData.category || !formData.bankCode || !formData.accountNumber) { toast.error('필수 항목을 입력해주세요.'); return; } setIsSaving(true); try { const result = await onSubmit(formData); if (result.success) { toast.success(isCreateMode ? '계좌가 등록되었습니다.' : '계좌가 수정되었습니다.'); router.push('/ko/settings/accounts'); } else { toast.error(result.error || '저장에 실패했습니다.'); } } catch { toast.error('서버 오류가 발생했습니다.'); } finally { setIsSaving(false); } }, [formData, onSubmit, isCreateMode, router]); const handleDelete = useCallback(async () => { if (!onDelete) return; setIsSaving(true); try { const result = await onDelete(); if (result.success) { toast.success('계좌가 삭제되었습니다.'); router.push('/ko/settings/accounts'); } else { toast.error(result.error || '삭제에 실패했습니다.'); } } catch { toast.error('서버 오류가 발생했습니다.'); } finally { setIsSaving(false); setShowDeleteDialog(false); } }, [onDelete, router]); const handleBack = useCallback(() => { router.push('/ko/settings/accounts'); }, [router]); const handleEdit = useCallback(() => { setMode('edit'); if (initialData?.id) { router.push(`/ko/settings/accounts/${initialData.id}?mode=edit`); } }, [initialData?.id, router]); // ===== 로딩 ===== if (isLoading) { return (
); } // ===== 렌더링 ===== return (
{/* ===== 기본 정보 ===== */} 기본 정보 {/* 구분 & 유형 */}
handleChange('accountType', v)} options={typeOptions} selectPlaceholder="유형 선택" disabled={disabled} />
{/* 금융기관 & 계좌번호 */}
handleChange('accountNumber', v)} placeholder="계좌번호 입력" disabled={disabled || mode === 'edit'} />
{/* 계좌명 & 예금주/계약자/피보험자 */}
handleChange('accountName', v)} placeholder="계좌명 입력" disabled={disabled} /> handleChange('accountHolder', v)} placeholder={`${holderLabel} 입력`} disabled={disabled} />
{/* 상태 */}
handleChange('status', v)} options={ACCOUNT_STATUS_OPTIONS} selectPlaceholder="상태 선택" disabled={disabled} />
{/* ===== 구분별 상세 정보 ===== */} {formData.category === 'bank_account' && ( )} {formData.category === 'loan_account' && ( )} {formData.category === 'securities_account' && ( )} {formData.category === 'insurance_account' && ( )} {/* ===== 하단 버튼 ===== */}
{isViewMode ? ( <> {onDelete && ( )} ) : ( <> {!isCreateMode && onDelete && ( )} )}
{/* ===== 삭제 확인 ===== */}
); } // ============================================ // 구분별 상세 섹션 컴포넌트 // ============================================ interface SectionProps { formData: AccountFormData; onChange: (field: keyof AccountFormData, value: string | number | undefined) => void; disabled: boolean; } // ===== 은행계좌 정보 ===== function BankAccountSection({ formData, onChange, disabled }: SectionProps) { return ( 계좌 정보
onChange('contractAmount', v)} placeholder="0" disabled={disabled} /> onChange('interestRate', v ? Number(v) : undefined)} placeholder="0.00" disabled={disabled} allowDecimal decimalPlaces={2} suffix="%" />
onChange('startDate', v)} disabled={disabled} /> onChange('maturityDate', v)} disabled={disabled} />
onChange('carryoverBalance', v)} placeholder="0" disabled={disabled} /> onChange('note', v)} placeholder="비고" disabled={disabled} />
); } // ===== 대출계좌 정보 ===== function LoanAccountSection({ formData, onChange, disabled }: SectionProps) { return ( 대출 정보
onChange('loanAmount', v)} placeholder="0" disabled={disabled} /> onChange('interestRate', v ? Number(v) : undefined)} placeholder="0.00" disabled={disabled} allowDecimal decimalPlaces={2} suffix="%" />
onChange('startDate', v)} disabled={disabled} /> onChange('maturityDate', v)} disabled={disabled} />
onChange('loanBalance', v)} placeholder="0" disabled={disabled} /> onChange('interestPaymentCycle', v)} options={[ { value: 'monthly', label: '월납' }, { value: 'quarterly', label: '분기납' }, { value: 'semi_annual', label: '반기납' }, { value: 'annual', label: '연납' }, ]} selectPlaceholder="주기 선택" disabled={disabled} />
onChange('repaymentMethod', v)} options={[ { value: 'equal_principal', label: '원금균등' }, { value: 'equal_installment', label: '원리금균등' }, { value: 'bullet', label: '만기일시' }, { value: 'other', label: '기타' }, ]} selectPlaceholder="방식 선택" disabled={disabled} /> onChange('gracePeriod', v)} placeholder="예: 6개월" disabled={disabled} />
onChange('monthlyRepayment', v)} placeholder="0" disabled={disabled} /> onChange('collateral', v)} placeholder="담보물 입력" disabled={disabled} />
onChange('note', v)} placeholder="비고" disabled={disabled} />
); } // ===== 증권계좌 정보 ===== function SecuritiesAccountSection({ formData, onChange, disabled }: SectionProps) { return ( 증권 정보
onChange('investmentAmount', v)} placeholder="0" disabled={disabled} /> onChange('returnRate', v ? Number(v) : undefined)} placeholder="0.00" disabled={disabled} allowDecimal decimalPlaces={2} suffix="%" />
onChange('startDate', v)} disabled={disabled} /> onChange('maturityDate', v)} disabled={disabled} />
onChange('evaluationAmount', v)} placeholder="0" disabled={disabled} /> onChange('note', v)} placeholder="비고" disabled={disabled} />
); } // ===== 보험계좌 정보 ===== function InsuranceAccountSection({ formData, onChange, disabled }: SectionProps) { return ( 보험 정보 {/* 공통 */}
onChange('contractAmount', v)} placeholder="0" disabled={disabled} /> onChange('interestRate', v ? Number(v) : undefined)} placeholder="0.00" disabled={disabled} allowDecimal decimalPlaces={2} suffix="%" />
onChange('startDate', v)} disabled={disabled} /> onChange('maturityDate', v)} disabled={disabled} />
onChange('surrenderValue', v)} placeholder="0" disabled={disabled} /> onChange('policyNumber', v)} placeholder="증권번호" disabled={disabled} />
onChange('paymentCycle', v)} options={[ { value: 'monthly', label: '월납' }, { value: 'quarterly', label: '분기납' }, { value: 'semi_annual', label: '반기납' }, { value: 'annual', label: '연납' }, { value: 'lump_sum', label: '일시납' }, ]} selectPlaceholder="주기 선택" disabled={disabled} /> onChange('premiumPerCycle', v)} placeholder="0" disabled={disabled} />
{/* 단체보험 전용 */} {formData.accountType === 'group_insurance' && (
onChange('premiumPerPerson', v)} placeholder="0" disabled={disabled} /> onChange('enrolledCount', v ? Number(v) : undefined)} placeholder="0" disabled={disabled} />
)} {/* 화재보험 전용 */} {formData.accountType === 'fire_insurance' && (
onChange('insuredProperty', v)} placeholder="보험 대상물 입력" disabled={disabled} /> onChange('propertyAddress', v)} placeholder="주소 입력" disabled={disabled} />
)} {/* CEO보험 전용 */} {formData.accountType === 'ceo_insurance' && (
onChange('beneficiary', v)} placeholder="수익자 입력" disabled={disabled} />
)} {/* 비고 */}
onChange('note', v)} placeholder="비고" disabled={disabled} />
); }