'use client'; import { useState, useCallback } from 'react'; import { useRouter } from 'next/navigation'; import { format, subDays, subMonths } from 'date-fns'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/select'; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from '@/components/ui/table'; import { DateRangeSelector } from '@/components/molecules/DateRangeSelector'; import { PageLayout, PageHeader, StatCards, EmptyState, SearchableSelectionModal, } from '@/components/organisms'; import { Checkbox } from '@/components/ui/checkbox'; import { FileText, DollarSign, Calculator, Hash, Settings, PlusCircle, Search } from 'lucide-react'; import { toast } from 'sonner'; import { SupplierSettingModal } from './SupplierSettingModal'; import { TaxInvoiceForm } from './TaxInvoiceForm'; import { createTaxInvoice, searchVendorsForTaxInvoice } from './actions'; import { DATE_TYPE_OPTIONS, STATUS_OPTIONS, SORT_BY_OPTIONS, SORT_ORDER_OPTIONS, TAX_INVOICE_STATUS_MAP, createEmptyBusinessEntity, } from './types'; import type { TaxInvoiceRecord, SupplierSettings, VendorSearchItem, FilterState, TaxInvoiceFormData, BusinessEntity, } from './types'; interface TaxInvoiceIssuancePageProps { initialData: TaxInvoiceRecord[]; initialSupplierSettings: SupplierSettings; } export function TaxInvoiceIssuancePage({ initialData, initialSupplierSettings, }: TaxInvoiceIssuancePageProps) { const router = useRouter(); // 데이터 const [records, setRecords] = useState(initialData); const [supplierSettings, setSupplierSettings] = useState(initialSupplierSettings); // UI 상태 const [showNewForm, setShowNewForm] = useState(false); const [showSupplierModal, setShowSupplierModal] = useState(false); const [showVendorSearch, setShowVendorSearch] = useState(false); const [selectedVendor, setSelectedVendor] = useState(null); const [selectedIds, setSelectedIds] = useState>(new Set()); // 필터 const [filters, setFilters] = useState({ dateType: 'write', startDate: format(subMonths(new Date(), 1), 'yyyy-MM-dd'), endDate: format(new Date(), 'yyyy-MM-dd'), vendorSearch: '', status: 'all', sortBy: 'writeDate', sortOrder: 'desc', }); const updateFilter = useCallback((field: keyof FilterState, value: string) => { setFilters((prev) => ({ ...prev, [field]: value })); }, []); // 통계 const totalSupply = records.reduce((sum, r) => sum + r.supplyAmount, 0); const totalTax = records.reduce((sum, r) => sum + r.taxAmount, 0); const totalAmount = records.reduce((sum, r) => sum + r.totalAmount, 0); const issuedCount = records.filter((r) => r.status === 'issued' || r.status === 'nts_sent').length; const sentCount = records.filter((r) => r.status === 'nts_sent').length; const stats = [ { label: '발행건수', sublabel: `발행 ${issuedCount} / 전송 ${sentCount}`, value: `${totalAmount.toLocaleString('ko-KR')}원`, icon: Hash, iconColor: 'text-blue-600', }, { label: '총 합계금액', value: `${totalAmount.toLocaleString('ko-KR')}원`, icon: Calculator, iconColor: 'text-green-600', }, { label: '총 공급가액', value: `${totalSupply.toLocaleString('ko-KR')}원`, icon: DollarSign, iconColor: 'text-purple-600', }, { label: '총 세액', value: `${totalTax.toLocaleString('ko-KR')}원`, icon: FileText, iconColor: 'text-orange-600', }, ]; // 세금계산서 발행 처리 const handleSubmitInvoice = useCallback( async (data: TaxInvoiceFormData) => { const result = await createTaxInvoice(data); if (result.success && result.data) { setRecords((prev) => [result.data!, ...prev]); setShowNewForm(false); setSelectedVendor(null); toast.success('세금계산서가 발행되었습니다.'); } else { toast.error(result.error || '발행에 실패했습니다.'); } }, [] ); // 거래처 선택 처리 const handleVendorSelect = useCallback((vendor: VendorSearchItem) => { setSelectedVendor({ businessNumber: vendor.businessNumber, companyName: vendor.companyName, representativeName: vendor.representativeName, address: vendor.address, businessType: vendor.businessType, businessItem: vendor.businessItem, contactName: vendor.contactName, contactPhone: vendor.contactPhone, contactEmail: vendor.contactEmail, }); setShowVendorSearch(false); }, []); // 기간 단축 버튼 const handleQuickDate = useCallback( (type: '1week' | '1month' | '3months') => { const today = new Date(); const endDate = format(today, 'yyyy-MM-dd'); let startDate: string; switch (type) { case '1week': startDate = format(subDays(today, 7), 'yyyy-MM-dd'); break; case '1month': startDate = format(subMonths(today, 1), 'yyyy-MM-dd'); break; case '3months': startDate = format(subMonths(today, 3), 'yyyy-MM-dd'); break; } setFilters((prev) => ({ ...prev, startDate, endDate })); }, [] ); const handleSearch = useCallback(() => { // TODO: 실제 API 연동 시 필터 조건으로 getTaxInvoices 호출 toast.info('조회 기능은 API 연동 후 사용 가능합니다.'); }, []); return ( {/* 통계 카드 */} {/* 전자세금계산서 발행 섹션 */} 전자세금계산서 발행
{showNewForm && ( { setShowNewForm(false); setSelectedVendor(null); }} onVendorSearch={() => setShowVendorSearch(true)} selectedVendor={selectedVendor} /> )}
{/* 필터 */} {/* Row1: 일자타입 + 날짜범위 + 기간 버튼 + 조회 */}
updateFilter('startDate', v)} onEndDateChange={(v) => updateFilter('endDate', v)} hidePresets />
{/* Row2: 거래처 검색 */}
updateFilter('vendorSearch', e.target.value)} placeholder="사업자 번호 또는 사업자명" className="pl-9 h-9" />
{/* Row3: 상태 + 정렬 */}
{/* 테이블 */}
0 && selectedIds.size === records.length} onCheckedChange={(checked) => { if (checked) { setSelectedIds(new Set(records.map((r) => r.id))); } else { setSelectedIds(new Set()); } }} /> 번호 발행번호 공급받는자 작성일자 전송일자 공급가액 세액 합계금액 상태 {records.length > 0 ? ( records.map((record, index) => { const statusInfo = TAX_INVOICE_STATUS_MAP[record.status]; return ( router.push(`?mode=edit&id=${record.id}`)} > e.stopPropagation()}> { setSelectedIds((prev) => { const next = new Set(prev); if (checked) next.add(record.id); else next.delete(record.id); return next; }); }} /> {index + 1} {record.invoiceNumber} {record.vendorName} {record.writeDate} {record.sendDate || '-'} {record.supplyAmount.toLocaleString('ko-KR')} {record.taxAmount.toLocaleString('ko-KR')} {record.totalAmount.toLocaleString('ko-KR')} {statusInfo.label} ); }) ) : ( )}
{/* 공급자 설정 모달 */} {/* 거래처 검색 모달 */} open={showVendorSearch} onOpenChange={setShowVendorSearch} title="거래처 검색" searchPlaceholder="거래처명, 사업자번호, 담당자명" fetchData={searchVendorsForTaxInvoice} keyExtractor={(item) => item.id} mode="single" onSelect={handleVendorSelect} searchMode="enter" dialogClassName="sm:max-w-xl" listContainerClassName="max-h-[400px] overflow-y-auto border rounded-md" noResultMessage="거래처가 없습니다." emptyQueryMessage="거래처명 또는 사업자번호를 입력하세요." listWrapper={(children) => (
거래처명
사업자번호
{children}
)} renderItem={(item) => (
{item.companyName}
{item.businessNumber}
)} />
); }