From 00092099271ff743ac195511c03994e80f91a2e9 Mon Sep 17 00:00:00 2001 From: dujinkim Date: Fri, 10 Oct 2025 07:37:26 +0000 Subject: (최겸) 기술영업 import 수정 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../contact-table-toolbar-actions.tsx | 124 ++++++++++++++++++++- 1 file changed, 119 insertions(+), 5 deletions(-) (limited to 'lib/tech-vendors/contacts-table/contact-table-toolbar-actions.tsx') diff --git a/lib/tech-vendors/contacts-table/contact-table-toolbar-actions.tsx b/lib/tech-vendors/contacts-table/contact-table-toolbar-actions.tsx index 84228a54..7b81967e 100644 --- a/lib/tech-vendors/contacts-table/contact-table-toolbar-actions.tsx +++ b/lib/tech-vendors/contacts-table/contact-table-toolbar-actions.tsx @@ -10,10 +10,13 @@ import { exportTableToExcel } from "@/lib/export" import { Button } from "@/components/ui/button" import { TechVendorContact } from "@/db/schema/techVendors" import { AddContactDialog } from "./add-contact-dialog" -import { - importTechVendorContacts, - generateContactImportTemplate, - parseContactImportFile +import { + importTechVendorContacts, + generateContactImportTemplate, + parseContactImportFile, + importTechVendorDistinctContacts, + generateDistinctContactImportTemplate, + parseDistinctContactImportFile } from "@/lib/tech-vendors/service" interface TechVendorContactsTableToolbarActionsProps { @@ -24,6 +27,7 @@ interface TechVendorContactsTableToolbarActionsProps { export function TechVendorContactsTableToolbarActions({ table, vendorId }: TechVendorContactsTableToolbarActionsProps) { // 파일 input을 숨기고, 버튼 클릭 시 참조해 클릭하는 방식 const fileInputRef = React.useRef(null) + const distinctFileInputRef = React.useRef(null) // 파일이 선택되었을 때 처리 async function onFileChange(event: React.ChangeEvent) { @@ -99,6 +103,11 @@ export function TechVendorContactsTableToolbarActions({ table, vendorId }: TechV fileInputRef.current?.click() } + function handleDistinctImportClick() { + // Distinct Import용 숨겨진 요소를 클릭 + distinctFileInputRef.current?.click() + } + async function handleTemplateDownload() { try { const templateBlob = await generateContactImportTemplate() @@ -115,6 +124,89 @@ export function TechVendorContactsTableToolbarActions({ table, vendorId }: TechV } } + // Distinct Import 파일 처리 함수 + async function onDistinctFileChange(event: React.ChangeEvent) { + const file = event.target.files?.[0] + if (!file) return + + // 파일 초기화 (동일 파일 재업로드 시에도 onChange가 트리거되도록) + event.target.value = "" + + try { + // Excel 파일 파싱 (새로운 함수 사용) + const distinctContactData = await parseDistinctContactImportFile(file) + + if (distinctContactData.length === 0) { + toast.error("유효한 데이터가 없습니다. 템플릿 형식을 확인해주세요.") + return + } + + // 서버로 데이터 전송 (새로운 함수 사용) + const result = await importTechVendorDistinctContacts(distinctContactData) + + if (result.successCount > 0) { + toast.success(`${result.successCount}개 담당자가 성공적으로 추가되었습니다.`) + } + + if (result.failedRows.length > 0) { + toast.error(`${result.failedRows.length}개 행에서 오류가 발생했습니다.`) + + // 에러 데이터를 Excel로 다운로드 + const errorWorkbook = new ExcelJS.Workbook() + const errorWorksheet = errorWorkbook.addWorksheet("오류내역") + + // 헤더 추가 + errorWorksheet.columns = [ + { header: "행번호", key: "row", width: 10 }, + { header: "벤더이름", key: "vendorName", width: 20 }, + { header: "이메일", key: "email", width: 25 }, + { header: "오류내용", key: "error", width: 80, style: { alignment: { wrapText: true }, font: { color: { argb: "FFFF0000" } } } }, + ] + + // 오류 데이터 추가 + result.failedRows.forEach(failedRow => { + errorWorksheet.addRow({ + row: failedRow.row, + error: failedRow.error, + vendorName: failedRow.vendorName, + email: failedRow.email, + }) + }) + + const buffer = await errorWorkbook.xlsx.writeBuffer() + const blob = new Blob([buffer], { + type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + }) + const url = URL.createObjectURL(blob) + const link = document.createElement("a") + link.href = url + link.download = "distinct-contact-import-errors.xlsx" + link.click() + URL.revokeObjectURL(url) + } + + } catch (error) { + toast.error("파일 업로드 중 오류가 발생했습니다.") + console.error("Distinct Import error:", error) + } + } + + async function handleDistinctTemplateDownload() { + try { + const templateBlob = await generateDistinctContactImportTemplate() + const url = URL.createObjectURL(templateBlob) + const link = document.createElement("a") + link.href = url + link.download = "tech-vendor-distinct-contacts-template.xlsx" + link.click() + URL.revokeObjectURL(url) + toast.success("Distinct Import 템플릿이 다운로드되었습니다.") + } catch (error) { + toast.error("템플릿 다운로드 중 오류가 발생했습니다.") + console.error("Distinct Template download error:", error) + } + } + return (
@@ -123,7 +215,13 @@ export function TechVendorContactsTableToolbarActions({ table, vendorId }: TechV {/** 템플릿 다운로드 버튼 */} + + {/** Distinct Import 템플릿 다운로드 버튼 */} + {/** Import 버튼 (파일 업로드) */} @@ -131,6 +229,13 @@ export function TechVendorContactsTableToolbarActions({ table, vendorId }: TechV