summaryrefslogtreecommitdiff
path: root/lib/vendors/contract-history/contract-history-table.tsx
blob: 62831aaa9c5650c302dc9be71b1810c0cd4e307e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
"use client"

import * as React from "react"
import type {
  DataTableAdvancedFilterField,
  DataTableFilterField,
  DataTableRowAction,
} from "@/types/table"

import { useDataTable } from "@/hooks/use-data-table"
import { DataTable } from "@/components/data-table/data-table"
import { DataTableAdvancedToolbar } from "@/components/data-table/data-table-advanced-toolbar"

import { getColumns } from "./contract-history-table-columns"
import { ContractDetailParsed } from "@/db/schema/contract"
import type { ColumnDef } from "@tanstack/react-table"
import { getVendorContractHistoryExtended } from "../contract-history-service"

interface ContractHistoryTableProps {
  vendorId: number
  onRowAction?: (action: DataTableRowAction<ContractDetailParsed>) => void
}

// 컬럼 정보를 기반으로 필터 필드 생성하는 유틸리티 함수
function generateFilterFieldsFromColumns(columns: ColumnDef<ContractDetailParsed>[]): {
  basic: DataTableFilterField<ContractDetailParsed>[]
  advanced: DataTableAdvancedFilterField<ContractDetailParsed>[]
} {
  const basicFields: DataTableFilterField<ContractDetailParsed>[] = []
  const advancedFields: DataTableAdvancedFilterField<ContractDetailParsed>[] = []

  // 필터링에서 제외할 컬럼 ID들
  const excludeIds = new Set(['select', 'contractDetail'])

  columns.forEach((column) => {
    // 타입 안전하게 accessorKey 추출
    const accessorKey = (column as { accessorKey?: string }).accessorKey
    const header = (column as { header?: unknown }).header

    // 제외할 컬럼이나 accessorKey가 없는 경우 스킵
    if (!accessorKey || excludeIds.has(accessorKey)) return

    // 헤더에서 타이틀 추출
    let title = ''

    // accessorKey를 기반으로 한글 타이틀 매핑 (contract.ts 스키마 기준)
    const titleMap: Record<string, string> = {
      contractNo: 'PO/계약번호',
      contractVersion: 'Rev. / 품번',
      status: '계약상태',
      projectName: '프로젝트',
      projectCode: 'PKG No.',
      vendorName: '협력업체',
      contractName: '계약명',
      materialGroupCode: '자재그룹코드',
      materialGroupName: '자재그룹명',
      paymentTerms: '지불조건',
      deliveryTerms: 'Incoterms',
      shippmentPlace: '선적지',
      deliveryDate: '계약납기일',
      deliveryLocation: '납품장소',
      priceIndexYn: '연동제대상',
      currency: '통화',
      totalAmount: '계약금액',
      advancePaymentYn: '선급금',
      startDate: 'PO/계약발송일',
      endDate: '계약종료일',
      electronicApprovalDate: 'PO/계약체결일',
      hasSignature: '전자서명상태',
      partialShippingAllowed: '분할선적허용',
      partialPaymentAllowed: '분할결제허용',
      createdAt: '생성일',
      updatedAt: '수정일',
      netTotal: '순총액',
      discount: '할인',
      tax: '세금',
      shippingFee: '배송비',
      remarks: '비고',
      version: '버전'
    }

    // 매핑된 타이틀이 있으면 사용
    if (titleMap[accessorKey]) {
      title = titleMap[accessorKey]
    } else {
      // 함수형 헤더에서 추출 시도
      if (typeof header === 'function') {
        try {
          const headerProps = header({ column: { id: accessorKey } })
          if (React.isValidElement(headerProps) && headerProps.props && typeof headerProps.props === 'object' && 'title' in headerProps.props) {
            const props = headerProps.props as { title?: string }
            title = props.title || ''
          }
        } catch {
          // 헤더 함수 실행 실패 시 스킵
        }
      } else if (typeof header === 'string') {
        title = header
      }
    }

    if (!title) return

    // 필터 타입 결정 (간단한 휴리스틱)
    const getFilterType = (key: string): "text" | "number" | "date" => {
      if (key.includes('Date') || key.includes('date') || key.includes('At')) return 'date'
      if (key.includes('Amount') || key.includes('amount') || key.includes('total') || key.includes('price')) return 'number'
      return 'text'
    }

    const filterType = getFilterType(accessorKey)

    // 기본 필터 (주요 필드만)
    const importantFields = ['contractNo', 'contractName', 'status', 'projectName', 'vendorName', 'currency']
    if (importantFields.includes(accessorKey)) {
      basicFields.push({
        id: accessorKey as keyof ContractDetailParsed,
        label: title,
      })
    }

    // 고급 필터 (모든 필터링 가능한 필드)
    advancedFields.push({
      id: accessorKey as keyof ContractDetailParsed,
      label: title,
      type: filterType,
    })
  })

  return { basic: basicFields, advanced: advancedFields }
}

