1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
|
"use client"
import * as React from "react"
import { Download, X } from "lucide-react"
import { toast } from "sonner"
import { getErrorMessage } from "@/lib/handle-error"
import { formatDateTime } from "@/lib/utils"
import { Button } from "@/components/ui/button"
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog"
import {
FileList,
FileListItem,
FileListIcon,
FileListInfo,
FileListName,
FileListDescription,
FileListAction,
} from "@/components/ui/file-list"
import { getTbeFilesForVendor } from "@/lib/rfqs/service"
interface TBEFileDialogProps {
isOpen: boolean
onOpenChange: (open: boolean) => void
tbeId: number
vendorId: number
rfqId: number
onRefresh?: () => void
}
export function TBEFileDialog({
isOpen,
onOpenChange,
vendorId,
rfqId,
onRefresh,
}: TBEFileDialogProps) {
const [submittedFiles, setSubmittedFiles] = React.useState<any[]>([])
const [isFetchingFiles, setIsFetchingFiles] = React.useState(false)
// Fetch submitted files when dialog opens
React.useEffect(() => {
if (isOpen && rfqId && vendorId) {
fetchSubmittedFiles()
}
}, [isOpen, rfqId, vendorId])
// Fetch submitted files using the service function
const fetchSubmittedFiles = async () => {
if (!rfqId || !vendorId) return
setIsFetchingFiles(true)
try {
const { files, error } = await getTbeFilesForVendor(rfqId, vendorId)
if (error) {
throw new Error(error)
}
setSubmittedFiles(files)
} catch (error) {
toast.error("Failed to load files: " + getErrorMessage(error))
} finally {
setIsFetchingFiles(false)
}
}
// Download submitted file
const downloadSubmittedFile = async (file: any) => {
try {
const response = await fetch(`/api/file/${file.id}/download`)
if (!response.ok) {
throw new Error("Failed to download file")
}
const blob = await response.blob()
const url = window.URL.createObjectURL(blob)
const a = document.createElement("a")
a.href = url
a.download = file.fileName
document.body.appendChild(a)
a.click()
window.URL.revokeObjectURL(url)
document.body.removeChild(a)
} catch (error) {
toast.error("Failed to download file: " + getErrorMessage(error))
}
}
return (
<Dialog open={isOpen} onOpenChange={onOpenChange}>
<DialogContent className="sm:max-w-lg">
<DialogHeader>
<DialogTitle>TBE 응답 파일</DialogTitle>
<DialogDescription>제출된 파일 목록을 확인하고 다운로드하세요.</DialogDescription>
</DialogHeader>
{/* 제출된 파일 목록 */}
{isFetchingFiles ? (
<div className="flex justify-center items-center py-8">
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-primary"></div>
</div>
) : submittedFiles.length > 0 ? (
<div className="grid gap-2">
<FileList>
{submittedFiles.map((file) => (
<FileListItem key={file.id} className="flex items-center justify-between gap-3">
<div className="flex items-center gap-3 flex-1">
<FileListIcon className="flex-shrink-0" />
<FileListInfo className="flex-1 min-w-0">
<FileListName className="text-sm font-medium truncate">{file.fileName}</FileListName>
<FileListDescription className="text-xs text-muted-foreground">
{file.uploadedAt ? formatDateTime(file.uploadedAt, "KR") : ""}
</FileListDescription>
</FileListInfo>
</div>
<FileListAction className="flex-shrink-0 ml-2">
<Button variant="ghost" size="icon" onClick={() => downloadSubmittedFile(file)}>
<Download className="h-4 w-4" />
<span className="sr-only">파일 다운로드</span>
</Button>
</FileListAction>
</FileListItem>
))}
</FileList>
</div>
) : (
<div className="text-center py-8 text-muted-foreground">제출된 파일이 없습니다.</div>
)}
</DialogContent>
</Dialog>
)
}
|