"use client" import * as React from "react" import { useSearchParams } from "next/navigation" import { Button } from "@/components/ui/button" import { PanelLeftClose, PanelLeftOpen } from "lucide-react" import type { DataTableAdvancedFilterField, DataTableRowAction, } from "@/types/table" import { ResizablePanelGroup, ResizablePanel, ResizableHandle, } from "@/components/ui/resizable" import { useDataTable } from "@/hooks/use-data-table" import { DataTable } from "@/components/data-table/data-table" import { getColumns, EditingCellState } from "./rfq-table-column" import { useEffect, useCallback, useRef, useMemo, useLayoutEffect } from "react" import { DataTableAdvancedToolbar } from "@/components/data-table/data-table-advanced-toolbar" import { RFQTableToolbarActions } from "./rfq-table-toolbar-actions" import { ProcurementRfqsView } from "@/db/schema" import { getPORfqs } from "../services" import { toast } from "sonner" import { updateRfqRemark } from "@/lib/procurement-rfqs/services" import { useTablePresets } from "@/components/data-table/use-table-presets" import { TablePresetManager } from "@/components/data-table/data-table-preset" import { Loader2 } from "lucide-react" import { RFQFilterSheet } from "./rfq-filter-sheet" import { RfqDetailTables } from "./detail-table/rfq-detail-table" import { cn } from "@/lib/utils" interface RFQListTableProps { promises: Promise<[Awaited>]> className?: string; calculatedHeight?: string; // 계산된 높이 추가 } export function RFQListTable({ promises, className, calculatedHeight }: RFQListTableProps) { const searchParams = useSearchParams() // 필터 패널 상태 const [isFilterPanelOpen, setIsFilterPanelOpen] = React.useState(false) // 선택된 RFQ 상태 const [selectedRfq, setSelectedRfq] = React.useState(null) // 패널 collapse 상태 const [isTopCollapsed, setIsTopCollapsed] = React.useState(false) const [panelHeight, setPanelHeight] = React.useState(55) // refs const headerRef = React.useRef(null) // 고정 높이 설정을 위한 상수 (실제 측정값으로 조정 필요) const LAYOUT_HEADER_HEIGHT = 64 // Layout Header 높이 const LAYOUT_FOOTER_HEIGHT = 60 // Layout Footer 높이 (있다면 실제 값) const LOCAL_HEADER_HEIGHT = 72 // 로컬 헤더 바 높이 (p-4 + border) const FILTER_PANEL_WIDTH = 400 // 필터 패널 너비 // 높이 계산 // 필터 패널 높이 - Layout Header와 Footer 사이 const FIXED_FILTER_HEIGHT = `calc(100vh - ${LAYOUT_HEADER_HEIGHT*2}px)` console.log(calculatedHeight) // 테이블 컨텐츠 높이 - 전달받은 높이에서 로컬 헤더 제외 const FIXED_TABLE_HEIGHT = calculatedHeight ? `calc(${calculatedHeight} - ${LOCAL_HEADER_HEIGHT}px)` : `calc(100vh - ${LAYOUT_HEADER_HEIGHT + LAYOUT_FOOTER_HEIGHT + LOCAL_HEADER_HEIGHT+76}px)` // fallback // Suspense 방식으로 데이터 처리 const [promiseData] = React.use(promises) const tableData = promiseData const [rowAction, setRowAction] = React.useState | null>(null) const [editingCell, setEditingCell] = React.useState(null) // 초기 설정 정의 const initialSettings = React.useMemo(() => ({ page: parseInt(searchParams.get('page') || '1'), perPage: parseInt(searchParams.get('perPage') || '10'), sort: searchParams.get('sort') ? JSON.parse(searchParams.get('sort')!) : [{ id: "updatedAt", desc: true }], filters: searchParams.get('filters') ? JSON.parse(searchParams.get('filters')!) : [], joinOperator: (searchParams.get('joinOperator') as "and" | "or") || "and", basicFilters: searchParams.get('basicFilters') ? JSON.parse(searchParams.get('basicFilters')!) : [], basicJoinOperator: (searchParams.get('basicJoinOperator') as "and" | "or") || "and", search: searchParams.get('search') || '', from: searchParams.get('from') || undefined, to: searchParams.get('to') || undefined, columnVisibility: {}, columnOrder: [], pinnedColumns: { left: [], right: [] }, groupBy: [], expandedRows: [] }), [searchParams]) // DB 기반 프리셋 훅 사용 const { presets, activePresetId, hasUnsavedChanges, isLoading: presetsLoading, createPreset, applyPreset, updatePreset, deletePreset, setDefaultPreset, renamePreset, getCurrentSettings, } = useTablePresets('rfq-list-table', initialSettings) // 비고 업데이트 함수 const updateRemark = async (rfqId: number, remark: string) => { try { const result = await updateRfqRemark(rfqId, remark); if (result.success) { toast.success("비고가 업데이트되었습니다"); } else { toast.error(result.message || "업데이트 중 오류가 발생했습니다"); } } catch (error) { console.error("비고 업데이트 오류:", error); toast.error("업데이트 중 오류가 발생했습니다"); } } // 행 액션 처리 useEffect(() => { if (rowAction) { switch (rowAction.type) { case "select": setSelectedRfq(rowAction.row.original) break; case "update": console.log("Update rfq:", rowAction.row.original) break; case "delete": console.log("Delete rfq:", rowAction.row.original) break; } setRowAction(null) } }, [rowAction]) const columns = React.useMemo( () => getColumns({ setRowAction, editingCell, setEditingCell, updateRemark }), [setRowAction, editingCell, setEditingCell, updateRemark] ) // 고급 필터 필드 정의 const advancedFilterFields: DataTableAdvancedFilterField[] = [ { id: "rfqCode", label: "RFQ No.", type: "text", }, { id: "projectCode", label: "프로젝트", type: "text", }, { id: "itemCode", label: "자재그룹", type: "text", }, { id: "itemName", label: "자재명", type: "text", }, { id: "rfqSealedYn", label: "RFQ 밀봉여부", type: "text", }, { id: "majorItemMaterialCode", label: "자재코드", type: "text", }, { id: "rfqSendDate", label: "RFQ 전송일", type: "date", }, { id: "dueDate", label: "RFQ 마감일", type: "date", }, { id: "createdByUserName", label: "요청자", type: "text", }, ] // 현재 설정 가져오기 const currentSettings = useMemo(() => { return getCurrentSettings() }, [getCurrentSettings]) // useDataTable 초기 상태 설정 const initialState = useMemo(() => { return { sorting: initialSettings.sort.filter(sortItem => { const columnExists = columns.some(col => col.accessorKey === sortItem.id) return columnExists }) as any, columnVisibility: currentSettings.columnVisibility, columnPinning: currentSettings.pinnedColumns, } }, [currentSettings, initialSettings.sort, columns]) // useDataTable 훅 설정 (PQ와 동일한 설정) const { table } = useDataTable({ data: tableData?.data || [], columns, pageCount: tableData?.pageCount || 0, rowCount: tableData?.total || 0, filterFields: [], // PQ와 동일하게 빈 배열 enablePinning: true, enableAdvancedFilter: true, initialState, getRowId: (originalRow) => String(originalRow.id), shallow: false, // PQ와 동일하게 false clearOnDefault: true, }) // 조회 버튼 클릭 핸들러 const handleSearch = () => { setIsFilterPanelOpen(false) } // Get active basic filter count (PQ와 동일한 방식) const getActiveBasicFilterCount = () => { try { const basicFilters = searchParams.get('basicFilters') return basicFilters ? JSON.parse(basicFilters).length : 0 } catch (e) { return 0 } } console.log(panelHeight) return (
{/* Filter Panel - 계산된 높이 적용 */}
{/* Filter Content */}
setIsFilterPanelOpen(false)} onSearch={handleSearch} isLoading={false} />
{/* Main Content */}
{/* Header Bar - 고정 높이 */}
{/* Right side info */}
{tableData && ( 총 {tableData.total || 0}건 )}
{/* Table Content Area - 계산된 높이 사용 */}
{ setPanelHeight(size) }} className="flex flex-col overflow-hidden" > {/* 상단 테이블 영역 */}
presets={presets} activePresetId={activePresetId} currentSettings={currentSettings} hasUnsavedChanges={hasUnsavedChanges} isLoading={presetsLoading} onCreatePreset={createPreset} onUpdatePreset={updatePreset} onDeletePreset={deletePreset} onApplyPreset={applyPreset} onSetDefaultPreset={setDefaultPreset} onRenamePreset={renamePreset} /> {}} onSuccess={() => {}} />
{/* 하단 상세 테이블 영역 */}
) }