summaryrefslogtreecommitdiff
path: root/lib/vendor-pool/table/vendor-pool-table.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'lib/vendor-pool/table/vendor-pool-table.tsx')
-rw-r--r--lib/vendor-pool/table/vendor-pool-table.tsx224
1 files changed, 138 insertions, 86 deletions
diff --git a/lib/vendor-pool/table/vendor-pool-table.tsx b/lib/vendor-pool/table/vendor-pool-table.tsx
index 43dd64c1..46a0588d 100644
--- a/lib/vendor-pool/table/vendor-pool-table.tsx
+++ b/lib/vendor-pool/table/vendor-pool-table.tsx
@@ -16,19 +16,20 @@ import { BulkImportDialog } from "./bulk-import-dialog"
import { columns, type VendorPoolItem } from "./vendor-pool-table-columns"
import { createVendorPool, updateVendorPool, deleteVendorPool } from "../service"
-import type { VendorPool } from "../types"
-
-// 테이블 메타 타입 확장
-declare module "@tanstack/react-table" {
- interface TableMeta<TData> {
- onCellUpdate?: (id: string, field: keyof TData, newValue: any) => Promise<void>
- onCellCancel?: (id: string, field: keyof TData) => void
- onAction?: (action: string, data?: any) => void
- onSaveEmptyRow?: (tempId: string) => Promise<void>
- onCancelEmptyRow?: (tempId: string) => void
- isEmptyRow?: (id: string) => boolean
- getPendingChanges?: () => Record<string, Partial<VendorPoolItem>>
- }
+import type { VendorPool } from "@/db/schema/avl/vendor-pool"
+import { Download, FileSpreadsheet, Upload } from "lucide-react"
+import { ImportVendorPoolButton } from "./vendor-pool-excel-import-button"
+import { exportVendorPoolToExcel, createVendorPoolTemplate } from "../excel-utils"
+
+// vendor-pool 테이블 메타 타입
+interface VendorPoolTableMeta {
+ onCellUpdate?: (id: string | number, field: string, newValue: any) => Promise<void>
+ onCellCancel?: (id: string | number, field: string) => void
+ onAction?: (action: string, data?: any) => void
+ onSaveEmptyRow?: (tempId: string) => Promise<void>
+ onCancelEmptyRow?: (tempId: string) => void
+ isEmptyRow?: (id: string) => boolean
+ getPendingChanges?: () => Record<string, Partial<VendorPoolItem>>
}
interface VendorPoolTableProps {
@@ -37,6 +38,67 @@ interface VendorPoolTableProps {
onRefresh?: () => void // 데이터 새로고침 콜백
}
+// 빈 행 기본값 객체
+const createEmptyVendorPoolBase = (): Omit<VendorPool, 'id'> & { id?: string | number } => ({
+ constructionSector: "",
+ htDivision: "",
+ designCategoryCode: "",
+ designCategory: "",
+ equipBulkDivision: "",
+ packageCode: null,
+ packageName: null,
+ materialGroupCode: null,
+ materialGroupName: null,
+ smCode: null,
+ similarMaterialNamePurchase: null,
+ similarMaterialNameOther: null,
+ vendorCode: null,
+ vendorName: "",
+ taxId: null,
+ faTarget: false,
+ faStatus: null,
+ faRemark: null,
+ tier: null,
+ isAgent: false,
+ contractSignerCode: null,
+ contractSignerName: "",
+ headquarterLocation: "",
+ manufacturingLocation: "",
+ avlVendorName: null,
+ similarVendorName: null,
+ hasAvl: false,
+ isBlacklist: false,
+ isBcc: false,
+ purchaseOpinion: null,
+ shipTypeCommon: false,
+ shipTypeAmax: false,
+ shipTypeSmax: false,
+ shipTypeVlcc: false,
+ shipTypeLngc: false,
+ shipTypeCont: false,
+ offshoreTypeCommon: false,
+ offshoreTypeFpso: false,
+ offshoreTypeFlng: false,
+ offshoreTypeFpu: false,
+ offshoreTypePlatform: false,
+ offshoreTypeWtiv: false,
+ offshoreTypeGom: false,
+ picName: null,
+ picEmail: null,
+ picPhone: null,
+ agentName: null,
+ agentEmail: null,
+ agentPhone: null,
+ recentQuoteDate: null,
+ recentQuoteNumber: null,
+ recentOrderDate: null,
+ recentOrderNumber: null,
+ registrationDate: null,
+ registrant: null,
+ lastModifiedDate: null,
+ lastModifier: null,
+})
+
export function VendorPoolTable({ data, pageCount, onRefresh }: VendorPoolTableProps) {
const { data: session } = useSession()
@@ -54,7 +116,7 @@ export function VendorPoolTable({ data, pageCount, onRefresh }: VendorPoolTableP
// 인라인 편집 핸들러 (일괄 저장용)
- const handleCellUpdate = React.useCallback(async (id: string, field: keyof VendorPoolItem, newValue: any) => {
+ const handleCellUpdate = React.useCallback(async (id: string | number, field: string, newValue: any) => {
const isEmptyRow = String(id).startsWith('temp-')
if (isEmptyRow) {
@@ -81,7 +143,7 @@ export function VendorPoolTable({ data, pageCount, onRefresh }: VendorPoolTableP
// 편집 취소 핸들러
- const handleCellCancel = React.useCallback((id: string, field: keyof VendorPoolItem) => {
+ const handleCellCancel = React.useCallback((id: string | number, field: string) => {
const isEmptyRow = String(id).startsWith('temp-')
if (isEmptyRow) {
@@ -142,13 +204,13 @@ export function VendorPoolTable({ data, pageCount, onRefresh }: VendorPoolTableP
for (const [id, changes] of Object.entries(pendingChanges)) {
try {
// changes에서 id 필드 제거 (서버에서 자동 생성)
- const { id: _, ...updateData } = changes as any
+ const { id: _, no: __, selected: ___, ...updateData } = changes
// 최종변경자를 현재 세션 사용자 정보로 설정
- const updateDataWithModifier = {
+ const updateDataWithModifier: any = {
...updateData,
- lastModifier: session?.user?.name || ""
+ lastModifier: session?.user?.name || null
}
- const result = await updateVendorPool(Number(id), updateDataWithModifier as Partial<VendorPool>)
+ const result = await updateVendorPool(Number(id), updateDataWithModifier)
if (result) {
successCount++
} else {
@@ -190,68 +252,18 @@ export function VendorPoolTable({ data, pageCount, onRefresh }: VendorPoolTableP
if (isCreating) return // 이미 생성 중이면 중복 생성 방지
const tempId = `temp-${Date.now()}`
+ const userName = session?.user?.name || null
+
const emptyRow: VendorPoolItem = {
- id: tempId,
+ ...createEmptyVendorPoolBase(),
+ id: tempId, // string 타입으로 설정
no: 0, // 나중에 계산
selected: false,
- constructionSector: "",
- htDivision: "",
- designCategoryCode: "",
- designCategory: "",
- equipBulkDivision: "",
- packageCode: "",
- packageName: "",
- materialGroupCode: "",
- materialGroupName: "",
- smCode: "",
- similarMaterialNamePurchase: "",
- similarMaterialNameOther: "",
- vendorCode: "",
- vendorName: "",
- taxId: "",
- faTarget: false,
- faStatus: "",
- faRemark: "",
- tier: "",
- isAgent: false,
- contractSignerCode: "",
- contractSignerName: "",
- headquarterLocation: "",
- manufacturingLocation: "",
- avlVendorName: "",
- similarVendorName: "",
- hasAvl: false,
- isBlacklist: false,
- isBcc: false,
- purchaseOpinion: "",
- shipTypeCommon: false,
- shipTypeAmax: false,
- shipTypeSmax: false,
- shipTypeVlcc: false,
- shipTypeLngc: false,
- shipTypeCont: false,
- offshoreTypeCommon: false,
- offshoreTypeFpso: false,
- offshoreTypeFlng: false,
- offshoreTypeFpu: false,
- offshoreTypePlatform: false,
- offshoreTypeWtiv: false,
- offshoreTypeGom: false,
- picName: "",
- picEmail: "",
- picPhone: "",
- agentName: "",
- agentEmail: "",
- agentPhone: "",
- recentQuoteDate: "",
- recentQuoteNumber: "",
- recentOrderDate: "",
- recentOrderNumber: "",
- registrationDate: "",
- registrant: session?.user?.name || "",
+ registrationDate: "", // 빈 행에서는 string으로 표시
+ registrant: userName,
lastModifiedDate: "",
- lastModifier: session?.user?.name || "",
- }
+ lastModifier: userName,
+ } as unknown as VendorPoolItem
setEmptyRows(prev => ({ ...prev, [tempId]: emptyRow }))
setIsCreating(true)
@@ -312,10 +324,10 @@ export function VendorPoolTable({ data, pageCount, onRefresh }: VendorPoolTableP
try {
setIsSaving(true)
- // id 필드 제거 (서버에서 자동 생성)
- const { id: _, no: __, selected: ___, ...createData } = finalData
+ // id, no, selected 필드 제거 및 타입 변환
+ const { id: _, no: __, selected: ___, registrationDate: ____, lastModifiedDate: _____, ...createData } = finalData
- const result = await createVendorPool(createData as Omit<VendorPool, 'id' | 'registrationDate' | 'lastModifiedDate'>)
+ const result = await createVendorPool(createData as any)
if (result) {
toast.success("새 항목이 추가되었습니다.")
@@ -591,8 +603,34 @@ export function VendorPoolTable({ data, pageCount, onRefresh }: VendorPoolTableP
toast.info('저장 기능은 개발 중입니다.')
break
- case 'excel-import':
- toast.info('Excel Import 기능은 개발 중입니다.')
+
+ case 'excel-export':
+ try {
+ // 현재 테이블 데이터를 Excel로 내보내기 (ID 포함)
+ const currentData = table.getFilteredRowModel().rows.map(row => row.original)
+ await exportVendorPoolToExcel(
+ currentData,
+ `vendor-pool-${new Date().toISOString().split('T')[0]}.xlsx`,
+ true // ID 포함
+ )
+ toast.success('Excel 파일이 다운로드되었습니다.')
+ } catch (error) {
+ console.error('Excel export 실패:', error)
+ toast.error('Excel 내보내기에 실패했습니다.')
+ }
+ break
+
+ case 'excel-template':
+ try {
+ // 템플릿 파일 다운로드 (데이터 없음, ID 컬럼 제외)
+ await createVendorPoolTemplate(
+ `vendor-pool-template-${new Date().toISOString().split('T')[0]}.xlsx`
+ )
+ toast.success('Excel 템플릿이 다운로드되었습니다.')
+ } catch (error) {
+ console.error('Excel template export 실패:', error)
+ toast.error('Excel 템플릿 다운로드에 실패했습니다.')
+ }
break
case 'delete':
@@ -634,7 +672,7 @@ export function VendorPoolTable({ data, pageCount, onRefresh }: VendorPoolTableP
// 제공된 값들만 적용 (빈 값이나 undefined는 건너뜀)
Object.entries(bulkData).forEach(([field, value]) => {
if (value !== undefined && value !== null && value !== '') {
- handleCellUpdate(rowId, field as keyof VendorPoolItem, value)
+ handleCellUpdate(rowId, field as keyof VendorPool, value)
}
})
}
@@ -648,7 +686,7 @@ export function VendorPoolTable({ data, pageCount, onRefresh }: VendorPoolTableP
}, [table, handleCellUpdate])
// 테이블 메타에 핸들러 설정
- table.options.meta = {
+ const tableMeta: VendorPoolTableMeta = {
onAction: handleAction,
onCellUpdate: handleCellUpdate,
onCellCancel: handleCellCancel,
@@ -657,6 +695,8 @@ export function VendorPoolTable({ data, pageCount, onRefresh }: VendorPoolTableP
isEmptyRow: (id: string) => String(id).startsWith('temp-'),
getPendingChanges: () => pendingChanges
}
+
+ table.options.meta = tableMeta as any
// 툴바 액션 핸들러들
@@ -699,12 +739,24 @@ export function VendorPoolTable({ data, pageCount, onRefresh }: VendorPoolTableP
일괄입력
</Button>
+ <ImportVendorPoolButton onSuccess={onRefresh} />
+
+ <Button
+ onClick={() => handleToolbarAction('excel-export')}
+ variant="outline"
+ size="sm"
+ >
+ <Download className="mr-2 h-4 w-4" />
+ Excel Export
+ </Button>
+
<Button
- onClick={() => handleToolbarAction('excel-import')}
+ onClick={() => handleToolbarAction('excel-template')}
variant="outline"
size="sm"
>
- Excel Import
+ <FileSpreadsheet className="mr-2 h-4 w-4" />
+ Template
</Button>
<Button