"use client" import * as React from "react" import { useReactTable, getCoreRowModel, getSortedRowModel, getFilteredRowModel, type SortingState, type ColumnFiltersState, flexRender, type Column, } from "@tanstack/react-table" import { useVirtualizer } from "@tanstack/react-virtual" import { toast } from "sonner" import { ChevronDown, ChevronUp, Search, Download } from "lucide-react" import { Button } from "@/components/ui/button" import { Input } from "@/components/ui/input" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select" import { BackButton } from "@/components/ui/back-button" import { virtualColumns } from "./avl-detail-virtual-columns" import type { AvlDetailItem } from "../types" import { exportTableToExcel } from "@/lib/export_all" interface AvlDetailVirtualTableProps { data: AvlDetailItem[] avlListId: number avlType?: '프로젝트AVL' | '선종별표준AVL' | string projectInfo?: { code?: string pspid?: string OWN_NM?: string kunnrNm?: string } shipOwnerName?: string businessType?: string } function Filter({ column }: { column: Column }) { const columnFilterValue = column.getFilterValue() const id = column.id // Boolean 필터 (faTarget, shiBlacklist, shiBcc) if (id === 'faTarget' || id === 'shiBlacklist' || id === 'shiBcc' || id === 'shiAvl') { return (
e.stopPropagation()} className="mt-2">
) } // FA Status 필터 (O 또는 빈 값 - 데이터에 따라 조정 필요) if (id === 'faStatus') { return (
e.stopPropagation()} className="mt-2">
) } // 일반 텍스트 검색 return (
e.stopPropagation()} className="mt-2"> column.setFilterValue(e.target.value)} placeholder="Search..." className="h-8 w-full font-normal bg-background" />
) } export function AvlDetailVirtualTable({ data, avlType, projectInfo, businessType, }: AvlDetailVirtualTableProps) { // 상태 관리 const [sorting, setSorting] = React.useState([]) const [columnFilters, setColumnFilters] = React.useState([]) const [globalFilter, setGlobalFilter] = React.useState("") // 액션 핸들러 const handleAction = React.useCallback(async (action: string) => { try { switch (action) { case 'vendor-pool': window.open('/evcp/vendor-pool', '_blank') break case 'excel-export': try { toast.info("엑셀 파일을 생성 중입니다...") await exportTableToExcel(table, { filename: `AVL_상세내역_${new Date().toISOString().split('T')[0]}`, allPages: true, excludeColumns: ["select", "actions"], useGroupHeader: true, }) toast.success('Excel 파일이 다운로드되었습니다.') } catch (error) { console.error('Excel export 실패:', error) toast.error('Excel 내보내기에 실패했습니다.') } break default: console.log('알 수 없는 액션:', action) toast.error('알 수 없는 액션입니다.') } } catch (error) { console.error('액션 처리 실패:', error) toast.error('액션 처리 중 오류가 발생했습니다.') } }, []) // TanStack Table 설정 const table = useReactTable({ data, columns: virtualColumns, state: { sorting, columnFilters, globalFilter, }, onSortingChange: setSorting, onColumnFiltersChange: setColumnFilters, onGlobalFilterChange: setGlobalFilter, columnResizeMode: "onChange", getCoreRowModel: getCoreRowModel(), getSortedRowModel: getSortedRowModel(), getFilteredRowModel: getFilteredRowModel(), getRowId: (originalRow) => String(originalRow.id), }) // Virtual Scrolling 설정 const tableContainerRef = React.useRef(null) const { rows } = table.getRowModel() const rowVirtualizer = useVirtualizer({ count: rows.length, getScrollElement: () => tableContainerRef.current, estimateSize: () => 50, // 행 높이 추정값 overscan: 10, // 화면 밖 렌더링할 행 수 }) const virtualRows = rowVirtualizer.getVirtualItems() const totalSize = rowVirtualizer.getTotalSize() const paddingTop = virtualRows.length > 0 ? virtualRows?.[0]?.start || 0 : 0 const paddingBottom = virtualRows.length > 0 ? totalSize - (virtualRows?.[virtualRows.length - 1]?.end || 0) : 0 return (
{/* 상단 정보 표시 영역 */}

AVL 상세내역

{avlType} [{businessType}] {projectInfo?.code || projectInfo?.pspid || '코드정보없음(표준AVL)'} ({projectInfo?.OWN_NM || projectInfo?.kunnrNm || '선주정보 없음'})
목록으로
{/* 툴바 */}
setGlobalFilter(e.target.value)} className="pl-8" />
전체 {data.length}건 중 {rows.length}건 표시
{/* 테이블 */}
{/* GroupHeader를 사용할 때 table-layout: fixed에서 열 너비가 올바르게 적용되도록 colgroup 추가 */} {table.getLeafHeaders().map((header) => ( ))} {table.getHeaderGroups().map((headerGroup) => ( {headerGroup.headers.map((header) => ( ))} ))} {paddingTop > 0 && ( )} {virtualRows.map((virtualRow) => { const row = rows[virtualRow.index] return ( {row.getVisibleCells().map((cell) => ( ))} ) })} {paddingBottom > 0 && ( )}
{header.isPlaceholder ? null : ( <>
{flexRender( header.column.columnDef.header, header.getContext() )} {header.column.getCanSort() && (
{header.column.getIsSorted() === "asc" ? ( ) : header.column.getIsSorted() === "desc" ? ( ) : (
)}
)}
{header.column.getCanFilter() && ( )}
)}
{flexRender( cell.column.columnDef.cell, cell.getContext() )}
) }