"use client"; import { useCallback, useState, useEffect } from "react"; import { toast } from "sonner"; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from "@/components/ui/dialog"; import { Button } from "@/components/ui/button"; import { fetchTbeTemplateFiles, uploadTbeResponseFile, getTbeSubmittedFiles, getFileFromRfqAttachmentsbyid, } from "../../rfqs/service"; import { Dropzone, DropzoneDescription, DropzoneInput, DropzoneTitle, DropzoneUploadIcon, DropzoneZone, } from "@/components/ui/dropzone"; import { FileList, FileListAction, FileListDescription, FileListIcon, FileListInfo, FileListItem, FileListName, FileListSize, } from "@/components/ui/file-list"; import { Download, X } from "lucide-react"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { formatDateTime } from "@/lib/utils"; export function useTbeFileHandlers() { // 모달 열림 여부, 현재 선택된 IDs const [isUploadDialogOpen, setIsUploadDialogOpen] = useState(false); const [currentTbeId, setCurrentTbeId] = useState(null); const [currentVendorId, setCurrentVendorId] = useState(null); const [currentRfqId, setCurrentRfqId] = useState(null); const [currentvendorResponseId, setCurrentvendorResponseId] = useState(null); // 로딩 상태들 const [isLoading, setIsLoading] = useState(false); const [isFetchingFiles, setIsFetchingFiles] = useState(false); // 업로드할 파일, 제출된 파일 목록 const [selectedFile, setSelectedFile] = useState(null); const [submittedFiles, setSubmittedFiles] = useState< Array<{ id: number; fileName: string; filePath: string; uploadedAt: Date }> >([]); // =================================== // 1) 제출된 파일 목록 가져오기 // =================================== const fetchSubmittedFiles = useCallback(async (vendorResponseId: number) => { if (!vendorResponseId ) return; setIsFetchingFiles(true); try { const { files, error } = await getTbeSubmittedFiles(vendorResponseId); if (error) { console.error(error); return; } setSubmittedFiles(files); } catch (error) { console.error("Failed to fetch submitted files:", error); } finally { setIsFetchingFiles(false); } }, []); // =================================== // 2) TBE 템플릿 다운로드 // =================================== const handleDownloadTbeTemplate = useCallback( async (tbeId: number, vendorId: number, rfqId: number) => { setCurrentTbeId(tbeId); setCurrentVendorId(vendorId); setCurrentRfqId(rfqId); setIsLoading(true); try { const { files, error } = await fetchTbeTemplateFiles(tbeId); if (error) { toast.error(error); return; } if (files.length === 0) { toast.warning("다운로드할 템플릿 파일이 없습니다"); return; } // 순차적으로 파일 다운로드 for (const file of files) { await downloadFile(file.id); } toast.success("모든 템플릿 파일이 다운로드되었습니다"); } catch (error) { toast.error("템플릿 파일을 다운로드하는 데 실패했습니다"); console.error(error); } finally { setIsLoading(false); } }, [] ); // 실제 다운로드 로직 const downloadFile = useCallback(async (fileId: number) => { try { const { file, error } = await getFileFromRfqAttachmentsbyid(fileId); if (error || !file) { throw new Error(error || "파일 정보를 가져오는 데 실패했습니다"); } const link = document.createElement("a"); link.href = `/api/rfq-download?path=${encodeURIComponent(file.filePath)}`; link.download = file.fileName; document.body.appendChild(link); link.click(); document.body.removeChild(link); return true; } catch (error) { console.error(error); return false; } }, []); // =================================== // 3) 제출된 파일 다운로드 // =================================== const downloadSubmittedFile = useCallback((file: { id: number; fileName: string; filePath: string }) => { try { const link = document.createElement("a"); link.href = `/api/tbe-download?path=${encodeURIComponent(file.filePath)}`; link.download = file.fileName; document.body.appendChild(link); link.click(); document.body.removeChild(link); toast.success(`${file.fileName} 다운로드 시작`); } catch (error) { console.error("Failed to download file:", error); toast.error("파일 다운로드에 실패했습니다"); } }, []); // =================================== // 4) TBE 응답 업로드 모달 열기 // (이 시점에서는 데이터 fetch하지 않음) // =================================== const handleUploadTbeResponse = useCallback((tbeId: number, vendorId: number, rfqId: number, vendorResponseId:number) => { setCurrentTbeId(tbeId); setCurrentVendorId(vendorId); setCurrentRfqId(rfqId); setCurrentvendorResponseId(vendorResponseId); setIsUploadDialogOpen(true); }, []); // =================================== // 5) Dialog 열고 닫힐 때 상태 초기화 // 열렸을 때 -> useEffect로 파일 목록 가져오기 // =================================== useEffect(() => { if (!isUploadDialogOpen) { // 닫힐 때는 파일 상태들 초기화 setSelectedFile(null); setSubmittedFiles([]); } }, [isUploadDialogOpen]); useEffect(() => { // Dialog가 열렸고, ID들이 유효하면 if (isUploadDialogOpen &¤tvendorResponseId) { fetchSubmittedFiles(currentvendorResponseId); } }, [isUploadDialogOpen, currentvendorResponseId, fetchSubmittedFiles]); // =================================== // 6) 드롭존 파일 선택 & 제거 // =================================== const handleFileDrop = useCallback((files: File[]) => { if (files && files.length > 0) { setSelectedFile(files[0]); } }, []); const handleRemoveFile = useCallback(() => { setSelectedFile(null); }, []); // =================================== // 7) 응답 파일 업로드 // =================================== const handleSubmitResponse = useCallback(async () => { if (!selectedFile || !currentTbeId || !currentVendorId || !currentRfqId ||!currentvendorResponseId) { toast.error("업로드할 파일을 선택해주세요"); return; } setIsLoading(true); try { // FormData 생성 const formData = new FormData(); formData.append("file", selectedFile); formData.append("rfqId", currentRfqId.toString()); formData.append("vendorId", currentVendorId.toString()); formData.append("evaluationId", currentTbeId.toString()); formData.append("vendorResponseId", currentvendorResponseId.toString()); const result = await uploadTbeResponseFile(formData); if (!result.success) { throw new Error(result.error || "파일 업로드에 실패했습니다"); } toast.success(result.message || "응답이 성공적으로 업로드되었습니다"); // 업로드 후 다시 제출된 파일 목록 가져오기 await fetchSubmittedFiles(currentvendorResponseId); // 업로드 성공 시 선택 파일 초기화 setSelectedFile(null); } catch (error) { toast.error(error instanceof Error ? error.message : "응답 업로드에 실패했습니다"); console.error(error); } finally { setIsLoading(false); } }, [selectedFile, currentTbeId, currentVendorId, currentRfqId, currentvendorResponseId,fetchSubmittedFiles]); // =================================== // 8) 실제 Dialog 컴포넌트 // =================================== const UploadDialog = () => ( TBE 응답 파일 제출된 파일을 확인하거나 새 파일을 업로드하세요. 새 파일 업로드 0 ? "relative" : ""} > 제출된 파일{" "} {submittedFiles.length > 0 && ( {submittedFiles.length} )} {/* 업로드 탭 */}
{selectedFile ? ( {selectedFile.name} {selectedFile.size} ) : ( 파일을 드래그하거나 클릭하여 업로드 TBE 응답 파일 (XLSX, XLS, DOCX, PDF 등) )}
{/* 제출된 파일 탭 */} {isFetchingFiles ? (
) : submittedFiles.length > 0 ? (
{submittedFiles.map((file) => (
{file.fileName} {file.uploadedAt ? formatDateTime(file.uploadedAt) : ""}
))}
) : (
제출된 파일이 없습니다.
)}
); // =================================== // 9) Hooks 내보내기 // =================================== return { handleDownloadTbeTemplate, handleUploadTbeResponse, UploadDialog, }; }