From ee57cc221ff2edafd3c0f12a181214c602ed257e Mon Sep 17 00:00:00 2001 From: dujinkim 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 (
@@ -256,6 +298,11 @@ export function TechVendorInfoForm() {

기술영업 벤더 정보를 확인하고 업데이트할 수 있습니다.

+ + {/* 보안 정보 표시 */} +
+

📁 허용 파일 크기: {securityInfo.maxFileSizeFormatted} | 남은 다운로드: {securityInfo.remainingDownloads}/분

+
{attachments.length > 0 && ( )} @@ -395,7 +446,6 @@ export function TechVendorInfoForm() { - {/* 연락처 정보 */} {contacts.length > 0 && ( @@ -455,7 +505,7 @@ export function TechVendorInfoForm() { )} - {/* 첨부파일 정보 */} + {/* 첨부파일 정보 - 보안 강화된 버전 */} {attachments.length > 0 && ( @@ -466,29 +516,45 @@ export function TechVendorInfoForm() {
- {attachments.map((file) => ( -
-
-

{file.fileName}

-
- {file.attachmentType} - - {new Date(file.createdAt).toLocaleDateString()} + {attachments.map((file) => { + const fileInfo = getFileInfo(file.fileName); + return ( +
+
+
+ {fileInfo.icon} +

{file.fileName}

+
+
+ {file.attachmentType} + + {new Date(file.createdAt).toLocaleDateString()} + {file.fileSize && ( + <> + + {formatFileSize(file.fileSize)} + + )} +
+
- -
- ))} + ); + })}
@@ -513,4 +579,4 @@ export function TechVendorInfoForm() {
) -} \ No newline at end of file +} \ No newline at end of file -- cgit v1.2.3