summaryrefslogtreecommitdiff
path: root/lib/tech-vendors/possible-items
diff options
context:
space:
mode:
authordujinkim <dujin.kim@dtsolution.co.kr>2025-08-26 08:23:00 +0000
committerdujinkim <dujin.kim@dtsolution.co.kr>2025-08-26 08:23:00 +0000
commit30683aee60eedd39893c79773fbae913349a0417 (patch)
treea0e1ea0a062cbfce0f9f5a1c615ef01b2f3eaa14 /lib/tech-vendors/possible-items
parentebab13aa5962f8974a3d8f80e745280d66eb255e (diff)
(최겸) gtc 레코드 생성 및 기술영업 아이템 import 수정
Diffstat (limited to 'lib/tech-vendors/possible-items')
-rw-r--r--lib/tech-vendors/possible-items/possible-items-table.tsx8
-rw-r--r--lib/tech-vendors/possible-items/possible-items-toolbar-actions.tsx156
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"