'use client'; import { useState, useEffect, useCallback } from 'react'; import { useRouter, useSearchParams } from 'next/navigation'; import { CreditCard, Save, Trash2, X, Edit, Loader2, ExternalLink } from 'lucide-react'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { CardNumberInput } from '@/components/ui/card-number-input'; import { formatCardNumber } from '@/lib/formatters'; import { Label } from '@/components/ui/label'; import { Badge } from '@/components/ui/badge'; import { Textarea } from '@/components/ui/textarea'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/select'; import { DeleteConfirmDialog } from '@/components/ui/confirm-dialog'; import { useDeleteDialog } from '@/hooks/useDeleteDialog'; import { PageLayout } from '@/components/organisms/PageLayout'; import { PageHeader } from '@/components/organisms/PageHeader'; import { ContentSkeleton } from '@/components/ui/skeleton'; import { toast } from 'sonner'; import { formatAmountWon as formatCurrency } from '@/lib/utils/amount'; import type { Card as CardType, CardFormData, CardStatus } from './types'; import { CARD_COMPANIES, CARD_TYPE_OPTIONS, PAYMENT_DAY_OPTIONS, CARD_STATUS_LABELS, CARD_STATUS_COLORS, getCardCompanyLabel, } from './types'; import { createCard, updateCard, deleteCard, getActiveEmployees, getApprovalFormUrl, } from './actions'; function formatExpiryDate(value: string): string { if (value && value.length === 4) { return `${value.slice(0, 2)}/${value.slice(2)}`; } return value || '-'; } function getPaymentDayLabel(value: string): string { const option = PAYMENT_DAY_OPTIONS.find(o => o.value === value); return option?.label || value || '-'; } function getCardTypeLabel(value: string): string { const option = CARD_TYPE_OPTIONS.find(o => o.value === value); return option?.label || value || '-'; } interface CardDetailProps { card?: CardType; mode: 'create' | 'view' | 'edit'; isLoading?: boolean; } export function CardDetail({ card, mode: initialMode, isLoading }: CardDetailProps) { const router = useRouter(); const searchParams = useSearchParams(); const [mode, setMode] = useState(initialMode); const [isSaving, setIsSaving] = useState(false); const [isLoadingApproval, setIsLoadingApproval] = useState(false); const [employees, setEmployees] = useState>([]); useEffect(() => { const urlMode = searchParams.get('mode'); if (urlMode === 'edit' && card) setMode('edit'); }, [searchParams, card]); // 직원 목록 로드 (수정/등록 모드) useEffect(() => { if (mode !== 'view') { getActiveEmployees().then(result => { if (result.success && result.data) setEmployees(result.data); }); } }, [mode]); const [formData, setFormData] = useState({ cardCompany: card?.cardCompany || '', cardType: card?.cardType || '', cardNumber: card?.cardNumber || '', cardName: card?.cardName || '', alias: card?.alias || '', expiryDate: card?.expiryDate || '', csv: card?.csv || '', paymentDay: card?.paymentDay || '', pinPrefix: '', totalLimit: card?.totalLimit || 0, usedAmount: card?.usedAmount || 0, remainingLimit: card?.remainingLimit || 0, status: card?.status || 'active', userId: card?.user?.id || '', departmentId: card?.user?.departmentId || '', positionId: card?.user?.positionId || '', memo: card?.memo || '', }); const isViewMode = mode === 'view'; const isCreateMode = mode === 'create'; const handleChange = useCallback((field: keyof CardFormData, value: string | number) => { setFormData(prev => ({ ...prev, [field]: value })); }, []); const handleBack = () => { router.push('/ko/hr/card-management'); }; const handleSubmit = async () => { if (!formData.cardCompany) { toast.error('카드사를 선택해주세요.'); return; } setIsSaving(true); try { if (isCreateMode) { const result = await createCard(formData); if (result.success) { toast.success('카드가 등록되었습니다.'); router.push('/ko/hr/card-management'); } else { toast.error(result.error || '카드 등록에 실패했습니다.'); } } else { if (!card?.id) return; const result = await updateCard(card.id, formData); if (result.success) { toast.success('카드가 수정되었습니다.'); router.push('/ko/hr/card-management'); } else { toast.error(result.error || '카드 수정에 실패했습니다.'); } } } catch { toast.error('저장 중 오류가 발생했습니다.'); } finally { setIsSaving(false); } }; const deleteDialog = useDeleteDialog({ onDelete: async (id) => deleteCard(id), onSuccess: () => router.push('/ko/hr/card-management'), entityName: '카드', }); const handleCancel = () => { if (isCreateMode) { router.push('/ko/hr/card-management'); } else { setMode('view'); if (card) { setFormData({ cardCompany: card.cardCompany || '', cardType: card.cardType || '', cardNumber: card.cardNumber || '', cardName: card.cardName || '', alias: card.alias || '', expiryDate: card.expiryDate || '', csv: card.csv || '', paymentDay: card.paymentDay || '', pinPrefix: '', totalLimit: card.totalLimit || 0, usedAmount: card.usedAmount || 0, remainingLimit: card.remainingLimit || 0, status: card.status || 'active', userId: card.user?.id || '', departmentId: card.user?.departmentId || '', positionId: card.user?.positionId || '', memo: card.memo || '', }); } } }; const handleEdit = () => { setMode('edit'); if (card?.id) { router.push(`/ko/hr/card-management/${card.id}?mode=edit`); } }; const handleApprovalForm = async () => { if (!card?.id) return; setIsLoadingApproval(true); try { const result = await getApprovalFormUrl(card.id); if (result.success && result.data?.url) { window.open(result.data.url, '_blank'); } else { toast.error(result.error || '품의서 작성 페이지 URL 조회에 실패했습니다.'); } } catch { toast.error('품의서 작성 URL 조회 중 오류가 발생했습니다.'); } finally { setIsLoadingApproval(false); } }; if (isLoading) { return ( ); } // ===== 뷰 모드 ===== if (isViewMode) { return (
{/* 기본 정보 */} 기본 정보
카드사
{getCardCompanyLabel(card?.cardCompany || '')}
종류
{getCardTypeLabel(card?.cardType || '')}
카드번호
{card?.cardNumber ? formatCardNumber(card.cardNumber) : '-'}
카드명
{card?.cardName || '-'}
카드 별칭
{card?.alias || '-'}
유효기간(15/05)
{formatExpiryDate(card?.expiryDate || '')}
CSV
{card?.csv || '-'}
결제일
{getPaymentDayLabel(card?.paymentDay || '')}
총 한도
{formatCurrency(card?.totalLimit || 0)}
사용 금액
{formatCurrency(card?.usedAmount || 0)}
잔여한도
{formatCurrency(card?.remainingLimit || 0)}
상태
{CARD_STATUS_LABELS[card?.status || 'active']}
{/* 사용자 정보 */} 사용자 정보
부서
{card?.user?.departmentName || '-'}
사용자
{card?.user?.employeeName || '-'}
직책
{card?.user?.positionName || '-'}
메모
{card?.memo || '-'}
{/* 선결제 신청 */} 선결제 신청