export function ContractHistoryTable({ vendorId, onRowAction }: ContractHistoryTableProps) {
  // Row action state 관리
  const [rowAction, setRowAction] = React.useState<DataTableRowAction<ContractDetailParsed> | null>(null)

  // 데이터 상태 관리
  const [serviceData, setServiceData] = React.useState<{
    data: ContractDetailParsed[]
    pageCount: number
    totalCount: number
  }>({ data: [], pageCount: 0, totalCount: 0 })
  const [isLoading, setIsLoading] = React.useState(false)

  console.log('ContractHistoryTable data:', serviceData.data.length, 'contracts')

  // Row action이 발생하면 외부 핸들러 호출
  React.useEffect(() => {
    if (rowAction && onRowAction) {
      onRowAction(rowAction)
      setRowAction(null) // Reset action after handling
    }
  }, [rowAction, onRowAction])

  // 초기 데이터 로드
  React.useEffect(() => {
    const loadInitialData = async () => {
      setIsLoading(true)
      try {
        const result = await getVendorContractHistoryExtended(vendorId, {
          page: 1,
          pageSize: 10
        })
        setServiceData(result)
      } catch (error) {
        console.error('Failed to load contract history:', error)
        setServiceData({ data: [], pageCount: 0, totalCount: 0 })
      } finally {
        setIsLoading(false)
      }
    }

    loadInitialData()
  }, [vendorId])

  const columns = React.useMemo(
    () => getColumns({ setRowAction }),
    []
  )

  // 컬럼 정보를 기반으로 동적으로 필터 필드 생성
  const { basic: filterFields, advanced: advancedFilterFields } = React.useMemo(() => {
    return generateFilterFieldsFromColumns(columns)
  }, [columns])

  // useDataTable 훅 사용
  const {
    table,
  } = useDataTable({
    data: serviceData.data,
    columns,
    pageCount: serviceData.pageCount,
    filterFields,
    enablePinning: true,
    enableAdvancedFilter: true,
    initialState: {
      sorting: [{ id: "contractNo", desc: false }],
    },
    getRowId: (originalRow) => String(originalRow.id || 'unknown'),
    shallow: false,
    clearOnDefault: true,
  })

  return (
    <div className="w-full space-y-2.5 overflow-x-auto" style={{maxWidth:'100vw'}}>



        {/* 로딩 상태가 아닐 때만 테이블 렌더링 */}
        {!isLoading ? (
          <>
            {/* 도구 모음 */}
            <DataTableAdvancedToolbar
              table={table}
              filterFields={advancedFilterFields}
              shallow={false}
            />

            {/* 테이블 렌더링 */}
            <DataTable
              table={table}
              compact={false}
              autoSizeColumns={true}
            />
          </>
        ) : (
          /* 로딩 스켈레톤 */
          <div className="space-y-3">
            <div className="text-sm text-muted-foreground mb-4">
              계약 히스토리를 불러오는 중입니다...
            </div>
            {Array.from({ length: 10 }).map((_, i) => (
              <div key={i} className="h-12 w-full bg-muted animate-pulse rounded" />
            ))}
          </div>
        )}

    </div>
  )
}