diff options
Diffstat (limited to 'lib/project-gtc/table/view-gtc-file-dialog.tsx')
| -rw-r--r-- | lib/project-gtc/table/view-gtc-file-dialog.tsx | 230 |
1 files changed, 230 insertions, 0 deletions
diff --git a/lib/project-gtc/table/view-gtc-file-dialog.tsx b/lib/project-gtc/table/view-gtc-file-dialog.tsx new file mode 100644 index 00000000..f8cfecd9 --- /dev/null +++ b/lib/project-gtc/table/view-gtc-file-dialog.tsx @@ -0,0 +1,230 @@ +"use client" + +import * as React from "react" +import { Download, FileText, Calendar, HardDrive } from "lucide-react" + +import { Button } from "@/components/ui/button" +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, +} from "@/components/ui/dialog" +import { Badge } from "@/components/ui/badge" +import { format } from "date-fns" +import { ko } from "date-fns/locale" +import type { ProjectGtcView } from "@/db/schema" + +interface ViewGtcFileDialogProps { + project: ProjectGtcView | null + open: boolean + onOpenChange: (open: boolean) => void +} + +// 파일 크기 포맷팅 함수 +function formatBytes(bytes: number | null): string { + if (!bytes) return "0 B" + + const k = 1024 + const sizes = ['B', 'KB', 'MB', 'GB'] + const i = Math.floor(Math.log(bytes) / Math.log(k)) + + return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i] +} + +export function ViewGtcFileDialog({ + project, + open, + onOpenChange, +}: ViewGtcFileDialogProps) { + if (!project || !project.gtcFileId) return null + + const handleDownload = async () => { + try { + // API를 통해 파일 다운로드 + const response = await fetch(`/api/project-gtc?action=download&projectId=${project.id}`, { + method: 'GET', + }); + + if (!response.ok) { + throw new Error('파일 다운로드에 실패했습니다.'); + } + + // 파일 blob 생성 + const blob = await response.blob(); + + // 다운로드 링크 생성 + const url = window.URL.createObjectURL(blob); + const link = document.createElement('a'); + link.href = url; + link.download = project.originalFileName || 'gtc-file'; + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + + // 메모리 정리 + window.URL.revokeObjectURL(url); + } catch (error) { + console.error("파일 다운로드 오류:", error); + } + } + + const handlePreview = async () => { + try { + // API를 통해 파일 다운로드 + const response = await fetch(`/api/project-gtc?action=download&projectId=${project.id}`, { + method: 'GET', + }); + + if (!response.ok) { + throw new Error('파일을 열 수 없습니다.'); + } + + // 파일 blob 생성 + const blob = await response.blob(); + + // PDF 파일인 경우 새 탭에서 열기 + if (project.mimeType === 'application/pdf') { + const url = window.URL.createObjectURL(blob); + window.open(url, '_blank'); + // 메모리 정리는 브라우저가 탭을 닫을 때 자동으로 처리됨 + } else { + // 다른 파일 타입은 다운로드 + const url = window.URL.createObjectURL(blob); + const link = document.createElement('a'); + link.href = url; + link.download = project.originalFileName || 'gtc-file'; + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + window.URL.revokeObjectURL(url); + } + } catch (error) { + console.error("파일 미리보기 오류:", error); + } + } + + const getFileIcon = () => { + if (project.mimeType?.includes('pdf')) { + return "📄" + } else if (project.mimeType?.includes('word') || project.mimeType?.includes('document')) { + return "📝" + } else if (project.mimeType?.includes('text')) { + return "📃" + } + return "📎" + } + + return ( + <Dialog open={open} onOpenChange={onOpenChange}> + <DialogContent className="sm:max-w-[500px]"> + <DialogHeader> + <DialogTitle>GTC 파일 정보</DialogTitle> + <DialogDescription> + 프로젝트 "{project.name}" ({project.code})의 GTC 파일 정보입니다. + </DialogDescription> + </DialogHeader> + + <div className="space-y-4"> + {/* 프로젝트 정보 */} + <div className="p-4 bg-muted rounded-lg"> + <h4 className="font-medium mb-2">프로젝트 정보</h4> + <div className="space-y-2 text-sm"> + <div className="flex justify-between"> + <span className="text-muted-foreground">프로젝트 코드:</span> + <span className="font-medium">{project.code}</span> + </div> + <div className="flex justify-between"> + <span className="text-muted-foreground">프로젝트명:</span> + <span className="font-medium">{project.name}</span> + </div> + <div className="flex justify-between"> + <span className="text-muted-foreground">프로젝트 타입:</span> + <Badge variant="secondary">{project.type}</Badge> + </div> + </div> + </div> + + {/* 파일 정보 */} + <div className="p-4 bg-muted rounded-lg"> + <h4 className="font-medium mb-2">파일 정보</h4> + <div className="space-y-3"> + <div className="flex items-center space-x-3"> + <span className="text-2xl">{getFileIcon()}</span> + <div className="flex-1"> + <div className="font-medium">{project.originalFileName}</div> + <div className="text-sm text-muted-foreground"> + {project.fileName} + </div> + </div> + </div> + + <div className="grid grid-cols-2 gap-4 text-sm"> + <div className="flex items-center space-x-2"> + <HardDrive className="h-4 w-4 text-muted-foreground" /> + <span className="text-muted-foreground">파일 크기:</span> + <span className="font-medium"> + {project.fileSize ? formatBytes(project.fileSize) : '알 수 없음'} + </span> + </div> + <div className="flex items-center space-x-2"> + <FileText className="h-4 w-4 text-muted-foreground" /> + <span className="text-muted-foreground">파일 타입:</span> + <span className="font-medium"> + {project.mimeType || '알 수 없음'} + </span> + </div> + <div className="flex items-center space-x-2"> + <Calendar className="h-4 w-4 text-muted-foreground" /> + <span className="text-muted-foreground">업로드일:</span> + <span className="font-medium"> + {project.gtcCreatedAt ? + format(new Date(project.gtcCreatedAt), "yyyy-MM-dd HH:mm", { locale: ko }) : + '알 수 없음' + } + </span> + </div> + <div className="flex items-center space-x-2"> + <Calendar className="h-4 w-4 text-muted-foreground" /> + <span className="text-muted-foreground">수정일:</span> + <span className="font-medium"> + {project.gtcUpdatedAt ? + format(new Date(project.gtcUpdatedAt), "yyyy-MM-dd HH:mm", { locale: ko }) : + '알 수 없음' + } + </span> + </div> + </div> + </div> + </div> + </div> + + <DialogFooter className="flex space-x-2"> + <Button + type="button" + variant="outline" + onClick={() => onOpenChange(false)} + > + 닫기 + </Button> + <Button + type="button" + variant="outline" + onClick={handlePreview} + > + 미리보기 + </Button> + <Button + type="button" + onClick={handleDownload} + > + <Download className="mr-2 h-4 w-4" /> + 다운로드 + </Button> + </DialogFooter> + </DialogContent> + </Dialog> + ) +}
\ No newline at end of file |
