From ee57cc221ff2edafd3c0f12a181214c602ed257e Mon Sep 17 00:00:00 2001
From: dujinkim <dujin.kim@dtsolution.co.kr>
Date: Tue, 22 Jul 2025 02:57:00 +0000
Subject: (대표님, 최겸) 이메일 템플릿, 벤더데이터 변경사항 대응, 기술영업
 변경요구사항 구현
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../additional-info/tech-vendor-info-form.tsx      | 188 ++++++++++++++-------
 1 file changed, 127 insertions(+), 61 deletions(-)

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

diff --git a/components/additional-info/tech-vendor-info-form.tsx b/components/additional-info/tech-vendor-info-form.tsx
index 55d01d21..02358a45 100644
--- a/components/additional-info/tech-vendor-info-form.tsx
+++ b/components/additional-info/tech-vendor-info-form.tsx
@@ -31,6 +31,17 @@ import {
   CardTitle,
 } from "@/components/ui/card"
 import { InformationButton } from "@/components/information/information-button"
+
+// 보안 파일 다운로드 유틸리티 import
+import { 
+  downloadFile, 
+  quickDownload, 
+  smartFileAction, 
+  getFileInfo, 
+  formatFileSize,
+  getSecurityInfo 
+} from "@/lib/file-download"
+
 // 타입 정의
 interface TechVendorContact {
   id: number
@@ -46,6 +57,7 @@ interface TechVendorAttachment {
   fileName: string
   filePath: string
   attachmentType: string
+  fileSize?: number
   createdAt: Date
   updatedAt: Date
 }
@@ -133,69 +145,96 @@ export function TechVendorInfoForm() {
     fetchTechVendorData()
   }, [techCompanyId, form])
 
+  // 보안 다운로드 유틸리티를 사용한 개별 파일 다운로드
   const handleDownloadFile = async (file: TechVendorAttachment) => {
     try {
       setIsDownloading(true)
       
+      // API 엔드포인트 URL 구성
       const downloadUrl = `/api/tech-vendors/attachments/download?id=${file.id}&vendorId=${Number(techCompanyId)}`
       
-      const downloadLink = document.createElement('a')
-      downloadLink.href = downloadUrl
-      downloadLink.download = file.fileName
-      downloadLink.target = '_blank'
-      document.body.appendChild(downloadLink)
-      downloadLink.click()
-      
-      setTimeout(() => {
-        document.body.removeChild(downloadLink)
-      }, 100)
+      // 보안 다운로드 유틸리티 사용
+      const result = await downloadFile(downloadUrl, file.fileName, {
+        action: 'download',
+        showToast: false, // 우리가 직접 토스트 관리
+        onSuccess: (fileName, fileSize) => {
+          const sizeText = fileSize ? ` (${formatFileSize(fileSize)})` : '';
+          toast({
+            title: "다운로드 완료",
+            description: `파일 다운로드가 완료되었습니다: ${fileName}${sizeText}`,
+          });
+        },
+        onError: (error) => {
+          console.error("Download error:", error);
+          toast({
+            variant: "destructive",
+            title: "다운로드 오류",
+            description: error || "파일 다운로드 중 오류가 발생했습니다.",
+          });
+        }
+      });
+
+      if (!result.success && result.error) {
+        // 오류 처리는 onError 콜백에서 이미 처리됨
+        console.error("Download failed:", result.error);
+      }
       
-      toast({
-        title: "다운로드 시작",
-        description: "파일 다운로드가 시작되었습니다.",
-      })
     } catch (error) {
-      console.error("Error downloading file:", error)
+      console.error("Error downloading file:", error);
       toast({
         variant: "destructive",
         title: "다운로드 오류",
-        description: "파일 다운로드 중 오류가 발생했습니다.",
-      })
+        description: "파일 다운로드 중 예상치 못한 오류가 발생했습니다.",
+      });
     } finally {
-      setIsDownloading(false)
+      setIsDownloading(false);
     }
   }
 
+  // 보안 다운로드 유틸리티를 사용한 전체 파일 다운로드
   const handleDownloadAllFiles = async () => {
     try {
       setIsDownloading(true)
 
+      // 전체 파일 다운로드 API 엔드포인트
       const downloadUrl = `/api/tech-vendors/attachments/download-all?vendorId=${Number(techCompanyId)}`
+      const fileName = `tech-vendor-${techCompanyId}-files.zip`
       
-      const downloadLink = document.createElement('a')
-      downloadLink.href = downloadUrl
-      downloadLink.download = `tech-vendor-${techCompanyId}-files.zip`
-      downloadLink.target = '_blank'
-      document.body.appendChild(downloadLink)
-      downloadLink.click()
-      
-      setTimeout(() => {
-        document.body.removeChild(downloadLink)
-      }, 100)
+      // 보안 다운로드 유틸리티 사용
+      const result = await downloadFile(downloadUrl, fileName, {
+        action: 'download',
+        showToast: false, // 우리가 직접 토스트 관리
+        onSuccess: (fileName, fileSize) => {
+          const sizeText = fileSize ? ` (${formatFileSize(fileSize)})` : '';
+          toast({
+            title: "전체 다운로드 완료",
+            description: `전체 파일 다운로드가 완료되었습니다: ${fileName}${sizeText}`,
+          });
+        },
+        onError: (error) => {
+          console.error("Download all error:", error);
+          toast({
+            variant: "destructive",
+            title: "다운로드 오류",
+            description: error || "전체 파일 다운로드 중 오류가 발생했습니다.",
+          });
+        }
+      });
+
+      if (!result.success && result.error) {
+        // 오류 처리는 onError 콜백에서 이미 처리됨
+        console.error("Download all failed:", result.error);
+      }
       
-      toast({
-        title: "다운로드 시작",
-        description: "전체 파일 다운로드가 시작되었습니다.",
-      })
     } catch (error) {
-      console.error("Error downloading files:", error)
+      console.error("Error downloading files:", error);
       toast({
         variant: "destructive",
         title: "다운로드 오류",
-        description: "파일 다운로드 중 오류가 발생했습니다.",
-      })
+        description: "전체 파일 다운로드 중 예상치 못한 오류가 발생했습니다.",
+      });
     } finally {
-      setIsDownloading(false)
+      setIsDownloading(false);
     }
   }
 
