summaryrefslogtreecommitdiff
path: root/lib/vendor-pool/table/import-result-dialog.tsx
diff options
context:
space:
mode:
authorjoonhoekim <26rote@gmail.com>2025-11-05 16:07:43 +0900
committerjoonhoekim <26rote@gmail.com>2025-11-05 16:07:43 +0900
commit208ed7ff11d0f822d3d243c5833d31973904349e (patch)
treeb1a15e3a7f8242294397f433a8484102c5743e69 /lib/vendor-pool/table/import-result-dialog.tsx
parentad6bde0250cfe014d5f78747ec76ac59df95a25d (diff)
(김준회) vendor-pool: excel-import 코드기반검색처리, 템플릿 변경 (구매 김수진 프로 요청사항)
Diffstat (limited to 'lib/vendor-pool/table/import-result-dialog.tsx')
-rw-r--r--lib/vendor-pool/table/import-result-dialog.tsx167
1 files changed, 167 insertions, 0 deletions
diff --git a/lib/vendor-pool/table/import-result-dialog.tsx b/lib/vendor-pool/table/import-result-dialog.tsx
new file mode 100644
index 00000000..2e541271
--- /dev/null
+++ b/lib/vendor-pool/table/import-result-dialog.tsx
@@ -0,0 +1,167 @@
+"use client"
+
+import React from 'react'
+import {
+ Dialog,
+ DialogContent,
+ DialogDescription,
+ DialogHeader,
+ DialogTitle,
+} from "@/components/ui/dialog"
+import { Button } from "@/components/ui/button"
+import { ScrollArea } from "@/components/ui/scroll-area"
+import { CheckCircle2, XCircle, AlertCircle, RefreshCw, FileText } from "lucide-react"
+import { Separator } from "@/components/ui/separator"
+import { Badge } from "@/components/ui/badge"
+
+export interface ImportResultItem {
+ rowNumber: number
+ status: 'success' | 'error' | 'duplicate' | 'warning'
+ message: string
+ data?: {
+ vendorName?: string
+ materialGroupName?: string
+ designCategory?: string
+ }
+}
+
+export interface ImportResult {
+ totalRows: number
+ successCount: number
+ errorCount: number
+ duplicateCount: number
+ items: ImportResultItem[] // 실패한 건만 포함
+}
+
+interface ImportResultDialogProps {
+ open: boolean
+ onOpenChange: (open: boolean) => void
+ result: ImportResult | null
+}
+
+export function ImportResultDialog({ open, onOpenChange, result }: ImportResultDialogProps) {
+ if (!result) return null
+
+ const getStatusIcon = (status: ImportResultItem['status']) => {
+ switch (status) {
+ case 'success':
+ return <CheckCircle2 className="h-4 w-4 text-green-600" />
+ case 'error':
+ return <XCircle className="h-4 w-4 text-red-600" />
+ case 'duplicate':
+ return <RefreshCw className="h-4 w-4 text-yellow-600" />
+ case 'warning':
+ return <AlertCircle className="h-4 w-4 text-orange-600" />
+ }
+ }
+
+ const getStatusBadge = (status: ImportResultItem['status']) => {
+ switch (status) {
+ case 'success':
+ return <Badge variant="default" className="bg-green-600">성공</Badge>
+ case 'error':
+ return <Badge variant="destructive">실패</Badge>
+ case 'duplicate':
+ return <Badge variant="secondary" className="bg-yellow-600 text-white">중복</Badge>
+ case 'warning':
+ return <Badge variant="secondary" className="bg-orange-600 text-white">경고</Badge>
+ }
+ }
+
+ return (
+ <Dialog open={open} onOpenChange={onOpenChange}>
+ <DialogContent className="max-w-3xl max-h-[80vh]">
+ <DialogHeader>
+ <DialogTitle className="flex items-center gap-2">
+ <FileText className="h-5 w-5" />
+ 엑셀 Import 결과
+ </DialogTitle>
+ <DialogDescription>
+ 총 {result.totalRows}건의 데이터를 처리했습니다.
+ </DialogDescription>
+ </DialogHeader>
+
+ {/* 요약 정보 */}
+ <div className="grid grid-cols-3 gap-4 py-4">
+ <div className="flex flex-col items-center p-4 bg-green-50 rounded-lg border border-green-200">
+ <CheckCircle2 className="h-8 w-8 text-green-600 mb-2" />
+ <div className="text-2xl font-bold text-green-700">{result.successCount}</div>
+ <div className="text-sm text-green-600">성공</div>
+ </div>
+
+ <div className="flex flex-col items-center p-4 bg-yellow-50 rounded-lg border border-yellow-200">
+ <RefreshCw className="h-8 w-8 text-yellow-600 mb-2" />
+ <div className="text-2xl font-bold text-yellow-700">{result.duplicateCount}</div>
+ <div className="text-sm text-yellow-600">중복</div>
+ </div>
+
+ <div className="flex flex-col items-center p-4 bg-red-50 rounded-lg border border-red-200">
+ <XCircle className="h-8 w-8 text-red-600 mb-2" />
+ <div className="text-2xl font-bold text-red-700">{result.errorCount}</div>
+ <div className="text-sm text-red-600">실패</div>
+ </div>
+ </div>
+
+ {/* 상세 정보 - 실패한 건만 표시 */}
+ {result.errorCount > 0 && (
+ <>
+ <Separator />
+ <div className="space-y-2">
+ <h4 className="text-sm font-semibold text-red-600">실패한 항목 ({result.errorCount}건)</h4>
+ <ScrollArea className="h-[300px] rounded-md border p-4">
+ <div className="space-y-3">
+ {result.items.map((item, index) => (
+ <div
+ key={index}
+ className="flex items-start gap-3 p-3 rounded-lg bg-red-50 border border-red-200 hover:bg-red-100 transition-colors"
+ >
+ <div className="flex-shrink-0 mt-0.5">
+ {getStatusIcon(item.status)}
+ </div>
+ <div className="flex-1 min-w-0">
+ <div className="flex items-center gap-2 mb-1">
+ <span className="text-sm font-medium text-muted-foreground">
+ 행 {item.rowNumber}
+ </span>
+ {getStatusBadge(item.status)}
+ </div>
+ <p className="text-sm text-foreground">{item.message}</p>
+ {item.data && (
+ <div className="mt-2 text-xs text-muted-foreground space-y-1">
+ {item.data.vendorName && (
+ <div>• 협력업체: {item.data.vendorName}</div>
+ )}
+ {item.data.materialGroupName && (
+ <div>• 자재그룹: {item.data.materialGroupName}</div>
+ )}
+ {item.data.designCategory && (
+ <div>• 설계기능: {item.data.designCategory}</div>
+ )}
+ </div>
+ )}
+ </div>
+ </div>
+ ))}
+
+ {result.items.length === 0 && (
+ <div className="text-center text-muted-foreground py-8">
+ 모든 항목이 정상적으로 처리되었습니다.
+ </div>
+ )}
+ </div>
+ </ScrollArea>
+ </div>
+ </>
+ )}
+
+ {/* 하단 버튼 */}
+ <div className="flex justify-end gap-2">
+ <Button variant="outline" onClick={() => onOpenChange(false)}>
+ 닫기
+ </Button>
+ </div>
+ </DialogContent>
+ </Dialog>
+ )
+}
+