summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjoonhoekim <26rote@gmail.com>2025-09-23 20:05:46 +0900
committerjoonhoekim <26rote@gmail.com>2025-09-23 20:05:46 +0900
commit3cde5c2c7d157bb0fe353de5e67e4b35506bb4e2 (patch)
treeea2dd4927ce79543317ad5fc37a45d4bf193f670
parenta72c894c5b65b45f99fe03770f2d54098c5507c3 (diff)
(김준회) Vendorpool 수정요청사항
- 컬럼리사이징 관련 문제 해결 - 공통컴포넌트에 columnResizeMode: "onChange" 속성 추가
-rw-r--r--hooks/use-data-table.ts1
-rw-r--r--lib/avl/table/avl-table-columns.tsx568
-rw-r--r--lib/avl/table/avl-table.tsx187
-rw-r--r--lib/pos/get-pos.ts1
-rw-r--r--lib/vendor-pool/table/vendor-pool-table-columns.tsx (renamed from lib/vendor-pool/table/columns.tsx)51
-rw-r--r--lib/vendor-pool/table/vendor-pool-table.tsx83
6 files changed, 312 insertions, 579 deletions
diff --git a/hooks/use-data-table.ts b/hooks/use-data-table.ts
index 5cbf949f..5cdca877 100644
--- a/hooks/use-data-table.ts
+++ b/hooks/use-data-table.ts
@@ -562,6 +562,7 @@ export function useDataTable<TData>({
initialState: stableInitialState,
pageCount: isInfiniteMode ? -1 : pageCount,
state: tableState,
+ columnResizeMode: "onChange",
onRowSelectionChange: setRowSelection,
onPaginationChange,
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<AvlListItem>[] {
const columns: ColumnDef<AvlListItem>[] = [
- // 기본 정보 그룹
+ // select 컬럼
{
- header: "AVL 정보",
- columns: [
- {
- id: "select",
- header: () => <div className="text-center">선택</div>,
- cell: ({ row }) => (
- <div className="flex justify-center">
- <Checkbox
- checked={selectedRows.includes(row.original.id)}
- onCheckedChange={(checked) => {
- onRowSelect?.(row.original.id, !!checked)
- }}
- aria-label="행 선택"
- className="translate-y-[2px]"
- />
+ id: "select",
+ header: () => <div className="text-center">선택</div>,
+ cell: ({ row }) => (
+ <div className="flex justify-center">
+ <Checkbox
+ checked={selectedRows.includes(row.original.id)}
+ onCheckedChange={(checked) => {
+ onRowSelect?.(row.original.id, !!checked)
+ }}
+ aria-label="행 선택"
+ className="translate-y-[2px]"
+ />
+ </div>
+ ),
+ enableSorting: false,
+ enableHiding: false,
+ enableResizing: false,
+ size: 10,
+ minSize: 10,
+ maxSize: 10,
+ },
+ // No 컬럼
+ {
+ accessorKey: "no",
+ header: ({ column }) => (
+ <DataTableColumnHeaderSimple column={column} title="No" />
+ ),
+ cell: ({ getValue }) => <div className="text-center">{getValue() as number}</div>,
+ enableResizing: true,
+ size: 60,
+ },
+ // AVL 분류 컬럼
+ {
+ accessorKey: "isTemplate",
+ header: ({ column }) => (
+ <DataTableColumnHeaderSimple column={column} title="AVL 분류" />
+ ),
+ cell: ({ getValue }) => {
+ const value = getValue() as boolean
+ return (
+ <div className="text-center">
+ {value ? "표준 AVL" : "프로젝트 AVL"}
</div>
- ),
- enableSorting: false,
- enableHiding: false,
- size: 40,
- },
- {
- accessorKey: "no",
- header: ({ column }) => (
- <DataTableColumnHeaderSimple column={column} title="No" />
- ),
- cell: ({ getValue }) => <div className="text-center">{getValue() as number}</div>,
- size: 60,
- },
- {
- accessorKey: "isTemplate",
- header: ({ column }) => (
- <DataTableColumnHeaderSimple column={column} title="AVL 분류" />
- ),
- cell: ({ getValue }) => {
- const value = getValue() as boolean
- return (
- <div className="text-center">
- {value ? "표준 AVL" : "프로젝트 AVL"}
- </div>
- )
- },
- size: 120,
+ )
},
- {
- accessorKey: "constructionSector",
- header: ({ column }) => (
- <DataTableColumnHeaderSimple column={column} title="공사부문" />
- ),
- cell: ({ getValue }) => {
- const value = getValue() as string
- return (
- <div className="text-center">
- {value}
- </div>
- )
- },
- size: 100,
- },
- {
- accessorKey: "projectCode",
- header: ({ column }) => (
- <DataTableColumnHeaderSimple column={column} title="프로젝트 코드" />
- ),
- cell: ({ getValue }) => {
- const value = getValue() as string
- return (
- <div className="text-center">
- {value}
- </div>
- )
- },
- size: 140,
- },
- {
- accessorKey: "shipType",
- header: ({ column }) => (
- <DataTableColumnHeaderSimple column={column} title="선종" />
- ),
- cell: ({ getValue }) => {
- const value = getValue() as string
- return (
- <div className="text-center">
- {value}
- </div>
- )
- },
- size: 100,
- },
- {
- accessorKey: "avlKind",
- header: ({ column }) => (
- <DataTableColumnHeaderSimple column={column} title="AVL 종류" />
- ),
- cell: ({ getValue }) => {
- const value = getValue() as string
- return (
- <div className="text-center">
- {value}
- </div>
- )
- },
- size: 120,
- },
- {
- accessorKey: "htDivision",
- header: ({ column }) => (
- <DataTableColumnHeaderSimple column={column} title="H/T 구분" />
- ),
- cell: ({ getValue }) => {
- const value = getValue() as string
- return (
- <div className="text-center">
- {value}
- </div>
- )
- },
- size: 80,
- },
- {
- accessorKey: "rev",
- header: ({ column }) => (
- <DataTableColumnHeaderSimple column={column} title="Rev" />
- ),
- cell: ({ getValue, row, table }) => {
- const value = getValue() as number
- return (
- <div className="flex items-center gap-1">
- <Badge variant="outline" className="font-mono">
- {value || 1}
- </Badge>
- <Button
- variant="ghost"
- size="sm"
- className="h-6 w-6 p-0"
- onClick={() => table.options.meta?.onAction?.('view-history', row.original)}
- title="리비전 히스토리 보기"
- >
- <History className="h-3 w-3" />
- </Button>
- </div>
- )
- },
- size: 100,
- },
- ],
- },
-
- // 집계 그룹
- {
- header: "등록정보",
- columns: [
- {
- accessorKey: "PKG",
- header: ({ column }) => (
- <DataTableColumnHeaderSimple column={column} title="PKG" />
- ),
+ enableResizing: true,
+ size: 120,
+ },
+ // 공사부문 컬럼
+ {
+ accessorKey: "constructionSector",
+ header: ({ column }) => (
+ <DataTableColumnHeaderSimple column={column} title="공사부문" />
+ ),
+ cell: ({ getValue }) => {
+ const value = getValue() as string
+ return (
+ <div className="text-center">
+ {value}
+ </div>
+ )
},
- {
- accessorKey: "materialGroup",
- header: ({ column }) => (
- <DataTableColumnHeaderSimple column={column} title="자재그룹" />
- ),
+ enableResizing: true,
+ size: 100,
+ },
+ // 프로젝트 코드 컬럼
+ {
+ accessorKey: "projectCode",
+ header: ({ column }) => (
+ <DataTableColumnHeaderSimple column={column} title="프로젝트 코드" />
+ ),
+ cell: ({ getValue }) => {
+ const value = getValue() as string
+ return (
+ <div className="text-center">
+ {value}
+ </div>
+ )
},
- {
- accessorKey: "vendor",
- header: ({ column }) => (
- <DataTableColumnHeaderSimple column={column} title="협력업체" />
- ),
+ enableResizing: true,
+ size: 140,
+ },
+ // 선종 컬럼
+ {
+ accessorKey: "shipType",
+ header: ({ column }) => (
+ <DataTableColumnHeaderSimple column={column} title="선종" />
+ ),
+ cell: ({ getValue }) => {
+ const value = getValue() as string
+ return (
+ <div className="text-center">
+ {value}
+ </div>
+ )
},
- {
- accessorKey: "Tier",
- header: ({ column }) => (
- <DataTableColumnHeaderSimple column={column} title="Tier" />
- ),
+ enableResizing: true,
+ size: 100,
+ },
+ // AVL 종류 컬럼
+ {
+ accessorKey: "avlKind",
+ header: ({ column }) => (
+ <DataTableColumnHeaderSimple column={column} title="AVL 종류" />
+ ),
+ cell: ({ getValue }) => {
+ const value = getValue() as string
+ return (
+ <div className="text-center">
+ {value}
+ </div>
+ )
},
- {
- accessorKey: "ownerSuggestion",
- header: ({ column }) => (
- <DataTableColumnHeaderSimple column={column} title="선주 제안" />
- ),
+ enableResizing: true,
+ size: 120,
+ },
+ // H/T 구분 컬럼
+ {
+ accessorKey: "htDivision",
+ header: ({ column }) => (
+ <DataTableColumnHeaderSimple column={column} title="H/T 구분" />
+ ),
+ cell: ({ getValue }) => {
+ const value = getValue() as string
+ return (
+ <div className="text-center">
+ {value}
+ </div>
+ )
},
- {
- accessorKey: "shiSuggestion",
- header: ({ column }) => (
- <DataTableColumnHeaderSimple column={column} title="SHI 제안" />
- ),
+ enableResizing: true,
+ size: 80,
+ },
+ // Rev 컬럼
+ {
+ accessorKey: "rev",
+ header: ({ column }) => (
+ <DataTableColumnHeaderSimple column={column} title="Rev" />
+ ),
+ cell: ({ getValue, row, table }) => {
+ const value = getValue() as number
+ return (
+ <div className="flex items-center gap-1">
+ <Badge variant="outline" className="font-mono">
+ {value || 1}
+ </Badge>
+ <Button
+ variant="ghost"
+ size="sm"
+ className="h-6 w-6 p-0"
+ onClick={() => table.options.meta?.onAction?.('view-history', row.original)}
+ title="리비전 히스토리 보기"
+ >
+ <History className="h-3 w-3" />
+ </Button>
+ </div>
+ )
},
- ],
- },
-
- // 등록 정보 그룹
- {
- header: "작성정보",
- columns: [
- {
- accessorKey: "createdAt",
- header: ({ column }) => (
- <DataTableColumnHeaderSimple column={column} title="등록일" />
- ),
- cell: ({ getValue }) => {
- const date = getValue() as string
- return <div className="text-center text-sm">{date}</div>
- },
- size: 100,
+ enableResizing: true,
+ size: 100,
+ },
+ // PKG 컬럼
+ {
+ accessorKey: "PKG",
+ header: ({ column }) => (
+ <DataTableColumnHeaderSimple column={column} title="PKG" />
+ ),
+ enableResizing: true,
+ size: 80,
+ },
+ // 자재그룹 컬럼
+ {
+ accessorKey: "materialGroup",
+ header: ({ column }) => (
+ <DataTableColumnHeaderSimple column={column} title="자재그룹" />
+ ),
+ enableResizing: true,
+ size: 120,
+ },
+ // 협력업체 컬럼
+ {
+ accessorKey: "vendor",
+ header: ({ column }) => (
+ <DataTableColumnHeaderSimple column={column} title="협력업체" />
+ ),
+ enableResizing: true,
+ size: 150,
+ },
+ // Tier 컬럼
+ {
+ accessorKey: "Tier",
+ header: ({ column }) => (
+ <DataTableColumnHeaderSimple column={column} title="Tier" />
+ ),
+ enableResizing: true,
+ size: 60,
+ },
+ // 선주 제안 컬럼
+ {
+ accessorKey: "ownerSuggestion",
+ header: ({ column }) => (
+ <DataTableColumnHeaderSimple column={column} title="선주 제안" />
+ ),
+ enableResizing: true,
+ size: 100,
+ },
+ // SHI 제안 컬럼
+ {
+ accessorKey: "shiSuggestion",
+ header: ({ column }) => (
+ <DataTableColumnHeaderSimple column={column} title="SHI 제안" />
+ ),
+ enableResizing: true,
+ size: 100,
+ },
+ // 등록일 컬럼
+ {
+ accessorKey: "createdAt",
+ header: ({ column }) => (
+ <DataTableColumnHeaderSimple column={column} title="등록일" />
+ ),
+ cell: ({ getValue }) => {
+ const date = getValue() as string
+ return <div className="text-center text-sm">{date}</div>
},
- {
- accessorKey: "createdBy",
- header: ({ column }) => (
- <DataTableColumnHeaderSimple column={column} title="등록자" />
- ),
- cell: ({ getValue }) => {
- const date = getValue() as string
- return <div className="text-center text-sm">{date}</div>
- },
- size: 100,
+ enableResizing: true,
+ size: 100,
+ },
+ // 등록자 컬럼
+ {
+ accessorKey: "createdBy",
+ header: ({ column }) => (
+ <DataTableColumnHeaderSimple column={column} title="등록자" />
+ ),
+ cell: ({ getValue }) => {
+ const value = getValue() as string
+ return <div className="text-center text-sm">{value}</div>
},
- {
- accessorKey: "updatedAt",
- header: ({ column }) => (
- <DataTableColumnHeaderSimple column={column} title="최종변경일" />
- ),
- cell: ({ getValue }) => {
- const date = getValue() as string
- return <div className="text-center text-sm">{date}</div>
- },
- size: 100,
+ enableResizing: true,
+ size: 100,
+ },
+ // 최종변경일 컬럼
+ {
+ accessorKey: "updatedAt",
+ header: ({ column }) => (
+ <DataTableColumnHeaderSimple column={column} title="최종변경일" />
+ ),
+ cell: ({ getValue }) => {
+ const date = getValue() as string
+ return <div className="text-center text-sm">{date}</div>
},
- {
- accessorKey: "updatedBy",
- header: ({ column }) => (
- <DataTableColumnHeaderSimple column={column} title="최종변경자" />
- ),
- cell: ({ getValue }) => {
- const date = getValue() as string
- return <div className="text-center text-sm">{date}</div>
- },
- size: 100,
+ enableResizing: true,
+ size: 100,
+ },
+ // 최종변경자 컬럼
+ {
+ accessorKey: "updatedBy",
+ header: ({ column }) => (
+ <DataTableColumnHeaderSimple column={column} title="최종변경자" />
+ ),
+ cell: ({ getValue }) => {
+ const value = getValue() as string
+ return <div className="text-center text-sm">{value}</div>
},
- ],
- },
-
- // 액션 그룹
- {
- id: "actions",
- header: "액션",
- columns: [
- {
- id: "actions",
- header: () => <div className="text-center">액션</div>,
- cell: ({ row, table }) => {
- const isEmptyRow = table.options.meta?.isEmptyRow?.(row.id) || false
-
- if (isEmptyRow) {
- return (
- <div className="flex items-center justify-center gap-1">
- <Button
- variant="ghost"
- size="sm"
- onClick={() => table.options.meta?.onSaveEmptyRow?.(row.id)}
- className="h-8 w-8 p-0"
- >
- 저장
- </Button>
- <Button
- variant="ghost"
- size="sm"
- onClick={() => table.options.meta?.onCancelEmptyRow?.(row.id)}
- className="h-8 w-8 p-0 text-destructive hover:text-destructive"
- >
- 취소
- </Button>
- </div>
- )
- }
+ enableResizing: true,
+ size: 100,
+ },
+ // 액션 컬럼
+ {
+ id: "actions",
+ header: () => <div className="text-center">액션</div>,
+ cell: ({ row, table }) => {
+ const isEmptyRow = table.options.meta?.isEmptyRow?.(row.id) || false
+ if (isEmptyRow) {
return (
<div className="flex items-center justify-center gap-1">
<Button
variant="ghost"
size="sm"
- onClick={() => table.options.meta?.onAction?.("view-detail", { id: row.original.id })}
+ onClick={() => table.options.meta?.onSaveEmptyRow?.(row.id)}
className="h-8 w-8 p-0"
- title="상세보기"
>
- <Eye className="h-4 w-4" />
+ 저장
+ </Button>
+ <Button
+ variant="ghost"
+ size="sm"
+ onClick={() => table.options.meta?.onCancelEmptyRow?.(row.id)}
+ className="h-8 w-8 p-0 text-destructive hover:text-destructive"
+ >
+ 취소
</Button>
</div>
)
- },
- enableSorting: false,
- enableHiding: false,
- size: 120,
+ }
+
+ return (
+ <div className="flex items-center justify-center gap-1">
+ <Button
+ variant="ghost"
+ size="sm"
+ onClick={() => table.options.meta?.onAction?.("view-detail", { id: row.original.id })}
+ className="h-8 w-8 p-0"
+ title="상세보기"
+ >
+ <Eye className="h-4 w-4" />
+ </Button>
+ </div>
+ )
},
- ],
+ 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<number[]>([])
- // 수정사항 추적 (일괄 저장용)
- const [pendingChanges, setPendingChanges] = React.useState<Record<string, Partial<AvlListItem>>>({})
- const [isSaving, setIsSaving] = React.useState(false)
-
// 히스토리 모달 관리
const [historyModalOpen, setHistoryModalOpen] = React.useState(false)
const [selectedAvlItem, setSelectedAvlItem] = React.useState<AvlListItem | null>(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<AvlListItem>) => {
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 (
<div className="space-y-4">
{/* 툴바 */}
@@ -353,31 +204,11 @@ export function AvlTable({ data, pageCount, onRefresh, isLoading, onRegistration
프로젝트AVL등록
</Button>
- {/* 저장 버튼 - 변경사항이 있을 때만 활성화 */}
- {(hasPendingChanges) && (
- <Button
- variant="default"
- size="sm"
- onClick={() => handleAction('save')}
- disabled={isSaving}
- >
- {isSaving ? "저장 중..." : "저장"}
- </Button>
- )}
-
- {/* 새로고침 버튼 */}
- <Button
- variant="outline"
- size="sm"
- onClick={onRefresh}
- >
- 새로고침
- </Button>
</div>
</DataTableAdvancedToolbar>
{/* 데이터 테이블 */}
- <DataTable table={table} />
+ <DataTable table={table} autoSizeColumns={false} />
{/* 히스토리 모달 */}
<AvlHistoryModal
@@ -390,12 +221,6 @@ export function AvlTable({ data, pageCount, onRefresh, isLoading, onRegistration
onLoadHistory={loadHistoryData}
/>
- {/* 디버그 정보 (개발 환경에서만 표시) */}
- {process.env.NODE_ENV === 'development' && (hasPendingChanges) && (
- <div className="text-xs text-muted-foreground p-2 bg-muted rounded">
- <div>Pending Changes: {Object.keys(pendingChanges).length}</div>
- </div>
- )}
</div>
)
}
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/vendor-pool-table-columns.tsx
index 0a6b0c8f..8f09e684 100644
--- a/lib/vendor-pool/table/columns.tsx
+++ b/lib/vendor-pool/table/vendor-pool-table-columns.tsx
@@ -100,10 +100,7 @@ export type VendorPoolItem = {
// 테이블 컬럼 정의
export const columns: ColumnDef<VendorPoolItem>[] = [
- // 기본 정보 그룹
- {
- header: "기본 정보",
- columns: [
+
{
id: "select",
header: ({ table }) => (
@@ -466,12 +463,6 @@ export const columns: ColumnDef<VendorPoolItem>[] = [
},
size: 100,
},
- ]
- },
- // 자재 정보 그룹
- {
- header: "자재 정보",
- columns: [
{
accessorKey: "similarMaterialNamePurchase",
header: ({ column }) => (
@@ -524,12 +515,6 @@ export const columns: ColumnDef<VendorPoolItem>[] = [
},
size: 140,
},
- ]
- },
- // 협력업체 정보 그룹
- {
- header: "협력업체 정보",
- columns: [
{
accessorKey: "vendorCode",
header: ({ column }) => (
@@ -1009,12 +994,6 @@ export const columns: ColumnDef<VendorPoolItem>[] = [
},
size: 120,
},
- ]
- },
- // AVL 적용 선종(조선) 그룹
- {
- header: "AVL 적용 선종(조선)",
- columns: [
{
accessorKey: "shipTypeCommon",
header: "공통",
@@ -1151,12 +1130,6 @@ export const columns: ColumnDef<VendorPoolItem>[] = [
},
size: 80,
},
- ]
- },
- // AVL 적용 선종(해양) 그룹
- {
- header: "AVL 적용 선종(해양)",
- columns: [
{
accessorKey: "offshoreTypeCommon",
header: "공통",
@@ -1311,12 +1284,6 @@ export const columns: ColumnDef<VendorPoolItem>[] = [
},
size: 80,
},
- ]
- },
- // eVCP 미등록 정보 그룹
- {
- header: "eVCP 미등록 정보",
- columns: [
{
accessorKey: "picName",
header: ({ column }) => (
@@ -1354,7 +1321,6 @@ export const columns: ColumnDef<VendorPoolItem>[] = [
await table.options.meta.onCellUpdate(row.original.id, "picEmail", newValue)
}
}
-
return (
<EditableCell
value={value}
@@ -1467,12 +1433,6 @@ export const columns: ColumnDef<VendorPoolItem>[] = [
},
size: 120,
},
- ]
- },
- // 업체 실적 현황 그룹
- {
- header: "업체 실적 현황",
- columns: [
{
accessorKey: "recentQuoteDate",
header: ({ column }) => (
@@ -1575,12 +1535,6 @@ export const columns: ColumnDef<VendorPoolItem>[] = [
},
size: 130,
},
- ]
- },
- // 업데이트 히스토리 그룹
- {
- header: "업데이트 히스토리",
- columns: [
{
accessorKey: "registrationDate",
header: ({ column }) => (
@@ -1617,8 +1571,6 @@ export const columns: ColumnDef<VendorPoolItem>[] = [
},
size: 120,
},
- ]
- },
// 액션 그룹
{
id: "actions",
@@ -1685,3 +1637,4 @@ export const columns: ColumnDef<VendorPoolItem>[] = [
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<string, Partial<VendorPoolItem>>
- onTaxIdChange?: (id: string, taxId: string) => Promise<void>
- onMaterialGroupCodeChange?: (id: string, materialGroupCode: string) => Promise<void>
}
}
@@ -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
</Button>
<Button
- onClick={() => handleToolbarAction('fixed-values')}
+ onClick={() => handleToolbarAction('excel-import')}
variant="outline"
size="sm"
>
- FA 상세
+ Excel Import
</Button>
<Button