"use client" import * as React from "react" import { Upload, Download, FileText, AlertCircle, CheckCircle2, X } from "lucide-react" import { Button } from "@/components/ui/button" import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger, } from "@/components/ui/dialog" import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "@/components/ui/table" import { Badge } from "@/components/ui/badge" import { Alert, AlertDescription } from "@/components/ui/alert" import { ScrollArea } from "@/components/ui/scroll-area" import { Separator } from "@/components/ui/separator" import { type ExcelColumnDef } from "@/lib/export" import { downloadExcelTemplate, parseExcelFile } from "./excel-import" import { type GtcClauseTreeView } from "@/db/schema/gtc" import { toast } from "@/hooks/use-toast" interface ImportExcelDialogProps { documentId: number columns: ExcelColumnDef[] onSuccess?: () => void onImport?: (data: Partial[]) => Promise trigger?: React.ReactNode } type ImportStep = "upload" | "preview" | "importing" | "complete" export function ImportExcelDialog({ documentId, columns, onSuccess, onImport, trigger, }: ImportExcelDialogProps) { const [open, setOpen] = React.useState(false) const [step, setStep] = React.useState("upload") const [selectedFile, setSelectedFile] = React.useState(null) const [parsedData, setParsedData] = React.useState[]>([]) const [errors, setErrors] = React.useState([]) const [isProcessing, setIsProcessing] = React.useState(false) const fileInputRef = React.useRef(null) // 다이얼로그 열기/닫기 시 상태 초기화 const handleOpenChange = (isOpen: boolean) => { setOpen(isOpen) if (!isOpen) { // 다이얼로그 닫을 때 상태 초기화 setStep("upload") setSelectedFile(null) setParsedData([]) setErrors([]) setIsProcessing(false) if (fileInputRef.current) { fileInputRef.current.value = "" } } } // 템플릿 다운로드 const handleDownloadTemplate = async () => { try { await downloadExcelTemplate(columns, { filename: `gtc-clauses-template-${new Date().toISOString().split('T')[0]}`, includeExampleData: true, useGroupHeader: true, }) toast({ title: "템플릿 다운로드 완료", description: "Excel 템플릿이 다운로드되었습니다. 템플릿에 데이터를 입력한 후 업로드해주세요.", }) } catch (error) { toast({ title: "템플릿 다운로드 실패", description: "템플릿 다운로드 중 오류가 발생했습니다.", variant: "destructive", }) } } // 파일 선택 const handleFileSelect = (event: React.ChangeEvent) => { const file = event.target.files?.[0] if (file) { if (file.type !== "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" && file.type !== "application/vnd.ms-excel") { toast({ title: "잘못된 파일 형식", description: "Excel 파일(.xlsx, .xls)만 업로드할 수 있습니다.", variant: "destructive", }) return } setSelectedFile(file) } } // 파일 파싱 const handleParseFile = async () => { if (!selectedFile) return setIsProcessing(true) try { const result = await parseExcelFile( selectedFile, columns, { hasGroupHeader: true, sheetName: "GTC조항템플릿", } ) setParsedData(result.data) setErrors(result.errors) if (result.errors.length > 0) { toast({ title: "파싱 완료 (오류 있음)", description: `${result.data.length}개의 행을 파싱했지만 ${result.errors.length}개의 오류가 있습니다.`, variant: "destructive", }) } else { toast({ title: "파싱 완료", description: `${result.data.length}개의 행이 성공적으로 파싱되었습니다.`, }) } setStep("preview") } catch (error) { toast({ title: "파싱 실패", description: "파일 파싱 중 오류가 발생했습니다.", variant: "destructive", }) } finally { setIsProcessing(false) } } // 데이터 가져오기 실행 const handleImportData = async () => { if (parsedData.length === 0 || !onImport) return setStep("importing") try { await onImport(parsedData) setStep("complete") toast({ title: "가져오기 완료", description: `${parsedData.length}개의 조항이 성공적으로 가져와졌습니다.`, }) // 성공 콜백 호출 후 잠시 후 다이얼로그 닫기 setTimeout(() => { onSuccess?.() setOpen(false) }, 2000) } catch (error) { toast({ title: "가져오기 실패", description: "데이터 가져오기 중 오류가 발생했습니다.", variant: "destructive", }) setStep("preview") } } const renderUploadStep = () => (

Excel 파일로 조항 가져오기

먼저 템플릿을 다운로드하여 데이터를 입력한 후 업로드해주세요.

템플릿에는 입력 가이드와 예시 데이터가 포함되어 있습니다.

{selectedFile && ( )}
) const renderPreviewStep = () => (

데이터 미리보기

{parsedData.length}개 행 {errors.length > 0 && ( {errors.length}개 오류 )}
{errors.length > 0 && (
다음 오류들을 확인해주세요:
    {errors.slice(0, 5).map((error, index) => (
  • {error}
  • ))} {errors.length > 5 && (
  • ... 및 {errors.length - 5}개 추가 오류
  • )}
)} # 채번 소제목 상세항목 분류 상태 {parsedData.map((item, index) => ( {index + 1} {item.itemNumber || "-"} {item.subtitle || "-"} {item.content || "-"} {item.category || "-"} {item.isActive ? "활성" : "비활성"} ))}
) const renderImportingStep = () => (

가져오는 중...

{parsedData.length}개의 조항을 데이터베이스에 저장하고 있습니다.

) const renderCompleteStep = () => (

가져오기 완료!

{parsedData.length}개의 조항이 성공적으로 가져와졌습니다.

) return ( {trigger || ( )} Excel에서 조항 가져오기 Excel 파일을 사용하여 여러 조항을 한 번에 가져올 수 있습니다.
{step === "upload" && renderUploadStep()} {step === "preview" && renderPreviewStep()} {step === "importing" && renderImportingStep()} {step === "complete" && renderCompleteStep()}
) }