From 3cde5c2c7d157bb0fe353de5e67e4b35506bb4e2 Mon Sep 17 00:00:00 2001 From: joonhoekim <26rote@gmail.com> Date: Tue, 23 Sep 2025 20:05:46 +0900 Subject: (김준회) Vendorpool 수정요청사항 - 컬럼리사이징 관련 문제 해결 - 공통컴포넌트에 columnResizeMode: "onChange" 속성 추가 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/avl/table/avl-table-columns.tsx | 568 +++---- lib/avl/table/avl-table.tsx | 187 +-- lib/pos/get-pos.ts | 1 + lib/vendor-pool/table/columns.tsx | 1687 -------------------- .../table/vendor-pool-table-columns.tsx | 1640 +++++++++++++++++++ lib/vendor-pool/table/vendor-pool-table.tsx | 83 +- 6 files changed, 1949 insertions(+), 2217 deletions(-) delete mode 100644 lib/vendor-pool/table/columns.tsx create mode 100644 lib/vendor-pool/table/vendor-pool-table-columns.tsx (limited to 'lib') diff --git a/lib/avl/table/avl-table-columns.tsx b/lib/avl/table/avl-table-columns.tsx index d95a29b0..6ec2c3db 100644 --- a/lib/avl/table/avl-table-columns.tsx +++ b/lib/avl/table/avl-table-columns.tsx @@ -1,7 +1,7 @@ import { Checkbox } from "@/components/ui/checkbox" import { Button } from "@/components/ui/button" import { Badge } from "@/components/ui/badge" -import { Eye, Edit, Trash2, History } from "lucide-react" +import { Eye, History } from "lucide-react" import { type ColumnDef } from "@tanstack/react-table" import { DataTableColumnHeaderSimple } from "@/components/data-table/data-table-column-simple-header" import { AvlListItem } from "../types" @@ -26,303 +26,325 @@ declare module "@tanstack/react-table" { // 테이블 컬럼 정의 함수 export function getColumns({ selectedRows = [], onRowSelect }: GetColumnsProps): ColumnDef[] { const columns: ColumnDef[] = [ - // 기본 정보 그룹 + // select 컬럼 { - header: "AVL 정보", - columns: [ - { - id: "select", - header: () =>
선택
, - cell: ({ row }) => ( -
- { - onRowSelect?.(row.original.id, !!checked) - }} - aria-label="행 선택" - className="translate-y-[2px]" - /> + id: "select", + header: () =>
선택
, + cell: ({ row }) => ( +
+ { + onRowSelect?.(row.original.id, !!checked) + }} + aria-label="행 선택" + className="translate-y-[2px]" + /> +
+ ), + enableSorting: false, + enableHiding: false, + enableResizing: false, + size: 10, + minSize: 10, + maxSize: 10, + }, + // No 컬럼 + { + accessorKey: "no", + header: ({ column }) => ( + + ), + cell: ({ getValue }) =>
{getValue() as number}
, + enableResizing: true, + size: 60, + }, + // AVL 분류 컬럼 + { + accessorKey: "isTemplate", + header: ({ column }) => ( + + ), + cell: ({ getValue }) => { + const value = getValue() as boolean + return ( +
+ {value ? "표준 AVL" : "프로젝트 AVL"}
- ), - enableSorting: false, - enableHiding: false, - size: 40, - }, - { - accessorKey: "no", - header: ({ column }) => ( - - ), - cell: ({ getValue }) =>
{getValue() as number}
, - size: 60, - }, - { - accessorKey: "isTemplate", - header: ({ column }) => ( - - ), - cell: ({ getValue }) => { - const value = getValue() as boolean - return ( -
- {value ? "표준 AVL" : "프로젝트 AVL"} -
- ) - }, - size: 120, + ) }, - { - accessorKey: "constructionSector", - header: ({ column }) => ( - - ), - cell: ({ getValue }) => { - const value = getValue() as string - return ( -
- {value} -
- ) - }, - size: 100, - }, - { - accessorKey: "projectCode", - header: ({ column }) => ( - - ), - cell: ({ getValue }) => { - const value = getValue() as string - return ( -
- {value} -
- ) - }, - size: 140, - }, - { - accessorKey: "shipType", - header: ({ column }) => ( - - ), - cell: ({ getValue }) => { - const value = getValue() as string - return ( -
- {value} -
- ) - }, - size: 100, - }, - { - accessorKey: "avlKind", - header: ({ column }) => ( - - ), - cell: ({ getValue }) => { - const value = getValue() as string - return ( -
- {value} -
- ) - }, - size: 120, - }, - { - accessorKey: "htDivision", - header: ({ column }) => ( - - ), - cell: ({ getValue }) => { - const value = getValue() as string - return ( -
- {value} -
- ) - }, - size: 80, - }, - { - accessorKey: "rev", - header: ({ column }) => ( - - ), - cell: ({ getValue, row, table }) => { - const value = getValue() as number - return ( -
- - {value || 1} - - -
- ) - }, - size: 100, - }, - ], - }, - - // 집계 그룹 - { - header: "등록정보", - columns: [ - { - accessorKey: "PKG", - header: ({ column }) => ( - - ), + enableResizing: true, + size: 120, + }, + // 공사부문 컬럼 + { + accessorKey: "constructionSector", + header: ({ column }) => ( + + ), + cell: ({ getValue }) => { + const value = getValue() as string + return ( +
+ {value} +
+ ) }, - { - accessorKey: "materialGroup", - header: ({ column }) => ( - - ), + enableResizing: true, + size: 100, + }, + // 프로젝트 코드 컬럼 + { + accessorKey: "projectCode", + header: ({ column }) => ( + + ), + cell: ({ getValue }) => { + const value = getValue() as string + return ( +
+ {value} +
+ ) }, - { - accessorKey: "vendor", - header: ({ column }) => ( - - ), + enableResizing: true, + size: 140, + }, + // 선종 컬럼 + { + accessorKey: "shipType", + header: ({ column }) => ( + + ), + cell: ({ getValue }) => { + const value = getValue() as string + return ( +
+ {value} +
+ ) }, - { - accessorKey: "Tier", - header: ({ column }) => ( - - ), + enableResizing: true, + size: 100, + }, + // AVL 종류 컬럼 + { + accessorKey: "avlKind", + header: ({ column }) => ( + + ), + cell: ({ getValue }) => { + const value = getValue() as string + return ( +
+ {value} +
+ ) }, - { - accessorKey: "ownerSuggestion", - header: ({ column }) => ( - - ), + enableResizing: true, + size: 120, + }, + // H/T 구분 컬럼 + { + accessorKey: "htDivision", + header: ({ column }) => ( + + ), + cell: ({ getValue }) => { + const value = getValue() as string + return ( +
+ {value} +
+ ) }, - { - accessorKey: "shiSuggestion", - header: ({ column }) => ( - - ), + enableResizing: true, + size: 80, + }, + // Rev 컬럼 + { + accessorKey: "rev", + header: ({ column }) => ( + + ), + cell: ({ getValue, row, table }) => { + const value = getValue() as number + return ( +
+ + {value || 1} + + +
+ ) }, - ], - }, - - // 등록 정보 그룹 - { - header: "작성정보", - columns: [ - { - accessorKey: "createdAt", - header: ({ column }) => ( - - ), - cell: ({ getValue }) => { - const date = getValue() as string - return
{date}
- }, - size: 100, + enableResizing: true, + size: 100, + }, + // PKG 컬럼 + { + accessorKey: "PKG", + header: ({ column }) => ( + + ), + enableResizing: true, + size: 80, + }, + // 자재그룹 컬럼 + { + accessorKey: "materialGroup", + header: ({ column }) => ( + + ), + enableResizing: true, + size: 120, + }, + // 협력업체 컬럼 + { + accessorKey: "vendor", + header: ({ column }) => ( + + ), + enableResizing: true, + size: 150, + }, + // Tier 컬럼 + { + accessorKey: "Tier", + header: ({ column }) => ( + + ), + enableResizing: true, + size: 60, + }, + // 선주 제안 컬럼 + { + accessorKey: "ownerSuggestion", + header: ({ column }) => ( + + ), + enableResizing: true, + size: 100, + }, + // SHI 제안 컬럼 + { + accessorKey: "shiSuggestion", + header: ({ column }) => ( + + ), + enableResizing: true, + size: 100, + }, + // 등록일 컬럼 + { + accessorKey: "createdAt", + header: ({ column }) => ( + + ), + cell: ({ getValue }) => { + const date = getValue() as string + return
{date}
}, - { - accessorKey: "createdBy", - header: ({ column }) => ( - - ), - cell: ({ getValue }) => { - const date = getValue() as string - return
{date}
- }, - size: 100, + enableResizing: true, + size: 100, + }, + // 등록자 컬럼 + { + accessorKey: "createdBy", + header: ({ column }) => ( + + ), + cell: ({ getValue }) => { + const value = getValue() as string + return
{value}
}, - { - accessorKey: "updatedAt", - header: ({ column }) => ( - - ), - cell: ({ getValue }) => { - const date = getValue() as string - return
{date}
- }, - size: 100, + enableResizing: true, + size: 100, + }, + // 최종변경일 컬럼 + { + accessorKey: "updatedAt", + header: ({ column }) => ( + + ), + cell: ({ getValue }) => { + const date = getValue() as string + return
{date}
}, - { - accessorKey: "updatedBy", - header: ({ column }) => ( - - ), - cell: ({ getValue }) => { - const date = getValue() as string - return
{date}
- }, - size: 100, + enableResizing: true, + size: 100, + }, + // 최종변경자 컬럼 + { + accessorKey: "updatedBy", + header: ({ column }) => ( + + ), + cell: ({ getValue }) => { + const value = getValue() as string + return
{value}
}, - ], - }, - - // 액션 그룹 - { - id: "actions", - header: "액션", - columns: [ - { - id: "actions", - header: () =>
액션
, - cell: ({ row, table }) => { - const isEmptyRow = table.options.meta?.isEmptyRow?.(row.id) || false - - if (isEmptyRow) { - return ( -
- - -
- ) - } + enableResizing: true, + size: 100, + }, + // 액션 컬럼 + { + id: "actions", + header: () =>
액션
, + cell: ({ row, table }) => { + const isEmptyRow = table.options.meta?.isEmptyRow?.(row.id) || false + if (isEmptyRow) { return (
+
) - }, - enableSorting: false, - enableHiding: false, - size: 120, + } + + return ( +
+ +
+ ) }, - ], + enableSorting: false, + enableHiding: false, + enableResizing: false, + size: 120, + minSize: 120, + maxSize: 120, }, ] diff --git a/lib/avl/table/avl-table.tsx b/lib/avl/table/avl-table.tsx index 45da6268..61db658d 100644 --- a/lib/avl/table/avl-table.tsx +++ b/lib/avl/table/avl-table.tsx @@ -12,7 +12,7 @@ import { Button } from "@/components/ui/button" import { toast } from "sonner" import { getColumns } from "./avl-table-columns" -import { updateAvlListAction, deleteAvlListAction, handleAvlActionAction } from "../service" +import { handleAvlActionAction } from "../service" import type { AvlListItem } from "../types" import { AvlHistoryModal, type AvlHistoryRecord } from "@/lib/avl/components/avl-history-modal" import { getAvlHistory } from "@/lib/avl/history-service" @@ -33,21 +33,16 @@ declare module "@tanstack/react-table" { interface AvlTableProps { data: AvlListItem[] pageCount?: number - onRefresh?: () => void // 데이터 새로고침 콜백 isLoading?: boolean // 로딩 상태 onRegistrationModeChange?: (mode: 'standard' | 'project') => void // 등록 모드 변경 콜백 onRowSelect?: (selectedRow: AvlListItem | null) => void // 행 선택 콜백 } -export function AvlTable({ data, pageCount, onRefresh, isLoading, onRegistrationModeChange, onRowSelect }: AvlTableProps) { +export function AvlTable({ data, pageCount, isLoading, onRegistrationModeChange, onRowSelect }: AvlTableProps) { // 단일 선택을 위한 상태 (shi-vendor-po 방식) const [selectedRows, setSelectedRows] = React.useState([]) - // 수정사항 추적 (일괄 저장용) - const [pendingChanges, setPendingChanges] = React.useState>>({}) - const [isSaving, setIsSaving] = React.useState(false) - // 히스토리 모달 관리 const [historyModalOpen, setHistoryModalOpen] = React.useState(false) const [selectedAvlItem, setSelectedAvlItem] = React.useState(null) @@ -101,56 +96,6 @@ export function AvlTable({ data, pageCount, onRefresh, isLoading, onRegistration }, ] - - // 인라인 편집 핸들러 (일괄 저장용) - const handleCellUpdate = React.useCallback(async (id: string, field: keyof AvlListItem, newValue: string | boolean) => { - const isEmptyRow = String(id).startsWith('temp-') - - if (isEmptyRow) { - // 빈 행의 경우 pendingChanges 상태도 업데이트 - setPendingChanges(prev => ({ - ...prev, - [id]: { - ...prev[id], - [field]: newValue - } - })) - } - - // pendingChanges에 변경사항 저장 (실시간 표시용) - setPendingChanges(prev => ({ - ...prev, - [id]: { - ...prev[id], - [field]: newValue - } - })) - }, []) - - // 편집 취소 핸들러 - const handleCellCancel = React.useCallback((id: string, field: keyof AvlListItem) => { - const isEmptyRow = String(id).startsWith('temp-') - - if (isEmptyRow) { - // 빈 행의 경우 pendingChanges 취소 - setPendingChanges(prev => { - const itemChanges = { ...prev[id] } - delete itemChanges[field] - - if (Object.keys(itemChanges).length === 0) { - const newChanges = { ...prev } - delete newChanges[id] - return newChanges - } - - return { - ...prev, - [id]: itemChanges - } - }) - } - }, []) - // 액션 핸들러 const handleAction = React.useCallback(async (action: string, data?: Partial) => { try { @@ -166,89 +111,6 @@ export function AvlTable({ data, pageCount, onRefresh, isLoading, onRegistration } break - case 'project-registration': - // 프로젝트 AVL 등록 - const projectResult = await handleAvlActionAction('project-registration') - if (projectResult.success) { - toast.success(projectResult.message) - onRegistrationModeChange?.('project') // 등록 모드 변경 콜백 호출 - } else { - toast.error(projectResult.message) - } - break - - case 'bulk-import': - // 일괄 입력 - const bulkResult = await handleAvlActionAction('bulk-import') - if (bulkResult.success) { - toast.success(bulkResult.message) - } else { - toast.error(bulkResult.message) - } - break - - case 'save': - // 변경사항 저장 - if (Object.keys(pendingChanges).length === 0) { - toast.info("저장할 변경사항이 없습니다.") - return - } - - setIsSaving(true) - try { - // 각 변경사항을 순차적으로 저장 - for (const [id, changes] of Object.entries(pendingChanges)) { - if (String(id).startsWith('temp-')) continue // 빈 행은 제외 - - // id 속성을 명시적으로 추가 - const updateData = { - ...changes, - id: Number(id) - } - - const result = await updateAvlListAction(Number(id), updateData) - if (!result) { - throw new Error(`항목 ${id} 저장 실패`) - } - } - - setPendingChanges({}) - toast.success("변경사항이 저장되었습니다.") - onRefresh?.() - } catch (error) { - console.error('저장 실패:', error) - toast.error("저장 중 오류가 발생했습니다.") - } finally { - setIsSaving(false) - } - break - - case 'edit': - // 수정 모달 열기 (현재는 간단한 토스트로 처리) - toast.info(`${data?.id} 항목 수정`) - break - - case 'delete': - // 삭제 확인 및 실행 - if (!data?.id || String(data.id).startsWith('temp-')) return - - const confirmed = window.confirm(`항목 ${data.id}을(를) 삭제하시겠습니까?`) - if (!confirmed) return - - try { - const result = await deleteAvlListAction(Number(data.id)) - if (result) { - toast.success("항목이 삭제되었습니다.") - onRefresh?.() - } else { - toast.error("삭제에 실패했습니다.") - } - } catch (error) { - console.error('삭제 실패:', error) - toast.error("삭제 중 오류가 발생했습니다.") - } - break - case 'view-detail': // 상세 조회 (페이지 이동) if (data?.id && !String(data.id).startsWith('temp-')) { @@ -271,7 +133,7 @@ export function AvlTable({ data, pageCount, onRefresh, isLoading, onRegistration console.error('액션 처리 실패:', error) toast.error("액션 처리 중 오류가 발생했습니다.") } - }, [pendingChanges, onRefresh, onRegistrationModeChange]) + }, [onRegistrationModeChange]) // 빈 행 포함한 전체 데이터 const allData = React.useMemo(() => { @@ -297,15 +159,6 @@ export function AvlTable({ data, pageCount, onRefresh, isLoading, onRegistration } }, [allData, onRowSelect]) - // 테이블 메타 설정 - const tableMeta = React.useMemo(() => ({ - onCellUpdate: handleCellUpdate, - onCellCancel: handleCellCancel, - onAction: handleAction, - isEmptyRow: (id: string) => String(id).startsWith('temp-'), - getPendingChanges: () => pendingChanges, - }), [handleCellUpdate, handleCellCancel, handleAction, pendingChanges]) - // 데이터 테이블 설정 const { table } = useDataTable({ @@ -320,14 +173,12 @@ export function AvlTable({ data, pageCount, onRefresh, isLoading, onRegistration pageIndex: 0, pageSize: 10, }, + // 기본 컬럼 사이즈 설정 + columnSizing: {}, }, getRowId: (row) => String(row.id), - meta: tableMeta, }) - // 변경사항이 있는지 확인 - const hasPendingChanges = Object.keys(pendingChanges).length > 0 - return (
{/* 툴바 */} @@ -353,31 +204,11 @@ export function AvlTable({ data, pageCount, onRefresh, isLoading, onRegistration 프로젝트AVL등록 - {/* 저장 버튼 - 변경사항이 있을 때만 활성화 */} - {(hasPendingChanges) && ( - - )} - - {/* 새로고침 버튼 */} -
{/* 데이터 테이블 */} - + {/* 히스토리 모달 */} - {/* 디버그 정보 (개발 환경에서만 표시) */} - {process.env.NODE_ENV === 'development' && (hasPendingChanges) && ( -
-
Pending Changes: {Object.keys(pendingChanges).length}
-
- )}
) } diff --git a/lib/pos/get-pos.ts b/lib/pos/get-pos.ts index 6424b880..c24c1dab 100644 --- a/lib/pos/get-pos.ts +++ b/lib/pos/get-pos.ts @@ -81,6 +81,7 @@ export async function getEncryptDocumentumFile( 'GetEncryptDocumentumFile', xmlBody, async () => { + // SOAP 1.1 방식으로 송신 const res = await fetch(POS_SOAP_ENDPOINT, { method: 'POST', headers: { diff --git a/lib/vendor-pool/table/columns.tsx b/lib/vendor-pool/table/columns.tsx deleted file mode 100644 index 0a6b0c8f..00000000 --- a/lib/vendor-pool/table/columns.tsx +++ /dev/null @@ -1,1687 +0,0 @@ -import { Checkbox } from "@/components/ui/checkbox" -import { Button } from "@/components/ui/button" -import { MoreHorizontal, Eye, Edit, Trash2 } from "lucide-react" -import { type ColumnDef, TableMeta } from "@tanstack/react-table" -import { DataTableColumnHeaderSimple } from "@/components/data-table/data-table-column-simple-header" -import { EditableCell } from "@/components/data-table/editable-cell" - -// 수정 여부 확인 헬퍼 함수 -const getIsModified = (table: any, rowId: string, fieldName: string) => { - const pendingChanges = table.options.meta?.getPendingChanges?.() || {} - return String(rowId) in pendingChanges && fieldName in pendingChanges[String(rowId)] -} - -// 테이블 메타 타입 확장 -declare module "@tanstack/react-table" { - interface TableMeta { - onCellUpdate?: (id: string, field: keyof TData, newValue: any) => Promise - onCellCancel?: (id: string, field: keyof TData) => void - onAction?: (action: string, data?: any) => void - onSaveEmptyRow?: (tempId: string) => Promise - onCancelEmptyRow?: (tempId: string) => void - isEmptyRow?: (id: string) => boolean - onTaxIdChange?: (id: string, taxId: string) => Promise - onMaterialGroupCodeChange?: (id: string, materialGroupCode: string) => Promise - } -} - -// Vendor Pool 데이터 타입 -export type VendorPoolItem = { - id: string - no: number - selected: boolean - constructionSector: string // 공사부문: 조선 또는 해양 - htDivision: string // H/T구분: H 또는 T - designCategoryCode: string // 설계기능(공종) 코드: 2자리 영문대문자 - designCategory: string // 설계기능(공종): 전장 등 - equipBulkDivision: string // Equip/Bulk 구분: E 또는 B - // 패키지 정보 (스키마: packageCode, packageName) - packageCode: string - packageName: string - // 자재그룹 (스키마: materialGroupCode, materialGroupName) - materialGroupCode: string - materialGroupName: string - smCode: string // SM Code - similarMaterialNamePurchase: string // 유사자재명 (구매) - similarMaterialNameOther: string // 유사자재명 (구매 외) - // 협력업체 정보 (스키마: vendorCode, vendorName) - vendorCode: string - vendorName: string - taxId: string // 사업자번호(Tax ID) - faTarget: boolean // FA대상 - faStatus: string // FA현황 - faRemark: string // FA상세(Remark) - tier: string // 등급(Tier) - isAgent: boolean // Agent 여부 - // 계약서명주체 (스키마: contractSignerCode, contractSignerName) - contractSignerCode: string - contractSignerName: string - headquarterLocation: string // 본사 위치(국가) - manufacturingLocation: string // 제작/선적지(국가) - avlVendorName: string // AVL 등재업체명 - similarVendorName: string // 유사업체명(기술영업) - hasAvl: boolean // AVL: 존재여부 - isBlacklist: boolean // Blacklist - isBcc: boolean // BCC - purchaseOpinion: string // 구매의견 - // AVL 적용 선종(조선) - shipTypeCommon: boolean // 공통 - shipTypeAmax: boolean // A-max - shipTypeSmax: boolean // S-max - shipTypeVlcc: boolean // VLCC - shipTypeLngc: boolean // LNGC - shipTypeCont: boolean // CONT - // AVL 적용 선종(해양) - offshoreTypeCommon: boolean // 공통 - offshoreTypeFpso: boolean // FPSO - offshoreTypeFlng: boolean // FLNG - offshoreTypeFpu: boolean // FPU - offshoreTypePlatform: boolean // Platform - offshoreTypeWtiv: boolean // WTIV - offshoreTypeGom: boolean // GOM - // eVCP 미등록 정보 - picName: string // PIC(담당자) - picEmail: string // PIC(E-mail) - picPhone: string // PIC(Phone) - agentName: string // Agent(담당자) - agentEmail: string // Agent(E-mail) - agentPhone: string // Agent(Phone) - // 업체 실적 현황 - recentQuoteDate: string // 최근견적일 - recentQuoteNumber: string // 최근견적번호 - recentOrderDate: string // 최근발주일 - recentOrderNumber: string // 최근발주번호 - // 업데이트 히스토리 - registrationDate: string // 등재일 - registrant: string // 등재자 - lastModifiedDate: string // 최종변경일 - lastModifier: string // 최종변경자 -} - -// 테이블 컬럼 정의 -export const columns: ColumnDef[] = [ - // 기본 정보 그룹 - { - header: "기본 정보", - columns: [ - { - id: "select", - header: ({ table }) => ( - table.toggleAllPageRowsSelected(!!value)} - aria-label="Select all" - /> - ), - cell: ({ row }) => ( - row.toggleSelected(!!value)} - aria-label="Select row" - /> - ), - enableSorting: false, - enableHiding: false, - size: 40, - }, - { - accessorKey: "id", - header: ({ column }) => ( - - ), - cell: ({ row }) => { - const id = String(row.original.id) - - // 빈 행의 경우 No. 표시하지 않음 - if (id.startsWith('temp-')) { - return
신규
- } - - // vendor_pool 테이블의 실제 id 표시 - return
{id}
- }, - size: 60, - }, - { - accessorKey: "constructionSector", - header: ({ column }) => ( - 공사부문 *} /> - ), - cell: ({ row, table }) => { - const value = row.getValue("constructionSector") - const isEmptyRow = String(row.original.id).startsWith('temp-') - - const onSave = async (newValue: any) => { - if (table.options.meta?.onCellUpdate) { - await table.options.meta.onCellUpdate(row.original.id, "constructionSector", newValue) - } - } - - // 수정 여부 확인 - const isModified = getIsModified(table, row.original.id, "constructionSector") - - return ( - - ) - }, - size: 100, - }, - { - accessorKey: "htDivision", - header: ({ column }) => ( - H/T구분 *} /> - ), - cell: ({ row, table }) => { - const value = row.getValue("htDivision") - const isEmptyRow = String(row.original.id).startsWith('temp-') - const onSave = async (newValue: any) => { - if (table.options.meta?.onCellUpdate) { - await table.options.meta.onCellUpdate(row.original.id, "htDivision", newValue) - } - } - - // 수정 여부 확인 - const isModified = getIsModified(table, row.original.id, "htDivision") - - return ( - - ) - }, - size: 80, - }, - { - accessorKey: "designCategoryCode", - header: ({ column }) => ( - - ), - cell: ({ row, table }) => { - const value = row.getValue("designCategoryCode") - const onSave = async (newValue: any) => { - if (table.options.meta?.onCellUpdate) { - await table.options.meta.onCellUpdate(row.original.id, "designCategoryCode", newValue) - } - } - - // 수정 여부 확인 - const isModified = getIsModified(table, row.original.id, "designCategoryCode") - - return ( - - ) - }, - size: 120, - }, - { - accessorKey: "designCategory", - header: ({ column }) => ( - 설계기능(공종) *} /> - ), - cell: ({ row, table }) => { - const value = row.getValue("designCategory") - const isEmptyRow = String(row.original.id).startsWith('temp-') - const onSave = async (newValue: any) => { - if (table.options.meta?.onCellUpdate) { - await table.options.meta.onCellUpdate(row.original.id, "designCategory", newValue) - } - } - - // 수정 여부 확인 - const isModified = getIsModified(table, row.original.id, "designCategory") - - return ( - - ) - }, - size: 120, - }, - { - accessorKey: "equipBulkDivision", - header: ({ column }) => ( - - ), - cell: ({ row, table }) => { - const value = row.getValue("equipBulkDivision") - const onSave = async (newValue: any) => { - if (table.options.meta?.onCellUpdate) { - await table.options.meta.onCellUpdate(row.original.id, "equipBulkDivision", newValue) - } - } - - return ( - - ) - }, - size: 120, - }, - { - accessorKey: "packageCode", - header: ({ column }) => ( - - ), - cell: ({ row, table }) => { - const value = row.getValue("packageCode") - const onSave = async (newValue: any) => { - if (table.options.meta?.onCellUpdate) { - await table.options.meta.onCellUpdate(row.original.id, "packageCode", newValue) - } - } - - // 수정 여부 확인 - const isModified = getIsModified(table, row.original.id, "packageCode") - - return ( - - ) - }, - size: 120, - }, - { - accessorKey: "packageName", - header: ({ column }) => ( - - ), - cell: ({ row, table }) => { - const value = row.getValue("packageName") - const isEmptyRow = String(row.original.id).startsWith('temp-') - const onSave = async (newValue: any) => { - if (table.options.meta?.onCellUpdate) { - await table.options.meta.onCellUpdate(row.original.id, "packageName", newValue) - } - } - - // 수정 여부 확인 - const isModified = getIsModified(table, row.original.id, "packageName") - - return ( - - ) - }, - size: 120, - }, - { - accessorKey: "materialGroupCode", - header: ({ column }) => ( - 자재그룹 코드 *} /> - ), - cell: ({ row, table }) => { - const value = row.getValue("materialGroupCode") - const onSave = async (newValue: any) => { - if (table.options.meta?.onCellUpdate) { - await table.options.meta.onCellUpdate(row.original.id, "materialGroupCode", newValue) - } - } - - // 수정 여부 확인 - const isModified = getIsModified(table, row.original.id, "materialGroupCode") - - const onChange = async (newValue: any) => { - if (table.options.meta?.onMaterialGroupCodeChange) { - await table.options.meta.onMaterialGroupCodeChange(row.original.id, newValue) - } - } - - return ( - - ) - }, - size: 120, - }, - { - accessorKey: "materialGroupName", - header: ({ column }) => ( - 자재그룹 명 *} /> - ), - cell: ({ row, table }) => { - const value = row.getValue("materialGroupName") - const isEmptyRow = String(row.original.id).startsWith('temp-') - const onSave = async (newValue: any) => { - if (table.options.meta?.onCellUpdate) { - await table.options.meta.onCellUpdate(row.original.id, "materialGroupName", newValue) - } - } - - // 수정 여부 확인 - const isModified = getIsModified(table, row.original.id, "materialGroupName") - - return ( - - ) - }, - size: 120, - }, - { - accessorKey: "smCode", - header: ({ column }) => ( - - ), - cell: ({ row, table }) => { - const value = row.getValue("smCode") - const onSave = async (newValue: any) => { - if (table.options.meta?.onCellUpdate) { - await table.options.meta.onCellUpdate(row.original.id, "smCode", newValue) - } - } - - return ( - - ) - }, - size: 100, - }, - ] - }, - // 자재 정보 그룹 - { - header: "자재 정보", - columns: [ - { - accessorKey: "similarMaterialNamePurchase", - header: ({ column }) => ( - - ), - cell: ({ row, table }) => { - const value = row.getValue("similarMaterialNamePurchase") - const onSave = async (newValue: any) => { - if (table.options.meta?.onCellUpdate) { - await table.options.meta.onCellUpdate(row.original.id, "similarMaterialNamePurchase", newValue) - } - } - - return ( - - ) - }, - size: 140, - }, - { - accessorKey: "similarMaterialNameOther", - header: ({ column }) => ( - - ), - cell: ({ row, table }) => { - const value = row.getValue("similarMaterialNameOther") - const onSave = async (newValue: any) => { - if (table.options.meta?.onCellUpdate) { - await table.options.meta.onCellUpdate(row.original.id, "similarMaterialNameOther", newValue) - } - } - - return ( - - ) - }, - size: 140, - }, - ] - }, - // 협력업체 정보 그룹 - { - header: "협력업체 정보", - columns: [ - { - accessorKey: "vendorCode", - header: ({ column }) => ( - - ), - cell: ({ row, table }) => { - const value = row.getValue("vendorCode") - const onSave = async (newValue: any) => { - if (table.options.meta?.onCellUpdate) { - await table.options.meta.onCellUpdate(row.original.id, "vendorCode", newValue) - } - } - - // 수정 여부 확인 - const isModified = getIsModified(table, row.original.id, "vendorCode") - - return ( - - ) - }, - size: 130, - }, - { - accessorKey: "vendorName", - header: ({ column }) => ( - 협력업체 명 *} /> - ), - cell: ({ row, table }) => { - const value = row.getValue("vendorName") - const isEmptyRow = String(row.original.id).startsWith('temp-') - const onSave = async (newValue: any) => { - if (table.options.meta?.onCellUpdate) { - await table.options.meta.onCellUpdate(row.original.id, "vendorName", newValue) - } - } - - // 수정 여부 확인 - const isModified = getIsModified(table, row.original.id, "vendorName") - - return ( - - ) - }, - size: 130, - }, - { - accessorKey: "taxId", - header: ({ column }) => ( - 사업자번호 *} /> - ), - cell: ({ row, table }) => { - const value = row.getValue("taxId") - const onSave = async (newValue: any) => { - if (table.options.meta?.onCellUpdate) { - await table.options.meta.onCellUpdate(row.original.id, "taxId", newValue) - } - } - - const onChange = async (newValue: any) => { - if (table.options.meta?.onTaxIdChange) { - await table.options.meta.onTaxIdChange(row.original.id, newValue) - } - } - - return ( - - ) - }, - size: 120, - }, - { - accessorKey: "faTarget", - header: "FA대상", - cell: ({ row, table }) => { - const value = row.getValue("faTarget") as boolean - const onSave = async (newValue: any) => { - if (table.options.meta?.onCellUpdate) { - await table.options.meta.onCellUpdate(row.original.id, "faTarget", newValue) - } - } - - // 수정 여부 확인 - const isModified = getIsModified(table, row.original.id, "faTarget") - - return ( - - ) - }, - enableSorting: false, - size: 80, - }, - { - accessorKey: "faStatus", - header: ({ column }) => ( - - ), - cell: ({ row, table }) => { - const value = row.getValue("faStatus") - const onSave = async (newValue: any) => { - if (table.options.meta?.onCellUpdate) { - await table.options.meta.onCellUpdate(row.original.id, "faStatus", newValue) - } - } - - return ( - - ) - }, - size: 100, - }, - { - accessorKey: "faRemark", - header: ({ column }) => ( - - ), - cell: ({ row, table }) => { - const value = row.getValue("faRemark") - const onSave = async (newValue: any) => { - if (table.options.meta?.onCellUpdate) { - await table.options.meta.onCellUpdate(row.original.id, "faRemark", newValue) - } - } - - return ( - - ) - }, - size: 120, - }, - { - accessorKey: "tier", - header: ({ column }) => ( - 등급 *} /> - ), - cell: ({ row, table }) => { - const value = row.getValue("tier") - const onSave = async (newValue: any) => { - if (table.options.meta?.onCellUpdate) { - await table.options.meta.onCellUpdate(row.original.id, "tier", newValue) - } - } - - return ( - - ) - }, - size: 80, - }, - { - accessorKey: "isAgent", - header: "Agent 여부", - cell: ({ row, table }) => { - const value = row.getValue("isAgent") as boolean - const onSave = async (newValue: any) => { - if (table.options.meta?.onCellUpdate) { - await table.options.meta.onCellUpdate(row.original.id, "isAgent", newValue) - } - } - - return ( - - ) - }, - enableSorting: false, - size: 100, - }, - { - accessorKey: "contractSignerCode", - header: ({ column }) => ( - - ), - cell: ({ row, table }) => { - const value = row.getValue("contractSignerCode") - const onSave = async (newValue: any) => { - if (table.options.meta?.onCellUpdate) { - await table.options.meta.onCellUpdate(row.original.id, "contractSignerCode", newValue) - } - } - - // 수정 여부 확인 - const isModified = getIsModified(table, row.original.id, "contractSignerCode") - - return ( - - ) - }, - size: 120, - }, - { - accessorKey: "contractSignerName", - header: ({ column }) => ( - 계약서명주체 명 *} /> - ), - cell: ({ row, table }) => { - const value = row.getValue("contractSignerName") - const onSave = async (newValue: any) => { - if (table.options.meta?.onCellUpdate) { - await table.options.meta.onCellUpdate(row.original.id, "contractSignerName", newValue) - } - } - - // 수정 여부 확인 - const isModified = getIsModified(table, row.original.id, "contractSignerName") - - return ( - - ) - }, - size: 120, - }, - { - accessorKey: "headquarterLocation", - header: ({ column }) => ( - 본사 위치 *} /> - ), - cell: ({ row, table }) => { - const value = row.getValue("headquarterLocation") - const onSave = async (newValue: any) => { - if (table.options.meta?.onCellUpdate) { - await table.options.meta.onCellUpdate(row.original.id, "headquarterLocation", newValue) - } - } - - return ( - - ) - }, - size: 100, - }, - { - accessorKey: "manufacturingLocation", - header: ({ column }) => ( - 제작/선적지 *} /> - ), - cell: ({ row, table }) => { - const value = row.getValue("manufacturingLocation") - const onSave = async (newValue: any) => { - if (table.options.meta?.onCellUpdate) { - await table.options.meta.onCellUpdate(row.original.id, "manufacturingLocation", newValue) - } - } - - return ( - - ) - }, - size: 110, - }, - { - accessorKey: "avlVendorName", - header: ({ column }) => ( - AVL 등재업체명 *} /> - ), - cell: ({ row, table }) => { - const value = row.getValue("avlVendorName") - const onSave = async (newValue: any) => { - if (table.options.meta?.onCellUpdate) { - await table.options.meta.onCellUpdate(row.original.id, "avlVendorName", newValue) - } - } - - return ( - - ) - }, - size: 140, - }, - { - accessorKey: "similarVendorName", - header: ({ column }) => ( - - ), - cell: ({ row, table }) => { - const value = row.getValue("similarVendorName") - const onSave = async (newValue: any) => { - if (table.options.meta?.onCellUpdate) { - await table.options.meta.onCellUpdate(row.original.id, "similarVendorName", newValue) - } - } - - return ( - - ) - }, - size: 130, - }, - { - accessorKey: "hasAvl", - header: "AVL", - cell: ({ row, table }) => { - const value = row.getValue("hasAvl") as boolean - const onSave = async (newValue: any) => { - if (table.options.meta?.onCellUpdate) { - await table.options.meta.onCellUpdate(row.original.id, "hasAvl", newValue) - } - } - - return ( - - ) - }, - enableSorting: false, - size: 60, - }, - { - accessorKey: "isBlacklist", - header: "Blacklist", - cell: ({ row, table }) => { - const value = row.getValue("isBlacklist") as boolean - const onSave = async (newValue: any) => { - if (table.options.meta?.onCellUpdate) { - await table.options.meta.onCellUpdate(row.original.id, "isBlacklist", newValue) - } - } - - return ( - - ) - }, - enableSorting: false, - size: 100, - }, - { - accessorKey: "isBcc", - header: "BCC", - cell: ({ row, table }) => { - const value = row.getValue("isBcc") as boolean - const onSave = async (newValue: any) => { - if (table.options.meta?.onCellUpdate) { - await table.options.meta.onCellUpdate(row.original.id, "isBcc", newValue) - } - } - - return ( - - ) - }, - enableSorting: false, - size: 80, - }, - { - accessorKey: "purchaseOpinion", - header: ({ column }) => ( - - ), - cell: ({ row, table }) => { - const value = row.getValue("purchaseOpinion") - const onSave = async (newValue: any) => { - if (table.options.meta?.onCellUpdate) { - await table.options.meta.onCellUpdate(row.original.id, "purchaseOpinion", newValue) - } - } - - return ( - - ) - }, - size: 120, - }, - ] - }, - // AVL 적용 선종(조선) 그룹 - { - header: "AVL 적용 선종(조선)", - columns: [ - { - accessorKey: "shipTypeCommon", - header: "공통", - cell: ({ row, table }) => { - const value = row.getValue("shipTypeCommon") as boolean - const onSave = async (newValue: any) => { - if (table.options.meta?.onCellUpdate) { - await table.options.meta.onCellUpdate(row.original.id, "shipTypeCommon", newValue) - } - } - - return ( - - ) - }, - enableSorting: false, - size: 80, - }, - { - accessorKey: "shipTypeAmax", - header: "A-max", - cell: ({ row, table }) => { - const value = row.getValue("shipTypeAmax") as boolean - const onSave = async (newValue: any) => { - if (table.options.meta?.onCellUpdate) { - await table.options.meta.onCellUpdate(row.original.id, "shipTypeAmax", newValue) - } - } - - return ( - - ) - }, - enableSorting: false, - size: 80, - }, - { - accessorKey: "shipTypeSmax", - header: "S-max", - cell: ({ row, table }) => { - const value = row.getValue("shipTypeSmax") as boolean - const onSave = async (newValue: any) => { - if (table.options.meta?.onCellUpdate) { - await table.options.meta.onCellUpdate(row.original.id, "shipTypeSmax", newValue) - } - } - - return ( - - ) - }, - enableSorting: false, - size: 80, - }, - { - accessorKey: "shipTypeVlcc", - header: "VLCC", - cell: ({ row, table }) => { - const value = row.getValue("shipTypeVlcc") as boolean - const onSave = async (newValue: any) => { - if (table.options.meta?.onCellUpdate) { - await table.options.meta.onCellUpdate(row.original.id, "shipTypeVlcc", newValue) - } - } - - return ( - - ) - }, - enableSorting: false, - size: 80, - }, - { - accessorKey: "shipTypeLngc", - header: "LNGC", - cell: ({ row, table }) => { - const value = row.getValue("shipTypeLngc") as boolean - const onSave = async (newValue: any) => { - if (table.options.meta?.onCellUpdate) { - await table.options.meta.onCellUpdate(row.original.id, "shipTypeLngc", newValue) - } - } - - return ( - - ) - }, - size: 80, - }, - { - accessorKey: "shipTypeCont", - header: "CONT", - cell: ({ row, table }) => { - const value = row.getValue("shipTypeCont") as boolean - const onSave = async (newValue: any) => { - if (table.options.meta?.onCellUpdate) { - await table.options.meta.onCellUpdate(row.original.id, "shipTypeCont", newValue) - } - } - - return ( - - ) - }, - size: 80, - }, - ] - }, - // AVL 적용 선종(해양) 그룹 - { - header: "AVL 적용 선종(해양)", - columns: [ - { - accessorKey: "offshoreTypeCommon", - header: "공통", - cell: ({ row, table }) => { - const value = row.getValue("offshoreTypeCommon") as boolean - const onSave = async (newValue: any) => { - if (table.options.meta?.onCellUpdate) { - await table.options.meta.onCellUpdate(row.original.id, "offshoreTypeCommon", newValue) - } - } - - return ( - - ) - }, - size: 80, - }, - { - accessorKey: "offshoreTypeFpso", - header: "FPSO", - cell: ({ row, table }) => { - const value = row.getValue("offshoreTypeFpso") as boolean - const onSave = async (newValue: any) => { - if (table.options.meta?.onCellUpdate) { - await table.options.meta.onCellUpdate(row.original.id, "offshoreTypeFpso", newValue) - } - } - - return ( - - ) - }, - size: 80, - }, - { - accessorKey: "offshoreTypeFlng", - header: "FLNG", - cell: ({ row, table }) => { - const value = row.getValue("offshoreTypeFlng") as boolean - const onSave = async (newValue: any) => { - if (table.options.meta?.onCellUpdate) { - await table.options.meta.onCellUpdate(row.original.id, "offshoreTypeFlng", newValue) - } - } - - return ( - - ) - }, - size: 80, - }, - { - accessorKey: "offshoreTypeFpu", - header: "FPU", - cell: ({ row, table }) => { - const value = row.getValue("offshoreTypeFpu") as boolean - const onSave = async (newValue: any) => { - if (table.options.meta?.onCellUpdate) { - await table.options.meta.onCellUpdate(row.original.id, "offshoreTypeFpu", newValue) - } - } - - return ( - - ) - }, - size: 80, - }, - { - accessorKey: "offshoreTypePlatform", - header: "Platform", - cell: ({ row, table }) => { - const value = row.getValue("offshoreTypePlatform") as boolean - const onSave = async (newValue: any) => { - if (table.options.meta?.onCellUpdate) { - await table.options.meta.onCellUpdate(row.original.id, "offshoreTypePlatform", newValue) - } - } - - return ( - - ) - }, - size: 100, - }, - { - accessorKey: "offshoreTypeWtiv", - header: "WTIV", - cell: ({ row, table }) => { - const value = row.getValue("offshoreTypeWtiv") as boolean - const onSave = async (newValue: any) => { - if (table.options.meta?.onCellUpdate) { - await table.options.meta.onCellUpdate(row.original.id, "offshoreTypeWtiv", newValue) - } - } - - return ( - - ) - }, - size: 80, - }, - { - accessorKey: "offshoreTypeGom", - header: "GOM", - cell: ({ row, table }) => { - const value = row.getValue("offshoreTypeGom") as boolean - const onSave = async (newValue: any) => { - if (table.options.meta?.onCellUpdate) { - await table.options.meta.onCellUpdate(row.original.id, "offshoreTypeGom", newValue) - } - } - - return ( - - ) - }, - size: 80, - }, - ] - }, - // eVCP 미등록 정보 그룹 - { - header: "eVCP 미등록 정보", - columns: [ - { - accessorKey: "picName", - header: ({ column }) => ( - - ), - cell: ({ row, table }) => { - const value = row.getValue("picName") - const onSave = async (newValue: any) => { - if (table.options.meta?.onCellUpdate) { - await table.options.meta.onCellUpdate(row.original.id, "picName", newValue) - } - } - - return ( - - ) - }, - size: 120, - }, - { - accessorKey: "picEmail", - header: ({ column }) => ( - - ), - cell: ({ row, table }) => { - const value = row.getValue("picEmail") - const onSave = async (newValue: any) => { - if (table.options.meta?.onCellUpdate) { - await table.options.meta.onCellUpdate(row.original.id, "picEmail", newValue) - } - } - - return ( - - ) - }, - size: 140, - }, - { - accessorKey: "picPhone", - header: ({ column }) => ( - - ), - cell: ({ row, table }) => { - const value = row.getValue("picPhone") - const onSave = async (newValue: any) => { - if (table.options.meta?.onCellUpdate) { - await table.options.meta.onCellUpdate(row.original.id, "picPhone", newValue) - } - } - - return ( - - ) - }, - size: 120, - }, - { - accessorKey: "agentName", - header: ({ column }) => ( - - ), - cell: ({ row, table }) => { - const value = row.getValue("agentName") - const onSave = async (newValue: any) => { - if (table.options.meta?.onCellUpdate) { - await table.options.meta.onCellUpdate(row.original.id, "agentName", newValue) - } - } - - return ( - - ) - }, - size: 120, - }, - { - accessorKey: "agentEmail", - header: ({ column }) => ( - - ), - cell: ({ row, table }) => { - const value = row.getValue("agentEmail") - const onSave = async (newValue: any) => { - if (table.options.meta?.onCellUpdate) { - await table.options.meta.onCellUpdate(row.original.id, "agentEmail", newValue) - } - } - - return ( - - ) - }, - size: 140, - }, - { - accessorKey: "agentPhone", - header: ({ column }) => ( - - ), - cell: ({ row, table }) => { - const value = row.getValue("agentPhone") - const onSave = async (newValue: any) => { - if (table.options.meta?.onCellUpdate) { - await table.options.meta.onCellUpdate(row.original.id, "agentPhone", newValue) - } - } - - return ( - - ) - }, - size: 120, - }, - ] - }, - // 업체 실적 현황 그룹 - { - header: "업체 실적 현황", - columns: [ - { - accessorKey: "recentQuoteDate", - header: ({ column }) => ( - - ), - cell: ({ row, table }) => { - const value = row.getValue("recentQuoteDate") - const onSave = async (newValue: any) => { - if (table.options.meta?.onCellUpdate) { - await table.options.meta.onCellUpdate(row.original.id, "recentQuoteDate", newValue) - } - } - - return ( - - ) - }, - size: 120, - }, - { - accessorKey: "recentQuoteNumber", - header: ({ column }) => ( - - ), - cell: ({ row, table }) => { - const value = row.getValue("recentQuoteNumber") - const onSave = async (newValue: any) => { - if (table.options.meta?.onCellUpdate) { - await table.options.meta.onCellUpdate(row.original.id, "recentQuoteNumber", newValue) - } - } - - return ( - - ) - }, - size: 130, - }, - { - accessorKey: "recentOrderDate", - header: ({ column }) => ( - - ), - cell: ({ row, table }) => { - const value = row.getValue("recentOrderDate") - const onSave = async (newValue: any) => { - if (table.options.meta?.onCellUpdate) { - await table.options.meta.onCellUpdate(row.original.id, "recentOrderDate", newValue) - } - } - - return ( - - ) - }, - size: 120, - }, - { - accessorKey: "recentOrderNumber", - header: ({ column }) => ( - - ), - cell: ({ row, table }) => { - const value = row.getValue("recentOrderNumber") - const onSave = async (newValue: any) => { - if (table.options.meta?.onCellUpdate) { - await table.options.meta.onCellUpdate(row.original.id, "recentOrderNumber", newValue) - } - } - - return ( - - ) - }, - size: 130, - }, - ] - }, - // 업데이트 히스토리 그룹 - { - header: "업데이트 히스토리", - columns: [ - { - accessorKey: "registrationDate", - header: ({ column }) => ( - - ), - size: 120, - }, - { - accessorKey: "registrant", - header: ({ column }) => ( - - ), - cell: ({ row }) => { - const value = row.getValue("registrant") as string - return
{value || ""}
- }, - size: 100, - }, - { - accessorKey: "lastModifiedDate", - header: ({ column }) => ( - - ), - size: 120, - }, - { - accessorKey: "lastModifier", - header: ({ column }) => ( - - ), - cell: ({ row }) => { - const value = row.getValue("lastModifier") as string - return
{value || ""}
- }, - size: 120, - }, - ] - }, - // 액션 그룹 - { - id: "actions", - header: "액션", - cell: ({ row, table }) => { - const data = row.original - const isEmptyRow = (table.options.meta as any)?.isEmptyRow?.(String(data.id)) - - if (isEmptyRow) { - // 빈 행의 경우 저장/취소 버튼 표시 - return ( -
- - -
- ) - } - - // 일반 행의 경우 기존 액션 버튼들 표시 - return ( -
- -
- ) - }, - size: 120, - enableSorting: false, - enableHiding: false, - }, -] diff --git a/lib/vendor-pool/table/vendor-pool-table-columns.tsx b/lib/vendor-pool/table/vendor-pool-table-columns.tsx new file mode 100644 index 00000000..8f09e684 --- /dev/null +++ b/lib/vendor-pool/table/vendor-pool-table-columns.tsx @@ -0,0 +1,1640 @@ +import { Checkbox } from "@/components/ui/checkbox" +import { Button } from "@/components/ui/button" +import { MoreHorizontal, Eye, Edit, Trash2 } from "lucide-react" +import { type ColumnDef, TableMeta } from "@tanstack/react-table" +import { DataTableColumnHeaderSimple } from "@/components/data-table/data-table-column-simple-header" +import { EditableCell } from "@/components/data-table/editable-cell" + +// 수정 여부 확인 헬퍼 함수 +const getIsModified = (table: any, rowId: string, fieldName: string) => { + const pendingChanges = table.options.meta?.getPendingChanges?.() || {} + return String(rowId) in pendingChanges && fieldName in pendingChanges[String(rowId)] +} + +// 테이블 메타 타입 확장 +declare module "@tanstack/react-table" { + interface TableMeta { + onCellUpdate?: (id: string, field: keyof TData, newValue: any) => Promise + onCellCancel?: (id: string, field: keyof TData) => void + onAction?: (action: string, data?: any) => void + onSaveEmptyRow?: (tempId: string) => Promise + onCancelEmptyRow?: (tempId: string) => void + isEmptyRow?: (id: string) => boolean + onTaxIdChange?: (id: string, taxId: string) => Promise + onMaterialGroupCodeChange?: (id: string, materialGroupCode: string) => Promise + } +} + +// Vendor Pool 데이터 타입 +export type VendorPoolItem = { + id: string + no: number + selected: boolean + constructionSector: string // 공사부문: 조선 또는 해양 + htDivision: string // H/T구분: H 또는 T + designCategoryCode: string // 설계기능(공종) 코드: 2자리 영문대문자 + designCategory: string // 설계기능(공종): 전장 등 + equipBulkDivision: string // Equip/Bulk 구분: E 또는 B + // 패키지 정보 (스키마: packageCode, packageName) + packageCode: string + packageName: string + // 자재그룹 (스키마: materialGroupCode, materialGroupName) + materialGroupCode: string + materialGroupName: string + smCode: string // SM Code + similarMaterialNamePurchase: string // 유사자재명 (구매) + similarMaterialNameOther: string // 유사자재명 (구매 외) + // 협력업체 정보 (스키마: vendorCode, vendorName) + vendorCode: string + vendorName: string + taxId: string // 사업자번호(Tax ID) + faTarget: boolean // FA대상 + faStatus: string // FA현황 + faRemark: string // FA상세(Remark) + tier: string // 등급(Tier) + isAgent: boolean // Agent 여부 + // 계약서명주체 (스키마: contractSignerCode, contractSignerName) + contractSignerCode: string + contractSignerName: string + headquarterLocation: string // 본사 위치(국가) + manufacturingLocation: string // 제작/선적지(국가) + avlVendorName: string // AVL 등재업체명 + similarVendorName: string // 유사업체명(기술영업) + hasAvl: boolean // AVL: 존재여부 + isBlacklist: boolean // Blacklist + isBcc: boolean // BCC + purchaseOpinion: string // 구매의견 + // AVL 적용 선종(조선) + shipTypeCommon: boolean // 공통 + shipTypeAmax: boolean // A-max + shipTypeSmax: boolean // S-max + shipTypeVlcc: boolean // VLCC + shipTypeLngc: boolean // LNGC + shipTypeCont: boolean // CONT + // AVL 적용 선종(해양) + offshoreTypeCommon: boolean // 공통 + offshoreTypeFpso: boolean // FPSO + offshoreTypeFlng: boolean // FLNG + offshoreTypeFpu: boolean // FPU + offshoreTypePlatform: boolean // Platform + offshoreTypeWtiv: boolean // WTIV + offshoreTypeGom: boolean // GOM + // eVCP 미등록 정보 + picName: string // PIC(담당자) + picEmail: string // PIC(E-mail) + picPhone: string // PIC(Phone) + agentName: string // Agent(담당자) + agentEmail: string // Agent(E-mail) + agentPhone: string // Agent(Phone) + // 업체 실적 현황 + recentQuoteDate: string // 최근견적일 + recentQuoteNumber: string // 최근견적번호 + recentOrderDate: string // 최근발주일 + recentOrderNumber: string // 최근발주번호 + // 업데이트 히스토리 + registrationDate: string // 등재일 + registrant: string // 등재자 + lastModifiedDate: string // 최종변경일 + lastModifier: string // 최종변경자 +} + +// 테이블 컬럼 정의 +export const columns: ColumnDef[] = [ + + { + id: "select", + header: ({ table }) => ( + table.toggleAllPageRowsSelected(!!value)} + aria-label="Select all" + /> + ), + cell: ({ row }) => ( + row.toggleSelected(!!value)} + aria-label="Select row" + /> + ), + enableSorting: false, + enableHiding: false, + size: 40, + }, + { + accessorKey: "id", + header: ({ column }) => ( + + ), + cell: ({ row }) => { + const id = String(row.original.id) + + // 빈 행의 경우 No. 표시하지 않음 + if (id.startsWith('temp-')) { + return
신규
+ } + + // vendor_pool 테이블의 실제 id 표시 + return
{id}
+ }, + size: 60, + }, + { + accessorKey: "constructionSector", + header: ({ column }) => ( + 공사부문 *} /> + ), + cell: ({ row, table }) => { + const value = row.getValue("constructionSector") + const isEmptyRow = String(row.original.id).startsWith('temp-') + + const onSave = async (newValue: any) => { + if (table.options.meta?.onCellUpdate) { + await table.options.meta.onCellUpdate(row.original.id, "constructionSector", newValue) + } + } + + // 수정 여부 확인 + const isModified = getIsModified(table, row.original.id, "constructionSector") + + return ( + + ) + }, + size: 100, + }, + { + accessorKey: "htDivision", + header: ({ column }) => ( + H/T구분 *} /> + ), + cell: ({ row, table }) => { + const value = row.getValue("htDivision") + const isEmptyRow = String(row.original.id).startsWith('temp-') + const onSave = async (newValue: any) => { + if (table.options.meta?.onCellUpdate) { + await table.options.meta.onCellUpdate(row.original.id, "htDivision", newValue) + } + } + + // 수정 여부 확인 + const isModified = getIsModified(table, row.original.id, "htDivision") + + return ( + + ) + }, + size: 80, + }, + { + accessorKey: "designCategoryCode", + header: ({ column }) => ( + + ), + cell: ({ row, table }) => { + const value = row.getValue("designCategoryCode") + const onSave = async (newValue: any) => { + if (table.options.meta?.onCellUpdate) { + await table.options.meta.onCellUpdate(row.original.id, "designCategoryCode", newValue) + } + } + + // 수정 여부 확인 + const isModified = getIsModified(table, row.original.id, "designCategoryCode") + + return ( + + ) + }, + size: 120, + }, + { + accessorKey: "designCategory", + header: ({ column }) => ( + 설계기능(공종) *} /> + ), + cell: ({ row, table }) => { + const value = row.getValue("designCategory") + const isEmptyRow = String(row.original.id).startsWith('temp-') + const onSave = async (newValue: any) => { + if (table.options.meta?.onCellUpdate) { + await table.options.meta.onCellUpdate(row.original.id, "designCategory", newValue) + } + } + + // 수정 여부 확인 + const isModified = getIsModified(table, row.original.id, "designCategory") + + return ( + + ) + }, + size: 120, + }, + { + accessorKey: "equipBulkDivision", + header: ({ column }) => ( + + ), + cell: ({ row, table }) => { + const value = row.getValue("equipBulkDivision") + const onSave = async (newValue: any) => { + if (table.options.meta?.onCellUpdate) { + await table.options.meta.onCellUpdate(row.original.id, "equipBulkDivision", newValue) + } + } + + return ( + + ) + }, + size: 120, + }, + { + accessorKey: "packageCode", + header: ({ column }) => ( + + ), + cell: ({ row, table }) => { + const value = row.getValue("packageCode") + const onSave = async (newValue: any) => { + if (table.options.meta?.onCellUpdate) { + await table.options.meta.onCellUpdate(row.original.id, "packageCode", newValue) + } + } + + // 수정 여부 확인 + const isModified = getIsModified(table, row.original.id, "packageCode") + + return ( + + ) + }, + size: 120, + }, + { + accessorKey: "packageName", + header: ({ column }) => ( + + ), + cell: ({ row, table }) => { + const value = row.getValue("packageName") + const isEmptyRow = String(row.original.id).startsWith('temp-') + const onSave = async (newValue: any) => { + if (table.options.meta?.onCellUpdate) { + await table.options.meta.onCellUpdate(row.original.id, "packageName", newValue) + } + } + + // 수정 여부 확인 + const isModified = getIsModified(table, row.original.id, "packageName") + + return ( + + ) + }, + size: 120, + }, + { + accessorKey: "materialGroupCode", + header: ({ column }) => ( + 자재그룹 코드 *} /> + ), + cell: ({ row, table }) => { + const value = row.getValue("materialGroupCode") + const onSave = async (newValue: any) => { + if (table.options.meta?.onCellUpdate) { + await table.options.meta.onCellUpdate(row.original.id, "materialGroupCode", newValue) + } + } + + // 수정 여부 확인 + const isModified = getIsModified(table, row.original.id, "materialGroupCode") + + const onChange = async (newValue: any) => { + if (table.options.meta?.onMaterialGroupCodeChange) { + await table.options.meta.onMaterialGroupCodeChange(row.original.id, newValue) + } + } + + return ( + + ) + }, + size: 120, + }, + { + accessorKey: "materialGroupName", + header: ({ column }) => ( + 자재그룹 명 *} /> + ), + cell: ({ row, table }) => { + const value = row.getValue("materialGroupName") + const isEmptyRow = String(row.original.id).startsWith('temp-') + const onSave = async (newValue: any) => { + if (table.options.meta?.onCellUpdate) { + await table.options.meta.onCellUpdate(row.original.id, "materialGroupName", newValue) + } + } + + // 수정 여부 확인 + const isModified = getIsModified(table, row.original.id, "materialGroupName") + + return ( + + ) + }, + size: 120, + }, + { + accessorKey: "smCode", + header: ({ column }) => ( + + ), + cell: ({ row, table }) => { + const value = row.getValue("smCode") + const onSave = async (newValue: any) => { + if (table.options.meta?.onCellUpdate) { + await table.options.meta.onCellUpdate(row.original.id, "smCode", newValue) + } + } + + return ( + + ) + }, + size: 100, + }, + { + accessorKey: "similarMaterialNamePurchase", + header: ({ column }) => ( + + ), + cell: ({ row, table }) => { + const value = row.getValue("similarMaterialNamePurchase") + const onSave = async (newValue: any) => { + if (table.options.meta?.onCellUpdate) { + await table.options.meta.onCellUpdate(row.original.id, "similarMaterialNamePurchase", newValue) + } + } + + return ( + + ) + }, + size: 140, + }, + { + accessorKey: "similarMaterialNameOther", + header: ({ column }) => ( + + ), + cell: ({ row, table }) => { + const value = row.getValue("similarMaterialNameOther") + const onSave = async (newValue: any) => { + if (table.options.meta?.onCellUpdate) { + await table.options.meta.onCellUpdate(row.original.id, "similarMaterialNameOther", newValue) + } + } + + return ( + + ) + }, + size: 140, + }, + { + accessorKey: "vendorCode", + header: ({ column }) => ( + + ), + cell: ({ row, table }) => { + const value = row.getValue("vendorCode") + const onSave = async (newValue: any) => { + if (table.options.meta?.onCellUpdate) { + await table.options.meta.onCellUpdate(row.original.id, "vendorCode", newValue) + } + } + + // 수정 여부 확인 + const isModified = getIsModified(table, row.original.id, "vendorCode") + + return ( + + ) + }, + size: 130, + }, + { + accessorKey: "vendorName", + header: ({ column }) => ( + 협력업체 명 *} /> + ), + cell: ({ row, table }) => { + const value = row.getValue("vendorName") + const isEmptyRow = String(row.original.id).startsWith('temp-') + const onSave = async (newValue: any) => { + if (table.options.meta?.onCellUpdate) { + await table.options.meta.onCellUpdate(row.original.id, "vendorName", newValue) + } + } + + // 수정 여부 확인 + const isModified = getIsModified(table, row.original.id, "vendorName") + + return ( + + ) + }, + size: 130, + }, + { + accessorKey: "taxId", + header: ({ column }) => ( + 사업자번호 *} /> + ), + cell: ({ row, table }) => { + const value = row.getValue("taxId") + const onSave = async (newValue: any) => { + if (table.options.meta?.onCellUpdate) { + await table.options.meta.onCellUpdate(row.original.id, "taxId", newValue) + } + } + + const onChange = async (newValue: any) => { + if (table.options.meta?.onTaxIdChange) { + await table.options.meta.onTaxIdChange(row.original.id, newValue) + } + } + + return ( + + ) + }, + size: 120, + }, + { + accessorKey: "faTarget", + header: "FA대상", + cell: ({ row, table }) => { + const value = row.getValue("faTarget") as boolean + const onSave = async (newValue: any) => { + if (table.options.meta?.onCellUpdate) { + await table.options.meta.onCellUpdate(row.original.id, "faTarget", newValue) + } + } + + // 수정 여부 확인 + const isModified = getIsModified(table, row.original.id, "faTarget") + + return ( + + ) + }, + enableSorting: false, + size: 80, + }, + { + accessorKey: "faStatus", + header: ({ column }) => ( + + ), + cell: ({ row, table }) => { + const value = row.getValue("faStatus") + const onSave = async (newValue: any) => { + if (table.options.meta?.onCellUpdate) { + await table.options.meta.onCellUpdate(row.original.id, "faStatus", newValue) + } + } + + return ( + + ) + }, + size: 100, + }, + { + accessorKey: "faRemark", + header: ({ column }) => ( + + ), + cell: ({ row, table }) => { + const value = row.getValue("faRemark") + const onSave = async (newValue: any) => { + if (table.options.meta?.onCellUpdate) { + await table.options.meta.onCellUpdate(row.original.id, "faRemark", newValue) + } + } + + return ( + + ) + }, + size: 120, + }, + { + accessorKey: "tier", + header: ({ column }) => ( + 등급 *} /> + ), + cell: ({ row, table }) => { + const value = row.getValue("tier") + const onSave = async (newValue: any) => { + if (table.options.meta?.onCellUpdate) { + await table.options.meta.onCellUpdate(row.original.id, "tier", newValue) + } + } + + return ( + + ) + }, + size: 80, + }, + { + accessorKey: "isAgent", + header: "Agent 여부", + cell: ({ row, table }) => { + const value = row.getValue("isAgent") as boolean + const onSave = async (newValue: any) => { + if (table.options.meta?.onCellUpdate) { + await table.options.meta.onCellUpdate(row.original.id, "isAgent", newValue) + } + } + + return ( + + ) + }, + enableSorting: false, + size: 100, + }, + { + accessorKey: "contractSignerCode", + header: ({ column }) => ( + + ), + cell: ({ row, table }) => { + const value = row.getValue("contractSignerCode") + const onSave = async (newValue: any) => { + if (table.options.meta?.onCellUpdate) { + await table.options.meta.onCellUpdate(row.original.id, "contractSignerCode", newValue) + } + } + + // 수정 여부 확인 + const isModified = getIsModified(table, row.original.id, "contractSignerCode") + + return ( + + ) + }, + size: 120, + }, + { + accessorKey: "contractSignerName", + header: ({ column }) => ( + 계약서명주체 명 *} /> + ), + cell: ({ row, table }) => { + const value = row.getValue("contractSignerName") + const onSave = async (newValue: any) => { + if (table.options.meta?.onCellUpdate) { + await table.options.meta.onCellUpdate(row.original.id, "contractSignerName", newValue) + } + } + + // 수정 여부 확인 + const isModified = getIsModified(table, row.original.id, "contractSignerName") + + return ( + + ) + }, + size: 120, + }, + { + accessorKey: "headquarterLocation", + header: ({ column }) => ( + 본사 위치 *} /> + ), + cell: ({ row, table }) => { + const value = row.getValue("headquarterLocation") + const onSave = async (newValue: any) => { + if (table.options.meta?.onCellUpdate) { + await table.options.meta.onCellUpdate(row.original.id, "headquarterLocation", newValue) + } + } + + return ( + + ) + }, + size: 100, + }, + { + accessorKey: "manufacturingLocation", + header: ({ column }) => ( + 제작/선적지 *} /> + ), + cell: ({ row, table }) => { + const value = row.getValue("manufacturingLocation") + const onSave = async (newValue: any) => { + if (table.options.meta?.onCellUpdate) { + await table.options.meta.onCellUpdate(row.original.id, "manufacturingLocation", newValue) + } + } + + return ( + + ) + }, + size: 110, + }, + { + accessorKey: "avlVendorName", + header: ({ column }) => ( + AVL 등재업체명 *} /> + ), + cell: ({ row, table }) => { + const value = row.getValue("avlVendorName") + const onSave = async (newValue: any) => { + if (table.options.meta?.onCellUpdate) { + await table.options.meta.onCellUpdate(row.original.id, "avlVendorName", newValue) + } + } + + return ( + + ) + }, + size: 140, + }, + { + accessorKey: "similarVendorName", + header: ({ column }) => ( + + ), + cell: ({ row, table }) => { + const value = row.getValue("similarVendorName") + const onSave = async (newValue: any) => { + if (table.options.meta?.onCellUpdate) { + await table.options.meta.onCellUpdate(row.original.id, "similarVendorName", newValue) + } + } + + return ( + + ) + }, + size: 130, + }, + { + accessorKey: "hasAvl", + header: "AVL", + cell: ({ row, table }) => { + const value = row.getValue("hasAvl") as boolean + const onSave = async (newValue: any) => { + if (table.options.meta?.onCellUpdate) { + await table.options.meta.onCellUpdate(row.original.id, "hasAvl", newValue) + } + } + + return ( + + ) + }, + enableSorting: false, + size: 60, + }, + { + accessorKey: "isBlacklist", + header: "Blacklist", + cell: ({ row, table }) => { + const value = row.getValue("isBlacklist") as boolean + const onSave = async (newValue: any) => { + if (table.options.meta?.onCellUpdate) { + await table.options.meta.onCellUpdate(row.original.id, "isBlacklist", newValue) + } + } + + return ( + + ) + }, + enableSorting: false, + size: 100, + }, + { + accessorKey: "isBcc", + header: "BCC", + cell: ({ row, table }) => { + const value = row.getValue("isBcc") as boolean + const onSave = async (newValue: any) => { + if (table.options.meta?.onCellUpdate) { + await table.options.meta.onCellUpdate(row.original.id, "isBcc", newValue) + } + } + + return ( + + ) + }, + enableSorting: false, + size: 80, + }, + { + accessorKey: "purchaseOpinion", + header: ({ column }) => ( + + ), + cell: ({ row, table }) => { + const value = row.getValue("purchaseOpinion") + const onSave = async (newValue: any) => { + if (table.options.meta?.onCellUpdate) { + await table.options.meta.onCellUpdate(row.original.id, "purchaseOpinion", newValue) + } + } + + return ( + + ) + }, + size: 120, + }, + { + accessorKey: "shipTypeCommon", + header: "공통", + cell: ({ row, table }) => { + const value = row.getValue("shipTypeCommon") as boolean + const onSave = async (newValue: any) => { + if (table.options.meta?.onCellUpdate) { + await table.options.meta.onCellUpdate(row.original.id, "shipTypeCommon", newValue) + } + } + + return ( + + ) + }, + enableSorting: false, + size: 80, + }, + { + accessorKey: "shipTypeAmax", + header: "A-max", + cell: ({ row, table }) => { + const value = row.getValue("shipTypeAmax") as boolean + const onSave = async (newValue: any) => { + if (table.options.meta?.onCellUpdate) { + await table.options.meta.onCellUpdate(row.original.id, "shipTypeAmax", newValue) + } + } + + return ( + + ) + }, + enableSorting: false, + size: 80, + }, + { + accessorKey: "shipTypeSmax", + header: "S-max", + cell: ({ row, table }) => { + const value = row.getValue("shipTypeSmax") as boolean + const onSave = async (newValue: any) => { + if (table.options.meta?.onCellUpdate) { + await table.options.meta.onCellUpdate(row.original.id, "shipTypeSmax", newValue) + } + } + + return ( + + ) + }, + enableSorting: false, + size: 80, + }, + { + accessorKey: "shipTypeVlcc", + header: "VLCC", + cell: ({ row, table }) => { + const value = row.getValue("shipTypeVlcc") as boolean + const onSave = async (newValue: any) => { + if (table.options.meta?.onCellUpdate) { + await table.options.meta.onCellUpdate(row.original.id, "shipTypeVlcc", newValue) + } + } + + return ( + + ) + }, + enableSorting: false, + size: 80, + }, + { + accessorKey: "shipTypeLngc", + header: "LNGC", + cell: ({ row, table }) => { + const value = row.getValue("shipTypeLngc") as boolean + const onSave = async (newValue: any) => { + if (table.options.meta?.onCellUpdate) { + await table.options.meta.onCellUpdate(row.original.id, "shipTypeLngc", newValue) + } + } + + return ( + + ) + }, + size: 80, + }, + { + accessorKey: "shipTypeCont", + header: "CONT", + cell: ({ row, table }) => { + const value = row.getValue("shipTypeCont") as boolean + const onSave = async (newValue: any) => { + if (table.options.meta?.onCellUpdate) { + await table.options.meta.onCellUpdate(row.original.id, "shipTypeCont", newValue) + } + } + + return ( + + ) + }, + size: 80, + }, + { + accessorKey: "offshoreTypeCommon", + header: "공통", + cell: ({ row, table }) => { + const value = row.getValue("offshoreTypeCommon") as boolean + const onSave = async (newValue: any) => { + if (table.options.meta?.onCellUpdate) { + await table.options.meta.onCellUpdate(row.original.id, "offshoreTypeCommon", newValue) + } + } + + return ( + + ) + }, + size: 80, + }, + { + accessorKey: "offshoreTypeFpso", + header: "FPSO", + cell: ({ row, table }) => { + const value = row.getValue("offshoreTypeFpso") as boolean + const onSave = async (newValue: any) => { + if (table.options.meta?.onCellUpdate) { + await table.options.meta.onCellUpdate(row.original.id, "offshoreTypeFpso", newValue) + } + } + + return ( + + ) + }, + size: 80, + }, + { + accessorKey: "offshoreTypeFlng", + header: "FLNG", + cell: ({ row, table }) => { + const value = row.getValue("offshoreTypeFlng") as boolean + const onSave = async (newValue: any) => { + if (table.options.meta?.onCellUpdate) { + await table.options.meta.onCellUpdate(row.original.id, "offshoreTypeFlng", newValue) + } + } + + return ( + + ) + }, + size: 80, + }, + { + accessorKey: "offshoreTypeFpu", + header: "FPU", + cell: ({ row, table }) => { + const value = row.getValue("offshoreTypeFpu") as boolean + const onSave = async (newValue: any) => { + if (table.options.meta?.onCellUpdate) { + await table.options.meta.onCellUpdate(row.original.id, "offshoreTypeFpu", newValue) + } + } + + return ( + + ) + }, + size: 80, + }, + { + accessorKey: "offshoreTypePlatform", + header: "Platform", + cell: ({ row, table }) => { + const value = row.getValue("offshoreTypePlatform") as boolean + const onSave = async (newValue: any) => { + if (table.options.meta?.onCellUpdate) { + await table.options.meta.onCellUpdate(row.original.id, "offshoreTypePlatform", newValue) + } + } + + return ( + + ) + }, + size: 100, + }, + { + accessorKey: "offshoreTypeWtiv", + header: "WTIV", + cell: ({ row, table }) => { + const value = row.getValue("offshoreTypeWtiv") as boolean + const onSave = async (newValue: any) => { + if (table.options.meta?.onCellUpdate) { + await table.options.meta.onCellUpdate(row.original.id, "offshoreTypeWtiv", newValue) + } + } + + return ( + + ) + }, + size: 80, + }, + { + accessorKey: "offshoreTypeGom", + header: "GOM", + cell: ({ row, table }) => { + const value = row.getValue("offshoreTypeGom") as boolean + const onSave = async (newValue: any) => { + if (table.options.meta?.onCellUpdate) { + await table.options.meta.onCellUpdate(row.original.id, "offshoreTypeGom", newValue) + } + } + + return ( + + ) + }, + size: 80, + }, + { + accessorKey: "picName", + header: ({ column }) => ( + + ), + cell: ({ row, table }) => { + const value = row.getValue("picName") + const onSave = async (newValue: any) => { + if (table.options.meta?.onCellUpdate) { + await table.options.meta.onCellUpdate(row.original.id, "picName", newValue) + } + } + + return ( + + ) + }, + size: 120, + }, + { + accessorKey: "picEmail", + header: ({ column }) => ( + + ), + cell: ({ row, table }) => { + const value = row.getValue("picEmail") + const onSave = async (newValue: any) => { + if (table.options.meta?.onCellUpdate) { + await table.options.meta.onCellUpdate(row.original.id, "picEmail", newValue) + } + } + return ( + + ) + }, + size: 140, + }, + { + accessorKey: "picPhone", + header: ({ column }) => ( + + ), + cell: ({ row, table }) => { + const value = row.getValue("picPhone") + const onSave = async (newValue: any) => { + if (table.options.meta?.onCellUpdate) { + await table.options.meta.onCellUpdate(row.original.id, "picPhone", newValue) + } + } + + return ( + + ) + }, + size: 120, + }, + { + accessorKey: "agentName", + header: ({ column }) => ( + + ), + cell: ({ row, table }) => { + const value = row.getValue("agentName") + const onSave = async (newValue: any) => { + if (table.options.meta?.onCellUpdate) { + await table.options.meta.onCellUpdate(row.original.id, "agentName", newValue) + } + } + + return ( + + ) + }, + size: 120, + }, + { + accessorKey: "agentEmail", + header: ({ column }) => ( + + ), + cell: ({ row, table }) => { + const value = row.getValue("agentEmail") + const onSave = async (newValue: any) => { + if (table.options.meta?.onCellUpdate) { + await table.options.meta.onCellUpdate(row.original.id, "agentEmail", newValue) + } + } + + return ( + + ) + }, + size: 140, + }, + { + accessorKey: "agentPhone", + header: ({ column }) => ( + + ), + cell: ({ row, table }) => { + const value = row.getValue("agentPhone") + const onSave = async (newValue: any) => { + if (table.options.meta?.onCellUpdate) { + await table.options.meta.onCellUpdate(row.original.id, "agentPhone", newValue) + } + } + + return ( + + ) + }, + size: 120, + }, + { + accessorKey: "recentQuoteDate", + header: ({ column }) => ( + + ), + cell: ({ row, table }) => { + const value = row.getValue("recentQuoteDate") + const onSave = async (newValue: any) => { + if (table.options.meta?.onCellUpdate) { + await table.options.meta.onCellUpdate(row.original.id, "recentQuoteDate", newValue) + } + } + + return ( + + ) + }, + size: 120, + }, + { + accessorKey: "recentQuoteNumber", + header: ({ column }) => ( + + ), + cell: ({ row, table }) => { + const value = row.getValue("recentQuoteNumber") + const onSave = async (newValue: any) => { + if (table.options.meta?.onCellUpdate) { + await table.options.meta.onCellUpdate(row.original.id, "recentQuoteNumber", newValue) + } + } + + return ( + + ) + }, + size: 130, + }, + { + accessorKey: "recentOrderDate", + header: ({ column }) => ( + + ), + cell: ({ row, table }) => { + const value = row.getValue("recentOrderDate") + const onSave = async (newValue: any) => { + if (table.options.meta?.onCellUpdate) { + await table.options.meta.onCellUpdate(row.original.id, "recentOrderDate", newValue) + } + } + + return ( + + ) + }, + size: 120, + }, + { + accessorKey: "recentOrderNumber", + header: ({ column }) => ( + + ), + cell: ({ row, table }) => { + const value = row.getValue("recentOrderNumber") + const onSave = async (newValue: any) => { + if (table.options.meta?.onCellUpdate) { + await table.options.meta.onCellUpdate(row.original.id, "recentOrderNumber", newValue) + } + } + + return ( + + ) + }, + size: 130, + }, + { + accessorKey: "registrationDate", + header: ({ column }) => ( + + ), + size: 120, + }, + { + accessorKey: "registrant", + header: ({ column }) => ( + + ), + cell: ({ row }) => { + const value = row.getValue("registrant") as string + return
{value || ""}
+ }, + size: 100, + }, + { + accessorKey: "lastModifiedDate", + header: ({ column }) => ( + + ), + size: 120, + }, + { + accessorKey: "lastModifier", + header: ({ column }) => ( + + ), + cell: ({ row }) => { + const value = row.getValue("lastModifier") as string + return
{value || ""}
+ }, + size: 120, + }, + // 액션 그룹 + { + id: "actions", + header: "액션", + cell: ({ row, table }) => { + const data = row.original + const isEmptyRow = (table.options.meta as any)?.isEmptyRow?.(String(data.id)) + + if (isEmptyRow) { + // 빈 행의 경우 저장/취소 버튼 표시 + return ( +
+ + +
+ ) + } + + // 일반 행의 경우 기존 액션 버튼들 표시 + return ( +
+ +
+ ) + }, + size: 120, + enableSorting: false, + enableHiding: false, + }, +] + diff --git a/lib/vendor-pool/table/vendor-pool-table.tsx b/lib/vendor-pool/table/vendor-pool-table.tsx index 54c6ea4d..43dd64c1 100644 --- a/lib/vendor-pool/table/vendor-pool-table.tsx +++ b/lib/vendor-pool/table/vendor-pool-table.tsx @@ -14,12 +14,9 @@ import { Button } from "@/components/ui/button" import { toast } from "sonner" import { BulkImportDialog } from "./bulk-import-dialog" -import { columns, type VendorPoolItem } from "./columns" +import { columns, type VendorPoolItem } from "./vendor-pool-table-columns" import { createVendorPool, updateVendorPool, deleteVendorPool } from "../service" -import { getVendorByTaxId } from "@/lib/vendors/service" -import { getMaterialGroupDetail } from "@/lib/material-groups/services" import type { VendorPool } from "../types" -import { cn } from "@/lib/utils" // 테이블 메타 타입 확장 declare module "@tanstack/react-table" { @@ -31,8 +28,6 @@ declare module "@tanstack/react-table" { onCancelEmptyRow?: (tempId: string) => void isEmptyRow?: (id: string) => boolean getPendingChanges?: () => Record> - onTaxIdChange?: (id: string, taxId: string) => Promise - onMaterialGroupCodeChange?: (id: string, materialGroupCode: string) => Promise } } @@ -83,68 +78,6 @@ export function VendorPoolTable({ data, pageCount, onRefresh }: VendorPoolTableP })) }, []) - // 사업자번호 변경 시 자동 vendor 검색 핸들러 - const handleTaxIdChange = React.useCallback(async (id: string, taxId: string) => { - if (!taxId || taxId.trim() === '') return - - try { - const result = await getVendorByTaxId(taxId.trim()) - if (result.data) { - // vendor 정보가 있으면 vendorCode와 vendorName을 자동으로 설정 - await handleCellUpdate(id, 'vendorCode', result.data.vendorCode || '') - await handleCellUpdate(id, 'vendorName', result.data.vendorName || '') - toast.success(`사업자번호로 '${result.data.vendorName}' 업체 정보를 자동 입력했습니다.`) - } else { - // vendor 정보가 없으면 vendorCode와 vendorName을 빈 값으로 설정 - await handleCellUpdate(id, 'vendorCode', '') - await handleCellUpdate(id, 'vendorName', '') - } - } catch (error) { - console.error('사업자번호 검색 실패:', error) - toast.error('사업자번호 검색 중 오류가 발생했습니다.') - } - }, [handleCellUpdate]) - - // 자재그룹코드 변경 시 자동 materialGroupName 검색 및 Equip/Bulk 구분 설정 핸들러 - const handleMaterialGroupCodeChange = React.useCallback(async (id: string, materialGroupCode: string) => { - if (!materialGroupCode || materialGroupCode.trim() === '') return - - const code = materialGroupCode.trim() - - try { - const materialGroup = await getMaterialGroupDetail(code) - if (materialGroup) { - // 자재그룹 정보가 있으면 materialGroupName을 자동으로 설정 - await handleCellUpdate(id, 'materialGroupName', materialGroup.materialGroupDesc || '') - toast.success(`자재그룹코드로 '${materialGroup.materialGroupDesc}' 정보를 자동 입력했습니다.`) - } else { - // 자재그룹 정보가 없으면 materialGroupName을 빈 값으로 설정 - await handleCellUpdate(id, 'materialGroupName', '') - } - - // Equip/Bulk 구분 자동 설정 - let equipBulkDivision = '' - if (code.startsWith('A1')) { - equipBulkDivision = 'S' - } else if (code.startsWith('A') || code.startsWith('B7') || code === 'SP1328' || code === 'SP1329') { - equipBulkDivision = 'B' - } else if (code.startsWith('B')) { - equipBulkDivision = 'E' - } - - if (equipBulkDivision) { - await handleCellUpdate(id, 'equipBulkDivision', equipBulkDivision) - toast.success(`자재그룹코드에 따라 Equip/Bulk 구분을 '${equipBulkDivision}'으로 자동 설정했습니다.`) - } else { - // Equip/Bulk 구분을 빈 값으로 설정하여 pendingChanges에 반영 - await handleCellUpdate(id, 'equipBulkDivision', '') - toast.info('현 자재그룹코드에 따라 Equip/Bulk 구분을 자동 설정할 수 없습니다.') - } - } catch (error) { - console.error('자재그룹코드 검색 실패:', error) - toast.error('자재그룹코드 검색 중 오류가 발생했습니다.') - } - }, [handleCellUpdate]) // 편집 취소 핸들러 @@ -631,7 +564,7 @@ export function VendorPoolTable({ data, pageCount, onRefresh }: VendorPoolTableP filterFields, enablePinning: true, enableAdvancedFilter: true, - enableColumnResizing: true, + // enableColumnResizing: true, columnResizeMode: "onChange", initialState: { sorting: [{ id: "registrationDate", desc: true }], @@ -658,8 +591,8 @@ export function VendorPoolTable({ data, pageCount, onRefresh }: VendorPoolTableP toast.info('저장 기능은 개발 중입니다.') break - case 'fixed-values': - toast.info('고정값 설정 기능은 개발 중입니다.') + case 'excel-import': + toast.info('Excel Import 기능은 개발 중입니다.') break case 'delete': @@ -722,9 +655,7 @@ export function VendorPoolTable({ data, pageCount, onRefresh }: VendorPoolTableP onSaveEmptyRow: saveEmptyRow, onCancelEmptyRow: cancelEmptyRow, isEmptyRow: (id: string) => String(id).startsWith('temp-'), - getPendingChanges: () => pendingChanges, - onTaxIdChange: handleTaxIdChange, - onMaterialGroupCodeChange: handleMaterialGroupCodeChange + getPendingChanges: () => pendingChanges } @@ -769,11 +700,11 @@ export function VendorPoolTable({ data, pageCount, onRefresh }: VendorPoolTableP