"use client" import * as React from "react" import { toast } from "sonner" import { useTransition } from "react" import { Upload, FileSpreadsheet, AlertCircle, CheckCircle, X } from "lucide-react" import { Button } from "@/components/ui/button" import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from "@/components/ui/dialog" import { Tabs, TabsContent, TabsList, TabsTrigger, } from "@/components/ui/tabs" import { Input } from "@/components/ui/input" import { Label } from "@/components/ui/label" import { Checkbox } from "@/components/ui/checkbox" import { ScrollArea } from "@/components/ui/scroll-area" import { Badge } from "@/components/ui/badge" import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "@/components/ui/table" import { parseEsgExcelFile, validateExcelData, type ParsedExcelData } from "./excel-utils" import { importEsgDataFromExcel, checkDuplicateSerials, type ImportOptions } from "./excel-actions" interface ExcelImportDialogProps { open: boolean onOpenChange: (open: boolean) => void onSuccess: () => void } export function ExcelImportDialog({ open, onOpenChange, onSuccess, }: ExcelImportDialogProps) { const [isPending, startTransition] = useTransition() const [file, setFile] = React.useState(null) const [parsedData, setParsedData] = React.useState(null) const [validationErrors, setValidationErrors] = React.useState([]) const [duplicateSerials, setDuplicateSerials] = React.useState([]) const [currentStep, setCurrentStep] = React.useState<'upload' | 'preview' | 'options'>('upload') // 임포트 옵션 const [importOptions, setImportOptions] = React.useState({ skipDuplicates: false, updateExisting: false, }) // 파일 선택 처리 const handleFileChange = (event: React.ChangeEvent) => { const selectedFile = event.target.files?.[0] if (selectedFile) { if (!selectedFile.name.endsWith('.xlsx') && !selectedFile.name.endsWith('.xls')) { toast.error('Excel 파일(.xlsx, .xls)만 업로드 가능합니다.') return } setFile(selectedFile) } } // 파일 파싱 const handleParseFile = async () => { if (!file) return startTransition(async () => { try { const data = await parseEsgExcelFile(file) setParsedData(data) // 검증 const errors = validateExcelData(data) setValidationErrors(errors) // 중복 확인 const serials = data.evaluations.map(e => e.serialNumber) const duplicates = await checkDuplicateSerials(serials) setDuplicateSerials(duplicates) setCurrentStep('preview') } catch (error) { console.error('Parsing error:', error) toast.error(error instanceof Error ? error.message : 'Excel 파일 파싱에 실패했습니다.') } }) } // 임포트 실행 const handleImport = async () => { if (!parsedData) return startTransition(async () => { try { const result = await importEsgDataFromExcel(parsedData, importOptions) if (result.success) { toast.success(result.message) onSuccess() onOpenChange(false) } else { toast.error(result.message) } // 상세 결과가 있으면 콘솔에 출력 if (result.details.errors.length > 0) { console.warn('Import errors:', result.details.errors) } } catch (error) { console.error('Import error:', error) toast.error('임포트 중 오류가 발생했습니다.') } }) } // 다이얼로그 닫기 시 상태 리셋 const handleClose = () => { setFile(null) setParsedData(null) setValidationErrors([]) setDuplicateSerials([]) setCurrentStep('upload') setImportOptions({ skipDuplicates: false, updateExisting: false }) onOpenChange(false) } const canProceed = parsedData && validationErrors.length === 0 const hasDuplicates = duplicateSerials.length > 0 return ( Excel 데이터 임포트 Excel 파일에서 ESG 평가표 데이터를 임포트합니다.
파일 업로드 데이터 미리보기 임포트 옵션 {/* 파일 업로드 탭 */}
{file && (
{file.name} {(file.size / 1024).toFixed(1)} KB
)}
{/* 데이터 미리보기 탭 */} {parsedData && (
{/* 검증 결과 */}
{validationErrors.length > 0 ? (
검증 오류
    {validationErrors.map((error, index) => (
  • • {error}
  • ))}
) : (
검증 완료
)} {/* 중복 알림 */} {hasDuplicates && (
중복 데이터 발견

다음 시리얼번호가 이미 존재합니다:

{duplicateSerials.map(serial => ( {serial} ))}
)}
{/* 데이터 요약 */}
{parsedData.evaluations.length}
평가표
{parsedData.evaluationItems.length}
평가항목
{parsedData.answerOptions.length}
답변옵션
{/* 평가표 미리보기 */}

평가표 미리보기

시리얼번호 분류 점검항목 {parsedData.evaluations.slice(0, 10).map((evaluation, index) => ( {evaluation.serialNumber} {duplicateSerials.includes(evaluation.serialNumber) && ( 중복 )} {evaluation.category} {evaluation.inspectionItem} ))}
{parsedData.evaluations.length > 10 && (

...외 {parsedData.evaluations.length - 10}개 더

)}
{canProceed && ( )}
)}
{/* 임포트 옵션 탭 */}

임포트 옵션

{hasDuplicates && (
setImportOptions(prev => ({ ...prev, skipDuplicates: !!checked, updateExisting: false, // 상호 배타적 })) } />
setImportOptions(prev => ({ ...prev, updateExisting: !!checked, skipDuplicates: false, // 상호 배타적 })) } />

주의: 기존 데이터 업데이트를 선택하면 해당 평가표의 모든 평가항목과 답변옵션이 교체됩니다.

)}
) }