diff options
| author | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-08-26 08:23:00 +0000 |
|---|---|---|
| committer | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-08-26 08:23:00 +0000 |
| commit | 30683aee60eedd39893c79773fbae913349a0417 (patch) | |
| tree | a0e1ea0a062cbfce0f9f5a1c615ef01b2f3eaa14 /lib/tech-vendors/possible-items | |
| parent | ebab13aa5962f8974a3d8f80e745280d66eb255e (diff) | |
(최겸) gtc 레코드 생성 및 기술영업 아이템 import 수정
Diffstat (limited to 'lib/tech-vendors/possible-items')
| -rw-r--r-- | lib/tech-vendors/possible-items/possible-items-table.tsx | 8 | ||||
| -rw-r--r-- | lib/tech-vendors/possible-items/possible-items-toolbar-actions.tsx | 156 |
2 files changed, 159 insertions, 5 deletions
diff --git a/lib/tech-vendors/possible-items/possible-items-table.tsx b/lib/tech-vendors/possible-items/possible-items-table.tsx index b54e12d4..100ef04a 100644 --- a/lib/tech-vendors/possible-items/possible-items-table.tsx +++ b/lib/tech-vendors/possible-items/possible-items-table.tsx @@ -186,10 +186,14 @@ export function TechVendorPossibleItemsTable({ </Button> )} - <PossibleItemsTableToolbarActions - table={table} + <PossibleItemsTableToolbarActions + table={table} vendorId={vendorId} onAdd={() => setShowAddDialog(true)} // 주석처리 + onRefresh={() => { + // 페이지 새로고침을 위한 콜백 + window.location.reload() + }} /> </div> </DataTableAdvancedToolbar> diff --git a/lib/tech-vendors/possible-items/possible-items-toolbar-actions.tsx b/lib/tech-vendors/possible-items/possible-items-toolbar-actions.tsx index 192bf614..371f88f9 100644 --- a/lib/tech-vendors/possible-items/possible-items-toolbar-actions.tsx +++ b/lib/tech-vendors/possible-items/possible-items-toolbar-actions.tsx @@ -2,7 +2,7 @@ import * as React from "react" import type { Table } from "@tanstack/react-table" -import { Plus, Trash2 } from "lucide-react" +import { Plus, Trash2, Upload, Download } from "lucide-react" import { toast } from "sonner" import { Button } from "@/components/ui/button" @@ -19,21 +19,33 @@ import { } from "@/components/ui/alert-dialog" import type { TechVendorPossibleItem } from "../validations" -import { deleteTechVendorPossibleItemsNew } from "../service" +import { + deleteTechVendorPossibleItemsNew, + parsePossibleItemsImportFile, + importPossibleItemsFromExcel, + generatePossibleItemsImportTemplate, + generatePossibleItemsErrorExcel, + type PossibleItemImportData, + type PossibleItemErrorData +} from "../service" interface PossibleItemsTableToolbarActionsProps { table: Table<TechVendorPossibleItem> vendorId: number onAdd: () => void // 주석처리 + onRefresh?: () => void // 데이터 새로고침 콜백 } export function PossibleItemsTableToolbarActions({ table, vendorId, onAdd, // 주석처리 + onRefresh, }: PossibleItemsTableToolbarActionsProps) { const [showDeleteAlert, setShowDeleteAlert] = React.useState(false) const [isDeleting, setIsDeleting] = React.useState(false) + const [isImporting, setIsImporting] = React.useState(false) + const fileInputRef = React.useRef<HTMLInputElement>(null) const selectedRows = table.getFilteredSelectedRowModel().rows @@ -42,7 +54,7 @@ export function PossibleItemsTableToolbarActions({ try { const ids = selectedRows.map((row) => row.original.id) const { error } = await deleteTechVendorPossibleItemsNew(ids, vendorId) - + if (error) { throw new Error(error) } @@ -50,6 +62,7 @@ export function PossibleItemsTableToolbarActions({ toast.success(`${ids.length}개의 아이템이 삭제되었습니다`) table.resetRowSelection() setShowDeleteAlert(false) + onRefresh?.() // 데이터 새로고침 } catch { toast.error("아이템 삭제 중 오류가 발생했습니다") } finally { @@ -57,9 +70,146 @@ export function PossibleItemsTableToolbarActions({ } } + // 템플릿 다운로드 핸들러 + async function handleTemplateDownload() { + try { + const templateBlob = await generatePossibleItemsImportTemplate() + const url = window.URL.createObjectURL(templateBlob) + const link = document.createElement("a") + link.href = url + link.download = "벤더_possible_items_템플릿.xlsx" + document.body.appendChild(link) + link.click() + document.body.removeChild(link) + window.URL.revokeObjectURL(url) + toast.success("템플릿 파일이 다운로드되었습니다") + } catch (error) { + toast.error("템플릿 다운로드 중 오류가 발생했습니다") + } + } + + // 파일 선택 핸들러 + function handleFileSelect() { + fileInputRef.current?.click() + } + + // Excel 파일 import 핸들러 + async function handleFileImport(event: React.ChangeEvent<HTMLInputElement>) { + const file = event.target.files?.[0] + if (!file) return + + // 파일 타입 검증 + if (!file.name.endsWith('.xlsx') && !file.name.endsWith('.xls')) { + toast.error("Excel 파일(.xlsx 또는 .xls)만 업로드 가능합니다") + return + } + + setIsImporting(true) + try { + // Excel 파일 파싱 + const importData: PossibleItemImportData[] = await parsePossibleItemsImportFile(file) + + if (importData.length === 0) { + toast.error("업로드할 데이터가 없습니다") + return + } + + // 데이터 import 실행 + const result = await importPossibleItemsFromExcel(importData) + + // 결과 메시지 생성 + const successMessage = `${result.successCount}개의 아이템이 성공적으로 등록되었습니다` + const failMessage = result.failedRows.length > 0 + ? `, ${result.failedRows.length}개의 아이템 등록 실패` + : "" + + toast.success(successMessage + failMessage) + + // 실패한 행이 있는 경우 에러 파일 다운로드 + if (result.failedRows.length > 0) { + const errorData: PossibleItemErrorData[] = result.failedRows.map(failedRow => ({ + vendorEmail: failedRow.vendorEmail, + itemCode: failedRow.itemCode, + itemType: failedRow.itemType, + error: failedRow.error, + })) + + const errorBlob = await generatePossibleItemsErrorExcel(errorData) + const url = window.URL.createObjectURL(errorBlob) + const link = document.createElement("a") + link.href = url + link.download = "possible_items_import_에러.xlsx" + document.body.appendChild(link) + link.click() + document.body.removeChild(link) + window.URL.revokeObjectURL(url) + + toast.error("에러 내역 파일이 다운로드되었습니다") + } + + // 파일 입력 초기화 + if (fileInputRef.current) { + fileInputRef.current.value = "" + } + + // 데이터 새로고침 + onRefresh?.() + + } catch (error) { + console.error("Import error:", error) + toast.error(error instanceof Error ? error.message : "데이터 등록 중 오류가 발생했습니다") + } finally { + setIsImporting(false) + } + } + return ( <> <div className="flex items-center gap-2"> + {/* 템플릿 다운로드 버튼 */} + <Tooltip> + <TooltipTrigger asChild> + <Button + variant="outline" + size="sm" + onClick={handleTemplateDownload} + > + <Download className="mr-2 h-4 w-4" /> + 템플릿 + </Button> + </TooltipTrigger> + <TooltipContent> + Excel 템플릿 파일 다운로드 + </TooltipContent> + </Tooltip> + + {/* Excel Import 버튼 */} + <Tooltip> + <TooltipTrigger asChild> + <Button + variant="outline" + size="sm" + onClick={handleFileSelect} + disabled={isImporting} + > + <Upload className="mr-2 h-4 w-4" /> + {isImporting ? "등록 중..." : "Import"} + </Button> + </TooltipTrigger> + <TooltipContent> + Excel 파일로 아이템 일괄 등록 + </TooltipContent> + </Tooltip> + + {/* 숨겨진 파일 입력 */} + <input + ref={fileInputRef} + type="file" + accept=".xlsx,.xls" + onChange={handleFileImport} + className="hidden" + /> + {/* 아이템 추가 버튼 주석처리 */} <Button variant="outline" |
