summaryrefslogtreecommitdiff
path: root/lib/dolce/components
diff options
context:
space:
mode:
Diffstat (limited to 'lib/dolce/components')
-rw-r--r--lib/dolce/components/file-upload-progress-list.tsx98
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>
+ );
+}
+