@@ -247,6 +286,9 @@ export function TechVendorInfoForm() {
     )
   }
 
+  // 보안 정보 가져오기
+  const securityInfo = getSecurityInfo();
+
   return (
     <div className="space-y-6 p-6">
       <div className="flex items-center justify-between">
@@ -256,6 +298,11 @@ export function TechVendorInfoForm() {
             <InformationButton pagePath="partners/info" />
           </div>
           <p className="text-gray-600">기술영업 벤더 정보를 확인하고 업데이트할 수 있습니다.</p>
+          
+          {/* 보안 정보 표시 */}
+          <div className="text-xs text-muted-foreground mt-1">
+            <p>📁 허용 파일 크기: {securityInfo.maxFileSizeFormatted} | 남은 다운로드: {securityInfo.remainingDownloads}/분</p>
+          </div>
         </div>
         {attachments.length > 0 && (
           <Button
@@ -263,7 +310,11 @@ export function TechVendorInfoForm() {
             onClick={handleDownloadAllFiles}
             disabled={isDownloading}
           >
-            <Download className="h-4 w-4 mr-2" />
+            {isDownloading ? (
+              <Loader2 className="h-4 w-4 mr-2 animate-spin" />
+            ) : (
+              <Download className="h-4 w-4 mr-2" />
+            )}
             {isDownloading ? "다운로드 중..." : "전체 파일 다운로드"}
           </Button>
         )}
@@ -395,7 +446,6 @@ export function TechVendorInfoForm() {
             </CardContent>
           </Card>
 
-
           {/* 연락처 정보 */}
           {contacts.length > 0 && (
             <Card>
@@ -455,7 +505,7 @@ export function TechVendorInfoForm() {
             </Card>
           )}
 
-          {/* 첨부파일 정보 */}
+          {/* 첨부파일 정보 - 보안 강화된 버전 */}
           {attachments.length > 0 && (
             <Card>
               <CardHeader>
@@ -466,29 +516,45 @@ export function TechVendorInfoForm() {
               </CardHeader>
               <CardContent>
                 <div className="space-y-2">
-                  {attachments.map((file) => (
-                    <div
-                      key={file.id}
-                      className="flex items-center justify-between p-3 border rounded-lg hover:bg-gray-50"
-                    >
-                      <div className="flex-1">
-                        <h4 className="font-medium">{file.fileName}</h4>
-                        <div className="flex items-center space-x-2 text-sm text-gray-600">
-                          <span>{file.attachmentType}</span>
-                          <span>•</span>
-                          <span>{new Date(file.createdAt).toLocaleDateString()}</span>
+                  {attachments.map((file) => {
+                    const fileInfo = getFileInfo(file.fileName);
+                    return (
+                      <div
+                        key={file.id}
+                        className="flex items-center justify-between p-3 border rounded-lg hover:bg-gray-50"
+                      >
+                        <div className="flex-1">
+                          <div className="flex items-center space-x-2">
+                            <span className="text-lg">{fileInfo.icon}</span>
+                            <h4 className="font-medium">{file.fileName}</h4>
+                          </div>
+                          <div className="flex items-center space-x-2 text-sm text-gray-600 mt-1">
+                            <span>{file.attachmentType}</span>
+                            <span>•</span>
+                            <span>{new Date(file.createdAt).toLocaleDateString()}</span>
+                            {file.fileSize && (
+                              <>
+                                <span>•</span>
+                                <span>{formatFileSize(file.fileSize)}</span>
+                              </>
+                            )}
+                          </div>
                         </div>
+                        <Button
+                          variant="ghost"
+                          size="sm"
+                          onClick={() => handleDownloadFile(file)}
+                          disabled={isDownloading}
+                        >
+                          {isDownloading ? (
+                            <Loader2 className="h-4 w-4 animate-spin" />
+                          ) : (
+                            <Download className="h-4 w-4" />
+                          )}
+                        </Button>
                       </div>
-                      <Button
-                        variant="ghost"
-                        size="sm"
-                        onClick={() => handleDownloadFile(file)}
-                        disabled={isDownloading}
-                      >
-                        <Download className="h-4 w-4" />
-                      </Button>
-                    </div>
-                  ))}
+                    );
+                  })}
                 </div>
               </CardContent>
             </Card>
@@ -513,4 +579,4 @@ export function TechVendorInfoForm() {
       </Form>
     </div>
   )
-} 
\ No newline at end of file
+}
\ No newline at end of file
-- 
cgit v1.2.3

