diff options
Diffstat (limited to 'lib/dolce/components')
| -rw-r--r-- | lib/dolce/components/file-upload-progress-list.tsx | 98 |
1 files changed, 98 insertions, 0 deletions
diff --git a/lib/dolce/components/file-upload-progress-list.tsx b/lib/dolce/components/file-upload-progress-list.tsx new file mode 100644 index 00000000..e016402d --- /dev/null +++ b/lib/dolce/components/file-upload-progress-list.tsx @@ -0,0 +1,98 @@ +"use client"; + +import { FileText, CheckCircle2, XCircle, Loader2 } from "lucide-react"; +import { Progress } from "@/components/ui/progress"; +import { FileUploadProgress } from "../hooks/use-file-upload-with-progress"; + +interface FileUploadProgressListProps { + fileProgresses: FileUploadProgress[]; +} + +export function FileUploadProgressList({ fileProgresses }: FileUploadProgressListProps) { + if (fileProgresses.length === 0) { + return null; + } + + return ( + <div className="space-y-3"> + <h4 className="text-sm font-medium"> + 파일 업로드 진행 상황 ({fileProgresses.length}개) + </h4> + <div className="max-h-64 overflow-auto space-y-2"> + {fileProgresses.map((fileProgress, index) => ( + <FileUploadProgressItem key={index} fileProgress={fileProgress} /> + ))} + </div> + </div> + ); +} + +interface FileUploadProgressItemProps { + fileProgress: FileUploadProgress; +} + +function FileUploadProgressItem({ fileProgress }: FileUploadProgressItemProps) { + const { file, progress, status, error } = fileProgress; + + const getStatusIcon = () => { + switch (status) { + case "completed": + return <CheckCircle2 className="h-4 w-4 text-green-600 shrink-0" />; + case "error": + return <XCircle className="h-4 w-4 text-red-600 shrink-0" />; + case "uploading": + return <Loader2 className="h-4 w-4 text-primary shrink-0 animate-spin" />; + default: + return <FileText className="h-4 w-4 text-muted-foreground shrink-0" />; + } + }; + + const getStatusColor = () => { + switch (status) { + case "completed": + return "border-green-200 bg-green-50/50"; + case "error": + return "border-red-200 bg-red-50/50"; + case "uploading": + return "border-primary/30 bg-primary/5"; + default: + return "border-muted bg-muted/30"; + } + }; + + return ( + <div className={`border rounded-lg p-3 space-y-2 ${getStatusColor()}`}> + <div className="flex items-center gap-2"> + {getStatusIcon()} + <div className="flex-1 min-w-0"> + <p className="text-sm font-medium truncate">{file.name}</p> + <p className="text-xs text-muted-foreground"> + {(file.size / 1024 / 1024).toFixed(2)} MB + </p> + </div> + {status !== "pending" && ( + <div className="text-sm font-medium"> + {status === "completed" ? ( + <span className="text-green-600">완료</span> + ) : status === "error" ? ( + <span className="text-red-600">실패</span> + ) : ( + <span className="text-primary">{Math.round(progress)}%</span> + )} + </div> + )} + </div> + + {/* Progress Bar */} + {status === "uploading" && ( + <Progress value={progress} className="h-1.5" /> + )} + + {/* 에러 메시지 */} + {status === "error" && error && ( + <p className="text-xs text-red-600">{error}</p> + )} + </div> + ); +} + |