한도를 초과한 월 경우 품의서를 작성해서 승인을 요청하세요

{/* 하단 버튼 */}
카드를 정말 삭제하시겠습니까?
삭제된 카드 정보는 복구할 수 없습니다. } onConfirm={deleteDialog.single.confirm} loading={deleteDialog.isPending} />
); } // ===== 생성/수정 모드 ===== return (
{/* 기본 정보 */} 기본 정보 {/* Row 1: 카드사 | 종류 | 카드번호 | 카드명 */}
handleChange('cardNumber', v)} />
handleChange('cardName', e.target.value)} placeholder="카드명" />
{/* Row 2: 카드 별칭 | 유효기간 | CSV | 결제일 */}
handleChange('alias', e.target.value)} placeholder="별칭" />
handleChange('expiryDate', e.target.value)} placeholder="MMYY" maxLength={4} />
handleChange('csv', e.target.value)} placeholder="CSV" maxLength={4} />
{/* Row 3: 총 한도 | 사용 금액 | 잔여한도 | 상태 */}
handleChange('totalLimit', Number(e.target.value) || 0)} placeholder="0" />
handleChange('usedAmount', Number(e.target.value) || 0)} placeholder="0" />
handleChange('remainingLimit', Number(e.target.value) || 0)} placeholder="0" />
{/* 사용자 정보 */} 사용자 정보