From 50adedf48ee4674ebe00f1ee72d93485183cdc51 Mon Sep 17 00:00:00 2001
From: dujinkim <dujin.kim@dtsolution.co.kr>
Date: Fri, 5 Sep 2025 11:44:32 +0000
Subject: (대표님, 최겸, 임수민) EDP 입력 진행률, 견적목록관리, EDP excel
 import 오류수정, GTC-Contract
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../attachment/revision-historty-dialog.tsx        | 305 +++++++++++++++++++++
 1 file changed, 305 insertions(+)
 create mode 100644 lib/rfq-last/attachment/revision-historty-dialog.tsx

(limited to 'lib/rfq-last/attachment/revision-historty-dialog.tsx')

diff --git a/lib/rfq-last/attachment/revision-historty-dialog.tsx b/lib/rfq-last/attachment/revision-historty-dialog.tsx
new file mode 100644
index 00000000..6e4772cb
--- /dev/null
+++ b/lib/rfq-last/attachment/revision-historty-dialog.tsx
@@ -0,0 +1,305 @@
+// @/lib/rfq-last/attachment/revision-history-dialog.tsx
+
+"use client";
+
+import * as React from "react";
+import {
+  Dialog,
+  DialogContent,
+  DialogDescription,
+  DialogHeader,
+  DialogTitle,
+} from "@/components/ui/dialog";
+import {
+  Table,
+  TableBody,
+  TableCell,
+  TableHead,
+  TableHeader,
+  TableRow,
+} from "@/components/ui/table";
+import { Badge } from "@/components/ui/badge";
+import { Button } from "@/components/ui/button";
+import { ScrollArea } from "@/components/ui/scroll-area";
+import { Skeleton } from "@/components/ui/skeleton";
+import { Alert, AlertDescription } from "@/components/ui/alert";
+import {
+  Download,
+  Eye,
+  FileText,
+  Clock,
+  User,
+  MessageSquare,
+  AlertCircle,
+  CheckCircle,
+} from "lucide-react";
+import { format, formatDistanceToNow } from "date-fns";
+import { ko } from "date-fns/locale";
+import { toast } from "sonner";
+import { downloadFile } from "@/lib/file-download";
+import {
+  getRevisionHistory,
+  type AttachmentWithHistory,
+  type RevisionHistory,
+} from "../service";
+import { formatFileSize } from "@/lib/utils";
+
+interface RevisionHistoryDialogProps {
+  open: boolean;
+  onOpenChange: (open: boolean) => void;
+  attachmentId: number;
+  attachmentName?: string;
+}
+
+export function RevisionHistoryDialog({
+  open,
+  onOpenChange,
+  attachmentId,
+  attachmentName,
+}: RevisionHistoryDialogProps) {
+  const [loading, setLoading] = React.useState(false);
+  const [historyData, setHistoryData] = React.useState<AttachmentWithHistory | null>(null);
+  const [error, setError] = React.useState<string | null>(null);
+
+  // 다이얼로그가 열릴 때 데이터 로드
+  React.useEffect(() => {
+    if (open && attachmentId) {
+      loadRevisionHistory();
+    }
+  }, [open, attachmentId]);
+
+  const loadRevisionHistory = async () => {
+    setLoading(true);
+    setError(null);
+    try {
+      const result = await getRevisionHistory(attachmentId);
+      if (result.success && result.data) {
+        setHistoryData(result.data);
+      } else {
+        setError(result.error || "리비전 히스토리를 불러올 수 없습니다.");
+      }
+    } catch (err) {
+      console.error("Load revision history error:", err);
+      setError("리비전 히스토리 조회 중 오류가 발생했습니다.");
+    } finally {
+      setLoading(false);
+    }
+  };
+
+  // 리비전 다운로드
+  const handleDownloadRevision = async (revision: RevisionHistory) => {
+    try {
+      await downloadFile(revision.filePath, revision.originalFileName, {
+        action: 'download',
+        showToast: true,
+      });
+    } catch (err) {
+      console.error("Download revision error:", err);
+      toast.error("파일 다운로드 중 오류가 발생했습니다.");
+    }
+  };
+
+  // 리비전 미리보기
+  const handlePreviewRevision = async (revision: RevisionHistory) => {
+    try {
+      await downloadFile(revision.filePath, revision.originalFileName, {
+        action: 'preview',
+        showToast: true,
+      });
+    } catch (err) {
+      console.error("Preview revision error:", err);
+      toast.error("파일 미리보기 중 오류가 발생했습니다.");
+    }
+  };
+
+  // 리비전 번호에 따른 색상 결정
+  const getRevisionBadgeVariant = (isLatest: boolean) => {
+    return isLatest ? "default" : "secondary";
+  };
+
+  return (
+    <Dialog open={open} onOpenChange={onOpenChange}>
+      <DialogContent className="max-w-5xl max-h-[80vh]">
+        <DialogHeader>
+          <DialogTitle className="flex items-center gap-2">
+            <FileText className="h-5 w-5" />
+            리비전 히스토리
+          </DialogTitle>
+          <DialogDescription>
+            {historyData?.originalFileName || attachmentName || "파일"}의 모든 버전 히스토리를 확인할 수 있습니다.
+          </DialogDescription>
+        </DialogHeader>
+
+        <div className="mt-4">
+          {loading ? (
+            <div className="space-y-3">
+              <Skeleton className="h-10 w-full" />
+              <Skeleton className="h-10 w-full" />
+              <Skeleton className="h-10 w-full" />
+            </div>
+          ) : error ? (
+            <Alert variant="destructive">
+              <AlertCircle className="h-4 w-4" />
+              <AlertDescription>{error}</AlertDescription>
+            </Alert>
+          ) : historyData ? (
+            <>
+              {/* 파일 정보 헤더 */}
+              <div className="mb-4 p-3 bg-muted rounded-lg">
+                <div className="grid grid-cols-2 gap-2 text-sm">
+                  <div>
+                    <span className="text-muted-foreground">일련번호:</span>{" "}
+                    <span className="font-medium font-mono">{historyData.serialNo || "-"}</span>
+                  </div>
+                  <div>
+                    <span className="text-muted-foreground">현재 리비전:</span>{" "}
+                    <Badge variant="default" className="ml-1">
+                      Rev. {historyData.currentRevision || "A"}
+                    </Badge>
+                  </div>
+                  {historyData.description && (
+                    <div className="col-span-2">
+                      <span className="text-muted-foreground">설명:</span>{" "}
+                      <span className="font-medium">{historyData.description}</span>
+                    </div>
+                  )}
+                </div>
+              </div>
+
+              {/* 리비전 테이블 */}
+              <ScrollArea className="h-[400px] rounded-md border">
+                <Table>
+                  <TableHeader>
+                    <TableRow>
+                      <TableHead className="w-[80px]">리비전</TableHead>
+                      <TableHead>파일명</TableHead>
+                      <TableHead className="w-[80px]">크기</TableHead>
+                      <TableHead className="w-[100px]">업로드자</TableHead>
+                      <TableHead className="w-[150px]">업로드일시</TableHead>
+                      <TableHead>코멘트</TableHead>
+                      <TableHead className="w-[100px] text-center">작업</TableHead>
+                    </TableRow>
+                  </TableHeader>
+                  <TableBody>
+                    {historyData.revisions.length > 0 ? (
+                      historyData.revisions.map((revision) => (
+                        <TableRow key={revision.id}>
+                          <TableCell>
+                            <Badge
+                              variant={getRevisionBadgeVariant(revision.isLatest)}
+                              className="font-mono"
+                            >
+                              Rev. {revision.revisionNo}
+                              {revision.isLatest && (
+                                <CheckCircle className="ml-1 h-3 w-3" />
+                              )}
+                            </Badge>
+                          </TableCell>
+                          <TableCell>
+                            <div className="flex flex-col">
+                              <span className="text-sm font-medium truncate max-w-[200px]" title={revision.originalFileName}>
+                                {revision.originalFileName}
+                              </span>
+                              {revision.fileName !== revision.originalFileName && (
+                                <span className="text-xs text-muted-foreground truncate max-w-[200px]">
+                                  ({revision.fileName})
+                                </span>
+                              )}
+                            </div>
+                          </TableCell>
+                          <TableCell>
+                            <span className="text-sm text-muted-foreground">
+                              {formatFileSize(revision.fileSize)}
+                            </span>
+                          </TableCell>
+                          <TableCell>
+                            <div className="flex items-center gap-1">
+                              <User className="h-3 w-3 text-muted-foreground" />
+                              <span className="text-sm">
+                                {revision.createdByName || "Unknown"}
+                              </span>
+                            </div>
+                          </TableCell>
+                          <TableCell>
+                            <div className="flex items-center gap-1">
+                              <Clock className="h-3 w-3 text-muted-foreground" />
+                              <span className="text-sm">
+                                {format(new Date(revision.createdAt), "yyyy-MM-dd HH:mm")}
+                              </span>
+                            </div>
+                            <span className="text-xs text-muted-foreground">
+                              {formatDistanceToNow(new Date(revision.createdAt), {
+                                addSuffix: true,
+                                locale: ko,
+                              })}
+                            </span>
+                          </TableCell>
+                          <TableCell>
+                            {revision.revisionComment ? (
+                              <div className="flex items-start gap-1">
+                                <MessageSquare className="h-3 w-3 text-muted-foreground mt-0.5" />
+                                <span className="text-sm text-muted-foreground">
+                                  {revision.revisionComment}
+                                </span>
+                              </div>
+                            ) : (
+                              <span className="text-sm text-muted-foreground">-</span>
+                            )}
+                          </TableCell>
+                          <TableCell>
+                            <div className="flex items-center justify-center gap-1">
+                              <Button
+                                variant="ghost"
+                                size="icon"
+                                className="h-8 w-8"
+                                onClick={() => handleDownloadRevision(revision)}
+                                title="다운로드"
+                              >
+                                <Download className="h-4 w-4" />
+                              </Button>
+                              <Button
+                                variant="ghost"
+                                size="icon"
+                                className="h-8 w-8"
+                                onClick={() => handlePreviewRevision(revision)}
+                                title="미리보기"
+                              >
+                                <Eye className="h-4 w-4" />
+                              </Button>
+                            </div>
+                          </TableCell>
+                        </TableRow>
+                      ))
+                    ) : (
+                      <TableRow>
+                        <TableCell colSpan={7} className="text-center text-muted-foreground py-8">
+                          리비전 히스토리가 없습니다.
+                        </TableCell>
+                      </TableRow>
+                    )}
+                  </TableBody>
+                </Table>
+              </ScrollArea>
+
+              {/* 요약 정보 */}
+              <div className="mt-4 flex items-center justify-between text-sm text-muted-foreground">
+                <span>총 {historyData.revisions.length}개의 리비전</span>
+                <span>
+                  최초 업로드:{" "}
+                  {historyData.revisions.length > 0
+                    ? format(
+                        new Date(
+                          historyData.revisions[historyData.revisions.length - 1].createdAt
+                        ),
+                        "yyyy년 MM월 dd일"
+                      )
+                    : "-"}
+                </span>
+              </div>
+            </>
+          ) : null}
+        </div>
+      </DialogContent>
+    </Dialog>
+  );
+}
\ No newline at end of file
-- 
cgit v1.2.3

