diff options
Diffstat (limited to 'lib/vendor-pool/table/import-result-dialog.tsx')
| -rw-r--r-- | lib/vendor-pool/table/import-result-dialog.tsx | 167 |
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> + ) +} + |
