From ef4c533ebacc2cdc97e518f30e9a9350004fcdfb Mon Sep 17 00:00:00 2001
From: dujinkim <dujin.kim@dtsolution.co.kr>
Date: Mon, 28 Apr 2025 02:13:30 +0000
Subject: ~20250428 작업사항
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 components/additional-info/join-form.tsx | 130 +++++++++++++++++++++----------
 1 file changed, 89 insertions(+), 41 deletions(-)

(limited to 'components/additional-info/join-form.tsx')

diff --git a/components/additional-info/join-form.tsx b/components/additional-info/join-form.tsx
index 2cd385c3..4a9a3379 100644
--- a/components/additional-info/join-form.tsx
+++ b/components/additional-info/join-form.tsx
@@ -41,7 +41,7 @@ import { cn } from "@/lib/utils"
 import { useTranslation } from "@/i18n/client"
 
 import { getVendorDetailById, downloadVendorAttachments, updateVendorInfo } from "@/lib/vendors/service"
-import { updateVendorSchema, type UpdateVendorInfoSchema } from "@/lib/vendors/validations"
+import { updateVendorSchema, updateVendorSchemaWithConditions, type UpdateVendorInfoSchema } from "@/lib/vendors/validations"
 import {
   Select,
   SelectContent,
@@ -104,6 +104,14 @@ const creditRatingScaleMap: Record<string, string[]> = {
   SCI: ["AAA", "AA+", "AA", "AA-", "A+", "A", "A-", "BBB+", "BBB-", "B"],
 }
 
+const cashFlowRatingScaleMap: Record<string, string[]> = {
+  NICE: ["우수", "양호", "보통", "미흡", "불량"],
+  KIS: ["A+", "A", "B+", "B", "C", "D"],
+  KED: ["1등급", "2등급", "3등급", "4등급", "5등급"],
+  SCI: ["Level 1", "Level 2", "Level 3", "Level 4"],
+}
+
+
 const MAX_FILE_SIZE = 3e9
 
 // 파일 타입 정의
@@ -124,7 +132,7 @@ export function InfoForm() {
 
   const companyId = session?.user?.companyId || "17"
 
-  // 벤더 데이터 상태
+  // 협력업체 데이터 상태
   const [vendor, setVendor] = React.useState<any>(null)
   const [isLoading, setIsLoading] = React.useState(true)
   const [isSubmitting, setIsSubmitting] = React.useState(false)
@@ -137,10 +145,11 @@ export function InfoForm() {
   const [selectedFiles, setSelectedFiles] = React.useState<File[]>([])
   const [creditRatingFile, setCreditRatingFile] = React.useState<File[]>([])
   const [cashFlowRatingFile, setCashFlowRatingFile] = React.useState<File[]>([])
+  const [isDownloading, setIsDownloading] = React.useState(false);
 
   // React Hook Form
   const form = useForm<UpdateVendorInfoSchema>({
-    resolver: zodResolver(updateVendorSchema),
+    resolver: zodResolver(updateVendorSchemaWithConditions),
     defaultValues: {
       vendorName: "",
       taxId: "",
@@ -181,21 +190,21 @@ export function InfoForm() {
       name: "contacts",
     })
 
-  // 벤더 정보 가져오기
+  // 협력업체 정보 가져오기
   React.useEffect(() => {
     async function fetchVendorData() {
       if (!companyId) return
 
       try {
         setIsLoading(true)
-        // 벤더 상세 정보 가져오기 (view 사용)
+        // 협력업체 상세 정보 가져오기 (view 사용)
         const vendorData = await getVendorDetailById(Number(companyId))
 
         if (!vendorData) {
           toast({
             variant: "destructive",
             title: "오류",
-            description: "벤더 정보를 찾을 수 없습니다.",
+            description: "협력업체 정보를 찾을 수 없습니다.",
           })
           return
         }
@@ -258,7 +267,7 @@ export function InfoForm() {
         toast({
           variant: "destructive",
           title: "데이터 로드 오류",
-          description: "벤더 정보를 불러오는 중 오류가 발생했습니다.",
+          description: "협력업체 정보를 불러오는 중 오류가 발생했습니다.",
         })
       } finally {
         setIsLoading(false)
@@ -268,43 +277,82 @@ export function InfoForm() {
     fetchVendorData()
   }, [companyId, form, replaceContacts])
 
-  // 파일 다운로드 처리
-  const handleDownloadFile = async (fileId: number) => {
+  const handleDownloadFile = async (file: AttachmentFile) => {
     try {
-      const downloadInfo = await downloadVendorAttachments(Number(companyId), fileId)
-
-      if (downloadInfo && downloadInfo.url) {
-        // 브라우저에서 다운로드 링크 열기
-        window.open(downloadInfo.url, '_blank')
-      }
-    } catch (error) {
-      console.error("Error downloading file:", error)
+      setIsDownloading(true);
+      
+      // 파일이 객체인지 ID인지 확인하고 처리
+      const fileId = typeof file === 'object' ? file.id : file;
+      const fileName = typeof file === 'object' ? file.fileName : `file-${fileId}`;
+      
+      // 다운로드 링크 생성 (URL 인코딩 적용)
+      const downloadUrl = `/api/vendors/attachments/download?id=${fileId}&vendorId=${Number(companyId)}`;
+      
+      // a 태그를 사용한 다운로드
+      const downloadLink = document.createElement('a');
+      downloadLink.href = downloadUrl;
+      downloadLink.download = fileName;
+      downloadLink.target = '_blank'; // 추가: 새 탭에서 열도록 설정 (일부 브라우저에서 더 안정적)
+      document.body.appendChild(downloadLink);
+      downloadLink.click();
+      
+      // 정리 (메모리 누수 방지)
+      setTimeout(() => {
+        document.body.removeChild(downloadLink);
+      }, 100);
+      
       toast({
-        variant: "destructive",
-        title: "다운로드 오류",
-        description: "파일 다운로드 중 오류가 발생했습니다.",
-      })
-    }
-  }
-
-  // 모든 첨부파일 다운로드
-  const handleDownloadAllFiles = async () => {
-    try {
-      const downloadInfo = await downloadVendorAttachments(Number(companyId))
-
-      if (downloadInfo && downloadInfo.url) {
-        window.open(downloadInfo.url, '_blank')
-      }
+        title: "다운로드 시작",
+        description: "파일 다운로드가 시작되었습니다.",
+      });
     } catch (error) {
-      console.error("Error downloading files:", error)
+      console.error("Error downloading file:", error);
       toast({
         variant: "destructive",
         title: "다운로드 오류",
         description: "파일 다운로드 중 오류가 발생했습니다.",
-      })
+      });
+    } finally {
+      setIsDownloading(false);
     }
+  };
+  
+  // 전체 파일 다운로드 함수
+const handleDownloadAllFiles = async () => {
+  try {
+    setIsDownloading(true);
+
+    // 다운로드 URL 생성
+    const downloadUrl = `/api/vendors/attachments/download-all?vendorId=${Number(companyId)}`;
+    
+    // a 태그를 사용한 다운로드
+    const downloadLink = document.createElement('a');
+    downloadLink.href = downloadUrl;
+    downloadLink.download = `vendor-${companyId}-files.zip`;
+    downloadLink.target = '_blank';
+    document.body.appendChild(downloadLink);
+    downloadLink.click();
+    
+    // 정리
+    setTimeout(() => {
+      document.body.removeChild(downloadLink);
+    }, 100);
+    
+    toast({
+      title: "다운로드 시작",
+      description: "전체 파일 다운로드가 시작되었습니다.",
+    });
+  } catch (error) {
+    console.error("Error downloading files:", error);
+    toast({
+      variant: "destructive",
+      title: "다운로드 오류",
+      description: "파일 다운로드 중 오류가 발생했습니다.",
+    });
+  } finally {
+    setIsDownloading(false);
   }
-
+};
   // Dropzone handlers
   const handleDropAccepted = (acceptedFiles: File[]) => {
     const newFiles = [...selectedFiles, ...acceptedFiles]
@@ -476,7 +524,7 @@ export function InfoForm() {
     return (
       <div className="container py-10 flex justify-center items-center">
         <Loader2 className="h-8 w-8 animate-spin text-primary" />
-        <span className="ml-2">벤더 정보를 불러오는 중입니다...</span>
+        <span className="ml-2">협력업체 정보를 불러오는 중입니다...</span>
       </div>
     )
   }
@@ -543,7 +591,7 @@ export function InfoForm() {
                                   </FileListDescription>
                                 </FileListInfo>
                                 <div className="flex items-center space-x-2">
-                                  <FileListAction onClick={() => handleDownloadFile(file.id)}>
+                                  <FileListAction onClick={() => handleDownloadFile(file)}>
                                     <Download className="h-4 w-4" />
                                   </FileListAction>
                                   <FileListAction onClick={() => handleDeleteExistingFile(file.id)}>
@@ -574,7 +622,7 @@ export function InfoForm() {
                                   </FileListDescription>
                                 </FileListInfo>
                                 <div className="flex items-center space-x-2">
-                                  <FileListAction onClick={() => handleDownloadFile(file.id)}>
+                                  <FileListAction onClick={() => handleDownloadFile(file)}>
                                     <Download className="h-4 w-4" />
                                   </FileListAction>
                                   <FileListAction onClick={() => handleDeleteExistingFile(file.id)}>
@@ -605,7 +653,7 @@ export function InfoForm() {
                                   </FileListDescription>
                                 </FileListInfo>
                                 <div className="flex items-center space-x-2">
-                                  <FileListAction onClick={() => handleDownloadFile(file.id)}>
+                                  <FileListAction onClick={() => handleDownloadFile(file)}>
                                     <Download className="h-4 w-4" />
                                   </FileListAction>
                                   <FileListAction onClick={() => handleDeleteExistingFile(file.id)}>
@@ -1095,8 +1143,8 @@ export function InfoForm() {
                           render={({ field }) => {
                             const selectedAgency = form.watch("creditAgency")
                             const ratingScale =
-                              creditRatingScaleMap[
-                              selectedAgency as keyof typeof creditRatingScaleMap
+                            cashFlowRatingScaleMap[
+                              selectedAgency as keyof typeof cashFlowRatingScaleMap
                               ] || []
                             return (
                               <FormItem>
-- 
cgit v1.2.3

