From 20800b214145ee6056f94ca18fa1054f145eb977 Mon Sep 17 00:00:00 2001 From: dujinkim Date: Wed, 28 May 2025 00:32:31 +0000 Subject: (대표님) lib 파트 커밋 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/procurement-rfqs/table/rfq-table.tsx | 411 +++++++++++++++++-------------- 1 file changed, 224 insertions(+), 187 deletions(-) (limited to 'lib/procurement-rfqs/table/rfq-table.tsx') diff --git a/lib/procurement-rfqs/table/rfq-table.tsx b/lib/procurement-rfqs/table/rfq-table.tsx index 23cd66fa..ca976172 100644 --- a/lib/procurement-rfqs/table/rfq-table.tsx +++ b/lib/procurement-rfqs/table/rfq-table.tsx @@ -1,44 +1,86 @@ "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 } from "react" +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 { useSearchParams } from "next/navigation" 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 { - data?: Awaited>; - onSelectRFQ?: (rfq: ProcurementRfqsView | null) => void; - onDataRefresh?: () => void; - maxHeight?: string | number; + promises: Promise<[Awaited>]> + className?: string; + calculatedHeight?: string; // 계산된 높이 추가 } export function RFQListTable({ - data, - onSelectRFQ, - onDataRefresh, - maxHeight + 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 [localData, setLocalData] = React.useState(data || { data: [], pageCount: 0, total: 0 }) - const [isMounted, setIsMounted] = React.useState(false) - + // 초기 설정 정의 const initialSettings = React.useMemo(() => ({ page: parseInt(searchParams.get('page') || '1'), @@ -70,39 +112,16 @@ export function RFQListTable({ deletePreset, setDefaultPreset, renamePreset, - updateClientState, getCurrentSettings, } = useTablePresets('rfq-list-table', initialSettings) - // 클라이언트 마운트 체크 - useEffect(() => { - setIsMounted(true) - }, []) - - // 데이터 변경 감지 - useEffect(() => { - setLocalData(data || { data: [], pageCount: 0, total: 0 }) - }, [data]) - // 비고 업데이트 함수 const updateRemark = async (rfqId: number, remark: string) => { try { - if (localData && localData.data) { - const rowIndex = localData.data.findIndex(row => row.id === rfqId); - if (rowIndex >= 0) { - const newData = [...localData.data]; - newData[rowIndex] = { ...newData[rowIndex], remark }; - setLocalData({ ...localData, data: newData }); - } - } - const result = await updateRfqRemark(rfqId, remark); if (result.success) { toast.success("비고가 업데이트되었습니다"); - if (onDataRefresh) { - onDataRefresh(); - } } else { toast.error(result.message || "업데이트 중 오류가 발생했습니다"); } @@ -117,9 +136,7 @@ export function RFQListTable({ if (rowAction) { switch (rowAction.type) { case "select": - if (onSelectRFQ) { - onSelectRFQ(rowAction.row.original) - } + setSelectedRfq(rowAction.row.original) break; case "update": console.log("Update rfq:", rowAction.row.original) @@ -130,7 +147,7 @@ export function RFQListTable({ } setRowAction(null) } - }, [rowAction, onSelectRFQ]) + }, [rowAction]) const columns = React.useMemo( () => getColumns({ @@ -198,7 +215,6 @@ export function RFQListTable({ // useDataTable 초기 상태 설정 const initialState = useMemo(() => { - console.log('Setting initial state:', currentSettings) return { sorting: initialSettings.sort.filter(sortItem => { const columnExists = columns.some(col => col.accessorKey === sortItem.id) @@ -209,167 +225,188 @@ export function RFQListTable({ } }, [currentSettings, initialSettings.sort, columns]) - // useDataTable 훅 설정 + // useDataTable 훅 설정 (PQ와 동일한 설정) const { table } = useDataTable({ - data: localData?.data || [], + data: tableData?.data || [], columns, - pageCount: localData?.pageCount || 0, - rowCount: localData?.total || 0, - filterFields: [], + pageCount: tableData?.pageCount || 0, + rowCount: tableData?.total || 0, + filterFields: [], // PQ와 동일하게 빈 배열 enablePinning: true, enableAdvancedFilter: true, initialState, getRowId: (originalRow) => String(originalRow.id), - shallow: false, + shallow: false, // PQ와 동일하게 false clearOnDefault: true, - columnResizeMode: "onEnd", }) - - // 테이블 상태 변경 감지 및 자동 저장 - const lastKnownStateRef = useRef<{ - columnVisibility: string - columnPinning: string - columnOrder: string[] - }>({ - columnVisibility: '{}', - columnPinning: '{"left":[],"right":[]}', - columnOrder: [] - }) - - const checkAndUpdateTableState = useCallback(() => { - if (!presetsLoading && !activePresetId) return - + + // 조회 버튼 클릭 핸들러 + const handleSearch = () => { + setIsFilterPanelOpen(false) + } + + // Get active basic filter count (PQ와 동일한 방식) + const getActiveBasicFilterCount = () => { try { - const currentVisibility = table.getState().columnVisibility - const currentPinning = table.getState().columnPinning - - // 컬럼 순서 가져오기 - const allColumns = table.getAllColumns() - const leftPinned = table.getLeftHeaderGroups()[0]?.headers.map(h => h.column.id) || [] - const rightPinned = table.getRightHeaderGroups()[0]?.headers.map(h => h.column.id) || [] - const center = table.getCenterHeaderGroups()[0]?.headers.map(h => h.column.id) || [] - const currentOrder = [...leftPinned, ...center, ...rightPinned] - - const visibilityString = JSON.stringify(currentVisibility) - const pinningString = JSON.stringify(currentPinning) - const orderString = JSON.stringify(currentOrder) - - // 실제 변경이 있을 때만 업데이트 - if ( - visibilityString !== lastKnownStateRef.current.columnVisibility || - pinningString !== lastKnownStateRef.current.columnPinning || - orderString !== JSON.stringify(lastKnownStateRef.current.columnOrder) - ) { - console.log('Table state changed, updating preset...') - - const newClientState = { - columnVisibility: currentVisibility, - columnOrder: currentOrder, - pinnedColumns: currentPinning, - } - - // 상태 업데이트 전에 기록 - lastKnownStateRef.current = { - columnVisibility: visibilityString, - columnPinning: pinningString, - columnOrder: currentOrder - } - - updateClientState(newClientState) - } - } catch (error) { - console.error('Error checking table state:', error) - } - }, [activePresetId, table, updateClientState, presetsLoading ]) - - // 주기적으로 테이블 상태 체크 - useEffect(() => { - if (!isMounted || !activePresetId) return - - console.log('Starting table state polling') - const intervalId = setInterval(checkAndUpdateTableState, 500) - - return () => { - clearInterval(intervalId) - console.log('Stopped table state polling') + const basicFilters = searchParams.get('basicFilters') + return basicFilters ? JSON.parse(basicFilters).length : 0 + } catch (e) { + return 0 } - }, [isMounted, activePresetId, checkAndUpdateTableState]) - - // 프리셋 적용 시 테이블 상태 업데이트 - useEffect(() => { - if (isMounted && activePresetId && currentSettings) { - const settings = currentSettings - console.log('Applying preset settings to table:', settings) - - const currentVisibility = table.getState().columnVisibility - const currentPinning = table.getState().columnPinning - - if ( - JSON.stringify(currentVisibility) !== JSON.stringify(settings.columnVisibility) || - JSON.stringify(currentPinning) !== JSON.stringify(settings.pinnedColumns) - ) { - console.log('Updating table state to match preset...') - - // 테이블 상태 업데이트 - table.setColumnVisibility(settings.columnVisibility) - table.setColumnPinning(settings.pinnedColumns) - - // 상태 저장소 업데이트 - lastKnownStateRef.current = { - columnVisibility: JSON.stringify(settings.columnVisibility), - columnPinning: JSON.stringify(settings.pinnedColumns), - columnOrder: settings.columnOrder || [] - } - } - } - }, [isMounted, activePresetId, currentSettings, table]) + } - // 로딩 중일 때는 스켈레톤 표시 - if (!isMounted) { - return ( -
-
- - 테이블 설정을 로드하는 중... + console.log(panelHeight) + + return ( +
+ {/* Filter Panel - 계산된 높이 적용 */} +
+ {/* Filter Content */} +
+ setIsFilterPanelOpen(false)} + onSearch={handleSearch} + isLoading={false} + />
- ) - } - - return ( -
- - + {/* Header Bar - 고정 높이 */} +
-
- {/* DB 기반 테이블 프리셋 매니저 */} - - presets={presets} - activePresetId={activePresetId} - currentSettings={currentSettings} - hasUnsavedChanges={hasUnsavedChanges} - isLoading={presetsLoading} - onCreatePreset={createPreset} - onUpdatePreset={updatePreset} - onDeletePreset={deletePreset} - onApplyPreset={applyPreset} - onSetDefaultPreset={setDefaultPreset} - onRenamePreset={renamePreset} - /> - - {/* 기존 툴바 액션들 */} - +
+ +
+ + {/* 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={() => {}} + /> +
+
+
+
+
+ + + + + {/* 하단 상세 테이블 영역 */} +
+ +
+
+
+
+
) } \ No newline at end of file -- cgit v1.2.3